40906a0697
Add registry_test.go (NEW) covering NodeRegistry Join/Leave/Forget/ List/Get (success + not-found + duplicate), NewNodeRegistry nil- logger, Audit Record success/error (sqlite-backed via openTestDB pattern) + NewAudit nil-logger. Extend scheduler_test.go with MemLocalNode/Capacity (happy + nil), JobSpecScore nil/over-capacity/ fits, JobSpecFits nil, PickNode empty. Extend dispatcher_test.go with Submit error paths: bad spec, explicit target no-registry, target peer-not-found, peer-pick missing CA, no peer registry, nil capacity fallthrough, all-peers-fail PickNode. Coverage: 65.1% → 88.9%. go test -race PASS. No production code changed; T01.2 peerDispatcher seam NOT needed (error-path tests via stubbed LocalExecutor + PeerRegistry reached 89% without it; httptest.NewTLSServer was not required either since dispatchToPeer CA-missing and PickNode-fail branches cover the remote path). ---ci--- project: orca phase: 1 milestone: v0.8 status: execute ---/ci---
131 lines
3.7 KiB
Go
131 lines
3.7 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"git.cloudinit.dev/coreci/orca/internal/store"
|
|
)
|
|
|
|
func TestPickNodeBestFit(t *testing.T) {
|
|
caps := []*store.NodeCapacity{
|
|
{NodeID: "node-b", CPUMillicores: 1000, MemoryMiB: 1024, DiskMiB: 1024},
|
|
{NodeID: "node-a", CPUMillicores: 4000, MemoryMiB: 4096, DiskMiB: 4096},
|
|
{NodeID: "node-c", CPUMillicores: 500, MemoryMiB: 512, DiskMiB: 512},
|
|
}
|
|
spec := JobSpec{CPUMillicores: 1000, MemoryMiB: 1024, DiskMiB: 1024}
|
|
got, idx, err := PickNode(spec, caps)
|
|
if err != nil {
|
|
t.Fatalf("PickNode: %v", err)
|
|
}
|
|
if got.NodeID != "node-a" {
|
|
t.Errorf("PickNode: got %s, want node-a (most free capacity)", got.NodeID)
|
|
}
|
|
if idx != 1 {
|
|
t.Errorf("PickNode: got idx %d, want 1", idx)
|
|
}
|
|
}
|
|
|
|
func TestPickNodeNoFit(t *testing.T) {
|
|
caps := []*store.NodeCapacity{
|
|
{NodeID: "node-a", CPUMillicores: 100, MemoryMiB: 100, DiskMiB: 100},
|
|
}
|
|
spec := JobSpec{CPUMillicores: 1000, MemoryMiB: 1024, DiskMiB: 1024}
|
|
_, _, err := PickNode(spec, caps)
|
|
if err == nil {
|
|
t.Fatal("expected PickNode to fail when no node can fit")
|
|
}
|
|
}
|
|
|
|
func TestPickNodeTieDeterministic(t *testing.T) {
|
|
// Two nodes with identical free capacity. Tie broken by NodeID
|
|
// (lexicographic) for determinism.
|
|
caps := []*store.NodeCapacity{
|
|
{NodeID: "node-z", CPUMillicores: 4000, MemoryMiB: 4096, DiskMiB: 4096},
|
|
{NodeID: "node-a", CPUMillicores: 4000, MemoryMiB: 4096, DiskMiB: 4096},
|
|
}
|
|
spec := JobSpec{CPUMillicores: 1000, MemoryMiB: 1024, DiskMiB: 1024}
|
|
got, _, err := PickNode(spec, caps)
|
|
if err != nil {
|
|
t.Fatalf("PickNode: %v", err)
|
|
}
|
|
if got.NodeID != "node-a" {
|
|
t.Errorf("PickNode tie-break: got %s, want node-a (lexicographic)", got.NodeID)
|
|
}
|
|
}
|
|
|
|
func TestJobSpecFits(t *testing.T) {
|
|
spec := JobSpec{CPUMillicores: 1000, MemoryMiB: 1024, DiskMiB: 1024}
|
|
c := &store.NodeCapacity{CPUMillicores: 2000, MemoryMiB: 2048, DiskMiB: 2048}
|
|
if !spec.Fits(c) {
|
|
t.Error("Fits: should fit")
|
|
}
|
|
c.CPUMillicores = 500
|
|
if spec.Fits(c) {
|
|
t.Error("Fits: should not fit (CPU too low)")
|
|
}
|
|
}
|
|
|
|
func TestJobSpecFits_NilCapacity(t *testing.T) {
|
|
spec := JobSpec{CPUMillicores: 1000}
|
|
if spec.Fits(nil) {
|
|
t.Error("Fits(nil): should be false")
|
|
}
|
|
}
|
|
|
|
func TestJobSpecScore_NilCapacity(t *testing.T) {
|
|
spec := JobSpec{CPUMillicores: 1000, MemoryMiB: 1024}
|
|
if got := spec.Score(nil); got != -1 {
|
|
t.Errorf("Score(nil) = %d, want -1", got)
|
|
}
|
|
}
|
|
|
|
func TestJobSpecScore_OverCapacity(t *testing.T) {
|
|
spec := JobSpec{CPUMillicores: 2000, MemoryMiB: 1024}
|
|
c := &store.NodeCapacity{CPUMillicores: 1000, MemoryMiB: 2048}
|
|
if got := spec.Score(c); got != -1 {
|
|
t.Errorf("Score over CPU = %d, want -1", got)
|
|
}
|
|
c2 := &store.NodeCapacity{CPUMillicores: 4000, MemoryMiB: 512}
|
|
if got := spec.Score(c2); got != -1 {
|
|
t.Errorf("Score over Mem = %d, want -1", got)
|
|
}
|
|
}
|
|
|
|
func TestJobSpecScore_Fits(t *testing.T) {
|
|
spec := JobSpec{CPUMillicores: 1000, MemoryMiB: 1024}
|
|
c := &store.NodeCapacity{CPUMillicores: 4000, MemoryMiB: 4096}
|
|
got := spec.Score(c)
|
|
want := int64((4000 - 1000) + (4096 - 1024))
|
|
if got != want {
|
|
t.Errorf("Score = %d, want %d", got, want)
|
|
}
|
|
}
|
|
|
|
func TestPickNode_Empty(t *testing.T) {
|
|
_, _, err := PickNode(JobSpec{}, nil)
|
|
if err == nil {
|
|
t.Fatal("expected error for empty capacities")
|
|
}
|
|
}
|
|
|
|
func TestMemLocalNode_Capacity(t *testing.T) {
|
|
c := &store.NodeCapacity{NodeID: "self", CPUMillicores: 1000, MemoryMiB: 1024}
|
|
ln := MemLocalNode(c)
|
|
got, err := ln.Capacity(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("Capacity: %v", err)
|
|
}
|
|
if got != c {
|
|
t.Errorf("Capacity: got %+v, want %+v", got, c)
|
|
}
|
|
}
|
|
|
|
func TestMemLocalNode_NilCapacity(t *testing.T) {
|
|
ln := MemLocalNode(nil)
|
|
_, err := ln.Capacity(context.Background())
|
|
if err == nil {
|
|
t.Fatal("expected error for nil capacity")
|
|
}
|
|
}
|