/** * Settings API Tests – Key/Value settings (Get / Put / Upsert) */ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import type { FastifyInstance } from 'fastify'; import { SqliteAdapter } from '../src/database/SqliteAdapter.js'; import { createServer } from '../src/server.js'; describe('Settings API', () => { let app: FastifyInstance; let db: SqliteAdapter; beforeAll(async () => { db = new SqliteAdapter(':memory:'); await db.init(); app = await createServer({ db, port: 0 }); await app.ready(); }); afterAll(async () => { await app.close(); db.close(); }); // ─── Get (missing key → 404) ───────────────────────── describe('GET /api/settings/:key', () => { it('should return 404 for non-existent setting', async () => { const response = await app.inject({ method: 'GET', url: '/api/settings/non-existent-key', }); expect(response.statusCode).toBe(404); const body = JSON.parse(response.body); expect(body.error).toContain('Setting not found'); }); it('should return a setting that was previously set', async () => { // First set a value await app.inject({ method: 'PUT', url: '/api/settings/test-key', payload: { value: 'test-value' }, }); const response = await app.inject({ method: 'GET', url: '/api/settings/test-key', }); expect(response.statusCode).toBe(200); const body = JSON.parse(response.body); expect(body.key).toBe('test-key'); expect(body.value).toBe('test-value'); expect(body.updated_at).toBeDefined(); }); }); // ─── Put (upsert) ─────────────────────────────────── describe('PUT /api/settings/:key', () => { it('should create a new setting', async () => { const response = await app.inject({ method: 'PUT', url: '/api/settings/new-setting', payload: { value: 'new-value' }, }); expect(response.statusCode).toBe(200); const body = JSON.parse(response.body); expect(body.key).toBe('new-setting'); expect(body.value).toBe('new-value'); expect(body.updated_at).toBeDefined(); }); it('should update an existing setting (upsert)', async () => { // Create initial await app.inject({ method: 'PUT', url: '/api/settings/upsert-key', payload: { value: 'initial' }, }); // Update it const response = await app.inject({ method: 'PUT', url: '/api/settings/upsert-key', payload: { value: 'updated' }, }); expect(response.statusCode).toBe(200); const body = JSON.parse(response.body); expect(body.key).toBe('upsert-key'); expect(body.value).toBe('updated'); // Verify via GET const getRes = await app.inject({ method: 'GET', url: '/api/settings/upsert-key', }); const getBody = JSON.parse(getRes.body); expect(getBody.value).toBe('updated'); }); it('should reject setting without value', async () => { const response = await app.inject({ method: 'PUT', url: '/api/settings/no-value-key', payload: {}, }); const body = JSON.parse(response.body); expect(body.error).toContain('Value is required'); }); it('should handle complex JSON values', async () => { const complexValue = JSON.stringify({ nested: { object: true }, array: [1, 2, 3] }); const response = await app.inject({ method: 'PUT', url: '/api/settings/complex-config', payload: { value: complexValue }, }); expect(response.statusCode).toBe(200); const getRes = await app.inject({ method: 'GET', url: '/api/settings/complex-config', }); const body = JSON.parse(getRes.body); expect(body.value).toBe(complexValue); }); }); });