Files
qinglong/cli/test/remote/scope.test.cjs
T
whyour 801a71d740 feat(cli): cover OpenAPI with a remote npm CLI and internal panel tools (#3074)
* feat(cli): add unified Commander CLI for QingLong 2.x

* fix(cli): publish via npm and address security review feedback

* ci(cli): package npm artifacts and remove evaluation collateral

* test(cli): use a fixed shell fixture for log retention

* refactor(cli): separate remote npm client from panel tools

* feat(cli): cover active panel OpenAPI resources

* docs(cli): unify authentication and skill guidance

* refactor(cli): isolate internal commands and generate Commander help

* refactor(cli): organize remote and internal modules by responsibility

* ci(cli): publish verified npm archives from master

* fix(cli): publish under the whyour npm scope

* ci: use npm trusted publishing for both packages

* docs: introduce the published CLI on the project homepage

* fix(cli): preserve server log truncation and correct login hints

* fix(cli): accept dashboard record request bodies

* fix(cli): preserve stdin for local task execution

* fix(cli): resolve task executables after changing directory

* fix(cli): preserve shell function tasks and sanitize test failures

* fix(cli): preserve shell hook state and resolve workdir after hooks

* fix(cli): preserve cleanup across shared shell task timeouts

* fix(cli): isolate shell control descriptors and reap timed-out descendants
2026-09-25 23:24:41 +08:00

52 lines
2.8 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs/promises');
const path = require('node:path');
const os = require('node:os');
const http = require('node:http');
const { spawnSync } = require('node:child_process');
const { inspectPanel } = require('../../dist/internal/maintenance/check');
test('public task entries retain no-argument script discovery and help stays read-only', async (t) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'ql-public-inventory-'));
t.after(() => fs.rm(root, { recursive: true, force: true }));
await fs.mkdir(path.join(root, 'data/scripts'), { recursive: true });
await fs.writeFile(path.join(root, 'data/scripts/job.js'), 'throw Error("do not execute"); const $ = new Env("Job");');
for (const [entry, args] of [['task', []], ['ql', ['task']]]) {
const run = (...extra) => spawnSync(process.execPath, [path.resolve(__dirname, `../../dist/${entry}.js`), ...args, ...extra, '--json'], { env: { PATH: process.env.PATH, QL_DIR: root }, encoding: 'utf8' });
const listing = run();
assert.equal(listing.status, 0, listing.stderr);
assert.deepEqual(JSON.parse(listing.stdout).data.scripts, [{ file: 'job.js', name: 'Job' }]);
assert.equal(JSON.parse(run('--help').stdout).data.scripts, undefined);
}
});
test('removed development publication is rejected before doing any work', () => {
const result = spawnSync(process.execPath, [path.resolve(__dirname, '../../dist/ql.js'), 'dev', 'release', '--root', '/absent', '--json'], { encoding: 'utf8' });
assert.equal(result.status, 2);
assert.equal(result.stdout, '');
assert.equal(JSON.parse(result.stderr).code, 2);
});
test('health probes use the base path and reject failed or malformed scheduler health', async (t) => {
let health = { status: 503, body: { code: 503, data: { status: 'error' } } };
const seen = [];
const server = http.createServer((req, res) => {
seen.push(req.url);
if (req.url.startsWith('/ql/api/health?')) {
res.statusCode = health.status;
res.end(JSON.stringify(health.body));
} else if (req.url === '/ql/') res.end('<div id="root"></div>');
else res.end('{"code":200,"data":{"version":"2.21.0"}}');
});
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
t.after(() => new Promise(resolve => server.close(resolve)));
const context = { env: { QlPort: String(server.address().port), QlBaseUrl: '/ql/' } };
assert.equal((await inspectPanel(context)).backend.healthy, false);
health = { status: 200, body: { code: 200, data: {} } };
assert.equal((await inspectPanel(context)).backend.healthy, false);
health = { status: 200, body: { code: 200, data: { status: 'ok' } } };
assert.equal((await inspectPanel(context)).backend.healthy, true);
assert.equal(seen.some(route => route.includes('/api/system')), false);
});