Skip to content

Commit 4df268d

Browse files
committed
Make payments schema validator handle partial overrides
1 parent f89b97b commit 4df268d

2 files changed

Lines changed: 64 additions & 3 deletions

File tree

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { describe, expect, it } from "vitest";
2+
import { branchPaymentsSchema } from "./schema";
3+
4+
describe("branchPaymentsSchema", () => {
5+
it("accepts partial payments config without products", async () => {
6+
await expect(branchPaymentsSchema.validate({
7+
blockNewPurchases: true,
8+
})).resolves.toMatchObject({
9+
blockNewPurchases: true,
10+
});
11+
});
12+
13+
it("accepts product lines without products", async () => {
14+
await expect(branchPaymentsSchema.validate({
15+
productLines: {
16+
pro: {
17+
displayName: "Pro",
18+
customerType: "user",
19+
},
20+
},
21+
})).resolves.toMatchObject({
22+
productLines: {
23+
pro: {
24+
displayName: "Pro",
25+
customerType: "user",
26+
},
27+
},
28+
});
29+
});
30+
31+
it("rejects a product that references a missing product line", async () => {
32+
await expect(branchPaymentsSchema.validate({
33+
products: {
34+
pro: {
35+
customerType: "user",
36+
productLineId: "missing-line",
37+
},
38+
},
39+
})).rejects.toThrow('Product "pro" specifies product line ID "missing-line", but that product line does not exist');
40+
});
41+
42+
it("rejects a product whose customer type differs from its product line", async () => {
43+
await expect(branchPaymentsSchema.validate({
44+
productLines: {
45+
teamLine: {
46+
customerType: "team",
47+
},
48+
},
49+
products: {
50+
pro: {
51+
customerType: "user",
52+
productLineId: "teamLine",
53+
},
54+
},
55+
})).rejects.toThrow('Product "pro" has customer type "user" but its product line "teamLine" has customer type "team"');
56+
});
57+
});

packages/stack-shared/src/config/schema.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,13 @@ export const branchPaymentsSchema = yupObject({
181181
'Product customer type must match its product line customer type',
182182
function(this: yup.TestContext<yup.AnyObject>, value) {
183183
if (!value) return true;
184-
for (const [productId, product] of Object.entries(value.products)) {
185-
if (!product.productLineId) continue;
186-
const productLine = getOrUndefined(value.productLines, product.productLineId);
184+
const products = Reflect.get(value, "products");
185+
if (!isObjectLike(products)) return true;
186+
187+
const productLines = Reflect.get(value, "productLines");
188+
for (const [productId, product] of Object.entries(products)) {
189+
if (product.productLineId == null) continue;
190+
const productLine = isObjectLike(productLines) ? getOrUndefined(productLines, product.productLineId) : undefined;
187191
if (productLine === undefined) {
188192
return this.createError({
189193
message: `Product "${productId}" specifies product line ID "${product.productLineId}", but that product line does not exist`,

0 commit comments

Comments
 (0)