Files
Krow-workspace/backend/core-api/test/app.test.js

247 lines
7.9 KiB
JavaScript

import test, { beforeEach } from 'node:test';
import assert from 'node:assert/strict';
import request from 'supertest';
import { createApp } from '../src/app.js';
import { __resetLlmRateLimitForTests } from '../src/services/llm-rate-limit.js';
import { __resetVerificationJobsForTests } from '../src/services/verification-jobs.js';
beforeEach(() => {
process.env.AUTH_BYPASS = 'true';
process.env.LLM_MOCK = 'true';
process.env.SIGNED_URL_MOCK = 'true';
process.env.UPLOAD_MOCK = 'true';
process.env.MAX_SIGNED_URL_SECONDS = '900';
process.env.LLM_RATE_LIMIT_PER_MINUTE = '20';
process.env.VERIFICATION_REQUIRE_FILE_EXISTS = 'false';
process.env.VERIFICATION_ACCESS_MODE = 'authenticated';
process.env.VERIFICATION_ATTIRE_PROVIDER = 'mock';
__resetLlmRateLimitForTests();
__resetVerificationJobsForTests();
});
async function waitForMachineStatus(app, verificationId, maxAttempts = 30) {
let last;
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
last = await request(app)
.get(`/core/verifications/${verificationId}`)
.set('Authorization', 'Bearer test-token');
if (
last.body?.status === 'AUTO_PASS'
|| last.body?.status === 'AUTO_FAIL'
|| last.body?.status === 'NEEDS_REVIEW'
|| last.body?.status === 'ERROR'
) {
return last;
}
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => setTimeout(resolve, 10));
}
return last;
}
test('GET /healthz returns healthy response', async () => {
const app = createApp();
const res = await request(app).get('/healthz');
assert.equal(res.status, 200);
assert.equal(res.body.ok, true);
assert.equal(typeof res.body.requestId, 'string');
assert.equal(typeof res.headers['x-request-id'], 'string');
});
test('POST /core/create-signed-url requires auth', async () => {
process.env.AUTH_BYPASS = 'false';
const app = createApp();
const res = await request(app).post('/core/create-signed-url').send({
fileUri: 'gs://krow-workforce-dev-private/foo.pdf',
});
assert.equal(res.status, 401);
assert.equal(res.body.code, 'UNAUTHENTICATED');
process.env.AUTH_BYPASS = 'true';
});
test('POST /core/create-signed-url returns signed URL', async () => {
const app = createApp();
const res = await request(app)
.post('/core/create-signed-url')
.set('Authorization', 'Bearer test-token')
.send({
fileUri: 'gs://krow-workforce-dev-private/uploads/test-user/foo.pdf',
expiresInSeconds: 300,
});
assert.equal(res.status, 200);
assert.equal(typeof res.body.signedUrl, 'string');
assert.equal(typeof res.body.expiresAt, 'string');
assert.equal(typeof res.body.requestId, 'string');
});
test('POST /core/create-signed-url rejects non-owned path', async () => {
const app = createApp();
const res = await request(app)
.post('/core/create-signed-url')
.set('Authorization', 'Bearer test-token')
.send({
fileUri: 'gs://krow-workforce-dev-private/uploads/other-user/foo.pdf',
expiresInSeconds: 300,
});
assert.equal(res.status, 403);
assert.equal(res.body.code, 'FORBIDDEN');
});
test('POST /core/create-signed-url enforces expiry cap', async () => {
process.env.MAX_SIGNED_URL_SECONDS = '300';
const app = createApp();
const res = await request(app)
.post('/core/create-signed-url')
.set('Authorization', 'Bearer test-token')
.send({
fileUri: 'gs://krow-workforce-dev-private/uploads/test-user/foo.pdf',
expiresInSeconds: 301,
});
assert.equal(res.status, 400);
assert.equal(res.body.code, 'VALIDATION_ERROR');
});
test('POST /invokeLLM legacy alias works', async () => {
const app = createApp();
const res = await request(app)
.post('/invokeLLM')
.set('Authorization', 'Bearer test-token')
.send({
prompt: 'hello',
responseJsonSchema: { type: 'object' },
fileUrls: [],
});
assert.equal(res.status, 200);
assert.equal(typeof res.body.result, 'object');
assert.equal(typeof res.body.model, 'string');
});
test('POST /core/invoke-llm enforces per-user rate limit', async () => {
process.env.LLM_RATE_LIMIT_PER_MINUTE = '1';
const app = createApp();
const first = await request(app)
.post('/core/invoke-llm')
.set('Authorization', 'Bearer test-token')
.send({
prompt: 'hello',
responseJsonSchema: { type: 'object' },
fileUrls: [],
});
const second = await request(app)
.post('/core/invoke-llm')
.set('Authorization', 'Bearer test-token')
.send({
prompt: 'hello again',
responseJsonSchema: { type: 'object' },
fileUrls: [],
});
assert.equal(first.status, 200);
assert.equal(second.status, 429);
assert.equal(second.body.code, 'RATE_LIMITED');
assert.equal(typeof second.headers['retry-after'], 'string');
});
test('POST /core/verifications creates async job and GET returns status', async () => {
const app = createApp();
const created = await request(app)
.post('/core/verifications')
.set('Authorization', 'Bearer test-token')
.send({
type: 'attire',
subjectType: 'staff',
subjectId: 'staff_1',
fileUri: 'gs://krow-workforce-dev-private/uploads/test-user/attire.jpg',
rules: { attireType: 'shoes', expectedColor: 'black' },
});
assert.equal(created.status, 202);
assert.equal(created.body.type, 'attire');
assert.equal(created.body.status, 'PENDING');
assert.equal(typeof created.body.verificationId, 'string');
const status = await waitForMachineStatus(app, created.body.verificationId);
assert.equal(status.status, 200);
assert.equal(status.body.verificationId, created.body.verificationId);
assert.equal(status.body.type, 'attire');
assert.ok(['NEEDS_REVIEW', 'AUTO_PASS', 'AUTO_FAIL', 'ERROR'].includes(status.body.status));
});
test('POST /core/verifications rejects file paths not owned by actor', async () => {
const app = createApp();
const res = await request(app)
.post('/core/verifications')
.set('Authorization', 'Bearer test-token')
.send({
type: 'attire',
fileUri: 'gs://krow-workforce-dev-private/uploads/other-user/not-allowed.jpg',
rules: { attireType: 'shoes' },
});
assert.equal(res.status, 403);
assert.equal(res.body.code, 'FORBIDDEN');
});
test('POST /core/verifications/:id/review finalizes verification', async () => {
const app = createApp();
const created = await request(app)
.post('/core/verifications')
.set('Authorization', 'Bearer test-token')
.send({
type: 'certification',
subjectType: 'staff',
subjectId: 'staff_1',
fileUri: 'gs://krow-workforce-dev-private/uploads/test-user/cert.pdf',
rules: { certType: 'food_safety' },
});
const status = await waitForMachineStatus(app, created.body.verificationId);
assert.equal(status.status, 200);
const reviewed = await request(app)
.post(`/core/verifications/${created.body.verificationId}/review`)
.set('Authorization', 'Bearer test-token')
.send({
decision: 'APPROVED',
note: 'Looks good',
reasonCode: 'MANUAL_REVIEW',
});
assert.equal(reviewed.status, 200);
assert.equal(reviewed.body.status, 'APPROVED');
assert.equal(reviewed.body.review.decision, 'APPROVED');
});
test('POST /core/verifications/:id/retry requeues verification', async () => {
const app = createApp();
const created = await request(app)
.post('/core/verifications')
.set('Authorization', 'Bearer test-token')
.send({
type: 'government_id',
subjectType: 'staff',
subjectId: 'staff_1',
fileUri: 'gs://krow-workforce-dev-private/uploads/test-user/id-front.jpg',
rules: {},
});
const status = await waitForMachineStatus(app, created.body.verificationId);
assert.equal(status.status, 200);
const retried = await request(app)
.post(`/core/verifications/${created.body.verificationId}/retry`)
.set('Authorization', 'Bearer test-token')
.send({});
assert.equal(retried.status, 202);
assert.equal(retried.body.status, 'PENDING');
});