150 lines
5.6 KiB
JavaScript
150 lines
5.6 KiB
JavaScript
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);
|
|
});
|
|
});
|