Files
wehub-resource-sync 0d3cb498a3
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

177 lines
5.5 KiB
TypeScript

import { eq } from 'drizzle-orm';
import request from 'supertest';
import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
import { getDb } from '../../../src/database/index';
import { configsTable } from '../../../src/database/tables';
import { runDbMigrations } from '../../../src/migrate';
import { createApp } from '../../../src/server/server';
import {
CreateConfigResponseSchema,
GetConfigResponseSchema,
ListConfigsByTypeResponseSchema,
ListConfigsResponseSchema,
} from '../../../src/types/api/configs';
describe('configs routes', () => {
let app: ReturnType<typeof createApp>;
const testConfigIds = new Set<string>();
beforeAll(async () => {
await runDbMigrations();
});
beforeEach(() => {
app = createApp();
});
afterEach(async () => {
const db = await getDb();
for (const id of testConfigIds) {
try {
await db.delete(configsTable).where(eq(configsTable.id, id));
} catch {
// Ignore cleanup errors
}
}
testConfigIds.clear();
});
describe('POST /api/configs', () => {
it('should create a config and return id and createdAt', async () => {
const res = await request(app)
.post('/api/configs')
.send({
name: 'test-config',
type: 'eval',
config: { prompts: ['hello'] },
});
expect(res.status).toBe(200);
expect(res.body).toHaveProperty('id');
expect(res.body).toHaveProperty('createdAt');
testConfigIds.add(res.body.id);
const parsed = CreateConfigResponseSchema.safeParse(res.body);
expect(parsed.success).toBe(true);
});
it.each([
['missing name', { type: 'eval', config: {} }],
['missing type', { name: 'test-config', config: {} }],
['empty name', { name: '', type: 'eval', config: {} }],
['missing config', { name: 'test-config', type: 'eval' }],
['null config', { name: 'test-config', type: 'eval', config: null }],
])('should return 400 for %s', async (_label, body) => {
const res = await request(app).post('/api/configs').send(body);
expect(res.status).toBe(400);
expect(res.body).toHaveProperty('error');
});
});
describe('GET /api/configs', () => {
it('should return all configs with valid response schema', async () => {
const createRes = await request(app)
.post('/api/configs')
.send({
name: 'list-test',
type: 'eval',
config: { foo: 'bar' },
});
testConfigIds.add(createRes.body.id);
const res = await request(app).get('/api/configs');
expect(res.status).toBe(200);
expect(res.body.configs.length).toBeGreaterThanOrEqual(1);
const parsed = ListConfigsResponseSchema.safeParse(res.body);
expect(parsed.success).toBe(true);
});
it('should filter configs by type query parameter', async () => {
const createRes = await request(app).post('/api/configs').send({
name: 'filter-test',
type: 'redteam',
config: {},
});
testConfigIds.add(createRes.body.id);
const res = await request(app).get('/api/configs?type=redteam');
expect(res.status).toBe(200);
expect(res.body.configs.length).toBeGreaterThanOrEqual(1);
for (const config of res.body.configs) {
expect(config.type).toBe('redteam');
}
});
it('should return 400 for empty type query parameter', async () => {
const res = await request(app).get('/api/configs?type=');
expect(res.status).toBe(400);
expect(res.body).toHaveProperty('error');
});
});
describe('GET /api/configs/:type', () => {
it('should return configs filtered by type with valid response schema', async () => {
const createRes = await request(app)
.post('/api/configs')
.send({
name: 'type-filter-test',
type: 'eval',
config: { hello: 'world' },
});
testConfigIds.add(createRes.body.id);
const res = await request(app).get('/api/configs/eval');
expect(res.status).toBe(200);
expect(res.body.configs.length).toBeGreaterThanOrEqual(1);
const parsed = ListConfigsByTypeResponseSchema.safeParse(res.body);
expect(parsed.success).toBe(true);
});
it('should return empty array for unknown type', async () => {
const res = await request(app).get('/api/configs/nonexistent-type-xyz');
expect(res.status).toBe(200);
expect(res.body.configs).toEqual([]);
});
});
describe('GET /api/configs/:type/:id', () => {
it('should return a specific config with valid response schema', async () => {
const createRes = await request(app)
.post('/api/configs')
.send({
name: 'get-test',
type: 'eval',
config: { key: 'value' },
});
const configId = createRes.body.id;
testConfigIds.add(configId);
const res = await request(app).get(`/api/configs/eval/${configId}`);
expect(res.status).toBe(200);
expect(res.body.id).toBe(configId);
expect(res.body.name).toBe('get-test');
expect(res.body.type).toBe('eval');
expect(res.body.config).toEqual({ key: 'value' });
const parsed = GetConfigResponseSchema.safeParse(res.body);
expect(parsed.success).toBe(true);
});
it('should return 404 for non-existent config', async () => {
const res = await request(app).get('/api/configs/eval/non-existent-id');
expect(res.status).toBe(404);
expect(res.body).toHaveProperty('error', 'Config not found');
});
});
});