1import * as Provider from "@/Provider";
2import * as Stripe from "@/Stripe";
3import * as Test from "@/Test/Alchemy";
4import { GetTaxRate } from "@distilled.cloud/stripe/stripe";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import { MinimumLogLevel } from "effect/References";
8import * as Schedule from "effect/Schedule";
9import { isMissingStripeResource } from "@/Stripe/missing.ts";
10
11const { test } = Test.make({ providers: Stripe.providers() });
12
13const logLevel = Effect.provideService(
14 MinimumLogLevel,
15 process.env.DEBUG ? "Debug" : "Info",
16);
17
18const isMissing = isMissingStripeResource;
19
20const waitUntilInactive = (id: string) =>
21 GetTaxRate({ tax_rate: id }).pipe(
22 Effect.map((rate) =>
23 rate.active ? ("active" as const) : ("inactive" as const),
24 ),
25 Effect.catchIf(isMissing, () => Effect.succeed("inactive" as const)),
26 Effect.repeat({
27 schedule: Schedule.spaced("1 second"),
28 until: (status) => status === "inactive",
29 times: 10,
30 }),
31 );
32
33test.provider(
34 "create, update, and deactivate a tax rate",
35 (stack) =>
36 Effect.gen(function* () {
37 yield* stack.destroy();
38
39 const created = yield* stack.deploy(
40 Effect.gen(function* () {
41 return yield* Stripe.TaxRate("SalesTax", {
42 displayName: "Alchemy Sales Tax",
43 percentage: 8.25,
44 inclusive: false,
45 description: "Initial sales tax",
46 taxType: "sales_tax",
47 metadata: { region: "us" },
48 });
49 }),
50 );
51
52 expect(created.id).toMatch(/^txr_/);
53 expect(created.displayName).toEqual("Alchemy Sales Tax");
54 expect(created.percentage).toEqual(8.25);
55 expect(created.inclusive).toEqual(false);
56 expect(created.active).toEqual(true);
57 expect(created.description).toEqual("Initial sales tax");
58 expect(created.taxType).toEqual("sales_tax");
59 expect(created.metadata).toMatchObject({ region: "us" });
60 expect(created.created).toEqual(expect.any(Number));
61 expect(created.livemode).toEqual(false);
62
63 const fetched = yield* GetTaxRate({ tax_rate: created.id });
64 expect(fetched.id).toEqual(created.id);
65 expect(fetched.display_name).toEqual("Alchemy Sales Tax");
66 expect(fetched.percentage).toEqual(8.25);
67 expect(fetched.inclusive).toEqual(false);
68 expect(fetched.active).toEqual(true);
69 expect(fetched.description).toEqual("Initial sales tax");
70 expect(fetched.tax_type).toEqual("sales_tax");
71 expect(fetched.metadata?.region).toEqual("us");
72 expect(
73 fetched.metadata?.[Stripe.alchemyMetadataKeys.stack],
74 ).toBeDefined();
75 expect(
76 fetched.metadata?.[Stripe.alchemyMetadataKeys.stage],
77 ).toBeDefined();
78 expect(fetched.metadata?.[Stripe.alchemyMetadataKeys.id]).toBeDefined();
79
80 const updated = yield* stack.deploy(
81 Effect.gen(function* () {
82 return yield* Stripe.TaxRate("SalesTax", {
83 displayName: "Alchemy Sales Tax Updated",
84 percentage: 8.25,
85 inclusive: false,
86 description: "Updated sales tax",
87 taxType: "sales_tax",
88 metadata: { region: "ca", sku: "tax-2" },
89 });
90 }),
91 );
92
93 expect(updated.id).toEqual(created.id);
94 expect(updated.displayName).toEqual("Alchemy Sales Tax Updated");
95 expect(updated.percentage).toEqual(8.25);
96 expect(updated.inclusive).toEqual(false);
97 expect(updated.active).toEqual(true);
98 expect(updated.description).toEqual("Updated sales tax");
99 expect(updated.metadata).toEqual({ region: "ca", sku: "tax-2" });
100
101 const refetched = yield* GetTaxRate({ tax_rate: updated.id });
102 expect(refetched.id).toEqual(updated.id);
103 expect(refetched.display_name).toEqual("Alchemy Sales Tax Updated");
104 expect(refetched.percentage).toEqual(8.25);
105 expect(refetched.description).toEqual("Updated sales tax");
106 expect(refetched.metadata?.region).toEqual("ca");
107 expect(refetched.metadata?.sku).toEqual("tax-2");
108 expect(refetched.metadata?.[Stripe.alchemyMetadataKeys.id]).toBeDefined();
109
110 yield* stack.destroy();
111
112 const inactive = yield* waitUntilInactive(created.id);
113 expect(inactive).toEqual("inactive");
114 const deactivated = yield* GetTaxRate({ tax_rate: created.id });
115 expect(deactivated.active).toEqual(false);
116 }).pipe(logLevel),
117 {
118 tags: ["provider:stripe", "provider:stripe:taxrate", "live"],
119 timeout: 120_000,
120 },
121);
122
123test.provider(
124 "replace when percentage or inclusive changes",
125 (stack) =>
126 Effect.gen(function* () {
127 yield* stack.destroy();
128
129 const created = yield* stack.deploy(
130 Effect.gen(function* () {
131 return yield* Stripe.TaxRate("ReplaceTax", {
132 displayName: "Alchemy Replace Tax",
133 percentage: 5,
134 inclusive: false,
135 });
136 }),
137 );
138
139 expect(created.percentage).toEqual(5);
140 expect(created.inclusive).toEqual(false);
141
142 const replaced = yield* stack.deploy(
143 Effect.gen(function* () {
144 return yield* Stripe.TaxRate("ReplaceTax", {
145 displayName: "Alchemy Replace Tax",
146 percentage: 10,
147 inclusive: true,
148 });
149 }),
150 );
151
152 expect(replaced.id).not.toEqual(created.id);
153 expect(replaced.percentage).toEqual(10);
154 expect(replaced.inclusive).toEqual(true);
155
156 const fetched = yield* GetTaxRate({ tax_rate: replaced.id });
157 expect(fetched.id).toEqual(replaced.id);
158 expect(fetched.percentage).toEqual(10);
159 expect(fetched.inclusive).toEqual(true);
160
161 const oldInactive = yield* waitUntilInactive(created.id);
162 expect(oldInactive).toEqual("inactive");
163
164 yield* stack.destroy();
165
166 const gone = yield* waitUntilInactive(replaced.id);
167 expect(gone).toEqual("inactive");
168 }).pipe(logLevel),
169 {
170 tags: ["provider:stripe", "provider:stripe:taxrate", "live"],
171 timeout: 120_000,
172 },
173);
174
175test.provider(
176 "list enumerates the deployed tax rate",
177 (stack) =>
178 Effect.gen(function* () {
179 yield* stack.destroy();
180
181 const deployed = yield* stack.deploy(
182 Effect.gen(function* () {
183 return yield* Stripe.TaxRate("ListTax", {
184 displayName: "Alchemy List Tax",
185 percentage: 7,
186 inclusive: false,
187 metadata: { kind: "list" },
188 });
189 }),
190 );
191
192 const provider = yield* Provider.findProvider(Stripe.TaxRate);
193 const all = yield* provider.list();
194 const found = all.find((rate) => rate.id === deployed.id);
195 expect(found).toBeDefined();
196 expect(found?.displayName).toEqual(deployed.displayName);
197 expect(found?.percentage).toEqual(7);
198 expect(found?.metadata).toMatchObject({ kind: "list" });
199
200 yield* stack.destroy();
201
202 const inactive = yield* waitUntilInactive(deployed.id);
203 expect(inactive).toEqual("inactive");
204 }).pipe(logLevel),
205 {
206 tags: ["provider:stripe", "provider:stripe:taxrate", "live"],
207 timeout: 120_000,
208 },
209);