Files
makelore/tests/unit/works-routes.test.ts
2026-07-29 17:22:35 +08:00

961 lines
29 KiB
TypeScript

import { EventEmitter } from 'node:events';
import type { IncomingMessage, ServerResponse } from 'http';
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { handleWorksRoutes } from '@electron/api/routes/works';
const readWorksPublishFileMock = vi.hoisted(() => vi.fn());
const readWorksDeployCheckMock = vi.hoisted(() => vi.fn());
vi.mock('@electron/opencode/works-publish-file', () => ({
readWorksPublishFile: (...args: unknown[]) => readWorksPublishFileMock(...args),
}));
vi.mock('@electron/opencode/works-square-deploy-check', () => ({
readWorksDeployCheck: (...args: unknown[]) => readWorksDeployCheckMock(...args),
}));
function createResponse() {
const chunks: string[] = [];
const res = {
statusCode: 0,
setHeader: vi.fn(),
end: vi.fn((chunk?: string) => {
if (chunk) chunks.push(chunk);
}),
} as unknown as ServerResponse;
return {
res,
get statusCode() {
return res.statusCode;
},
json: () => JSON.parse(chunks.join('')) as unknown,
};
}
function createRequest(method: string, body?: unknown, headers: Record<string, string> = {}): IncomingMessage {
const req = new EventEmitter();
Object.assign(req, {
method,
headers: body === undefined ? headers : { 'content-type': 'application/json', ...headers },
[Symbol.asyncIterator]: async function* () {
if (body !== undefined) {
yield Buffer.from(JSON.stringify(body));
}
},
});
return req as IncomingMessage;
}
describe('works square host api routes', () => {
let tempDir: string | null = null;
beforeEach(() => {
vi.restoreAllMocks();
readWorksPublishFileMock.mockReset();
readWorksDeployCheckMock.mockReset();
});
afterEach(async () => {
if (tempDir) {
await rm(tempDir, { recursive: true, force: true });
tempDir = null;
}
});
it('lists public projects through the Works Square API', async () => {
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
items: [
{
app_id: 'space-cleaner',
title: '太空清洁队',
summary: '收集漂浮垃圾的小游戏。',
cover_url: null,
category: 'game',
age_band: '8-12',
difficulty: 'beginner',
updated_at: '2026-06-20T22:55:37.790408+08:00',
},
],
next_cursor: null,
limit: 24,
}), { status: 200 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('GET'),
response.res,
new URL('http://127.0.0.1/api/works/projects?q=space&category=game&limit=24'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
success: true,
page: {
items: [
{
app_id: 'space-cleaner',
title: '太空清洁队',
summary: '收集漂浮垃圾的小游戏。',
cover_url: null,
category: 'game',
age_band: '8-12',
difficulty: 'beginner',
updated_at: '2026-06-20T22:55:37.790408+08:00',
},
],
next_cursor: null,
limit: 24,
},
});
expect(fetchMock).toHaveBeenCalledWith(
'https://square.nianxx.cn/api/projects?q=space&category=game&limit=24',
{ method: 'GET' },
);
});
it('lists public assets through the Works Square API', async () => {
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
items: [
{
slug: 'tiny-farm',
title: 'Tiny Farm',
summary: 'Farm pixel resource package',
category: '2D',
source: 'Kenney',
tags: ['farm', 'pixel'],
preview_url: 'https://example.com/tiny-farm.png',
image_urls: ['https://example.com/tiny-farm.png'],
archive_size_bytes: 204800,
extracted_file_count: 141,
published_at: '2026-07-09T00:00:00Z',
updated_at: '2026-07-09T08:30:00Z',
},
],
next_cursor: null,
limit: 12,
}), { status: 200 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('GET'),
response.res,
new URL('http://127.0.0.1/api/works/assets?q=farm&category=2D&tag=pixel&source=Kenney&limit=12'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
success: true,
assets: {
items: [
{
slug: 'tiny-farm',
title: 'Tiny Farm',
summary: 'Farm pixel resource package',
category: '2D',
source: 'Kenney',
tags: ['farm', 'pixel'],
preview_url: 'https://example.com/tiny-farm.png',
image_urls: ['https://example.com/tiny-farm.png'],
archive_size_bytes: 204800,
extracted_file_count: 141,
published_at: '2026-07-09T00:00:00Z',
updated_at: '2026-07-09T08:30:00Z',
},
],
next_cursor: null,
limit: 12,
},
});
expect(fetchMock).toHaveBeenCalledWith(
'https://square.nianxx.cn/api/assets?q=farm&category=2D&tag=pixel&source=Kenney&limit=12',
{ method: 'GET' },
);
});
it('loads a public asset detail through the Works Square API', async () => {
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
slug: 'tiny-farm',
title: 'Tiny Farm',
summary: 'Farm pixel resource package',
category: '2D',
source: 'Kenney',
tags: ['farm'],
preview_url: null,
image_urls: [],
archive_size_bytes: null,
extracted_file_count: null,
published_at: null,
updated_at: null,
}), { status: 200 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('GET'),
response.res,
new URL('http://127.0.0.1/api/works/assets/tiny-farm'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
success: true,
asset: {
slug: 'tiny-farm',
title: 'Tiny Farm',
summary: 'Farm pixel resource package',
category: '2D',
source: 'Kenney',
tags: ['farm'],
preview_url: null,
image_urls: [],
archive_size_bytes: null,
extracted_file_count: null,
published_at: null,
updated_at: null,
},
});
expect(fetchMock).toHaveBeenCalledWith(
'https://square.nianxx.cn/api/assets/tiny-farm',
{ method: 'GET' },
);
});
it('downloads a public asset zip into a selected local project', async () => {
tempDir = await mkdtemp(join(tmpdir(), 'niancode-works-asset-download-'));
const fetchMock = vi.fn()
.mockResolvedValueOnce(new Response(null, {
status: 307,
headers: {
Location: 'https://cdn.example.com/tiny-farm.zip',
},
}))
.mockResolvedValueOnce(new Response(new Uint8Array(Buffer.from('zip bytes')), { status: 200 }));
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const listProjects = vi.fn().mockResolvedValue([
{
id: 'prj_123',
path: tempDir,
name: 'Tiny Game',
createdAt: '2026-07-09T00:00:00Z',
updatedAt: '2026-07-09T00:00:00Z',
lastOpenedAt: '2026-07-09T00:00:00Z',
},
]);
const handled = await handleWorksRoutes(
createRequest('POST', { projectId: 'prj_123' }),
response.res,
new URL('http://127.0.0.1/api/works/assets/tiny-farm/download'),
{ opencodeProjectStore: { listProjects } } as never,
);
const expectedFilePath = join(tempDir, 'assets', 'resource-square', 'tiny-farm.zip');
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
success: true,
download: {
slug: 'tiny-farm',
filePath: expectedFilePath,
relativePath: 'assets/resource-square/tiny-farm.zip',
bytesWritten: 9,
},
});
expect(await readFile(expectedFilePath, 'utf8')).toBe('zip bytes');
expect(fetchMock).toHaveBeenNthCalledWith(
1,
'https://square.nianxx.cn/api/assets/tiny-farm/download',
{ method: 'GET', redirect: 'manual' },
);
expect(fetchMock).toHaveBeenNthCalledWith(
2,
'https://cdn.example.com/tiny-farm.zip',
{ method: 'GET', redirect: 'manual' },
);
});
it('creates project metadata with the current SSO access token', async () => {
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
app_id: 'space-cleaner',
title: '太空清洁队',
summary: '收集漂浮垃圾的小游戏。',
cover_url: null,
category: 'game',
age_band: '8-12',
difficulty: 'beginner',
updated_at: '2026-06-20T22:55:37.790408+08:00',
}), { status: 201 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('POST', {
accessToken: 'access-token',
project: {
app_id: 'space-cleaner',
title: '太空清洁队',
summary: '收集漂浮垃圾的小游戏。',
cover_url: null,
category: 'game',
age_band: '8-12',
difficulty: 'beginner',
},
}),
response.res,
new URL('http://127.0.0.1/api/works/projects'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(201);
expect(response.json()).toEqual({
success: true,
project: {
app_id: 'space-cleaner',
title: '太空清洁队',
summary: '收集漂浮垃圾的小游戏。',
cover_url: null,
category: 'game',
age_band: '8-12',
difficulty: 'beginner',
updated_at: '2026-06-20T22:55:37.790408+08:00',
},
});
expect(fetchMock).toHaveBeenCalledWith(
'https://square.nianxx.cn/api/projects',
{
method: 'POST',
headers: {
Authorization: 'Bearer access-token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
app_id: 'space-cleaner',
title: '太空清洁队',
summary: '收集漂浮垃圾的小游戏。',
cover_url: null,
category: 'game',
age_band: '8-12',
difficulty: 'beginner',
}),
},
);
});
it('lists the current user projects through the Works Square API', async () => {
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
items: [
{
app_id: 'space-cleaner',
title: 'Space Cleaner',
summary: 'Catch space trash',
cover_url: null,
category: 'game',
age_band: '8-12',
difficulty: 'beginner',
status: 'draft',
updated_at: '2026-06-20T22:55:37.790408+08:00',
playable: false,
runtime_url: null,
},
],
next_cursor: null,
limit: 50,
}), { status: 200 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('GET', undefined, { 'x-niancode-access-token': 'access-token' }),
response.res,
new URL('http://127.0.0.1/api/works/projects/mine?limit=50'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
success: true,
page: {
items: [
{
app_id: 'space-cleaner',
title: 'Space Cleaner',
summary: 'Catch space trash',
cover_url: null,
category: 'game',
age_band: '8-12',
difficulty: 'beginner',
status: 'draft',
updated_at: '2026-06-20T22:55:37.790408+08:00',
playable: false,
runtime_url: null,
},
],
next_cursor: null,
limit: 50,
},
});
expect(fetchMock).toHaveBeenCalledWith(
'https://square.nianxx.cn/api/projects/mine?limit=50',
{
method: 'GET',
headers: {
Authorization: 'Bearer access-token',
},
},
);
});
it('loads current token plan usage through the Works Square API', async () => {
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
five_hour_remaining_percent: 72.5,
weekly_remaining_percent: 48,
}), { status: 200 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('GET', undefined, { 'x-niancode-access-token': 'access-token' }),
response.res,
new URL('http://127.0.0.1/api/works/billing/token-usage'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
success: true,
usage: {
five_hour_remaining_percent: 72.5,
weekly_remaining_percent: 48,
},
});
expect(fetchMock).toHaveBeenCalledWith(
'https://square.nianxx.cn/api/billing/token-usage',
{
method: 'GET',
headers: {
Authorization: 'Bearer access-token',
},
},
);
});
it('loads the current Agent Profile through the Works Square API', async () => {
const profile = {
display_name: '小泥',
age: 18,
gender: 'female',
share_age_with_agents: true,
share_gender_with_agents: true,
analysis_enabled: true,
completed: true,
version: 2,
updated_at: '2026-07-12T00:00:00Z',
};
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify(profile), { status: 200 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('GET', undefined, { 'x-niancode-access-token': 'access-token' }),
response.res,
new URL('http://127.0.0.1/api/works/user/agent-profile'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({ success: true, profile });
expect(fetchMock).toHaveBeenCalledWith(
'https://square.nianxx.cn/api/user/agent-profile',
{
method: 'GET',
headers: { Authorization: 'Bearer access-token' },
},
);
});
it('keeps Agent Profile version-conflict detail available to the renderer', async () => {
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
detail: {
code: 'agent_profile_version_conflict',
current_version: 4,
},
}), { status: 409 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('PUT', {
display_name: '小泥',
age: null,
gender: null,
share_age_with_agents: false,
share_gender_with_agents: false,
analysis_enabled: true,
version: 3,
}, { 'x-niancode-access-token': 'access-token' }),
response.res,
new URL('http://127.0.0.1/api/works/user/agent-profile'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
success: false,
status: 409,
error: 'Agent Profile request failed (409)',
detail: {
code: 'agent_profile_version_conflict',
current_version: 4,
},
});
expect(fetchMock).toHaveBeenCalledWith(
'https://square.nianxx.cn/api/user/agent-profile',
{
method: 'PUT',
headers: {
Authorization: 'Bearer access-token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
display_name: '小泥',
age: null,
gender: null,
share_age_with_agents: false,
share_gender_with_agents: false,
analysis_enabled: true,
version: 3,
}),
},
);
});
it('submits an image generation task through the Works Square AI gateway API', async () => {
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
task_id: 'task_1',
status: 'queued',
model: 'gpt-image-2',
result_urls: [],
}), { status: 202 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('POST', {
prompt: '未来城市里的儿童编程课海报',
image_urls: [],
size: '1:1',
quality: 'medium',
resolution: '2K',
}, { 'x-niancode-access-token': 'access-token' }),
response.res,
new URL('http://127.0.0.1/api/works/ai-gateway/images/generations'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(202);
expect(response.json()).toEqual({
success: true,
job: {
task_id: 'task_1',
status: 'queued',
model: 'gpt-image-2',
result_urls: [],
},
});
expect(fetchMock).toHaveBeenCalledWith(
'https://square.nianxx.cn/api/ai-gateway/images/generations',
{
method: 'POST',
headers: {
Authorization: 'Bearer access-token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt: '未来城市里的儿童编程课海报',
image_urls: [],
size: '1:1',
quality: 'medium',
resolution: '2K',
}),
},
);
});
it('loads an image generation task through the Works Square AI gateway API', async () => {
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
task_id: 'task_1',
status: 'succeeded',
model: 'gpt-image-2',
result_urls: ['https://example.com/result.png'],
}), { status: 200 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('GET', undefined, { 'x-niancode-access-token': 'access-token' }),
response.res,
new URL('http://127.0.0.1/api/works/ai-gateway/images/tasks/task_1'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
success: true,
job: {
task_id: 'task_1',
status: 'succeeded',
model: 'gpt-image-2',
result_urls: ['https://example.com/result.png'],
},
});
expect(fetchMock).toHaveBeenCalledWith(
'https://square.nianxx.cn/api/ai-gateway/images/tasks/task_1',
{
method: 'GET',
headers: {
Authorization: 'Bearer access-token',
},
},
);
});
it('loads current user project status with uploaded versions', async () => {
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
project: {
app_id: 'space-cleaner',
title: 'Space Cleaner',
summary: 'Catch space trash',
cover_url: null,
category: 'game',
age_band: '8-12',
difficulty: 'beginner',
status: 'draft',
updated_at: '2026-06-20T22:55:37.790408+08:00',
playable: false,
runtime_url: null,
},
latest_version: {
id: 'version-new',
version_name: 'v1.1.0',
review_status: 'building',
change_log: 'Add score board',
build_job_id: 'job-new',
created_at: '2026-06-21T10:30:00+08:00',
},
versions: [],
}), { status: 200 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('GET', undefined, { 'x-niancode-access-token': 'access-token' }),
response.res,
new URL('http://127.0.0.1/api/works/projects/mine/space-cleaner/status'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
success: true,
status: {
project: {
app_id: 'space-cleaner',
title: 'Space Cleaner',
summary: 'Catch space trash',
cover_url: null,
category: 'game',
age_band: '8-12',
difficulty: 'beginner',
status: 'draft',
updated_at: '2026-06-20T22:55:37.790408+08:00',
playable: false,
runtime_url: null,
},
latest_version: {
id: 'version-new',
version_name: 'v1.1.0',
review_status: 'building',
change_log: 'Add score board',
build_job_id: 'job-new',
created_at: '2026-06-21T10:30:00+08:00',
},
versions: [],
},
});
expect(fetchMock).toHaveBeenCalledWith(
'https://square.nianxx.cn/api/projects/mine/space-cleaner/status',
{
method: 'GET',
headers: {
Authorization: 'Bearer access-token',
},
},
);
});
it.each(['pass', 'warning'] as const)('uploads a zip version when the deployment check is %s', async (deployStatus) => {
tempDir = await mkdtemp(join(tmpdir(), 'niancode-works-upload-'));
const zipPath = join(tempDir, 'project.zip');
await writeFile(zipPath, Buffer.from('zip bytes'));
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
version_id: 'b2f6f1dd-cf4b-4f03-a4b8-a71e2c8dd5f1',
review_status: 'building',
}), { status: 201 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const project = { id: 'project-1', path: tempDir, name: 'space-cleaner' };
readWorksPublishFileMock.mockResolvedValue({
status: 'ready',
filePath: join(tempDir, 'works-publish.json'),
publish: {
app_id: 'space-cleaner',
title: '太空清洁队',
summary: '收集漂浮垃圾的小游戏。',
category: 'game',
age_band: '8-12',
difficulty: 'beginner',
version_name: 'v1.0.0',
change_log: 'First submitted version',
zip_file_path: zipPath,
},
});
readWorksDeployCheckMock.mockResolvedValue({
status: deployStatus,
filePath: join(tempDir, 'works-deploy-check.json'),
checks: {},
});
const handled = await handleWorksRoutes(
createRequest('POST', {
accessToken: 'access-token',
projectId: project.id,
versionName: 'v1.0.0',
changeLog: 'First submitted version',
zipFilePath: zipPath,
}),
response.res,
new URL('http://127.0.0.1/api/works/projects/space-cleaner/versions/upload'),
{ opencodeProjectStore: { listProjects: vi.fn(async () => [project]) } } as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(201);
expect(response.json()).toEqual({
success: true,
upload: {
version_id: 'b2f6f1dd-cf4b-4f03-a4b8-a71e2c8dd5f1',
review_status: 'building',
},
});
expect(fetchMock).toHaveBeenCalledOnce();
const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit];
expect(url).toBe('https://square.nianxx.cn/api/projects/space-cleaner/versions/upload');
expect(init.method).toBe('POST');
expect(init.headers).toEqual({ Authorization: 'Bearer access-token' });
expect(init.body).toBeInstanceOf(FormData);
const form = init.body as FormData;
expect(form.get('version_name')).toBe('v1.0.0');
expect(form.get('change_log')).toBe('First submitted version');
const archive = form.get('archive');
expect(archive).toBeInstanceOf(File);
expect((archive as File).name).toBe('project.zip');
expect((archive as File).type).toBe('application/zip');
});
it('blocks upload when the deployment check is not PASS', async () => {
tempDir = await mkdtemp(join(tmpdir(), 'niancode-works-upload-blocked-'));
const zipPath = join(tempDir, 'project.zip');
await writeFile(zipPath, Buffer.from('zip bytes'));
const project = { id: 'project-blocked', path: tempDir, name: 'blocked' };
readWorksPublishFileMock.mockResolvedValue({
status: 'ready',
filePath: join(tempDir, 'works-publish.json'),
publish: {
app_id: 'blocked-app',
title: '阻断测试',
summary: '阻断测试',
category: 'web',
age_band: '10-12',
difficulty: 'beginner',
version_name: 'v1.0.0',
change_log: '阻断测试',
zip_file_path: zipPath,
},
});
readWorksDeployCheckMock.mockResolvedValue({
status: 'blocked',
filePath: join(tempDir, 'works-deploy-check.json'),
error: 'HTTP smoke 失败',
});
const fetchMock = vi.fn();
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('POST', {
accessToken: 'access-token',
projectId: project.id,
versionName: 'v1.0.0',
changeLog: '阻断测试',
zipFilePath: zipPath,
}),
response.res,
new URL('http://127.0.0.1/api/works/projects/blocked-app/versions/upload'),
{ opencodeProjectStore: { listProjects: vi.fn(async () => [project]) } } as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(400);
expect(response.json()).toEqual({ success: false, error: 'BLOCKED: HTTP smoke 失败' });
expect(fetchMock).not.toHaveBeenCalled();
});
it('transcribes uploaded speech through the Works Square API', async () => {
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
text: 'open the file',
model: 'gpt-4o-mini-transcribe',
}), { status: 200 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('POST', {
accessToken: 'access-token',
audioBase64: Buffer.from('wav bytes').toString('base64'),
language: 'zh',
prompt: 'NianCode command',
}),
response.res,
new URL('http://127.0.0.1/api/works/speech/transcriptions'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
success: true,
transcription: {
text: 'open the file',
model: 'gpt-4o-mini-transcribe',
},
});
expect(fetchMock).toHaveBeenCalledOnce();
const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit];
expect(url).toBe('https://square.nianxx.cn/api/speech/transcriptions');
expect(init.method).toBe('POST');
expect(init.headers).toEqual({ Authorization: 'Bearer access-token' });
expect(init.body).toBeInstanceOf(FormData);
const form = init.body as FormData;
expect(form.get('language')).toBe('zh');
expect(form.get('prompt')).toBe('NianCode command');
const audio = form.get('audio');
expect(audio).toBeInstanceOf(File);
expect((audio as File).name).toBe('voice.wav');
expect((audio as File).type).toBe('audio/wav');
expect(await (audio as File).text()).toBe('wav bytes');
});
it('lists uploaded versions for a project with the current SSO access token', async () => {
const fetchMock = vi.fn().mockResolvedValueOnce(
new Response(JSON.stringify({
items: [
{
id: 'ver_1',
version_name: 'v1.0.0',
review_status: 'building',
change_log: 'First submitted version',
build_job_id: 'job_1',
created_at: '2026-06-21T10:30:00+08:00',
},
],
}), { status: 200 }),
);
vi.stubGlobal('fetch', fetchMock);
const response = createResponse();
const handled = await handleWorksRoutes(
createRequest('GET', undefined, { 'x-niancode-access-token': 'access-token' }),
response.res,
new URL('http://127.0.0.1/api/works/projects/space-cleaner/versions'),
{} as never,
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
success: true,
versions: {
items: [
{
id: 'ver_1',
version_name: 'v1.0.0',
review_status: 'building',
change_log: 'First submitted version',
build_job_id: 'job_1',
created_at: '2026-06-21T10:30:00+08:00',
},
],
},
});
expect(fetchMock).toHaveBeenCalledWith(
'https://square.nianxx.cn/api/projects/space-cleaner/versions',
{
method: 'GET',
headers: {
Authorization: 'Bearer access-token',
},
},
);
});
});