收敛客户端静态发布链路
This commit is contained in:
@@ -7,18 +7,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { handleWorksRoutes } from '@electron/api/routes/works';
|
||||
import { createProjectConfig } from '../../shared/project-config';
|
||||
|
||||
const readWorksPublishFileMock = vi.hoisted(() => vi.fn());
|
||||
const readWorksDeployCheckMock = vi.hoisted(() => vi.fn());
|
||||
const getValidWorksSquareAccessTokenMock = 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),
|
||||
}));
|
||||
|
||||
vi.mock('@electron/services/works-square-session', () => ({
|
||||
getValidWorksSquareAccessToken: (...args: unknown[]) => getValidWorksSquareAccessTokenMock(...args),
|
||||
}));
|
||||
@@ -87,8 +77,6 @@ describe('works square host api routes', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
readWorksPublishFileMock.mockReset();
|
||||
readWorksDeployCheckMock.mockReset();
|
||||
getValidWorksSquareAccessTokenMock.mockReset();
|
||||
getValidWorksSquareAccessTokenMock.mockResolvedValue('main-owned-access-token');
|
||||
});
|
||||
@@ -708,6 +696,7 @@ describe('works square host api routes', () => {
|
||||
status: 'draft',
|
||||
updated_at: '2026-06-20T22:55:37.790408+08:00',
|
||||
playable: false,
|
||||
play_url: '/apps/space-cleaner/',
|
||||
runtime_url: null,
|
||||
owner_email: 'private@example.com',
|
||||
},
|
||||
@@ -754,6 +743,7 @@ describe('works square host api routes', () => {
|
||||
status: 'draft',
|
||||
updated_at: '2026-06-20T22:55:37.790408+08:00',
|
||||
playable: false,
|
||||
play_url: '/apps/space-cleaner/',
|
||||
runtime_url: null,
|
||||
},
|
||||
latest_version: {
|
||||
@@ -785,6 +775,23 @@ describe('works square host api routes', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('does not expose the retired direct ZIP upload route', async () => {
|
||||
const fetchMock = vi.fn();
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
const response = createResponse();
|
||||
|
||||
const handled = await handleWorksRoutes(
|
||||
createRequest('POST', {}),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/works/projects/space-cleaner/versions/upload'),
|
||||
{} as never,
|
||||
);
|
||||
|
||||
expect(handled).toBe(true);
|
||||
expect(response.statusCode).toBe(404);
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('loads current project status with the Main-owned session when no renderer token is sent', async () => {
|
||||
const status = {
|
||||
project: {
|
||||
@@ -904,126 +911,6 @@ describe('works square host api routes', () => {
|
||||
expect(JSON.stringify(response.json())).not.toContain('token=secret');
|
||||
});
|
||||
|
||||
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('packages and submits source with Main-owned credentials and automatic release metadata', async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'makelore-source-publish-'));
|
||||
await writePublishableProject(tempDir);
|
||||
@@ -1054,7 +941,7 @@ describe('works square host api routes', () => {
|
||||
new URL('http://127.0.0.1/api/works/projects/publish-source'),
|
||||
{
|
||||
opencodeProjectStore: { listProjects: vi.fn(async () => [project]) },
|
||||
worksCloudDeployment: { recordSubmitted },
|
||||
worksSubmissionBinding: { recordSubmitted },
|
||||
} as never,
|
||||
);
|
||||
|
||||
@@ -1146,7 +1033,7 @@ describe('works square host api routes', () => {
|
||||
new URL('http://127.0.0.1/api/works/projects/publish-source'),
|
||||
{
|
||||
opencodeProjectStore: { listProjects: vi.fn(async () => [project]) },
|
||||
worksCloudDeployment: {
|
||||
worksSubmissionBinding: {
|
||||
recordSubmitted: vi.fn(async () => { throw new Error('disk unavailable'); }),
|
||||
},
|
||||
} as never,
|
||||
|
||||
Reference in New Issue
Block a user