1import * as GitHub from "@/GitHub";
2import { GitHubCredentials } from "@/GitHub/Credentials.ts";
3import * as Provider from "@/Provider";
4import * as Test from "@/Test/Alchemy";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import { MinimumLogLevel } from "effect/References";
8
9const { test } = Test.make({ providers: GitHub.providers() });
10
11const logLevel = Effect.provideService(
12 MinimumLogLevel,
13 process.env.DEBUG ? "Debug" : "Info",
14);
15
16// Creating a real repository + variable requires an owner the token can
17// write to — the dedicated test org (never a real one). Set
18// GITHUB_TEST_OWNER="" to skip.
19const owner = process.env.GITHUB_TEST_OWNER ?? "alchemy-run-test";
20const repo = process.env.GITHUB_TEST_REPOSITORY ?? "test-repo";
21
22test.provider.skipIf(!owner)(
23 "list enumerates the deployed variable",
24 (stack) =>
25 Effect.gen(function* () {
26 // Clean up any leftovers from a previous run before deploying.
27 yield* stack.destroy();
28
29 yield* stack.deploy(
30 Effect.gen(function* () {
31 // `Repository` defaults to `retain`, so we intentionally do NOT pipe
32 // `destroy()` here: the test only needs a repo to host the variable,
33 // and deleting it would require `delete_repo`/admin rights. The repo
34 // is created once and reused (reconcile is idempotent).
35 const repository = yield* GitHub.Repository("Repo", {
36 owner,
37 name: repo,
38 description: "alchemy-effect variable list test",
39 visibility: "private",
40 autoInit: true,
41 });
42
43 return yield* GitHub.Variable("Variable", {
44 owner,
45 // Use the known repo name; reference a repository output in `value`
46 // so the engine orders this resource after the repository exists.
47 repository: repo,
48 name: "ALCHEMY_LIST_TEST",
49 value: repository.fullName,
50 });
51 }),
52 );
53
54 // Resolve the provider with the typed helper so `list()`'s element type
55 // is the resource's `Attributes` (no `any`).
56 const provider = yield* Provider.findProvider(GitHub.Variable);
57 const credentials = yield* yield* GitHubCredentials;
58 const client = credentials.octokit({ baseUrl: undefined });
59 // Exercise provider pagination within the dedicated test organization.
60 client.hook.before("request", (options) => {
61 const url = new URL(options.url, "https://api.github.com");
62 if (url.pathname === "/user/repos") {
63 url.pathname = `/orgs/${owner}/repos`;
64 options.url = url.toString();
65 }
66 });
67 const all = yield* provider
68 .list()
69 .pipe(
70 Effect.provideService(
71 GitHubCredentials,
72 Effect.succeed({ ...credentials, octokit: () => client }),
73 ),
74 );
75
76 // `list()` enumerates variables across repositories in the test org.
77 // The variable we just deployed guarantees at least one row. The
78 // resource's `Attributes` only exposes `updatedAt`, so we assert presence
79 // by the enumeration being non-empty (it cannot key on a specific name).
80 expect(all.length).toBeGreaterThan(0);
81 expect(all.every((v) => typeof v.updatedAt === "string")).toBe(true);
82
83 yield* stack.destroy();
84 }).pipe(logLevel),
85 {
86 tags: [
87 "provider:github",
88 "provider:github:repository",
89 "provider:github:variable",
90 "live",
91 ],
92 timeout: 180_000,
93 },
94);