| 1 | import * as HttpRouter from "effect/http/HttpRouter"; |
| 2 | /** |
| 3 | * Shared test-stack fixture for the git-service suites (DESIGN.md §9). |
| 4 | * |
| 5 | * git-service ships no Worker of its own — the package exports building |
| 6 | * blocks (`ApiLive`, `ApiHandlersLive`, `ReposDurableObject`, |
| 7 | * `RegistryDurableObject`, …) that users assemble into their own |
| 8 | * `Cloudflare.Worker`. This fixture is exactly that assembly: the same |
| 9 | * shape the example app and the RFC's headline snippet use. |
| 10 | * |
| 11 | * Every suite deploys under its own stack name so concurrently-running |
| 12 | * suites never share (or race on) a deployment. |
| 13 | * |
| 14 | * The suites authenticate with two shared secrets, resolved by the |
| 15 | * user-land middleware in `test-auth.ts`; the engine sees no credential. |
| 16 | */ |
| 17 | import * as Alchemy from "@/index.ts"; |
| 18 | import * as Cloudflare from "@/Cloudflare"; |
| 19 | import * as Effect from "effect/Effect"; |
| 20 | import * as Layer from "effect/Layer"; |
| 21 | import { |
| 22 | BlobStoreR2, |
| 23 | GIT_WORKER_OPTIONS, |
| 24 | ApiHandlersLive, |
| 25 | HasherInline, |
| 26 | ReposDurableObject, |
| 27 | RegistryDurableObject, |
| 28 | } from "@/Git/index.ts"; |
| 29 | import { TestRoutes } from "./http.ts"; |
| 30 | import { TestApi, TestAuthLive, TestCaller } from "./test-auth.ts"; |
| 31 | |
| 32 | export { |
| 33 | TEST_SECRET, |
| 34 | TEST_SECRET_DEV, |
| 35 | TEST_USER, |
| 36 | TEST_USER_DEV, |
| 37 | TestApi, |
| 38 | TestAuthLive, |
| 39 | } from "./test-auth.ts"; |
| 40 | |
| 41 | /** The suites' bucket — owned by the assembly, like any user's. */ |
| 42 | const GitObjects = Cloudflare.R2.Bucket("GitObjects", { |
| 43 | // Test stacks must tear down even with packs/bundles/head snapshots |
| 44 | // still in the bucket (the e2e benches leave repos behind by design). |
| 45 | forceDestroy: true, |
| 46 | }); |
| 47 | |
| 48 | /** Storage and implementation layers for the application router. */ |
| 49 | const GitLive = TestRoutes.pipe( |
| 50 | Layer.provide(ApiHandlersLive), |
| 51 | Layer.provide(ReposDurableObject), |
| 52 | Layer.provide(RegistryDurableObject), |
| 53 | // In-process hashing: service-binding fan-out runs on the caller's |
| 54 | // thread on Workers (DESIGN §22.10), so the simpler layer is the reference. |
| 55 | Layer.provide(HasherInline), |
| 56 | Layer.provide(BlobStoreR2(GitObjects)), |
| 57 | ); |
| 58 | |
| 59 | /** The suites' git host: the reference building-block assembly. */ |
| 60 | export default class TestGitHost extends Cloudflare.Worker<TestGitHost>()( |
| 61 | "GitWorker", |
| 62 | { |
| 63 | main: import.meta.url, |
| 64 | ...GIT_WORKER_OPTIONS, |
| 65 | observability: { enabled: true }, |
| 66 | }, |
| 67 | Effect.gen(function* () { |
| 68 | const fetch = yield* HttpRouter.toHttpEffect(GitLive); |
| 69 | return { fetch }; |
| 70 | }), |
| 71 | ) {} |
| 72 | |
| 73 | /** |
| 74 | * Builds the deployable test stack under a suite-specific name. |
| 75 | * |
| 76 | * @example |
| 77 | * ```typescript |
| 78 | * const Stack = makeTestStack("GitServiceTestStack"); |
| 79 | * const stack = beforeAll(deploy(Stack)); |
| 80 | * afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack)); |
| 81 | * ``` |
| 82 | */ |
| 83 | export const makeTestStack = (name: string) => |
| 84 | Alchemy.Stack( |
| 85 | name, |
| 86 | { providers: Cloudflare.providers(), state: Cloudflare.state() }, |
| 87 | Effect.gen(function* () { |
| 88 | const host = yield* TestGitHost; |
| 89 | return { url: host.url.as<string>() }; |
| 90 | }), |
| 91 | ); |