1import { LibsqlClient } from "@effect/sql-libsql"
2import { Context, Effect, Layer } from "effect"
3import { GenericContainer, type StartedTestContainer } from "testcontainers"
4
5const waitForServer = async (container: StartedTestContainer) => {
6 const url = `http://${container.getHost()}:${container.getMappedPort(8080)}`
7 let error: unknown = undefined
8
9 for (let i = 0; i < 60; i++) {
10 try {
11 await fetch(url)
12 return
13 } catch (cause) {
14 error = cause
15 await new Promise((resolve) => setTimeout(resolve, 250))
16 }
17 }
18
19 throw error
20}
21
22export class LibsqlContainer extends Context.Service<
23 LibsqlContainer,
24 StartedTestContainer
25>()("test/LibsqlContainer") {
26 static readonly layer = Layer.effect(this)(
27 Effect.acquireRelease(
28 Effect.promise(async () => {
29 const container = await new GenericContainer("ghcr.io/tursodatabase/libsql-server:main")
30 .withExposedPorts(8080)
31 .withEnvironment({ SQLD_NODE: "primary" })
32 .withCommand(["sqld", "--no-welcome", "--http-listen-addr", "0.0.0.0:8080"]).start()
33
34 try {
35 await waitForServer(container)
36 return container
37 } catch (error) {
38 await container.stop()
39 throw error
40 }
41 }),
42 (container) => Effect.promise(() => container.stop())
43 )
44 )
45
46 static layerClient = Layer.unwrap(
47 Effect.gen(function*() {
48 const container = yield* LibsqlContainer
49 return LibsqlClient.layer({
50 url: `http://${container.getHost()}:${container.getMappedPort(8080)}`
51 })
52 })
53 ).pipe(Layer.provide(this.layer))
54}