Files
recflare/apps/studio/src/test/integration/distribution.test.ts
T
2026-10-21 17:32:56 -04:00

133 lines
4.5 KiB
TypeScript

import { env, SELF } from 'cloudflare:test'
import { beforeEach, expect, it } from 'vitest'
import type { Env } from '../../context'
declare module 'cloudflare:test' {
interface ProvidedEnv extends Env {}
}
const origin = 'https://example.com/1/apps/template-fixture'
beforeEach(async () => {
env.STUDIO_DISTRIBUTION_MANIFEST = 'distribution/catalog.json'
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode('template-fixture'))
await env.CDN_ASSETS.put(
env.STUDIO_DISTRIBUTION_MANIFEST,
JSON.stringify({
appSecretSha256: Array.from(new Uint8Array(digest), (b) =>
b.toString(16).padStart(2, '0')
).join(''),
latest: 170,
legacySummaries: { '169': { files: [{ path: 'original.tgz', hash: '87654321', size: 80 }] } },
releases: [
{
version: 170,
label: 'v170',
partSize: 32,
contentUrl: 'https://cdn.example.test/template.zip',
summary: {
hash_code: 'a'.repeat(64),
compression_method: 'zip',
size: 50,
files: [{ path: 'patched.tgz', hash: '12345678', size: 80 }],
chunks: { size: 32, hashes: ['12345678', '87654321'] },
},
},
],
})
)
})
it('serves the prepatched v170 catalog and complete multipart download contract', async () => {
expect(await (await SELF.fetch(origin + '/changelog')).json()).toEqual({
versions: [{ vid: 170, label: 'v170', changelog: 'v170' }],
})
expect(await (await SELF.fetch(origin + '/versions/latest/id')).json()).toEqual({ id: 170 })
expect(await (await SELF.fetch(origin)).json()).toMatchObject({
current_version: 170,
use_keys: false,
})
expect(await (await SELF.fetch(origin + '/versions/170')).json()).toMatchObject({
id: 170,
label: 'v170',
patch_links: { incoming: [], outgoing: [] },
content_guid: 'a'.repeat(64),
})
expect(await (await SELF.fetch(origin + '/versions/170/content_urls')).json()).toEqual([
{ url: 'https://cdn.example.test/template.zip', part_size: 32 },
])
expect(await (await SELF.fetch(origin + '/versions/170/content_summary')).json()).toMatchObject({
compression_method: 'zip',
chunks: { hashes: ['12345678', '87654321'] },
})
})
it('rejects malformed or unsafe catalogs without caching a broken update response', async () => {
const key = env.STUDIO_DISTRIBUTION_MANIFEST!
const original = await (await env.CDN_ASSETS.get(key))!.text()
for (const change of [
(c: any) => {
c.latest = 171
},
(c: any) => {
c.releases.push(c.releases[0])
},
(c: any) => {
c.legacySummaries['170'] = c.legacySummaries['169']
},
(c: any) => {
c.releases[0].contentUrl = 'http://cdn.example.test/content.zip'
},
(c: any) => {
c.releases[0].summary.files[0].path = '../outside'
},
(c: any) => {
c.releases[0].summary.files = null
},
(c: any) => {
delete c.releases[0].summary.hash_code
},
(c: any) => {
c.releases[0].summary.chunks.hashes = []
},
]) {
const catalog = JSON.parse(original)
change(catalog)
await env.CDN_ASSETS.put(key, JSON.stringify(catalog))
const response = await SELF.fetch(origin + '/versions/170/diff_summary')
expect(response.status).toBe(503)
expect(response.headers.get('Cache-Control')).toBe('no-store')
}
await env.CDN_ASSETS.put(key, '{')
expect((await SELF.fetch(origin)).status).toBe(503)
await env.CDN_ASSETS.put(key, ' '.repeat(8 * 1024 * 1024 + 1))
expect((await SELF.fetch(origin)).status).toBe(503)
})
it('does not route other applications, missing versions, or disabled catalogs', async () => {
expect((await SELF.fetch('https://example.com/1/apps/unrelated/versions/latest/id')).status).toBe(
404
)
expect(await (await SELF.fetch(origin + '/versions/169')).json()).toMatchObject({
id: 169,
label: 'v169',
})
expect((await SELF.fetch(origin + '/versions/168')).status).toBe(404)
expect((await SELF.fetch(origin + '/versions/169/content_urls')).status).toBe(404)
delete env.STUDIO_DISTRIBUTION_MANIFEST
expect((await SELF.fetch(origin + '/versions/latest/id')).status).toBe(404)
})
it('provides cached comparison and full-replacement update metadata without legacy downloads', async () => {
expect(await (await SELF.fetch(origin + '/versions/169/content_summary')).json()).toMatchObject({
files: [{ path: 'original.tgz', hash: '87654321' }],
})
expect(await (await SELF.fetch(origin + '/versions/170/diff_summary')).json()).toMatchObject({
compression_method: 'zip',
added_files: ['patched.tgz'],
modified_files: [],
unchanged_files: [],
})
expect(await (await SELF.fetch(origin + '/versions/170/diff_urls')).json()).toEqual([
{ url: 'https://cdn.example.test/template.zip', part_size: 32 },
])
})