1- import { assertEquals , assertExists } from "@std/assert" ;
1+ import { assertEquals , assertExists } from "jsr: @std/assert" ;
22import { createCacheHandler } from "../../src/handlers.ts" ;
3+ import { assertSpyCalls , spy } from "jsr:@std/testing/mock" ;
34
45// Unified handler tests replacing legacy read/write/middleware handlers
56
67Deno . test ( "cache miss invokes handler and caches response" , async ( ) => {
78 await caches . delete ( "test-miss" ) ;
89 const cacheName = "test-miss" ;
910 const handle = createCacheHandler ( { cacheName } ) ;
10- let invoked = 0 ;
1111 const url = "http://example.com/api/users" ;
12- const res = await handle ( new Request ( url ) , {
13- handler : ( ) => {
14- invoked ++ ;
15- return Promise . resolve (
16- new Response ( "fresh" , {
17- headers : {
18- "cache-control" : "max-age=3600, public" ,
19- "cache-tag" : "user:123" ,
20- "content-type" : "application/json" ,
21- } ,
22- } ) ,
23- ) ;
24- } ,
25- } ) ;
26- assertEquals ( invoked , 1 ) ;
12+ const handler = spy ( ( ) =>
13+ Promise . resolve (
14+ new Response ( "fresh" , {
15+ headers : {
16+ "cache-control" : "max-age=3600, public" ,
17+ "cache-tag" : "user:123" ,
18+ "content-type" : "application/json" ,
19+ } ,
20+ } ) ,
21+ )
22+ ) ;
23+ const res = await handle ( new Request ( url ) , { handler } ) ;
24+ assertSpyCalls ( handler , 1 ) ;
2725 assertEquals ( await res . clone ( ) . text ( ) , "fresh" ) ;
2826 const cache = await caches . open ( cacheName ) ;
2927 const cached = await cache . match ( url ) ;
@@ -36,25 +34,19 @@ Deno.test("cache hit returns cached without invoking handler", async () => {
3634 await caches . delete ( "test-hit" ) ;
3735 const cacheName = "test-hit" ;
3836 const handle = createCacheHandler ( { cacheName } ) ;
39- let invoked = 0 ;
4037 const url = "http://example.com/api/users" ;
41- await handle ( new Request ( url ) , {
42- handler : ( ) => {
43- invoked ++ ;
44- return Promise . resolve (
45- new Response ( "value" , {
46- headers : { "cache-control" : "max-age=3600, public" } ,
47- } ) ,
48- ) ;
49- } ,
50- } ) ;
51- const second = await handle ( new Request ( url ) , {
52- handler : ( ) => {
53- invoked ++ ;
54- return Promise . resolve ( new Response ( "should-not" ) ) ;
55- } ,
56- } ) ;
57- assertEquals ( invoked , 1 ) ;
38+ const prime = spy ( ( ) =>
39+ Promise . resolve (
40+ new Response ( "value" , {
41+ headers : { "cache-control" : "max-age=3600, public" } ,
42+ } ) ,
43+ )
44+ ) ;
45+ await handle ( new Request ( url ) , { handler : prime } ) ;
46+ const missHandler = spy ( ( ) => Promise . resolve ( new Response ( "should-not" ) ) ) ;
47+ const second = await handle ( new Request ( url ) , { handler : missHandler } ) ;
48+ assertSpyCalls ( prime , 1 ) ;
49+ assertSpyCalls ( missHandler , 0 ) ;
5850 assertEquals ( await second . text ( ) , "value" ) ;
5951 await caches . delete ( cacheName ) ;
6052} ) ;
@@ -72,18 +64,15 @@ Deno.test("expired cached entry is ignored and handler re-invoked", async () =>
7264 } ) ,
7365 ) ;
7466 const handle = createCacheHandler ( { cacheName } ) ;
75- let invoked = 0 ;
76- const res = await handle ( new Request ( url ) , {
77- handler : ( ) => {
78- invoked ++ ;
79- return Promise . resolve (
80- new Response ( "new" , {
81- headers : { "cache-control" : "max-age=60, public" } ,
82- } ) ,
83- ) ;
84- } ,
85- } ) ;
86- assertEquals ( invoked , 1 ) ;
67+ const handler = spy ( ( ) =>
68+ Promise . resolve (
69+ new Response ( "new" , {
70+ headers : { "cache-control" : "max-age=60, public" } ,
71+ } ) ,
72+ )
73+ ) ;
74+ const res = await handle ( new Request ( url ) , { handler } ) ;
75+ assertSpyCalls ( handler , 1 ) ;
8776 assertEquals ( await res . text ( ) , "new" ) ;
8877 await caches . delete ( cacheName ) ;
8978} ) ;
@@ -92,22 +81,17 @@ Deno.test("non-cacheable response is not stored", async () => {
9281 await caches . delete ( "test-non-cacheable" ) ;
9382 const cacheName = "test-non-cacheable" ;
9483 const handle = createCacheHandler ( { cacheName } ) ;
95- let invoked = 0 ;
9684 const url = "http://example.com/api/users" ;
97- await handle ( new Request ( url ) , {
98- handler : ( ) => {
99- invoked ++ ;
100- return Promise . resolve (
101- new Response ( "nc" , {
102- headers : { "cache-control" : "no-cache, private" } ,
103- } ) ,
104- ) ;
105- } ,
106- } ) ;
85+ const handler = spy ( ( ) =>
86+ Promise . resolve (
87+ new Response ( "nc" , { headers : { "cache-control" : "no-cache, private" } } ) ,
88+ )
89+ ) ;
90+ await handle ( new Request ( url ) , { handler } ) ;
10791 const cache = await caches . open ( cacheName ) ;
10892 const cached = await cache . match ( url ) ;
10993 assertEquals ( cached , undefined ) ;
110- assertEquals ( invoked , 1 ) ;
94+ assertSpyCalls ( handler , 1 ) ;
11195 await caches . delete ( cacheName ) ;
11296} ) ;
11397
@@ -116,22 +100,23 @@ Deno.test("second call after cacheable response strips cache-tag header from ret
116100 const cacheName = "test-strip" ;
117101 const handle = createCacheHandler ( { cacheName } ) ;
118102 const url = "http://example.com/api/users" ;
119- const first = await handle ( new Request ( url ) , {
120- handler : ( ) =>
121- Promise . resolve (
122- new Response ( "body" , {
123- headers : {
124- "cache-control" : "max-age=3600, public" ,
125- "cache-tag" : "user:1" ,
126- } ,
127- } ) ,
128- ) ,
129- } ) ;
103+ const prime = spy ( ( ) =>
104+ Promise . resolve (
105+ new Response ( "body" , {
106+ headers : {
107+ "cache-control" : "max-age=3600, public" ,
108+ "cache-tag" : "user:1" ,
109+ } ,
110+ } ) ,
111+ )
112+ ) ;
113+ const first = await handle ( new Request ( url ) , { handler : prime } ) ;
114+ assertSpyCalls ( prime , 1 ) ;
130115 // Returned response should not expose cache-tag header (implementation strips during write)
131116 assertEquals ( first . headers . has ( "cache-tag" ) , false ) ;
132- const second = await handle ( new Request ( url ) , {
133- handler : ( ) => Promise . resolve ( new Response ( "should-not" ) ) ,
134- } ) ;
117+ const miss = spy ( ( ) => Promise . resolve ( new Response ( "should-not" ) ) ) ;
118+ const second = await handle ( new Request ( url ) , { handler : miss } ) ;
119+ assertSpyCalls ( miss , 0 ) ;
135120 assertEquals ( await second . text ( ) , "body" ) ;
136121 await caches . delete ( cacheName ) ;
137122} ) ;
@@ -141,24 +126,18 @@ Deno.test("cached response served instead of invoking handler (middleware analog
141126 const cacheName = "test-middleware-analogue" ;
142127 const handle = createCacheHandler ( { cacheName } ) ;
143128 const url = "http://example.com/api/users" ;
144- let invoked = 0 ;
145- await handle ( new Request ( url ) , {
146- handler : ( ) => {
147- invoked ++ ;
148- return Promise . resolve (
149- new Response ( "prime" , {
150- headers : { "cache-control" : "max-age=120, public" } ,
151- } ) ,
152- ) ;
153- } ,
154- } ) ;
155- const hit = await handle ( new Request ( url ) , {
156- handler : ( ) => {
157- invoked ++ ;
158- return Promise . resolve ( new Response ( "miss" ) ) ;
159- } ,
160- } ) ;
161- assertEquals ( invoked , 1 ) ;
129+ const prime = spy ( ( ) =>
130+ Promise . resolve (
131+ new Response ( "prime" , {
132+ headers : { "cache-control" : "max-age=120, public" } ,
133+ } ) ,
134+ )
135+ ) ;
136+ await handle ( new Request ( url ) , { handler : prime } ) ;
137+ const miss = spy ( ( ) => Promise . resolve ( new Response ( "miss" ) ) ) ;
138+ const hit = await handle ( new Request ( url ) , { handler : miss } ) ;
139+ assertSpyCalls ( prime , 1 ) ;
140+ assertSpyCalls ( miss , 0 ) ;
162141 assertEquals ( await hit . text ( ) , "prime" ) ;
163142 await caches . delete ( cacheName ) ;
164143} ) ;
0 commit comments