Skip to content

Commit 7a4c990

Browse files
committed
Don't test the cache on index.html
1 parent e015fb2 commit 7a4c990

File tree

5 files changed

+53
-58
lines changed

5 files changed

+53
-58
lines changed

project/src/routes.scala

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -75,24 +75,23 @@ def routes[F[_]: Files: MonadThrow](
7575
val zdt = ZonedDateTime.now()
7676

7777
def userBrowserCacheHeaders(resp: Response[IO], lastModZdt: ZonedDateTime, injectStyles: Boolean) =
78-
resp.putHeaders(
79-
Header.Raw(ci"Cache-Control", "no-cache"),
80-
Header.Raw(
81-
ci"ETag",
82-
injectStyles match
83-
case true => hashTrue
84-
case false => hashFalse
85-
),
86-
Header.Raw(
87-
ci"Last-Modified",
88-
formatter.format(lastModZdt)
89-
),
90-
Header.Raw(
91-
ci"Expires",
92-
httpCacheFormat(ZonedDateTime.ofInstant(Instant.now().plusSeconds(10000000), ZoneId.of("GMT")))
78+
val hash = resp.body.through(fs2.hash.md5).through(fs2.text.hex.encode).compile.string
79+
hash.map: h =>
80+
resp.putHeaders(
81+
Header.Raw(ci"Cache-Control", "no-cache"),
82+
Header.Raw(
83+
ci"ETag",
84+
h
85+
),
86+
Header.Raw(
87+
ci"Last-Modified",
88+
formatter.format(lastModZdt)
89+
),
90+
Header.Raw(
91+
ci"Expires",
92+
httpCacheFormat(ZonedDateTime.ofInstant(Instant.now().plusSeconds(10000000), ZoneId.of("GMT")))
93+
)
9394
)
94-
)
95-
resp
9695
end userBrowserCacheHeaders
9796

9897
object StaticHtmlMiddleware:
@@ -101,7 +100,7 @@ def routes[F[_]: Files: MonadThrow](
101100
req.headers.get(ci"If-None-Match").map(_.toList) match
102101
case Some(h :: Nil) if h.value == hashFalse => OptionT.liftF(IO(Response[IO](Status.NotModified)))
103102
case Some(h :: Nil) if h.value == hashTrue => OptionT.liftF(IO(Response[IO](Status.NotModified)))
104-
case _ => service(req).map(userBrowserCacheHeaders(_, zdt, injectStyles))
103+
case _ => service(req).semiflatMap(userBrowserCacheHeaders(_, zdt, injectStyles))
105104
end match
106105

107106
}

project/test/resources/cat.webp

248 KB
Loading

project/test/resources/dog.webp

243 KB
Loading

project/test/src/RoutesSpec.scala

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -358,19 +358,22 @@ class RoutesSuite extends CatsEffectSuite:
358358
}
359359
}
360360

