import assert from 'node:assert/strict'; import test from 'node:test'; import { createAdminApp } from './app.mjs'; function createServices({ role = 'admin', imageMakeAdminConfigService } = {}) { return { ready: Promise.resolve(), parseCookies: () => ({ adm_session: 'test-token' }), USER_COOKIE: 'adm_session', userLoginCookies: () => ['adm_session=test-token; Path=/; HttpOnly'], clearUserSessionCookie: () => ['adm_session=; Path=/; Max-Age=0'], resolveCookieDomainForRequest: () => null, userAuth: { getMe: async () => ({ id: 'admin-user-id', username: 'admin', role }), }, imageMakeAdminConfigService, }; } async function withServer(services, callback) { const app = createAdminApp(services); const server = await new Promise((resolve) => { const instance = app.listen(0, '127.0.0.1', () => resolve(instance)); }); const address = server.address(); const baseUrl = `http://127.0.0.1:${address.port}`; try { await callback(baseUrl); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } } const adminHeaders = { cookie: 'adm_session=test-token', 'content-type': 'application/json', }; test('image_make admin routes read and update config with the admin identity', async () => { let updateCall = null; const service = { getAdminConfig: async () => ({ source: 'default', config: { defaultProvider: 'mock', jobDefaultTimeoutSeconds: 120, providers: { mock: { enabled: true }, aliyun_bailian: { enabled: false }, comfyui: { enabled: false } }, }, }), updateAdminConfig: async (patch, context) => { updateCall = { patch, context }; return { ok: true, source: 'admin-db', config: patch }; }, getRuntimeConfig: async () => ({ ok: true, fingerprint: '1234567890abcdef', defaultProvider: 'aliyun_bailian', jobDefaultTimeoutSeconds: 120, providers: { mock: { enabled: true }, aliyun_bailian: { enabled: true, model: 'qwen-image-plus', apiBase: 'https://dashscope.aliyuncs.com/api/v1', apiKey: 'must-not-leak', }, comfyui: { enabled: false }, }, }), }; await withServer(createServices({ imageMakeAdminConfigService: service }), async (baseUrl) => { const configResponse = await fetch(`${baseUrl}/admin-api/image-make/config`, { headers: adminHeaders }); assert.equal(configResponse.status, 200); assert.equal((await configResponse.json()).config.defaultProvider, 'mock'); const patch = { defaultProvider: 'mock', jobDefaultTimeoutSeconds: 180 }; const updateResponse = await fetch(`${baseUrl}/admin-api/image-make/config`, { method: 'PATCH', headers: adminHeaders, body: JSON.stringify(patch), }); assert.equal(updateResponse.status, 200); assert.deepEqual(updateCall, { patch, context: { updatedBy: 'admin-user-id' } }); const runtimeResponse = await fetch(`${baseUrl}/admin-api/image-make/runtime`, { headers: adminHeaders }); assert.equal(runtimeResponse.status, 200); const runtime = await runtimeResponse.json(); assert.equal(runtime.providers.aliyun_bailian.apiKeyConfigured, true); assert.equal('apiKey' in runtime.providers.aliyun_bailian, false); assert.equal(JSON.stringify(runtime).includes('must-not-leak'), false); }); }); test('image_make admin config validation failures are returned as 400', async () => { const service = { updateAdminConfig: async () => ({ ok: false, message: '默认 Provider 必须处于启用状态' }), }; await withServer(createServices({ imageMakeAdminConfigService: service }), async (baseUrl) => { const response = await fetch(`${baseUrl}/admin-api/image-make/config`, { method: 'PATCH', headers: adminHeaders, body: JSON.stringify({ defaultProvider: 'comfyui' }), }); assert.equal(response.status, 400); assert.equal((await response.json()).message, '默认 Provider 必须处于启用状态'); }); }); test('image_make admin routes require an admin user and an enabled service', async () => { await withServer(createServices({ role: 'user', imageMakeAdminConfigService: {} }), async (baseUrl) => { const forbidden = await fetch(`${baseUrl}/admin-api/image-make/config`, { headers: adminHeaders }); assert.equal(forbidden.status, 403); }); await withServer(createServices({ imageMakeAdminConfigService: {} }), async (baseUrl) => { const unavailable = await fetch(`${baseUrl}/admin-api/image-make/config`, { headers: adminHeaders }); assert.equal(unavailable.status, 503); }); }); test('gray read-only mode blocks admin writes while keeping reads available', async () => { let updateCalled = false; const service = { getAdminConfig: async () => ({ config: { defaultProvider: 'mock', providers: {} } }), updateAdminConfig: async () => { updateCalled = true; return { ok: true }; }, }; const services = { ...createServices({ imageMakeAdminConfigService: service }), readOnlyMode: true, }; await withServer(services, async (baseUrl) => { const readResponse = await fetch(`${baseUrl}/admin-api/image-make/config`, { headers: adminHeaders }); assert.equal(readResponse.status, 200); const writeResponse = await fetch(`${baseUrl}/admin-api/image-make/config`, { method: 'PATCH', headers: adminHeaders, body: JSON.stringify({ defaultProvider: 'mock' }), }); assert.equal(writeResponse.status, 423); assert.match((await writeResponse.json()).message, /灰度只读模式/); assert.equal(updateCalled, false); }); });