1import * as AWS from "@/AWS";
2import { Terminology } from "@/AWS/Translate/Terminology.ts";
3import * as Provider from "@/Provider";
4import * as Test from "@/Test/Alchemy";
5import * as translate from "@distilled.cloud/aws/translate";
6import { expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8import * as Schedule from "effect/Schedule";
9
10const { test } = Test.make({ providers: AWS.providers() });
11
12const terminologyName = "alchemy-test-translate-terminology";
13
14// Checked-in CSV fixtures — never generated at test time. The first row is
15// the language-code header; each subsequent row is a term pair.
16const CSV_V1 = ["en,es", "Alchemy,Alquimia"].join("\n");
17const CSV_V2 = ["en,es", "Alchemy,Alquimia", "Stack,Pila"].join("\n");
18
19const getTerminology = (name: string) =>
20 translate
21 .getTerminology({ Name: name })
22 .pipe(
23 Effect.catchTag("ResourceNotFoundException", () =>
24 Effect.succeed(undefined),
25 ),
26 );
27
28test.provider(
29 "lifecycle: create named terminology, update terms, destroy",
30 (stack) =>
31 Effect.gen(function* () {
32 yield* stack.destroy();
33
34 // Create — a named CSV terminology with a single term pair.
35 const deployed = yield* stack.deploy(
36 Effect.gen(function* () {
37 return yield* Terminology("TestGlossary", {
38 terminologyName,
39 file: CSV_V1,
40 format: "CSV",
41 description: "alchemy translate terminology test",
42 tags: { purpose: "alchemy-test" },
43 });
44 }),
45 );
46 expect(deployed.terminologyName).toBe(terminologyName);
47 expect(deployed.terminologyArn).toContain(
48 `:terminology/${terminologyName}`,
49 );
50 expect(deployed.sourceLanguageCode).toBe("en");
51 expect(deployed.targetLanguageCodes).toContain("es");
52 expect(deployed.termCount).toBe(1);
53
54 // Out-of-band verification via distilled — including tag sync.
55 const created = yield* getTerminology(terminologyName);
56 expect(created?.TerminologyProperties?.TermCount).toBe(1);
57 const tags = yield* translate.listTagsForResource({
58 ResourceArn: deployed.terminologyArn,
59 });
60 const tagRecord = Object.fromEntries(
61 (tags.Tags ?? []).map((t) => [t.Key, t.Value]),
62 );
63 expect(tagRecord.purpose).toBe("alchemy-test");
64 expect(tagRecord["alchemy::stack"]).toBeTruthy();
65
66 // Canonical list() coverage.
67 const provider = yield* Provider.findProvider(Terminology);
68 const all = yield* provider.list();
69 expect(all.some((t) => t.terminologyName === terminologyName)).toBe(true);
70
71 // Update — ImportTerminology is an OVERWRITE upsert; the second term
72 // pair lands in place. TermCount converges shortly after import.
73 yield* stack.deploy(
74 Effect.gen(function* () {
75 return yield* Terminology("TestGlossary", {
76 terminologyName,
77 file: CSV_V2,
78 format: "CSV",
79 description: "alchemy translate terminology test",
80 tags: { purpose: "alchemy-test" },
81 });
82 }),
83 );
84 const updated = yield* getTerminology(terminologyName).pipe(
85 Effect.repeat({
86 schedule: Schedule.spaced("3 seconds"),
87 until: (r): boolean => r?.TerminologyProperties?.TermCount === 2,
88 times: 10,
89 }),
90 );
91 expect(updated?.TerminologyProperties?.TermCount).toBe(2);
92
93 // Destroy — the terminology is gone.
94 yield* stack.destroy();
95 const after = yield* getTerminology(terminologyName);
96 expect(after).toBeUndefined();
97 }),
98 {
99 tags: ["provider:aws", "provider:aws:translate", "live"],
100 timeout: 180_000,
101 },
102);
103
104test.provider(
105 "generates a valid name when terminologyName is omitted",
106 (stack) =>
107 Effect.gen(function* () {
108 yield* stack.destroy();
109
110 const deployed = yield* stack.deploy(
111 Effect.gen(function* () {
112 return yield* Terminology("GeneratedName", {
113 file: CSV_V1,
114 format: "CSV",
115 });
116 }),
117 );
118 // Translate constrains names to ^([A-Za-z0-9-]_?)+$.
119 expect(deployed.terminologyName).toMatch(/^[A-Za-z0-9-]+$/);
120
121 const created = yield* getTerminology(deployed.terminologyName);
122 expect(created).toBeDefined();
123
124 yield* stack.destroy();
125 const after = yield* getTerminology(deployed.terminologyName);
126 expect(after).toBeUndefined();
127 }),
128 {
129 tags: ["provider:aws", "provider:aws:translate", "live"],
130 timeout: 180_000,
131 },
132);