1import * as AWS from "@/AWS";
2import { Domain } from "@/AWS/CodeArtifact/Domain.ts";
3import { Repository } from "@/AWS/CodeArtifact/Repository.ts";
4import * as Provider from "@/Provider";
5import * as Test from "@/Test/Alchemy";
6import * as codeartifact from "@distilled.cloud/aws/codeartifact";
7import { expect } from "alchemy-test";
8import * as Effect from "effect/Effect";
9
10const { test } = Test.make({ providers: AWS.providers() });
11
12// CodeArtifact domain names must be lowercase; keep them deterministic.
13const domainName = "alchemy-test-ca";
14const repositoryName = "alchemy-test-repo";
15
16const getRepository = codeartifact
17 .describeRepository({ domain: domainName, repository: repositoryName })
18 .pipe(
19 Effect.map((res) => res.repository),
20 Effect.catchTag("ResourceNotFoundException", () =>
21 Effect.succeed(undefined),
22 ),
23 );
24
25const makeStack = (description: string, upstreamDescription: string) =>
26 Effect.gen(function* () {
27 const domain = yield* Domain("Domain", { domainName });
28 const shared = yield* Repository("Shared", {
29 domain: domain.domainName,
30 repositoryName: `${repositoryName}-shared`,
31 description: upstreamDescription,
32 });
33 const repo = yield* Repository("Repo", {
34 domain: domain.domainName,
35 repositoryName,
36 description,
37 upstreams: [shared.repositoryName],
38 tags: { env: "test" },
39 });
40 return { domain, repo };
41 });
42
43test.provider(
44 "lifecycle: create domain + repository with upstream, update, destroy",
45 (stack) =>
46 Effect.gen(function* () {
47 yield* stack.destroy();
48
49 // Create.
50 const deployed = yield* stack.deploy(makeStack("first", "shared-first"));
51 expect(deployed.repo.repositoryName).toBe(repositoryName);
52 expect(deployed.repo.repositoryArn).toContain(`:repository/`);
53 expect(deployed.domain.domainName).toBe(domainName);
54 expect(deployed.domain.domainArn).toContain(`:domain/`);
55
56 // Out-of-band verification via distilled.
57 const created = yield* getRepository;
58 expect(created?.name).toBe(repositoryName);
59 expect(created?.description).toBe("first");
60 expect(created?.upstreams?.map((u) => u.repositoryName)).toContain(
61 `${repositoryName}-shared`,
62 );
63
64 const domainCheck = yield* codeartifact.describeDomain({
65 domain: domainName,
66 });
67 expect(domainCheck.domain?.name).toBe(domainName);
68 expect(domainCheck.domain?.repositoryCount).toBeGreaterThanOrEqual(2);
69
70 // Canonical list() coverage for Domain.
71 const domainProvider = yield* Provider.findProvider(Domain);
72 const domains = yield* domainProvider.list();
73 expect(domains.some((d) => d.domainName === domainName)).toBe(true);
74
75 // Update — change the repository description in place (no replacement).
76 yield* stack.deploy(makeStack("second", "shared-first"));
77 const updated = yield* getRepository;
78 expect(updated?.description).toBe("second");
79
80 // Destroy — verify both resources are gone out-of-band.
81 yield* stack.destroy();
82 const afterRepo = yield* getRepository;
83 expect(afterRepo).toBeUndefined();
84 const afterDomain = yield* codeartifact
85 .describeDomain({ domain: domainName })
86 .pipe(
87 Effect.map((res) => res.domain),
88 Effect.catchTag("ResourceNotFoundException", () =>
89 Effect.succeed(undefined),
90 ),
91 );
92 expect(afterDomain).toBeUndefined();
93 }),
94 {
95 tags: ["provider:aws", "provider:aws:codeartifact", "live"],
96 timeout: 300_000,
97 },
98);