1import * as AWS from "@/AWS";
2import { Transformer } from "@/AWS/B2BI";
3import * as Test from "@/Test/Alchemy";
4import * as b2bi from "@distilled.cloud/aws/b2bi";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import * as Schedule from "effect/Schedule";
8
9const { test } = Test.make({ providers: AWS.providers() });
10
11const assertTransformerGone = (transformerId: string) =>
12 Effect.gen(function* () {
13 const result = yield* b2bi.getTransformer({ transformerId }).pipe(
14 Effect.map(() => "present" as const),
15 Effect.catchTag("ResourceNotFoundException", () =>
16 Effect.succeed("gone" as const),
17 ),
18 );
19 if (result === "present") {
20 return yield* Effect.fail(
21 new Error(`Transformer '${transformerId}' still exists`),
22 );
23 }
24 }).pipe(
25 Effect.retry({
26 schedule: Schedule.max([Schedule.fixed("2 seconds"), Schedule.recurs(8)]),
27 }),
28 );
29
30const inputConversion: b2bi.InputConversion = {
31 fromFormat: "X12",
32 formatOptions: {
33 x12: { transactionSet: "X12_850", version: "VERSION_4010" },
34 },
35};
36
37const mapping: b2bi.Mapping = {
38 templateLanguage: "JSONATA",
39 template: '{ "orderId": "test" }',
40};
41
42// Transformers are credential-free; the full lifecycle runs ungated.
43test.provider(
44 "create, update, activate, replace-on-active-change, and destroy",
45 (stack) =>
46 Effect.gen(function* () {
47 yield* stack.destroy();
48
49 // Create (defaults to inactive).
50 const created = yield* stack.deploy(
51 Transformer("X12ToJson", {
52 name: "alchemy-b2bi-transformer",
53 inputConversion,
54 mapping,
55 }),
56 );
57 expect(created.transformerId).toMatch(/^tr-/);
58 expect(created.name).toBe("alchemy-b2bi-transformer");
59 expect(created.status).toBe("inactive");
60
61 // Out-of-band verification.
62 const described = yield* b2bi.getTransformer({
63 transformerId: created.transformerId,
64 });
65 expect(described.name).toBe("alchemy-b2bi-transformer");
66
67 // Update mapping template in place while inactive.
68 const updated = yield* stack.deploy(
69 Transformer("X12ToJson", {
70 name: "alchemy-b2bi-transformer",
71 inputConversion,
72 mapping: {
73 templateLanguage: "JSONATA",
74 template: '{ "orderId": "updated" }',
75 },
76 }),
77 );
78 expect(updated.transformerId).toBe(created.transformerId);
79
80 // Activate in place.
81 const active = yield* stack.deploy(
82 Transformer("X12ToJson", {
83 name: "alchemy-b2bi-transformer",
84 status: "active",
85 inputConversion,
86 mapping: {
87 templateLanguage: "JSONATA",
88 template: '{ "orderId": "updated" }',
89 },
90 }),
91 );
92 expect(active.transformerId).toBe(created.transformerId);
93 expect(active.status).toBe("active");
94 const reDescribed = yield* b2bi.getTransformer({
95 transformerId: created.transformerId,
96 });
97 expect(reDescribed.status).toBe("active");
98
99 // A config change on an ACTIVE transformer is a delete-first
100 // replacement — B2BI rejects every update to an active transformer.
101 const replaced = yield* stack.deploy(
102 Transformer("X12ToJson", {
103 name: "alchemy-b2bi-transformer",
104 status: "active",
105 inputConversion,
106 mapping: {
107 templateLanguage: "JSONATA",
108 template: '{ "orderId": "replaced" }',
109 },
110 }),
111 );
112 expect(replaced.transformerId).not.toBe(created.transformerId);
113 expect(replaced.status).toBe("active");
114 yield* assertTransformerGone(created.transformerId);
115
116 // Destroy (works even while active) and verify.
117 yield* stack.destroy();
118 yield* assertTransformerGone(replaced.transformerId);
119 }),
120 { tags: ["provider:aws", "provider:aws:b2bi", "live"], timeout: 150_000 },
121);