361-
externalIndexHtml.test("Static files are updated when needed") {
361+
externalIndexHtml.test("Static files are updated when needed and cached otherwise".only) {
362362
staticDir =>
363-
def cacheFormatTime = fileLastModified((staticDir / "index.html").toFs2).map {
364-
seconds =>
365-
httpCacheFormat(ZonedDateTime.ofInstant(Instant.ofEpochSecond(seconds), ZoneId.of("GMT")))
366-
}
363+
val cat = os.read.bytes(os.resource / "cat.webp")
367364

368365
val app = for
369366
logger <- IO(scribe.cats[IO]).toResource
370367
fileToHashRef <- Ref[IO].of(Map.empty[String, String]).toResource
371368
fileToHashMapRef = MapRef.fromSingleImmutableMapRef[IO, String, String](fileToHashRef)
372369
refreshPub <- Topic[IO, Unit].toResource
373-
// subscriber = refreshPub.subscribe(10).take(5).compile.toList
370+
_ <- IO.blocking(os.write(staticDir / "image.webp", cat)).toResource
371+
modifedAt <- fileLastModified((staticDir / "index.html").toFs2)
372+
.map {
373+
seconds =>
374+
httpCacheFormat(ZonedDateTime.ofInstant(Instant.ofEpochSecond(seconds), ZoneId.of("GMT")))
375+
}
376+
.toResource
374377
theseRoutes <- routes(
375378
os.temp.dir().toString,
376379
refreshPub,
@@ -379,40 +382,35 @@ class RoutesSuite extends CatsEffectSuite:
379382
fileToHashRef,
380383
None
381384
)(logger)
382-
yield (theseRoutes.orNotFound, logger)
385+
yield (theseRoutes.orNotFound, logger, modifedAt)
383386

384-
app
385-
.both(cacheFormatTime.toResource)
386-
.use {
387-
case ((served, logger), firstModified) =>
388-
val request1 = org.http4s.Request[IO](uri = org.http4s.Uri.unsafeFromString("/index.html"))
387+
app.use {
388+
case (served, logger, firstModified) =>
389+
val request1 = org.http4s.Request[IO](uri = org.http4s.Uri.unsafeFromString("/image.webp"))
389390

390-
val request2 = org
391-
.http4s
392-
.Request[IO](uri = org.http4s.Uri.unsafeFromString("/index.html"))
393-
.withHeaders(
394-
org.http4s.Headers.of(org.http4s.Header.Raw(ci"If-Modified-Since", firstModified.toString()))
395-
)
391+
val request2 = org
392+
.http4s
393+
.Request[IO](uri = org.http4s.Uri.unsafeFromString("/image.webp"))
394+
.withHeaders(
395+
org.http4s.Headers.of(org.http4s.Header.Raw(ci"If-Modified-Since", firstModified.toString()))
396+
)
396397

397-
served(request1).flatTap(r => logger.debug("headers" + r.headers.headers.mkString(","))) >>
398-
logger.trace("first modified " + firstModified) >>
399-
// You need these ... otherwise no caching.
400-
// https://simonhearne.com/2022/caching-header-best-practices/
401-
assertIOBoolean(served(request1).map(_.headers.get(ci"ETag").isDefined)) >>
402-
assertIOBoolean(served(request1).map(_.headers.get(ci"Cache-Control").isDefined)) >>
403-
assertIOBoolean(served(request1).map(_.headers.get(ci"Expires").isDefined)) >>
404-
assertIOBoolean(served(request1).map(_.headers.get(ci"Last-Modified").isDefined)) >>
405-
// Don't forget to set them _all_
406-
assertIO(served(request1).map(_.status.code), 200) >>
407-
assertIO(served(request2).map(_.status.code), 304) >>
408-
IO.sleep(
409-
1500.millis
410-
) >> // have to wait at least one second otherwish last modified could be the same, if test took <1 sceond to get to this point
411-
IO.blocking(os.write.over(staticDir / "index.html", """<head><title>Test</title></head>""")) >>
412-
served(request2).flatMap(_.bodyText.compile.string).flatMap(s => logger.trace(s)) >>
413-
assertIO(served(request2).map(_.status.code), 200)
398+
served(request1).flatTap(r => logger.debug("headers" + r.headers.headers.mkString(","))) >>
399+
logger.trace("first modified " + firstModified) >>
400+
// You need these ... otherwise no caching.
401+
// https://simonhearne.com/2022/caching-header-best-practices/
402+
assertIOBoolean(served(request1).map(_.headers.get(ci"ETag").isDefined)) >>
403+
assertIOBoolean(served(request1).map(_.headers.get(ci"Cache-Control").isDefined)) >>
404+
assertIOBoolean(served(request1).map(_.headers.get(ci"Expires").isDefined)) >>
405+
assertIOBoolean(served(request1).map(_.headers.get(ci"Last-Modified").isDefined)) >>
406+
// Don't forget to set them _all_
407+
assertIO(served(request1).map(_.status.code), 200) >>
408+
assertIO(served(request2).map(_.status.code), 304) >>
409+
IO.blocking(os.write.over(staticDir / "image.webp", os.read.bytes(os.resource / "dog.webp"))) >>
410+
served(request2).flatMap(_.bodyText.compile.string).flatMap(s => logger.trace(s)) >>
411+
assertIO(served(request2).map(_.status.code), 200)
414412

415-
}
413+
}
416414
}
417415

418416
externalIndexHtml.test("Client SPA routes return index.html") {

project/test/src/liveServer.test.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ trait PlaywrightTest extends CatsEffectSuite:
8787
}
8888
.toResource
8989

90-
val externalHtmlStyles = IO {
90+
val externalHtml = IO {
9191
val tempDir = os.temp.dir()
9292
val staticDir = tempDir / "assets"
9393
os.makeDir(staticDir)
@@ -204,12 +204,10 @@ trait PlaywrightTest extends CatsEffectSuite:
204204
}
205205

206206
ResourceFunFixture {
207-
externalHtmlStyles
207+
externalHtml
208208
.both(client)
209209
.flatMap {
210210
case ((dir, extHtmlDir), client) =>
211-
println(dir)
212-
println(extHtmlDir)
213211
val lsc = LiveServerConfig(
214212
baseDir = Some(dir.toString),
215213
indexHtmlTemplate = Some(extHtmlDir.toString),

0 commit comments

Comments
 (0)