177 lines
5.1 KiB
TypeScript
177 lines
5.1 KiB
TypeScript
import { strict as assert } from 'node:assert';
|
|
import { describe, it } from 'node:test';
|
|
import { runCli } from '../src/cli';
|
|
|
|
interface HarnessResult {
|
|
code: number;
|
|
stdout: string;
|
|
stderr: string;
|
|
}
|
|
|
|
async function invoke(
|
|
args: string[],
|
|
fetchImpl: typeof fetch,
|
|
env: NodeJS.ProcessEnv = {},
|
|
): Promise<HarnessResult> {
|
|
let stdout = '';
|
|
let stderr = '';
|
|
const code = await runCli(args, {
|
|
env,
|
|
fetchImpl,
|
|
stdout: (value) => {
|
|
stdout += value;
|
|
},
|
|
stderr: (value) => {
|
|
stderr += value;
|
|
},
|
|
});
|
|
return { code, stdout, stderr };
|
|
}
|
|
|
|
describe('pig CLI JSON contract', () => {
|
|
it('emits exactly one machine JSON value and authenticates with the API key', async () => {
|
|
const requests: { url: string; authorization: string | null }[] = [];
|
|
const payload = {
|
|
id: '10000000-0000-4000-8000-000000000001',
|
|
name: 'Ada',
|
|
email: 'ada@example.com',
|
|
teams: [],
|
|
};
|
|
const fetchImpl: typeof fetch = async (input, init) => {
|
|
requests.push({
|
|
url: String(input),
|
|
authorization: new Headers(init?.headers).get('authorization'),
|
|
});
|
|
return new Response(JSON.stringify(payload), {
|
|
status: 200,
|
|
headers: { 'content-type': 'application/json' },
|
|
});
|
|
};
|
|
|
|
const result = await invoke(['--json', 'me'], fetchImpl, {
|
|
PIG_API_URL: 'https://pig.example/',
|
|
PIG_API_KEY: 'pig_test_secret',
|
|
});
|
|
|
|
assert.equal(result.code, 0);
|
|
assert.equal(result.stdout, `${JSON.stringify(payload)}\n`);
|
|
assert.equal(result.stderr, '');
|
|
assert.deepEqual(requests, [
|
|
{ url: 'https://pig.example/api/me', authorization: 'Bearer pig_test_secret' },
|
|
]);
|
|
});
|
|
|
|
it('writes structured API failures only to stderr and redacts echoed credentials', async () => {
|
|
const key = 'pig_should_never_print';
|
|
const fetchImpl: typeof fetch = async () =>
|
|
new Response(
|
|
JSON.stringify({
|
|
code: 'insufficient_scope',
|
|
error: `Credential ${key} cannot write.`,
|
|
}),
|
|
{ status: 403, headers: { 'content-type': 'application/json' } },
|
|
);
|
|
|
|
const result = await invoke(['--json', 'commitments'], fetchImpl, {
|
|
PIG_API_URL: 'https://pig.example',
|
|
PIG_API_KEY: key,
|
|
});
|
|
|
|
assert.equal(result.code, 1);
|
|
assert.equal(result.stdout, '');
|
|
assert.equal(result.stderr.includes(key), false);
|
|
assert.deepEqual(JSON.parse(result.stderr), {
|
|
error: {
|
|
code: 'insufficient_scope',
|
|
message: 'Credential [REDACTED] cannot write.',
|
|
status: 403,
|
|
details: {
|
|
code: 'insufficient_scope',
|
|
error: 'Credential [REDACTED] cannot write.',
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
it('returns JSON usage failures without making a request', async () => {
|
|
let called = false;
|
|
const fetchImpl: typeof fetch = async () => {
|
|
called = true;
|
|
return new Response('{}');
|
|
};
|
|
const result = await invoke(['--json', 'me'], fetchImpl);
|
|
|
|
assert.equal(result.code, 2);
|
|
assert.equal(result.stdout, '');
|
|
assert.deepEqual(JSON.parse(result.stderr), {
|
|
error: { code: 'invalid_usage', message: 'Set PIG_API_URL or pass --api-url.' },
|
|
});
|
|
assert.equal(called, false);
|
|
});
|
|
});
|
|
|
|
describe('pig CLI route decisions', () => {
|
|
it('uses the bounded release endpoint and sends an explicit JSON body', async () => {
|
|
const calls: { url: string; method: string | undefined; body: string | null | undefined }[] = [];
|
|
const fetchImpl: typeof fetch = async (input, init) => {
|
|
calls.push({ url: String(input), method: init?.method, body: init?.body?.toString() });
|
|
return new Response(JSON.stringify({ id: 'allocation-1', status: 'released' }), {
|
|
status: 200,
|
|
headers: { 'content-type': 'application/json' },
|
|
});
|
|
};
|
|
|
|
const result = await invoke(
|
|
['--json', 'allocations', 'release', 'allocation-1', '--reason', 'Customer changed scope'],
|
|
fetchImpl,
|
|
{ PIG_API_URL: 'https://pig.example', PIG_API_KEY: 'pig_writer' },
|
|
);
|
|
|
|
assert.equal(result.code, 0);
|
|
assert.deepEqual(calls, [
|
|
{
|
|
url: 'https://pig.example/api/allocations/allocation-1/release',
|
|
method: 'POST',
|
|
body: JSON.stringify({ reason: 'Customer changed scope' }),
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('maps capacity search flags to the public inventory query', async () => {
|
|
let requestedUrl = '';
|
|
const fetchImpl: typeof fetch = async (input) => {
|
|
requestedUrl = String(input);
|
|
return new Response('[]', {
|
|
status: 200,
|
|
headers: { 'content-type': 'application/json' },
|
|
});
|
|
};
|
|
|
|
const result = await invoke(
|
|
[
|
|
'--json',
|
|
'--api-url',
|
|
'https://pig.example',
|
|
'--api-key',
|
|
'pig_reader',
|
|
'capacity',
|
|
'search',
|
|
'--gpu-type',
|
|
'H100_80GB',
|
|
'--min-gpu-count',
|
|
'8',
|
|
'--fast-fabric',
|
|
'--limit',
|
|
'25',
|
|
],
|
|
fetchImpl,
|
|
);
|
|
|
|
assert.equal(result.code, 0);
|
|
assert.equal(
|
|
requestedUrl,
|
|
'https://pig.example/api/inventory?gpuType=H100_80GB&minGpuCount=8&fastFabric=true&limit=25',
|
|
);
|
|
});
|
|
});
|