1- import type { Response } from "@cloudflare/workers-types" ;
21import ezSpawn from "@jsdevtools/ez-spawn" ;
32import { simulation } from "@simulacrum/github-api-simulator" ;
43import fs from "node:fs/promises" ;
@@ -151,7 +150,8 @@ describe.sequential.each([
151150
152151 it ( `serves and installs playground-a for ${ mode } ` , async ( ) => {
153152 const [ owner , repo ] = payload . repository . full_name . split ( "/" ) ;
154- const sha = payload . workflow_run . head_sha . substring ( 0 , 7 ) ;
153+ const fullSha = payload . workflow_run . head_sha ;
154+ const sha = fullSha . substring ( 0 , 7 ) ;
155155 const ref = pr ?. payload . number ?? payload . workflow_run . head_branch ;
156156
157157 // Test download with SHA
@@ -163,9 +163,15 @@ describe.sequential.each([
163163 expect ( shaBlob . size ) . toBeGreaterThan ( 0 ) ;
164164
165165 // Test download with ref matches SHA content
166- const refResponse = await fetchWithRedirect (
166+ const refResponse = await worker . fetch (
167167 `/${ owner } /${ repo } /playground-a@${ ref } ` ,
168168 ) ;
169+ expect ( refResponse . status ) . toBe ( 200 ) ;
170+ expect ( refResponse . headers . get ( "x-pkg-name-key" ) ) . toBe ( "playground-a" ) ;
171+ expect ( refResponse . headers . get ( "x-commit-key" ) ) . toBe (
172+ `${ owner } :${ repo } :${ fullSha } ` ,
173+ ) ;
174+
169175 const refBlob = await refResponse . blob ( ) ;
170176 const shaBlobSize = await shaBlob . arrayBuffer ( ) ;
171177 const refBlobSize = await refBlob . arrayBuffer ( ) ;
@@ -189,6 +195,28 @@ describe.sequential.each([
189195 ) ;
190196 } , 10_000 ) ;
191197
198+ it ( `returns metadata for HEAD requests (${ mode } )` , async ( ) => {
199+ const [ owner , repo ] = payload . repository . full_name . split ( "/" ) ;
200+ const sha = payload . workflow_run . head_sha . substring ( 0 , 7 ) ;
201+
202+ const headResponse = await worker . fetch (
203+ `/${ owner } /${ repo } /playground-a@${ sha } ` ,
204+ { method : "HEAD" } ,
205+ ) ;
206+
207+ expect ( headResponse . status ) . toBe ( 200 ) ;
208+ expect ( headResponse . headers . get ( "x-pkg-name-key" ) ) . toBe ( "playground-a" ) ;
209+ expect ( headResponse . headers . get ( "x-commit-key" ) ) . toBe (
210+ `${ owner } :${ repo } :${ sha } ` ,
211+ ) ;
212+ expect ( headResponse . headers . get ( "content-type" ) ) . toBe (
213+ "application/tar+gzip" ,
214+ ) ;
215+ expect ( headResponse . headers . get ( "etag" ) ) . toBeDefined ( ) ;
216+ const lastModified = headResponse . headers . get ( "last-modified" ) ;
217+ expect ( new Date ( lastModified ! ) . toString ( ) ) . not . toBe ( "Invalid Date" ) ;
218+ } , 10_000 ) ;
219+
192220 it ( `serves and installs playground-b for ${ mode } ` , async ( ) => {
193221 const [ owner , repo ] = payload . repository . full_name . split ( "/" ) ;
194222 const sha = payload . workflow_run . head_sha . substring ( 0 , 7 ) ;
@@ -222,48 +250,57 @@ describe.sequential.each([
222250 } , 10_000 ) ;
223251} ) ;
224252
225- describe ( "URL redirects " , ( ) => {
253+ describe ( "URL resolution " , ( ) => {
226254 describe ( "standard packages" , ( ) => {
227- it ( "redirects full URLs correctly" , async ( ) => {
228- const response = await fetchWithRedirect ( "/tinylibs/tinybench@a832a55" ) ;
229- expect ( response . url ) . toContain ( "/tinylibs/tinybench/tinybench@a832a55" ) ;
255+ it . each ( [
256+ [ "full" , "/tinylibs/tinybench/tinybench@a832a55" ] ,
257+ [ "compact" , "/tinybench@a832a55" ] ,
258+ [ "with .tgz extension" , "/tinybench@a832a55.tgz" ] ,
259+ ] ) ( "resolves %s URLs" , async ( _ , url ) => {
260+ const response = await worker . fetch ( url ) ;
261+
262+ expect ( response . headers . get ( "x-commit-key" ) ) . toBe (
263+ "tinylibs:tinybench:a832a55" ,
264+ ) ;
265+ expect ( response . headers . get ( "x-pkg-name-key" ) ) . toBe ( "tinybench" ) ;
230266 } ) ;
231267
232- it ( "redirects compact URLs correctly" , async ( ) => {
233- const response = await fetchWithRedirect ( "/tinybench@a832a55" ) ;
234- expect ( response . url ) . toContain ( "/tinylibs/tinybench/tinybench@a832a55" ) ;
268+ it ( "resolves URL with full Git SHA" , async ( ) => {
269+ const response = await worker . fetch (
270+ "/tinylibs/tinybench/tinybench@a832a55e8f50c419ed8414024899e37e69b1f999" ,
271+ ) ;
272+
273+ expect ( response . headers . get ( "x-pkg-name-key" ) ) . toBe ( "tinybench" ) ;
274+ expect ( response . headers . get ( "x-commit-key" ) ) . toBe (
275+ "tinylibs:tinybench:a832a55e8f50c419ed8414024899e37e69b1f999" ,
276+ ) ;
235277 } ) ;
236278 } ) ;
237279
238280 describe ( "scoped packages" , ( ) => {
239- const expectedPath = `/stackblitz/sdk/${ encodeURIComponent ( "@stackblitz/sdk" ) } @a832a55` ;
281+ it . each ( [
282+ [ "full" , "/stackblitz/sdk/@stackblitz/sdk@a832a55" ] ,
283+ [ "encoded" , "/stackblitz/sdk/%40stackblitz%2Fsdk@a832a55" ] ,
284+ [ "compact" , "/@stackblitz/sdk@a832a55" ] ,
285+ [ "compact encoded" , "/%40stackblitz%2Fsdk@a832a55" ] ,
286+ ] ) ( "resolves %s URLs" , async ( _ , url ) => {
287+ const response = await worker . fetch ( url ) ;
240288
241- it ( "redirects full scoped package URLs correctly" , async ( ) => {
242- const response = await fetchWithRedirect (
243- "/ stackblitz/ sdk/@stackblitz/sdk@ a832a55" ,
289+ expect ( response . headers . get ( "x-pkg-name-key" ) ) . toBe ( "@stackblitz:sdk" ) ;
290+ expect ( response . headers . get ( "x-commit-key" ) ) . toBe (
291+ "stackblitz: sdk: a832a55" ,
244292 ) ;
245- expect ( response . url ) . toContain ( expectedPath ) ;
246293 } ) ;
247294
248- it ( "redirects compact scoped package URLs correctly" , async ( ) => {
249- const response = await fetchWithRedirect ( "/@stackblitz/sdk@a832a55" ) ;
250- expect ( response . url ) . toContain ( expectedPath ) ;
295+ it ( "resolves URL with full Git SHA" , async ( ) => {
296+ const response = await worker . fetch (
297+ "/stackblitz/sdk/@stackblitz/sdk@a832a55e8f50c419ed8414024899e37e69b1f999" ,
298+ ) ;
299+
300+ expect ( response . headers . get ( "x-pkg-name-key" ) ) . toBe ( "@stackblitz:sdk" ) ;
301+ expect ( response . headers . get ( "x-commit-key" ) ) . toBe (
302+ "stackblitz:sdk:a832a55e8f50c419ed8414024899e37e69b1f999" ,
303+ ) ;
251304 } ) ;
252305 } ) ;
253306} ) ;
254-
255- async function fetchWithRedirect (
256- url : string ,
257- maxRedirects = 999 ,
258- ) : Promise < Response > {
259- const response = await worker . fetch ( url , { redirect : "manual" } ) ;
260-
261- if ( response . status >= 300 && response . status < 400 && maxRedirects > 0 ) {
262- const location = response . headers . get ( "location" ) ;
263- if ( location ) {
264- return fetchWithRedirect ( location , maxRedirects - 1 ) ;
265- }
266- }
267-
268- return response as unknown as Response ;
269- }
0 commit comments