fix(learning): remove archive size validation

This commit is contained in:
2026-08-20 14:34:31 +08:00
parent 2168e291b2
commit 85090844b4
6 changed files with 146 additions and 25 deletions

View File

@@ -67,6 +67,46 @@ describe('Learning project verified download', () => {
);
});
it('downloads without comparing metadata or transport-reported archive sizes', async () => {
const fetchImpl = vi.fn<typeof fetch>().mockResolvedValue(new Response(archive, {
status: 200,
headers: { 'Content-Type': 'application/zip', 'Content-Length': String(archive.length + 100) },
}));
const destinationPath = join(root, 'large-project.zip');
await saveLearningProjectArchive({
project: project({ archiveBytes: 512 * 1024 * 1024 + 1 }),
destinationPath,
binding,
fetchImpl,
getAccessToken: vi.fn().mockResolvedValue('works-token'),
isCurrentAccountBinding: () => true,
apiBaseUrl: 'https://square.example',
});
await expect(readFile(destinationPath)).resolves.toEqual(archive);
});
it('downloads when the archive response omits Content-Length', async () => {
const fetchImpl = vi.fn<typeof fetch>().mockResolvedValue(new Response(archive, {
status: 200,
headers: { 'Content-Type': 'application/zip' },
}));
const destinationPath = join(root, 'unknown-size-project.zip');
await saveLearningProjectArchive({
project: project({ archiveBytes: 1 }),
destinationPath,
binding,
fetchImpl,
getAccessToken: vi.fn().mockResolvedValue('works-token'),
isCurrentAccountBinding: () => true,
apiBaseUrl: 'https://square.example',
});
await expect(readFile(destinationPath)).resolves.toEqual(archive);
});
it('rejects an unsafe redirect and never forwards Bearer credentials to redirects', async () => {
const fetchImpl = vi.fn<typeof fetch>().mockResolvedValue(new Response(null, {
status: 302,

View File

@@ -161,6 +161,26 @@ describe('Learning project Main route boundary', () => {
expect(JSON.stringify(response.json)).not.toContain('private/project.zip');
});
it('accepts project metadata above the former client archive-size limit', async () => {
getAccessToken.mockResolvedValue('works-token');
const archiveBytes = 512 * 1024 * 1024 + 1;
fetchImpl.mockResolvedValue(envelope(project({ archiveBytes })));
const handler = createLearningRouteHandler({ fetchImpl, getAccessToken });
const response = createResponse();
await handler(
{ method: 'GET' } as IncomingMessage,
response.res,
new URL('http://127.0.0.1/api/works/learning/projects/project-1'),
{} as never,
);
expect(response.json).toMatchObject({
success: true,
data: { id: 'project-1', archiveBytes },
});
});
it('rejects malformed identities and unsafe media URLs in successful DTOs', async () => {
getAccessToken.mockResolvedValue('works-token');
fetchImpl.mockResolvedValue(envelope({