1import * as Cloudflare from "alchemy/Cloudflare";
2import * as Test from "alchemy/Test/Bun";
3import { expect } from "bun:test";
4import * as Effect from "effect/Effect";
5import * as HttpApiClient from "effect/http-api/HttpApiClient";
6import { RepoApi } from "../src/Api.ts";
7import Stack from "../alchemy.run.ts";
8
9const { test, beforeAll, afterAll, deploy, destroy } = Test.make({
10 providers: Cloudflare.providers(),
11 state: Cloudflare.state(),
12});
13
14const stack = beforeAll(deploy(Stack));
15afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack));
16
17// Deterministic repo name — the same on every run (never Date.now()/random)
18// so a crashed run's leftover repo is purged below instead of leaking.
19const repoName = "tutorial-integ-lifecycle";
20
21test(
22 "repo lifecycle",
23 Effect.gen(function* () {
24 const { url } = yield* stack;
25 const client = yield* HttpApiClient.make(RepoApi, { baseUrl: url });
26
27 // Purge any leftover from a previously crashed run so createRepo
28 // doesn't fail with RepoConflict.
29 yield* client.repos
30 .deleteRepo({ params: { name: repoName } })
31 .pipe(Effect.catchTag("RepoNotFound", () => Effect.void));
32
33 const created = yield* client.repos.createRepo({
34 payload: { name: repoName, description: "tutorial repo" },
35 });
36 expect(created.name).toBe(repoName);
37 expect(created.remote).toBeString();
38 expect(created.token).toBeString();
39
40 const info = yield* client.repos.getRepo({ params: { name: repoName } });
41 expect(info.name).toBe(repoName);
42 expect(info.defaultBranch).toBe("main");
43 expect(info.metadata?.description).toBe("tutorial repo");
44 expect(info.metadata?.stars).toBe(0);
45
46 const token = yield* client.repos.cloneToken({
47 params: { name: repoName },
48 payload: { scope: "read", ttl: 600 },
49 });
50 expect(token.plaintext).toBeString();
51 expect(token.scope).toBe("read");
52
53 const updated = yield* client.repos.updateRepo({
54 params: { name: repoName },
55 payload: { description: "now with stars", topics: ["demo", "alchemy"] },
56 });
57 expect(updated.description).toBe("now with stars");
58 expect(updated.topics).toEqual(["demo", "alchemy"]);
59
60 const starred = yield* client.repos.starRepo({
61 params: { name: repoName },
62 });
63 expect(starred.stars).toBe(1);
64
65 yield* client.repos.deleteRepo({ params: { name: repoName } });
66 }),
67 { timeout: 120_000 },
68);