1import * as Provider from "@/Provider";
2import * as Stripe from "@/Stripe";
3import * as Test from "@/Test/Alchemy";
4import { GetTaxSettings } from "@distilled.cloud/stripe/stripe";
5import { describe, expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import { MinimumLogLevel } from "effect/References";
8import * as Schedule from "effect/Schedule";
9
10const { test } = Test.make({ providers: Stripe.providers() });
11
12const logLevel = Effect.provideService(
13 MinimumLogLevel,
14 process.env.DEBUG ? "Debug" : "Info",
15);
16
17const getLive = GetTaxSettings({});
18
19const waitForTaxBehavior = (expected: string | null | undefined) =>
20 getLive.pipe(
21 Effect.flatMap((live) =>
22 (live.defaults.tax_behavior ?? null) === (expected ?? null)
23 ? Effect.succeed(live)
24 : Effect.fail({ _tag: "TaxSettingsNotApplied" as const }),
25 ),
26 Effect.retry({
27 while: (e) => e._tag === "TaxSettingsNotApplied",
28 schedule: Schedule.spaced("1 second"),
29 times: 10,
30 }),
31 );
32
33const otherBehavior = (
34 current: string | null | undefined,
35): "exclusive" | "inclusive" =>
36 current === "exclusive" ? "inclusive" : "exclusive";
37
38// Account-level singleton: run serially so tests do not clobber each
39// other's captured baseline under the concurrent suite.
40describe.sequential(
41 "TaxSettings",
42 { tags: ["provider:stripe", "provider:stripe:taxsettings", "live"] },
43 () => {
44 test.provider(
45 "updates tax behavior in place and restores the baseline on destroy",
46 (stack) =>
47 Effect.gen(function* () {
48 yield* stack.destroy();
49
50 const baseline = yield* getLive;
51 const baselineBehavior = baseline.defaults.tax_behavior ?? undefined;
52 const target = otherBehavior(baselineBehavior);
53 const target2 = otherBehavior(target);
54
55 const created = yield* stack.deploy(
56 Effect.gen(function* () {
57 return yield* Stripe.TaxSettings("AccountTax", {
58 defaults: { taxBehavior: target },
59 });
60 }),
61 );
62
63 expect(created.object).toEqual("tax.settings");
64 expect(created.taxBehavior).toEqual(target);
65 expect(created.initialSettings.taxBehavior).toEqual(baselineBehavior);
66 expect(created.livemode).toEqual(false);
67
68 const live1 = yield* waitForTaxBehavior(target);
69 expect(live1.defaults.tax_behavior).toEqual(target);
70
71 const updated = yield* stack.deploy(
72 Effect.gen(function* () {
73 return yield* Stripe.TaxSettings("AccountTax", {
74 defaults: { taxBehavior: target2 },
75 });
76 }),
77 );
78
79 expect(updated.object).toEqual("tax.settings");
80 expect(updated.taxBehavior).toEqual(target2);
81 expect(updated.initialSettings.taxBehavior).toEqual(baselineBehavior);
82
83 const live2 = yield* waitForTaxBehavior(target2);
84 expect(live2.defaults.tax_behavior).toEqual(target2);
85
86 yield* stack.destroy();
87
88 // Stripe will not unset a field that was originally null. Restore
89 // only applies when the captured baseline already had a value.
90 if (baselineBehavior !== undefined) {
91 const restored = yield* waitForTaxBehavior(baselineBehavior);
92 expect(restored.defaults.tax_behavior).toEqual(baselineBehavior);
93 } else {
94 const after = yield* getLive;
95 expect(after.defaults.tax_behavior).toEqual(target2);
96 }
97 }).pipe(logLevel),
98 { timeout: 120_000 },
99 );
100
101 test.provider(
102 "no-op deploy when desired settings already match the live account",
103 (stack) =>
104 Effect.gen(function* () {
105 yield* stack.destroy();
106
107 const baseline = yield* getLive;
108 const taxBehavior =
109 (baseline.defaults.tax_behavior as
110 | Stripe.TaxSettingsTaxBehavior
111 | null
112 | undefined) ?? undefined;
113
114 const setting = yield* stack.deploy(
115 Effect.gen(function* () {
116 return yield* Stripe.TaxSettings("NoopTax", {
117 ...(taxBehavior !== undefined
118 ? { defaults: { taxBehavior } }
119 : {}),
120 });
121 }),
122 );
123
124 expect(setting.taxBehavior).toEqual(taxBehavior);
125 expect(setting.initialSettings.taxBehavior).toEqual(taxBehavior);
126 expect(setting.taxCode).toEqual(
127 baseline.defaults.tax_code ?? undefined,
128 );
129
130 yield* stack.destroy();
131
132 const after = yield* getLive;
133 expect(after.defaults.tax_behavior).toEqual(
134 baseline.defaults.tax_behavior,
135 );
136 expect(after.defaults.tax_code).toEqual(baseline.defaults.tax_code);
137 }).pipe(logLevel),
138 { timeout: 120_000 },
139 );
140
141 test.provider(
142 "list returns the account tax settings singleton",
143 (stack) =>
144 Effect.gen(function* () {
145 yield* stack.destroy();
146
147 const provider = yield* Provider.findProvider(Stripe.TaxSettings);
148 const all = yield* provider.list();
149
150 expect(all.length).toEqual(1);
151 expect(all[0]?.object).toEqual("tax.settings");
152 expect(all[0]?.initialSettings.taxBehavior).toEqual(
153 all[0]?.taxBehavior,
154 );
155 expect(all[0]?.initialSettings.taxCode).toEqual(all[0]?.taxCode);
156
157 yield* stack.destroy();
158 }).pipe(logLevel),
159 { timeout: 120_000 },
160 );
161 },
162);