diff --git a/packages/stack/src/services/AuthStorage.integration.test.ts b/packages/stack/src/services/AuthStorage.integration.test.ts index d271baabd3..7a2fb725be 100644 --- a/packages/stack/src/services/AuthStorage.integration.test.ts +++ b/packages/stack/src/services/AuthStorage.integration.test.ts @@ -1,6 +1,6 @@ import { NodeHttpClient, NodeServices } from "@effect/platform-node"; -import { describe, expect, it } from "@effect/vitest"; -import { Effect, FileSystem, Layer, Redacted, Schema } from "effect"; +import { afterAll, beforeAll, describe, expect, it } from "@effect/vitest"; +import { Effect, Exit, FileSystem, Layer, Redacted, Schema, Scope } from "effect"; import { HttpClient, HttpClientRequest } from "effect/unstable/http"; import { SignJWT } from "jose"; import { makeStandaloneService } from "../../tests/standalone-service.ts"; @@ -10,55 +10,71 @@ import { makeDockerDatabaseRoot } from "../../tests/docker-fixture.ts"; import { httpHost } from "../../tests/helpers/endpoint.ts"; import { engineTarget, testEngine } from "../../tests/engine-target.ts"; -const options = (root: string) => ({ +const dockerOptions = (root: string) => ({ stackId: "catalog-auth-storage", instanceId: "instance", root, cacheRoot: `${root}/cache`, - runtime: "native" as const, -}); - -const dockerOptions = (root: string) => ({ - ...options(root), runtime: testEngine, engineTarget, }); +const secret = "catalog-auth-storage-secret-with-at-least-32-chars"; + +let scope: Scope.Closeable; +let root: string; +let stopDatabase: Effect.Effect | undefined; +let databaseUrl: string; + describe("service catalog", () => { + beforeAll(() => { + scope = Scope.makeUnsafe(); + return Effect.gen(function* () { + const databaseRoot = yield* makeDockerDatabaseRoot("catalog-auth-storage-"); + const databaseRecipe = yield* makeServiceRecipe( + { + service: "database", + config: { + version: "17", + databasePassword: Redacted.make("postgres"), + jwtSecret: Redacted.make(secret), + jwtExpiry: 3600, + }, + }, + dockerOptions(databaseRoot), + ); + const database = yield* makeStandaloneService(databaseRecipe.definition, { + id: "database", + config: databaseRecipe.creation, + }); + yield* database.start; + yield* database.ready; + const databaseRelay = yield* makeDockerTcpRelay(databaseRecipe.endpoint("sql")); + root = databaseRoot; + stopDatabase = database.stop.pipe(Effect.asVoid, Effect.orDie); + databaseUrl = `postgresql://supabase_admin:postgres@${databaseRelay.host}:${databaseRelay.port}/postgres`; + }).pipe( + Effect.provide(Layer.merge(NodeServices.layer, NodeHttpClient.layerNodeHttp)), + Scope.provide(scope), + Effect.runPromise, + ); + }, 120_000); + + afterAll( + () => + Effect.suspend(() => stopDatabase ?? Effect.void).pipe( + Effect.ensuring(Scope.close(scope, Exit.void)), + Effect.runPromise, + ), + 60_000, + ); + it.live( - "migrates Auth and serves Storage against the owned database", + "migrates Auth and signs in a signed-up user against the owned database", () => Effect.scoped( Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem; const client = yield* HttpClient.HttpClient; - const root = yield* makeDockerDatabaseRoot("catalog-auth-storage-"); - // Ownership is by location: Storage's filePath is a caller path and must live outside - // the stack's data root, not merely outside the service's own instance root. - const callerRoot = yield* fs.makeTempDirectoryScoped({ - prefix: "catalog-auth-storage-caller-", - }); - const secret = "catalog-auth-storage-secret-with-at-least-32-chars"; - const databaseRecipe = yield* makeServiceRecipe( - { - service: "database", - config: { - version: "17", - databasePassword: Redacted.make("postgres"), - jwtSecret: Redacted.make(secret), - jwtExpiry: 3600, - }, - }, - dockerOptions(root), - ); - const database = yield* makeStandaloneService(databaseRecipe.definition, { - id: "database", - config: databaseRecipe.creation, - }); - yield* database.start; - yield* database.ready; - const databaseRelay = yield* makeDockerTcpRelay(databaseRecipe.endpoint("sql")); - const databaseUrl = `postgresql://supabase_admin:postgres@${databaseRelay.host}:${databaseRelay.port}/postgres`; const authRecipe = yield* makeServiceRecipe( { service: "auth", @@ -100,7 +116,24 @@ describe("service catalog", () => { Schema.fromJsonString(Schema.Struct({ access_token: Schema.String })), )(loginBody); expect(loginToken.access_token.length).toBeGreaterThan(0); + yield* auth.stop; + }), + ).pipe(Effect.provide(Layer.merge(NodeServices.layer, NodeHttpClient.layerNodeHttp))), + { timeout: 120_000 }, + ); + it.live( + "serves Storage objects and imgproxy transforms against the owned database", + () => + Effect.scoped( + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const client = yield* HttpClient.HttpClient; + // Ownership is by location: Storage's filePath is a caller path and must live outside + // the stack's data root, not merely outside the service's own instance root. + const callerRoot = yield* fs.makeTempDirectoryScoped({ + prefix: "catalog-auth-storage-caller-", + }); const storageRoot = `${callerRoot}/storage`; yield* fs.makeDirectory(storageRoot, { recursive: true }); const imgproxyRecipe = yield* makeServiceRecipe( @@ -191,10 +224,8 @@ describe("service catalog", () => { ); expect(transformedImage.status).toBe(200); expect(transformedImage.headers["content-type"]).toContain("image/"); - yield* imgproxy.stop; yield* storage.stop; - yield* auth.stop; - yield* database.stop; + yield* imgproxy.stop; }), ).pipe(Effect.provide(Layer.merge(NodeServices.layer, NodeHttpClient.layerNodeHttp))), { timeout: 120_000 }, diff --git a/packages/stack/vitest.config.ts b/packages/stack/vitest.config.ts index ca0f72b3de..66ceb402d0 100644 --- a/packages/stack/vitest.config.ts +++ b/packages/stack/vitest.config.ts @@ -24,8 +24,9 @@ export default defineConfig({ include: ["**/*.integration.test.ts"], hookTimeout: 120_000, testTimeout: 30_000, - // Integration workers start real service processes and containers. - maxWorkers: 4, + // Most files start real containers, so the Docker engine bounds throughput; more + // workers only stretch each test's duration toward its timeout. + maxWorkers: 2, sequence: { groupOrder: 1 }, globalSetup: ["tests/docker-volume-run.ts"], },