mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:32:44 +08:00
165 lines
4.2 KiB
JavaScript
165 lines
4.2 KiB
JavaScript
require('ts-node/register/transpile-only');
|
|
|
|
const assert = require('node:assert/strict');
|
|
const { test } = require('node:test');
|
|
const {
|
|
matchesWorkerPlacement,
|
|
normalizeWorkerPlacementSpec,
|
|
selectWorkerCandidates,
|
|
} = require('../../back/runtime/domain/workerPlacement');
|
|
|
|
const NOW = 10_000;
|
|
|
|
function worker(id, overrides = {}) {
|
|
return {
|
|
id,
|
|
sessionId: '019f7700-0000-7000-8000-000000000001',
|
|
generation: 1,
|
|
status: 'online',
|
|
version: 0,
|
|
capabilities: {
|
|
architecture: 'arm64',
|
|
operatingSystem: 'linux',
|
|
executors: ['local_process'],
|
|
runtimes: [{ name: 'node', version: '24.14.0' }],
|
|
labels: { region: 'cn-east', storage: 'ssd' },
|
|
capacity: {
|
|
cpuCores: 4,
|
|
memoryBytes: 8 * 1024 * 1024 * 1024,
|
|
diskBytes: 100 * 1024 * 1024 * 1024,
|
|
gpu: [{ vendor: 'nvidia', model: 'T4', memoryBytes: 16_000_000_000 }],
|
|
},
|
|
features: ['direct_file_log'],
|
|
},
|
|
capabilitiesHash: 'a'.repeat(64),
|
|
maxConcurrentRuns: 4,
|
|
availableSlots: 2,
|
|
registeredAtMs: 1,
|
|
lastHeartbeatAtMs: 9_000,
|
|
leaseExpiresAtMs: 20_000,
|
|
updatedAtMs: 9_000,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('matches every required capability including semantic runtime ranges', () => {
|
|
const placement = {
|
|
required: {
|
|
architectures: ['arm64'],
|
|
operatingSystems: ['linux'],
|
|
executors: ['local_process'],
|
|
runtimes: [{ name: 'node', versionRange: '>=24 <25' }],
|
|
labels: { storage: 'ssd' },
|
|
minMemoryBytes: 4 * 1024 * 1024 * 1024,
|
|
minDiskBytes: 50 * 1024 * 1024 * 1024,
|
|
gpuVendor: 'nvidia',
|
|
features: ['direct_file_log'],
|
|
},
|
|
};
|
|
assert.deepEqual(matchesWorkerPlacement(worker('worker-a'), placement, NOW), {
|
|
matches: true,
|
|
score: 0,
|
|
mismatches: [],
|
|
});
|
|
|
|
const mismatch = matchesWorkerPlacement(
|
|
worker('worker-b', {
|
|
capabilities: {
|
|
...worker('worker-b').capabilities,
|
|
architecture: 'x64',
|
|
executors: [],
|
|
runtimes: [{ name: 'node', version: '22.14.0' }],
|
|
labels: {},
|
|
capacity: {},
|
|
features: [],
|
|
},
|
|
}),
|
|
placement,
|
|
NOW,
|
|
);
|
|
assert.equal(mismatch.matches, false);
|
|
assert.deepEqual(mismatch.mismatches, [
|
|
'architecture',
|
|
'executor',
|
|
'runtime',
|
|
'label',
|
|
'memory',
|
|
'disk',
|
|
'gpu',
|
|
'feature',
|
|
]);
|
|
});
|
|
|
|
test('excludes unavailable Workers and ranks preferences deterministically', () => {
|
|
const placement = {
|
|
required: { architectures: ['arm64'] },
|
|
preferred: [
|
|
{ labels: { region: 'cn-east' }, weight: 50 },
|
|
{ labels: { storage: 'ssd' }, weight: 20 },
|
|
],
|
|
};
|
|
const selected = selectWorkerCandidates(
|
|
[
|
|
worker('worker-c', {
|
|
capabilities: {
|
|
...worker('worker-c').capabilities,
|
|
labels: { region: 'cn-west', storage: 'ssd' },
|
|
},
|
|
availableSlots: 4,
|
|
}),
|
|
worker('worker-b', { availableSlots: 3 }),
|
|
worker('worker-a', { availableSlots: 1 }),
|
|
worker('worker-expired', { leaseExpiresAtMs: NOW }),
|
|
worker('worker-draining', { status: 'draining', availableSlots: 0 }),
|
|
],
|
|
placement,
|
|
NOW,
|
|
);
|
|
assert.deepEqual(
|
|
selected.map((candidate) => [candidate.worker.id, candidate.score]),
|
|
[
|
|
['worker-b', 70],
|
|
['worker-a', 70],
|
|
['worker-c', 20],
|
|
],
|
|
);
|
|
});
|
|
|
|
test('rejects ambiguous or unbounded placement input', () => {
|
|
assert.throws(
|
|
() =>
|
|
normalizeWorkerPlacementSpec({
|
|
required: { runtimes: [{ name: 'node', versionRange: 'not a range' }] },
|
|
}),
|
|
/not semver/,
|
|
);
|
|
assert.throws(
|
|
() => normalizeWorkerPlacementSpec({ required: { command: 'secret' } }),
|
|
/unknown field/,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
selectWorkerCandidates(
|
|
Array.from({ length: 65 }, (_, index) => worker(`worker-${index}`)),
|
|
{},
|
|
NOW,
|
|
),
|
|
/at most 64/,
|
|
);
|
|
});
|
|
|
|
test('normalizes one placement snapshot for a bounded candidate page', () => {
|
|
const placement = {
|
|
required: { architectures: ['arm64'] },
|
|
};
|
|
const normalized = normalizeWorkerPlacementSpec(placement);
|
|
assert.deepEqual(
|
|
selectWorkerCandidates(
|
|
[worker('worker-b'), worker('worker-a')],
|
|
normalized,
|
|
NOW,
|
|
).map(({ worker: candidate }) => candidate.id),
|
|
['worker-a', 'worker-b'],
|
|
);
|
|
});
|