This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
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',
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user