1import * 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 */
17import * as Alchemy from "@/index.ts";
18import * as Cloudflare from "@/Cloudflare";
19import * as Effect from "effect/Effect";
20import * as Layer from "effect/Layer";
21import {
22 BlobStoreR2,
23 GIT_WORKER_OPTIONS,
24 ApiHandlersLive,
25 HasherInline,
26 ReposDurableObject,
27 RegistryDurableObject,
28} from "@/Git/index.ts";
29import { TestRoutes } from "./http.ts";
30import { TestApi, TestAuthLive, TestCaller } from "./test-auth.ts";
31
32export {
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. */
42const 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. */
49const 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. */
60export 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 */
83export 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 );