test: add unit tests for runtime-internal.controller (API-AI-067)
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 43s
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 43s
- 12 tests covering all 9 endpoints + instanceId extraction - instanceId: from header / default "unknown" - pollJobs: limit default 5, capabilities pass-through - lockJob/heartbeatJob: runtimeInstanceId fallback from dto→header - getSnapshot: direct delegation - resolveCredential: all fields + undefined credentialId - submitResult/submitFailure: entire dto pass-through - submitInvocationLogs: logs array extraction Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
5fbd437232
commit
3e43b2b52d
@ -0,0 +1,191 @@
|
||||
import { RuntimeInternalController } from './runtime-internal.controller';
|
||||
|
||||
describe('RuntimeInternalController', () => {
|
||||
let controller: RuntimeInternalController;
|
||||
let mockService: any;
|
||||
|
||||
beforeEach(() => {
|
||||
mockService = {
|
||||
pollJobs: jest.fn(),
|
||||
lockJob: jest.fn(),
|
||||
heartbeatJob: jest.fn(),
|
||||
getSnapshot: jest.fn(),
|
||||
resolveCredential: jest.fn(),
|
||||
submitResult: jest.fn(),
|
||||
submitFailure: jest.fn(),
|
||||
submitInvocationLogs: jest.fn(),
|
||||
};
|
||||
controller = new RuntimeInternalController(mockService);
|
||||
});
|
||||
|
||||
const req = (instanceId?: string) =>
|
||||
({ headers: { 'x-runtime-instance-id': instanceId } }) as any;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// instanceId extraction
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
describe('instanceId extraction', () => {
|
||||
it('extracts x-runtime-instance-id from header', async () => {
|
||||
const dto = { supportedJobTypes: ['quiz_generation'], limit: 10, capabilities: {} };
|
||||
mockService.pollJobs.mockResolvedValue({ jobs: [] });
|
||||
|
||||
await controller.pollJobs(req('rt-001'), dto as any);
|
||||
|
||||
expect(mockService.pollJobs).toHaveBeenCalledWith(
|
||||
'rt-001', ['quiz_generation'], 10, {},
|
||||
);
|
||||
});
|
||||
|
||||
it('falls back to "unknown" when header is missing', async () => {
|
||||
const dto = { supportedJobTypes: ['quiz_generation'] };
|
||||
mockService.pollJobs.mockResolvedValue({ jobs: [] });
|
||||
|
||||
await controller.pollJobs(req(undefined), dto as any);
|
||||
|
||||
expect(mockService.pollJobs).toHaveBeenCalledWith(
|
||||
'unknown', ['quiz_generation'], 5, undefined,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Poll
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
describe('pollJobs', () => {
|
||||
it('delegates with defaults (limit=5 when undefined)', async () => {
|
||||
const dto = { supportedJobTypes: ['learning_state_analysis'], limit: undefined };
|
||||
mockService.pollJobs.mockResolvedValue({ jobs: [] });
|
||||
|
||||
await controller.pollJobs(req('rt-1'), dto as any);
|
||||
|
||||
expect(mockService.pollJobs).toHaveBeenCalledWith('rt-1', ['learning_state_analysis'], 5, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Lock
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
describe('lockJob', () => {
|
||||
it('uses dto.runtimeInstanceId when provided', async () => {
|
||||
const dto = { runtimeInstanceId: 'rt-custom' };
|
||||
mockService.lockJob.mockResolvedValue({ status: 'locked' });
|
||||
|
||||
await controller.lockJob(req('rt-head'), 'j1', dto);
|
||||
|
||||
expect(mockService.lockJob).toHaveBeenCalledWith('j1', 'rt-custom');
|
||||
});
|
||||
|
||||
it('falls back to header instanceId', async () => {
|
||||
const dto = { runtimeInstanceId: undefined as any };
|
||||
mockService.lockJob.mockResolvedValue({ status: 'locked' });
|
||||
|
||||
await controller.lockJob(req('rt-head'), 'j1', dto);
|
||||
|
||||
expect(mockService.lockJob).toHaveBeenCalledWith('j1', 'rt-head');
|
||||
});
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Heartbeat
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
describe('heartbeatJob', () => {
|
||||
it('delegates with instanceId fallback', async () => {
|
||||
const dto = { runtimeInstanceId: undefined as any };
|
||||
mockService.heartbeatJob.mockResolvedValue({ lockUntil: 123, cancelRequested: false });
|
||||
|
||||
await controller.heartbeatJob(req('rt-hb'), 'j1', dto);
|
||||
|
||||
expect(mockService.heartbeatJob).toHaveBeenCalledWith('j1', 'rt-hb');
|
||||
});
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Snapshot
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
describe('getSnapshot', () => {
|
||||
it('delegates to service.getSnapshot', async () => {
|
||||
mockService.getSnapshot.mockResolvedValue({ snapshotId: 's1' });
|
||||
|
||||
const result = await controller.getSnapshot('j1');
|
||||
|
||||
expect(mockService.getSnapshot).toHaveBeenCalledWith('j1');
|
||||
expect(result).toEqual({ snapshotId: 's1' });
|
||||
});
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Credential Resolve
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
describe('resolveCredential', () => {
|
||||
it('delegates with all fields', async () => {
|
||||
const dto = { jobId: 'j1', apiKeyMode: 'user_deepseek_key', provider: 'deepseek', credentialId: 'c1' };
|
||||
mockService.resolveCredential.mockResolvedValue({ apiKey: 'sk-xx' });
|
||||
|
||||
await controller.resolveCredential(dto as any);
|
||||
|
||||
expect(mockService.resolveCredential).toHaveBeenCalledWith('j1', 'user_deepseek_key', 'deepseek', 'c1');
|
||||
});
|
||||
|
||||
it('passes undefined credentialId when omitted', async () => {
|
||||
const dto = { jobId: 'j1', apiKeyMode: 'platform_key', provider: 'deepseek' };
|
||||
mockService.resolveCredential.mockResolvedValue({ apiKey: '' });
|
||||
|
||||
await controller.resolveCredential(dto as any);
|
||||
|
||||
expect(mockService.resolveCredential).toHaveBeenCalledWith('j1', 'platform_key', 'deepseek', undefined);
|
||||
});
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Submit Result
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
describe('submitResult', () => {
|
||||
it('delegates entire dto to service', async () => {
|
||||
const dto = { runtimeInstanceId: 'rt-1', schemaVersion: 'v1', status: 'succeeded', attemptNo: 1 };
|
||||
mockService.submitResult.mockResolvedValue({ status: 'ok' });
|
||||
|
||||
const result = await controller.submitResult('j1', dto as any);
|
||||
|
||||
expect(mockService.submitResult).toHaveBeenCalledWith('j1', dto);
|
||||
expect(result).toEqual({ status: 'ok' });
|
||||
});
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Submit Failure
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
describe('submitFailure', () => {
|
||||
it('delegates entire dto to service', async () => {
|
||||
const dto = { runtimeInstanceId: 'rt-1', errorCode: 'ERR', errorMessage: 'msg', retryable: false };
|
||||
mockService.submitFailure.mockResolvedValue({ status: 'failed' });
|
||||
|
||||
await controller.submitFailure('j1', dto as any);
|
||||
|
||||
expect(mockService.submitFailure).toHaveBeenCalledWith('j1', dto);
|
||||
});
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Submit Invocation Logs
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
describe('submitInvocationLogs', () => {
|
||||
it('extracts logs array from dto', async () => {
|
||||
const dto = { logs: [{ jobId: 'j1', provider: 'deepseek' }] };
|
||||
mockService.submitInvocationLogs.mockResolvedValue({ accepted: 1 });
|
||||
|
||||
const result = await controller.submitInvocationLogs(dto as any);
|
||||
|
||||
expect(mockService.submitInvocationLogs).toHaveBeenCalledWith(dto.logs);
|
||||
expect(result).toEqual({ accepted: 1 });
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user