test(ql3): exercise actual panel client in Linux runtime gate

This commit is contained in:
whyour
2026-09-04 04:49:45 +08:00
parent e4ba5d405f
commit e684a6e64a
11 changed files with 423 additions and 72 deletions
@@ -11,7 +11,7 @@ const {
function report(profile = 'edge') {
return {
schemaVersion: 1,
schemaVersion: 2,
profile,
platform: { os: 'linux', architecture: 'arm64', procfs: true },
resourceEnvelope: {
@@ -36,6 +36,21 @@ function report(profile = 'edge') {
physicalDevice: false,
passed: true,
},
panelClient: {
authSourceSha256: 'a'.repeat(64),
controlSourceSha256: 'b'.repeat(64),
unauthenticatedStatus: 401,
capabilityDiscovered: true,
runListed: true,
logMarkerObserved: true,
restartLogMarkerObserved: true,
cancellationResponseLost: true,
exactCancellationBodyReplay: true,
startPosts: 1,
cancellationPosts: 2,
browserRendering: false,
ownerProvisioning: 'seeded_fixture',
},
compatible: true,
artifact: {
profile: `${profile}-application-api`,
@@ -121,3 +136,29 @@ test('requires a fresh private report before Docker opt-in is checked', () => {
fs.rmSync(root, { recursive: true, force: true });
}
});
test('rejects old reports and every missing or overstated panel client observation', () => {
const old = report();
old.schemaVersion = 1;
delete old.panelClient;
assert.equal(validateLocalApiCancellationLiveReport(old).compatible, false);
for (const key of Object.keys(report().panelClient)) {
const missing = report();
delete missing.panelClient[key];
assert.equal(
validateLocalApiCancellationLiveReport(missing).compatible,
false,
key,
);
const invalid = report();
invalid.panelClient[key] =
typeof invalid.panelClient[key] === 'boolean'
? !invalid.panelClient[key]
: null;
assert.equal(
validateLocalApiCancellationLiveReport(invalid).compatible,
false,
key,
);
}
});
@@ -0,0 +1,107 @@
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_'));
});