merge: integrate remote learning module safely
This commit is contained in:
commit
f7171a471a
125 files changed
+11451
-12132
No files matched your search
@@ -1100,6 +1100,7 @@ describe('works square host api routes', () => {
|
||||
app_id: 'space-cleaner',
|
||||
title: 'Space Cleaner',
|
||||
summary: 'Catch space trash',
|
||||
status: 'draft',
|
||||
},
|
||||
latest_version: {
|
||||
id: 'version-new',
|
||||
@@ -1218,6 +1219,42 @@ describe('works square host api routes', () => {
|
||||
expect(JSON.stringify(response.json())).not.toContain('token=secret');
|
||||
});
|
||||
|
||||
it.each([
|
||||
['missing', undefined],
|
||||
['unknown', 'retired'],
|
||||
])('fails safely when the project status is %s', async (_case, status) => {
|
||||
vi.stubGlobal('fetch', vi.fn().mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({
|
||||
project: {
|
||||
app_id: 'space-cleaner',
|
||||
title: 'Space Cleaner',
|
||||
summary: 'Catch trash',
|
||||
...(status === undefined ? {} : { status }),
|
||||
internal: 'private token=secret',
|
||||
},
|
||||
latest_version: null,
|
||||
versions: [],
|
||||
}), { status: 200 }),
|
||||
));
|
||||
const response = createResponse();
|
||||
|
||||
await handleWorksRoutes(
|
||||
createRequest('GET'),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/works/projects/mine/space-cleaner/status'),
|
||||
{} as never,
|
||||
);
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json()).toEqual({
|
||||
success: false,
|
||||
status: 502,
|
||||
code: 'PROJECT_STATUS_UNAVAILABLE',
|
||||
error: '暂时无法获取作品处理状态,请稍后重试。',
|
||||
});
|
||||
expect(JSON.stringify(response.json())).not.toContain('token=secret');
|
||||
});
|
||||
|
||||
it.each([
|
||||
['missing', {}],
|
||||
['incorrect', { [RENDERER_CAPABILITY_HEADER]: 'incorrect-capability' }],
|
||||
@@ -1265,6 +1302,7 @@ describe('works square host api routes', () => {
|
||||
category: 'game',
|
||||
};
|
||||
const fetchMock = vi.fn()
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 404 }))
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify(projectMetadata), { status: 201 }))
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify({
|
||||
version_id: 'version-1',
|
||||
@@ -1321,8 +1359,8 @@ describe('works square host api routes', () => {
|
||||
expect(JSON.stringify(response.json())).not.toContain('internal_trace');
|
||||
expect(response.json().package).not.toHaveProperty('archivePath');
|
||||
expect(getValidWorksSquareAccessTokenMock).toHaveBeenCalledOnce();
|
||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||
expect(fetchMock.mock.calls[0]).toEqual([
|
||||
expect(fetchMock).toHaveBeenCalledTimes(3);
|
||||
expect(fetchMock.mock.calls[1]).toEqual([
|
||||
'https://square.nianxx.cn/api/projects',
|
||||
{
|
||||
method: 'POST',
|
||||
@@ -1333,7 +1371,7 @@ describe('works square host api routes', () => {
|
||||
body: JSON.stringify(projectMetadata),
|
||||
},
|
||||
]);
|
||||
const [, uploadInit] = fetchMock.mock.calls[1] as [string, RequestInit];
|
||||
const [, uploadInit] = fetchMock.mock.calls[2] as [string, RequestInit];
|
||||
expect(uploadInit.headers).toMatchObject({
|
||||
Authorization: 'Bearer main-owned-access-token',
|
||||
'Idempotency-Key': expect.stringMatching(/^makelore-/),
|
||||
@@ -1366,6 +1404,380 @@ describe('works square host api routes', () => {
|
||||
expect(JSON.stringify(response.json())).not.toContain('private-source.zip');
|
||||
});
|
||||
|
||||
it('fails before upload or create when a 404 preflight cannot atomically attach the submitted cover', async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'makelore-source-cover-publish-'));
|
||||
await writePublishableProject(tempDir);
|
||||
const project = { id: 'project-1', path: tempDir, name: 'space-cleaner' };
|
||||
const projectMetadata = {
|
||||
app_id: 'space-cleaner',
|
||||
title: '太空清洁队',
|
||||
summary: '收集漂浮垃圾的小游戏。',
|
||||
description: '清理轨道上的垃圾并完成关卡。',
|
||||
cover_url: null,
|
||||
creator_name: '小明',
|
||||
creator_age: 12,
|
||||
category: 'game',
|
||||
age_band: '6-12岁',
|
||||
difficulty: '入门',
|
||||
};
|
||||
const cover = {
|
||||
fileName: 'cover.png',
|
||||
mimeType: 'image/png',
|
||||
dataBase64: Buffer.from('cover bytes').toString('base64'),
|
||||
};
|
||||
const fetchMock = vi.fn().mockResolvedValueOnce(new Response('{}', { status: 404 }));
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
const response = createResponse();
|
||||
prepareProjectReleaseMock.mockResolvedValueOnce(preparedRelease(tempDir));
|
||||
|
||||
await handleWorksRoutes(
|
||||
createRendererRequest('POST', { projectId: project.id, project: projectMetadata, cover }),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/works/projects/publish-source'),
|
||||
{
|
||||
opencodeProjectStore: { listProjects: vi.fn(async () => [project]) },
|
||||
agentBrowser: { preflightStaticArtifact: vi.fn(async () => ({ ok: true })) },
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json()).toEqual({
|
||||
success: false,
|
||||
status: 503,
|
||||
code: 'WORKS_SQUARE_UNAVAILABLE',
|
||||
error: '发布服务暂时无法安全保存封面,请稍后重试。',
|
||||
});
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
expect(fetchMock.mock.calls.some(([url]) => String(url).endsWith('/api/projects/covers'))).toBe(false);
|
||||
expect(fetchMock.mock.calls.some(([url]) => String(url).endsWith('/api/projects'))).toBe(false);
|
||||
expect(fetchMock.mock.calls.some(([url]) => String(url).endsWith('/versions/upload'))).toBe(false);
|
||||
});
|
||||
|
||||
it('preserves published metadata and does not upload an unused cover when submitting a new version', async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'makelore-source-published-resubmit-'));
|
||||
await writePublishableProject(tempDir);
|
||||
const project = { id: 'project-1', path: tempDir, name: 'space-cleaner' };
|
||||
const submittedMetadata = {
|
||||
app_id: 'space-cleaner',
|
||||
title: '不会保存的新标题',
|
||||
summary: '不会保存的新简介',
|
||||
creator_name: '新作者',
|
||||
creator_age: 12,
|
||||
status: 'published',
|
||||
};
|
||||
const cover = {
|
||||
fileName: 'replacement.png',
|
||||
mimeType: 'image/png',
|
||||
dataBase64: Buffer.from('unused replacement cover').toString('base64'),
|
||||
};
|
||||
const fetchMock = vi.fn()
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify({
|
||||
project: {
|
||||
app_id: 'space-cleaner',
|
||||
title: '现有标题',
|
||||
summary: '现有简介',
|
||||
status: 'published',
|
||||
cover_url: 'coverimage/existing.jpg',
|
||||
},
|
||||
latest_version: null,
|
||||
versions: [],
|
||||
}), { status: 200 }))
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify({
|
||||
version_id: 'version-2',
|
||||
review_status: 'building',
|
||||
build_job_id: 'job-2',
|
||||
build_status: 'queued',
|
||||
}), { status: 201 }));
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
const response = createResponse();
|
||||
prepareProjectReleaseMock.mockResolvedValueOnce(preparedRelease(tempDir));
|
||||
|
||||
await handleWorksRoutes(
|
||||
createRendererRequest('POST', {
|
||||
projectId: project.id,
|
||||
project: submittedMetadata,
|
||||
cover,
|
||||
}),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/works/projects/publish-source'),
|
||||
{
|
||||
opencodeProjectStore: { listProjects: vi.fn(async () => [project]) },
|
||||
agentBrowser: { preflightStaticArtifact: vi.fn(async () => ({ ok: true })) },
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(response.statusCode).toBe(201);
|
||||
expect(response.json()).toMatchObject({
|
||||
success: true,
|
||||
upload: { version_id: 'version-2' },
|
||||
metadata_disposition: {
|
||||
code: 'PUBLISHED_METADATA_PRESERVED',
|
||||
message: '作品已发布;本次仅提交新版本,名称、简介、作者信息和封面保持不变。',
|
||||
},
|
||||
});
|
||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||
expect(fetchMock.mock.calls[0]?.[0]).toBe(
|
||||
'https://square.nianxx.cn/api/projects/mine/space-cleaner/status',
|
||||
);
|
||||
expect(fetchMock.mock.calls[1]?.[0]).toBe(
|
||||
'https://square.nianxx.cn/api/projects/space-cleaner/versions/upload',
|
||||
);
|
||||
expect(fetchMock.mock.calls.some(([url]) => String(url).endsWith('/api/projects/covers'))).toBe(false);
|
||||
expect(fetchMock.mock.calls.some(([, init]) => init?.method === 'PATCH')).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['missing', undefined],
|
||||
['unknown', 'retired'],
|
||||
])('stops publishing when the ownership status is %s', async (_case, status) => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'makelore-source-unsafe-ownership-'));
|
||||
await writePublishableProject(tempDir);
|
||||
const project = { id: 'project-1', path: tempDir, name: 'space-cleaner' };
|
||||
const cover = {
|
||||
fileName: 'replacement.png',
|
||||
mimeType: 'image/png',
|
||||
dataBase64: Buffer.from('must not upload').toString('base64'),
|
||||
};
|
||||
const fetchMock = vi.fn().mockResolvedValueOnce(new Response(JSON.stringify({
|
||||
project: {
|
||||
app_id: 'space-cleaner',
|
||||
title: 'Existing title',
|
||||
summary: 'Existing summary',
|
||||
...(status === undefined ? {} : { status }),
|
||||
internal: 'private token=secret',
|
||||
},
|
||||
latest_version: null,
|
||||
versions: [],
|
||||
}), { status: 200 }));
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
const response = createResponse();
|
||||
prepareProjectReleaseMock.mockResolvedValueOnce(preparedRelease(tempDir));
|
||||
|
||||
await handleWorksRoutes(
|
||||
createRendererRequest('POST', {
|
||||
projectId: project.id,
|
||||
project: { app_id: 'space-cleaner', title: 'New title', summary: 'New summary' },
|
||||
cover,
|
||||
}),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/works/projects/publish-source'),
|
||||
{
|
||||
opencodeProjectStore: { listProjects: vi.fn(async () => [project]) },
|
||||
agentBrowser: { preflightStaticArtifact: vi.fn(async () => ({ ok: true })) },
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json()).toEqual({
|
||||
success: false,
|
||||
status: 502,
|
||||
code: 'PROJECT_OWNERSHIP_UNCONFIRMED',
|
||||
error: '这个作品的归属暂时无法确认,请稍后重试。',
|
||||
});
|
||||
expect(JSON.stringify(response.json())).not.toContain('token=secret');
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it.each(['draft', 'published'])('does not discard new-project form metadata when route preflight now sees %s', async (status) => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'makelore-source-form-race-'));
|
||||
await writePublishableProject(tempDir);
|
||||
const project = { id: 'project-1', path: tempDir, name: 'space-cleaner' };
|
||||
const fetchMock = vi.fn().mockResolvedValueOnce(new Response(JSON.stringify({
|
||||
project: {
|
||||
app_id: 'space-cleaner',
|
||||
title: 'Existing title',
|
||||
summary: 'Existing summary',
|
||||
status,
|
||||
},
|
||||
latest_version: null,
|
||||
versions: [],
|
||||
}), { status: 200 }));
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
const response = createResponse();
|
||||
prepareProjectReleaseMock.mockResolvedValueOnce(preparedRelease(tempDir));
|
||||
|
||||
await handleWorksRoutes(
|
||||
createRendererRequest('POST', {
|
||||
projectId: project.id,
|
||||
project: { app_id: 'space-cleaner', title: 'Form title', summary: 'Form summary' },
|
||||
}),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/works/projects/publish-source'),
|
||||
{
|
||||
opencodeProjectStore: { listProjects: vi.fn(async () => [project]) },
|
||||
agentBrowser: { preflightStaticArtifact: vi.fn(async () => ({ ok: true })) },
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json()).toEqual({
|
||||
success: false,
|
||||
status: 409,
|
||||
code: 'PROJECT_METADATA_CONFLICT',
|
||||
error: '作品状态已变化,本次未提交版本;请重新打开发布窗口确认现有资料。',
|
||||
});
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
expect(fetchMock.mock.calls.some(([url]) => String(url).endsWith('/api/projects'))).toBe(false);
|
||||
expect(fetchMock.mock.calls.some(([url]) => String(url).endsWith('/versions/upload'))).toBe(false);
|
||||
});
|
||||
|
||||
it('fails closed when a 404 create conflict reveals published metadata that the form did not show', async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'makelore-source-publish-race-'));
|
||||
await writePublishableProject(tempDir);
|
||||
const project = { id: 'project-1', path: tempDir, name: 'space-cleaner' };
|
||||
const submittedMetadata = {
|
||||
app_id: 'space-cleaner',
|
||||
title: 'Must not replace published title',
|
||||
summary: 'Must not replace published summary',
|
||||
};
|
||||
const ownedStatus = (status: 'published') => ({
|
||||
project: {
|
||||
app_id: 'space-cleaner',
|
||||
title: 'Existing title',
|
||||
summary: 'Existing summary',
|
||||
status,
|
||||
},
|
||||
latest_version: null,
|
||||
versions: [],
|
||||
});
|
||||
const fetchMock = vi.fn()
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 404 }))
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 409 }))
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify(ownedStatus('published')), { status: 200 }));
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
const response = createResponse();
|
||||
prepareProjectReleaseMock.mockResolvedValueOnce(preparedRelease(tempDir));
|
||||
|
||||
await handleWorksRoutes(
|
||||
createRendererRequest('POST', { projectId: project.id, project: submittedMetadata }),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/works/projects/publish-source'),
|
||||
{
|
||||
opencodeProjectStore: { listProjects: vi.fn(async () => [project]) },
|
||||
agentBrowser: { preflightStaticArtifact: vi.fn(async () => ({ ok: true })) },
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json()).toEqual({
|
||||
success: false,
|
||||
status: 409,
|
||||
code: 'PROJECT_METADATA_CONFLICT',
|
||||
error: '作品状态已变化,本次未提交版本;请重新打开发布窗口确认现有资料。',
|
||||
});
|
||||
expect(fetchMock.mock.calls.map(([url]) => String(url))).toEqual([
|
||||
'https://square.nianxx.cn/api/projects/mine/space-cleaner/status',
|
||||
'https://square.nianxx.cn/api/projects',
|
||||
'https://square.nianxx.cn/api/projects/mine/space-cleaner/status',
|
||||
]);
|
||||
expect(fetchMock.mock.calls.some(([url]) => String(url).endsWith('/api/projects/covers'))).toBe(false);
|
||||
expect(fetchMock.mock.calls.some(([, init]) => init?.method === 'PATCH')).toBe(false);
|
||||
});
|
||||
|
||||
it('fails closed without PATCH or version upload when a create conflict reconfirms draft ownership', async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'makelore-source-draft-conflict-'));
|
||||
await writePublishableProject(tempDir);
|
||||
const project = { id: 'project-1', path: tempDir, name: 'space-cleaner' };
|
||||
const submittedMetadata = {
|
||||
app_id: 'space-cleaner',
|
||||
title: 'Updated title',
|
||||
summary: 'Updated summary',
|
||||
};
|
||||
const draftStatus = {
|
||||
project: {
|
||||
app_id: 'space-cleaner',
|
||||
title: 'Existing title',
|
||||
summary: 'Existing summary',
|
||||
status: 'draft',
|
||||
},
|
||||
latest_version: null,
|
||||
versions: [],
|
||||
};
|
||||
const fetchMock = vi.fn()
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 404 }))
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 409 }))
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify(draftStatus), { status: 200 }));
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
const response = createResponse();
|
||||
prepareProjectReleaseMock.mockResolvedValueOnce(preparedRelease(tempDir));
|
||||
|
||||
await handleWorksRoutes(
|
||||
createRendererRequest('POST', { projectId: project.id, project: submittedMetadata }),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/works/projects/publish-source'),
|
||||
{
|
||||
opencodeProjectStore: { listProjects: vi.fn(async () => [project]) },
|
||||
agentBrowser: { preflightStaticArtifact: vi.fn(async () => ({ ok: true })) },
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json()).toEqual({
|
||||
success: false,
|
||||
status: 409,
|
||||
code: 'PROJECT_METADATA_CONFLICT',
|
||||
error: '作品状态已变化,本次未提交版本;请重新打开发布窗口确认现有资料。',
|
||||
});
|
||||
expect(fetchMock).toHaveBeenCalledTimes(3);
|
||||
expect(fetchMock.mock.calls.some(([url]) => String(url).endsWith('/api/projects/covers'))).toBe(false);
|
||||
expect(fetchMock.mock.calls.some(([, init]) => init?.method === 'PATCH')).toBe(false);
|
||||
expect(fetchMock.mock.calls.some(([url]) => String(url).endsWith('/versions/upload'))).toBe(false);
|
||||
});
|
||||
|
||||
it('submits an existing draft as version-only without create, cover upload, or metadata PATCH', async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'makelore-source-existing-draft-'));
|
||||
await writePublishableProject(tempDir);
|
||||
const project = { id: 'project-1', path: tempDir, name: 'space-cleaner' };
|
||||
const draftStatus = {
|
||||
project: {
|
||||
app_id: 'space-cleaner',
|
||||
title: 'Existing title',
|
||||
summary: 'Existing summary',
|
||||
status: 'draft',
|
||||
},
|
||||
latest_version: null,
|
||||
versions: [],
|
||||
};
|
||||
const fetchMock = vi.fn()
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify(draftStatus), { status: 200 }))
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify({
|
||||
version_id: 'version-2',
|
||||
review_status: 'building',
|
||||
build_job_id: 'job-2',
|
||||
build_status: 'queued',
|
||||
}), { status: 201 }));
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
const response = createResponse();
|
||||
prepareProjectReleaseMock.mockResolvedValueOnce(preparedRelease(tempDir));
|
||||
|
||||
await handleWorksRoutes(
|
||||
createRendererRequest('POST', {
|
||||
projectId: project.id,
|
||||
project: {
|
||||
app_id: 'space-cleaner',
|
||||
title: 'Existing title',
|
||||
summary: 'Existing summary',
|
||||
status: 'draft',
|
||||
},
|
||||
}),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/works/projects/publish-source'),
|
||||
{
|
||||
opencodeProjectStore: { listProjects: vi.fn(async () => [project]) },
|
||||
agentBrowser: { preflightStaticArtifact: vi.fn(async () => ({ ok: true })) },
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(response.statusCode).toBe(201);
|
||||
expect(response.json()).toMatchObject({ success: true, upload: { version_id: 'version-2' } });
|
||||
expect(fetchMock.mock.calls.map(([url]) => String(url))).toEqual([
|
||||
'https://square.nianxx.cn/api/projects/mine/space-cleaner/status',
|
||||
'https://square.nianxx.cn/api/projects/space-cleaner/versions/upload',
|
||||
]);
|
||||
expect(fetchMock.mock.calls.some(([url]) => String(url).endsWith('/api/projects/covers'))).toBe(false);
|
||||
expect(fetchMock.mock.calls.some(([, init]) => init?.method === 'PATCH')).toBe(false);
|
||||
});
|
||||
|
||||
it('stops before creating or uploading when the local browser preflight fails', async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'makelore-source-preflight-failure-'));
|
||||
await writePublishableProject(tempDir);
|
||||
@@ -1437,6 +1849,7 @@ describe('works square host api routes', () => {
|
||||
await writePublishableProject(tempDir);
|
||||
const project = { id: 'project-1', path: tempDir, name: 'space-cleaner' };
|
||||
const fetchMock = vi.fn()
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 404 }))
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 201 }))
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify({
|
||||
version_id: 'version-1',
|
||||
@@ -1475,7 +1888,7 @@ describe('works square host api routes', () => {
|
||||
},
|
||||
});
|
||||
expect(JSON.stringify(response.json())).not.toContain('disk unavailable');
|
||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||
expect(fetchMock).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('uses a bounded timestamp version when VERSION.md is oversized', async () => {
|
||||
@@ -1484,6 +1897,7 @@ describe('works square host api routes', () => {
|
||||
await writeFile(join(tempDir, 'VERSION.md'), Buffer.alloc(65 * 1024, 0x31));
|
||||
const project = { id: 'project-1', path: tempDir, name: 'space-cleaner' };
|
||||
const fetchMock = vi.fn()
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 404 }))
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 201 }))
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify({
|
||||
version_id: 'version-1',
|
||||
@@ -1510,7 +1924,7 @@ describe('works square host api routes', () => {
|
||||
);
|
||||
|
||||
expect(response.json().success).toBe(true);
|
||||
const uploadInit = fetchMock.mock.calls[1]?.[1] as RequestInit;
|
||||
const uploadInit = fetchMock.mock.calls[2]?.[1] as RequestInit;
|
||||
expect((uploadInit.body as FormData).get('version_name')).toMatch(/^v\d{14}$/);
|
||||
});
|
||||
|
||||
@@ -1527,9 +1941,11 @@ describe('works square host api routes', () => {
|
||||
const cancelSpy = vi.spyOn(retryResponse.body!, 'cancel');
|
||||
const uploadPayload = JSON.stringify({ version_id: 'version-1', review_status: 'building' });
|
||||
const fetchMock = vi.fn()
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 404 }))
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 201 }))
|
||||
.mockResolvedValueOnce(retryResponse)
|
||||
.mockResolvedValueOnce(new Response(uploadPayload, { status: 201 }))
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 404 }))
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 201 }))
|
||||
.mockRejectedValueOnce(new Error('socket reset with secret upstream detail'))
|
||||
.mockResolvedValueOnce(new Response(uploadPayload, { status: 201 }));
|
||||
@@ -1571,6 +1987,7 @@ describe('works square host api routes', () => {
|
||||
await writePublishableProject(tempDir);
|
||||
const project = { id: 'project-1', path: tempDir, name: 'space-cleaner' };
|
||||
const fetchMock = vi.fn()
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 404 }))
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 201 }))
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify({
|
||||
review_status: 'building',
|
||||
@@ -1616,7 +2033,9 @@ describe('works square host api routes', () => {
|
||||
token: 'secret-token',
|
||||
}), { status: 422 });
|
||||
const cancelSpy = vi.spyOn(upstream.body!, 'cancel');
|
||||
vi.stubGlobal('fetch', vi.fn().mockResolvedValueOnce(upstream));
|
||||
vi.stubGlobal('fetch', vi.fn()
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 404 }))
|
||||
.mockResolvedValueOnce(upstream));
|
||||
const response = createResponse();
|
||||
|
||||
await handleWorksRoutes(
|
||||
|
||||
Reference in new issue
Block a user