Initial Commit
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
|
||||
describe("network-plugin-assignment dep-graph", () => {
|
||||
it("loads index.js (acyclic static imports)", async () => {
|
||||
const mod = await import("../src/index.js");
|
||||
expect(mod.MeshCapacityTracker).toBeDefined();
|
||||
expect(mod.AssignmentEngine).toBeDefined();
|
||||
expect(mod.MeshBalancer).toBeDefined();
|
||||
expect(mod.AdmissionController).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import {
|
||||
AdmissionController,
|
||||
AssignmentEngine,
|
||||
MeshBalancer,
|
||||
MeshCapacityTracker,
|
||||
type MeshDescriptor,
|
||||
type PeerInfo,
|
||||
} from "../../src/index.js";
|
||||
|
||||
describe("assignment end-to-end", () => {
|
||||
it("admits peers, builds a rebalance plan, and applies moves", () => {
|
||||
const tracker = new MeshCapacityTracker();
|
||||
tracker.recordMesh("mesh-A", 10);
|
||||
tracker.recordMesh("mesh-B", 10);
|
||||
|
||||
const meshes: MeshDescriptor[] = [
|
||||
{ meshId: "mesh-A", region: "us-east", tierFloor: 0 },
|
||||
{ meshId: "mesh-B", region: "us-west", tierFloor: 0 },
|
||||
];
|
||||
|
||||
const engine = new AssignmentEngine({ tracker });
|
||||
const controller = new AdmissionController({
|
||||
engine,
|
||||
tracker,
|
||||
meshes: () => meshes,
|
||||
});
|
||||
|
||||
// Force imbalance by manually loading mesh-A.
|
||||
for (let i = 0; i < 8; i++) tracker.recordJoin("mesh-A", `p${i}`);
|
||||
|
||||
const balancer = new MeshBalancer({
|
||||
tracker,
|
||||
imbalanceThreshold: 0.3,
|
||||
maxMovesPerPlan: 2,
|
||||
});
|
||||
const plan = balancer.buildPlan((overloaded) => {
|
||||
expect(overloaded).toBe("mesh-A");
|
||||
return ["p0", "p1"];
|
||||
});
|
||||
expect(plan.moves).toHaveLength(2);
|
||||
for (const move of plan.moves) {
|
||||
tracker.recordLeave(move.fromMeshId, move.peerId);
|
||||
tracker.recordJoin(move.toMeshId, move.peerId);
|
||||
}
|
||||
expect(tracker.getUtilization("mesh-A")).toBe(0.6);
|
||||
expect(tracker.getUtilization("mesh-B")).toBe(0.2);
|
||||
|
||||
// A fresh peer can still be admitted via the controller.
|
||||
const fresh: PeerInfo = {
|
||||
peerId: "alice",
|
||||
latencyMap: new Map([
|
||||
["mesh-A", 50],
|
||||
["mesh-B", 50],
|
||||
]),
|
||||
region: "us-east",
|
||||
tierEligibility: 1,
|
||||
capabilities: "STANDARD",
|
||||
};
|
||||
const result = controller.admit(fresh);
|
||||
expect(["mesh-A", "mesh-B"]).toContain(result.meshId);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import * as fc from "fast-check";
|
||||
import {
|
||||
AssignmentEngine,
|
||||
MeshCapacityTracker,
|
||||
type MeshDescriptor,
|
||||
type PeerInfo,
|
||||
} from "../../src/index.js";
|
||||
|
||||
describe("AssignmentEngine scoring is deterministic", () => {
|
||||
it("same input twice produces identical scores", () => {
|
||||
fc.assert(
|
||||
fc.property(
|
||||
fc.array(
|
||||
fc.tuple(
|
||||
fc.string({ minLength: 1, maxLength: 6 }),
|
||||
fc.integer({ min: 1, max: 100 }),
|
||||
fc.integer({ min: 0, max: 5 })
|
||||
),
|
||||
{ minLength: 1, maxLength: 5 }
|
||||
),
|
||||
fc.integer({ min: 0, max: 5 }),
|
||||
(entries, tierEligibility) => {
|
||||
const seen = new Set<string>();
|
||||
const meshes: MeshDescriptor[] = [];
|
||||
const latencyMap = new Map<string, number>();
|
||||
for (const [name, latency, tierFloor] of entries) {
|
||||
if (seen.has(name)) continue;
|
||||
seen.add(name);
|
||||
meshes.push({ meshId: name, tierFloor });
|
||||
latencyMap.set(name, latency);
|
||||
}
|
||||
if (meshes.length === 0) return;
|
||||
const peer: PeerInfo = {
|
||||
peerId: "p",
|
||||
latencyMap,
|
||||
tierEligibility,
|
||||
capabilities: "STANDARD",
|
||||
};
|
||||
const tracker = new MeshCapacityTracker();
|
||||
for (const m of meshes) tracker.recordMesh(m.meshId, 10);
|
||||
const engine = new AssignmentEngine({ tracker });
|
||||
const a = engine.score(peer, meshes);
|
||||
const b = engine.score(peer, meshes);
|
||||
expect(a.map((s) => s.score)).toEqual(
|
||||
b.map((s) => s.score)
|
||||
);
|
||||
}
|
||||
),
|
||||
{ numRuns: 50 }
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
// Ensure `globalThis.crypto` is available in bare-Node vitest runs.
|
||||
import { webcrypto } from "node:crypto";
|
||||
|
||||
type WritableGlobal = { crypto?: Crypto };
|
||||
const g = globalThis as WritableGlobal;
|
||||
if (g.crypto === undefined) {
|
||||
g.crypto = webcrypto as unknown as Crypto;
|
||||
}
|
||||
Reference in New Issue
Block a user