mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 08:05:22 +08:00
108 lines
3.6 KiB
JavaScript
108 lines
3.6 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 {
|
|
preparePanelClient,
|
|
loadPanelClient,
|
|
} = require('../../scripts/lib/ql3-panel-run-control-live-client.cjs');
|
|
|
|
function fixture(t, transport) {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-panel-live-client-'));
|
|
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
|
|
const directory = path.join(root, 'client');
|
|
const manifest = preparePanelClient(
|
|
path.resolve(__dirname, '../..'),
|
|
directory,
|
|
);
|
|
const loaded = loadPanelClient(directory, 12345, transport);
|
|
assert.equal(
|
|
loaded.auth.setQingLong3Credential(`ql3c_test_${'A'.repeat(43)}`),
|
|
true,
|
|
);
|
|
return { ...loaded, directory, manifest };
|
|
}
|
|
|
|
test('live adapter compiles actual sources and rejects modified generated modules', (t) => {
|
|
const f = fixture(t, () => assert.fail('no HTTP expected'));
|
|
assert.equal(
|
|
f.manifest.control.source,
|
|
'src/components/qinglong3/runControl.ts',
|
|
);
|
|
assert.match(f.manifest.control.sourceSha256, /^[a-f0-9]{64}$/);
|
|
fs.appendFileSync(path.join(f.directory, 'control.cjs'), '\n// modified');
|
|
assert.throws(
|
|
() => loadPanelClient(f.directory, 12345),
|
|
/Expected values to be strictly equal/,
|
|
);
|
|
});
|
|
|
|
test('live adapter constrains capability discovery to the isolated loopback server', async (t) => {
|
|
let calls = 0;
|
|
const f = fixture(t, async (url, options) => {
|
|
calls++;
|
|
assert.equal(url, 'http://127.0.0.1:12345/api/v3/capabilities');
|
|
assert.equal(options.credentials, 'omit');
|
|
assert.equal(options.redirect, 'error');
|
|
return new Response('{}', { status: 404 });
|
|
});
|
|
for (const url of [
|
|
'https://example.com/',
|
|
'//example.com/',
|
|
'/\\example.com/',
|
|
]) {
|
|
assert.equal(await f.auth.discoverQingLong3(url), null);
|
|
}
|
|
assert.equal(calls, 0);
|
|
assert.equal(await f.auth.discoverQingLong3('/api/v3/capabilities'), null);
|
|
assert.equal(calls, 1);
|
|
});
|
|
|
|
test('response loss consumes an accepted receipt before actual client retries the same cancellation', async (t) => {
|
|
let cancelled = 0;
|
|
const f = fixture(t, async (url, options) => {
|
|
assert.ok(
|
|
url.startsWith('http://127.0.0.1:12345/api/v3/projects/default/'),
|
|
);
|
|
if (options.method === 'GET')
|
|
return Response.json({
|
|
run: {
|
|
id: 'run:a',
|
|
projectId: 'default',
|
|
taskId: 'task:a',
|
|
taskRevision: '1',
|
|
status: 'running',
|
|
createdAtMs: 1,
|
|
},
|
|
});
|
|
cancelled++;
|
|
return Response.json(
|
|
{
|
|
schema: 'qinglong/run-cancellation@v1',
|
|
projectId: 'default',
|
|
runId: 'run:a',
|
|
status: cancelled === 1 ? 'accepted' : 'already_requested',
|
|
},
|
|
{ status: cancelled === 1 ? 202 : 200 },
|
|
);
|
|
});
|
|
const client = f.control.createPanelRunControl(
|
|
{ ql3: { projectId: 'default', taskId: 'task:a' } },
|
|
{
|
|
panel: { runControl: 'task_run_v1' },
|
|
limits: { logChunkBytes: 16384 },
|
|
},
|
|
);
|
|
const action = client.prepareCancel(await client.readRun('run:a'));
|
|
f.loseNextCancellationResponse();
|
|
await assert.rejects(action.execute(), (error) => error.uncertain === true);
|
|
assert.equal(cancelled, 1);
|
|
assert.equal((await action.execute()).status, 'already_requested');
|
|
const writes = f.requests.filter((r) => r.method === 'POST');
|
|
assert.equal(writes.length, 2);
|
|
assert.equal(writes[0].body, writes[1].body);
|
|
assert.equal(writes[0].path, writes[1].path);
|
|
assert.ok(!JSON.stringify(f.requests).includes('ql3c_'));
|
|
});
|