feat: rebuild warm operations workbench

This commit is contained in:
Codex
2026-07-30 14:49:33 +08:00
parent 25c853dea8
commit 570edf6bb2
88 changed files with 13413 additions and 2250 deletions
@@ -104,8 +104,13 @@ describe('safe operation client', () => {
]);
});
it('refuses uncatalogued operations and mismatched schemas without a request', async () => {
const fetcher = vi.fn(async () => response(page));
it('refuses uncatalogued operations and mismatched schemas without a prepare request', async () => {
const fetcher = vi.fn(async (url: string) => {
if (String(url).includes('search=hiddenOp')) {
return response({ items: [], page: { page: 1, pageSize: 100, total: 0 } });
}
return response(page);
});
const client = createOperationClient(fetcher as typeof fetch);
await client.list();
@@ -123,7 +128,52 @@ describe('safe operation client', () => {
parameters: { parameterSchemaId: 'wrong', fields: [] },
}),
).rejects.toThrow('Operation parameter schema does not match the catalog.');
expect(fetcher).toHaveBeenCalledOnce();
// list + one search hydrate for the missing op; no prepare POST.
expect(fetcher.mock.calls.every((call) => !String(call[0]).includes('/prepare'))).toBe(true);
});
it('hydrates late-sorted restart ops when the first pageSize=100 page omits them', async () => {
const restartEntry = {
operationId: 'postServiceRestart',
title: 'Restart Service',
risk: 'R3',
capability: 'job',
batchable: false,
parameterSchemaId: 'simadmin.58e2204.postServiceRestart.parameters.v1',
};
const firstPage = {
items: [entry],
page: { page: 1, pageSize: 100, total: 117 },
};
const searchPage = {
items: [restartEntry],
page: { page: 1, pageSize: 100, total: 1 },
};
const restartPreparation = {
...preparation,
operationId: 'postServiceRestart',
risk: 'R3',
confirmationPrompt: 'Confirm restart service',
};
const fetcher = vi
.fn()
.mockResolvedValueOnce(response(firstPage))
.mockResolvedValueOnce(response(searchPage))
.mockResolvedValueOnce(response(restartPreparation));
const client = createOperationClient(fetcher as typeof fetch);
await client.list({ pageSize: 100 });
const result = await client.prepare({
operationId: 'postServiceRestart',
targets: [{ instanceId: 'instance-1', revision: 1 }],
parameters: {
parameterSchemaId: 'simadmin.58e2204.postServiceRestart.parameters.v1',
fields: [],
},
});
expect(result.operationId).toBe('postServiceRestart');
expect(String(fetcher.mock.calls[1]?.[0])).toContain('search=postServiceRestart');
expect(fetcher.mock.calls[2]?.[0]).toBe('/api/v1/operations/prepare');
});
it('executes with the in-memory token exactly once and validates public Job fields', async () => {