mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:21:50 +08:00
151 lines
4.1 KiB
JavaScript
151 lines
4.1 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const { test } = require('node:test');
|
|
const {
|
|
createLocalExecutionContextRecipe,
|
|
createLocalTaskExecutionRevision,
|
|
} = require('@qinglong/runtime-core/local-dispatch');
|
|
const {
|
|
RunRepositoryConstraintError,
|
|
} = require('@qinglong/runtime-core/run-repository');
|
|
const {
|
|
migrateLocalSqlitePath,
|
|
openLocalSqliteRuntimeDatabase,
|
|
} = require('../dist');
|
|
|
|
async function fixture(t) {
|
|
const directory = fs.mkdtempSync(
|
|
path.join(os.tmpdir(), 'ql3-local-dispatch-store-'),
|
|
);
|
|
const databasePath = path.join(directory, 'qinglong3.sqlite');
|
|
await migrateLocalSqlitePath({ databasePath, profile: 'edge' });
|
|
const runtime = await openLocalSqliteRuntimeDatabase({
|
|
databasePath,
|
|
profile: 'edge',
|
|
});
|
|
t.after(async () => {
|
|
await runtime.close();
|
|
fs.rmSync(directory, { recursive: true, force: true });
|
|
});
|
|
return runtime;
|
|
}
|
|
|
|
function run(id, priority, queuedAtMs, cancelled = false) {
|
|
return {
|
|
id: `run-${id}`,
|
|
projectId: 'default',
|
|
taskId: 'task-1',
|
|
taskRevision: 'revision-1',
|
|
triggerType: 'manual',
|
|
executionOrigin: 'manual',
|
|
executionOwner: 'runtime',
|
|
status: 'queued',
|
|
version: 0,
|
|
eventSequence: 0,
|
|
priority,
|
|
createdAtMs: 1,
|
|
queuedAtMs,
|
|
...(cancelled ? { cancelRequestedAtMs: 2, cancelReason: 'user' } : {}),
|
|
};
|
|
}
|
|
|
|
function attempt(id, createdAtMs) {
|
|
return {
|
|
id: `attempt-${id}`,
|
|
runId: `run-${id}`,
|
|
attempt: 1,
|
|
status: 'claimed',
|
|
executorType: 'local_process',
|
|
callbackSequence: 0,
|
|
createdAtMs,
|
|
};
|
|
}
|
|
|
|
test('append-only definitions replay by content and reject identity conflicts', async (t) => {
|
|
const runtime = await fixture(t);
|
|
const recipe = createLocalExecutionContextRecipe({
|
|
environment: [{ name: 'VALUE', kind: 'public', value: 'one' }],
|
|
createdAtMs: 1,
|
|
});
|
|
assert.equal(
|
|
await runtime.localDispatch.appendLocalExecutionContextRecipe(recipe),
|
|
'inserted',
|
|
);
|
|
assert.equal(
|
|
await runtime.localDispatch.appendLocalExecutionContextRecipe({
|
|
...recipe,
|
|
createdAtMs: 2,
|
|
}),
|
|
'existing',
|
|
);
|
|
const revision = createLocalTaskExecutionRevision({
|
|
projectId: 'default',
|
|
taskId: 'task-1',
|
|
taskRevision: 'revision-1',
|
|
executorType: 'local_process',
|
|
command: { kind: 'argv', file: '/bin/echo', args: ['one'] },
|
|
contextRef: recipe.contextRef,
|
|
createdAtMs: 1,
|
|
});
|
|
assert.equal(
|
|
await runtime.localDispatch.appendLocalTaskExecutionRevision(revision),
|
|
'inserted',
|
|
);
|
|
assert.equal(
|
|
await runtime.localDispatch.appendLocalTaskExecutionRevision({
|
|
...revision,
|
|
createdAtMs: 2,
|
|
}),
|
|
'existing',
|
|
);
|
|
await assert.rejects(
|
|
runtime.localDispatch.appendLocalTaskExecutionRevision(
|
|
createLocalTaskExecutionRevision({
|
|
...revision,
|
|
command: { kind: 'argv', file: '/bin/echo', args: ['different'] },
|
|
}),
|
|
),
|
|
RunRepositoryConstraintError,
|
|
);
|
|
});
|
|
|
|
test('candidate pages are bounded, ordered and exclude cancellation intent', async (t) => {
|
|
const runtime = await fixture(t);
|
|
await runtime.runRepository.transaction(async (transaction) => {
|
|
for (const value of [
|
|
['a', 10, 2, 2, false],
|
|
['b', 10, 1, 3, false],
|
|
['c', 5, 1, 1, false],
|
|
['cancelled', 100, 1, 1, true],
|
|
]) {
|
|
await transaction.insertRun(run(value[0], value[1], value[2], value[4]));
|
|
await transaction.insertAttempt(attempt(value[0], value[3]));
|
|
}
|
|
});
|
|
const first = await runtime.localDispatch.listLocalDispatchCandidates({
|
|
limit: 2,
|
|
});
|
|
assert.deepEqual(
|
|
first.candidates.map(({ runId }) => runId),
|
|
['run-b', 'run-a'],
|
|
);
|
|
assert.equal(first.truncated, true);
|
|
const last = first.candidates.at(-1);
|
|
const second = await runtime.localDispatch.listLocalDispatchCandidates({
|
|
limit: 2,
|
|
after: {
|
|
priority: last.priority,
|
|
queuedAtMs: last.queuedAtMs,
|
|
attemptCreatedAtMs: last.attemptCreatedAtMs,
|
|
attemptId: last.attemptId,
|
|
},
|
|
});
|
|
assert.deepEqual(
|
|
second.candidates.map(({ runId }) => runId),
|
|
['run-c'],
|
|
);
|
|
assert.equal(second.truncated, false);
|
|
});
|