You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix null-unsafe payments config validation for partial overrides (#1363)
## Summary
- Make the `branchPaymentsSchema` custom validator tolerant of partial
override objects
- Avoid crashing when `payments.products` or `payments.productLines` are
absent during validation
- Add regression tests for partial configs plus the existing
missing-line and customer-type mismatch cases
## Testing
- Added Vitest coverage for partial payments configs and validation
failures
- Lint passed for the touched schema files
- Typecheck passed for `packages/stack-shared`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved validation robustness with stricter type-safety checks for
payment-related data configurations.
* Enhanced error messages for clearer feedback on validation failures.
* **Tests**
* Added comprehensive test coverage for edge cases including missing
configurations and type mismatches.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
message: `Product "${productId}" has customer type "${product.customerType}" but its product line "${product.productLineId}" has customer type "${productLine.customerType}"`,
202
+
message: `Product "${productId}" has customer type "${product.customerType}" but its product line "${productLineId}" has customer type "${productLine.customerType}"`,
import.meta.vitest?.test("branchPaymentsSchema accepts partial payments config without products",async({ expect })=>{
211
+
awaitexpect(branchPaymentsSchema.validate({
212
+
blockNewPurchases: true,
213
+
},{abortEarly: false})).resolves.toMatchObject({
214
+
blockNewPurchases: true,
215
+
});
216
+
});
217
+
218
+
import.meta.vitest?.test("branchPaymentsSchema accepts product lines without products",async({ expect })=>{
219
+
awaitexpect(branchPaymentsSchema.validate({
220
+
productLines: {
221
+
pro: {
222
+
displayName: "Pro",
223
+
customerType: "user",
224
+
},
225
+
},
226
+
},{abortEarly: false})).resolves.toMatchObject({
227
+
productLines: {
228
+
pro: {
229
+
displayName: "Pro",
230
+
customerType: "user",
231
+
},
232
+
},
233
+
});
234
+
});
235
+
236
+
import.meta.vitest?.test("branchPaymentsSchema rejects a product that references a missing product line",async({ expect })=>{
237
+
awaitexpect(branchPaymentsSchema.validate({
238
+
products: {
239
+
pro: {
240
+
customerType: "user",
241
+
productLineId: "missing-line",
242
+
prices: "include-by-default",
243
+
},
244
+
},
245
+
},{abortEarly: false})).rejects.toThrowErrorMatchingInlineSnapshot(`[ValidationError: Product "pro" specifies product line ID "missing-line", but that product line does not exist]`);
246
+
});
247
+
248
+
import.meta.vitest?.test("branchPaymentsSchema rejects null product entries without throwing a raw TypeError",async({ expect })=>{
249
+
awaitexpect(branchPaymentsSchema.validate({
250
+
products: {
251
+
pro: null,
252
+
},
253
+
},{abortEarly: false})).rejects.toThrowErrorMatchingInlineSnapshot(`[ValidationError: products cannot be null]`);
254
+
});
255
+
256
+
import.meta.vitest?.test("branchPaymentsSchema rejects null product line entries without throwing a raw TypeError",async({ expect })=>{
257
+
awaitexpect(branchPaymentsSchema.validate({
258
+
productLines: {
259
+
teamLine: null,
260
+
},
261
+
products: {
262
+
pro: {
263
+
customerType: "user",
264
+
productLineId: "teamLine",
265
+
prices: "include-by-default",
266
+
},
267
+
},
268
+
},{abortEarly: false})).rejects.toThrowErrorMatchingInlineSnapshot(`[ValidationError: productLines cannot be null]`);
269
+
});
270
+
271
+
import.meta.vitest?.test("branchPaymentsSchema rejects a product whose customer type differs from its product line",async({ expect })=>{
272
+
awaitexpect(branchPaymentsSchema.validate({
273
+
productLines: {
274
+
teamLine: {
275
+
customerType: "team",
276
+
},
277
+
},
278
+
products: {
279
+
pro: {
280
+
customerType: "user",
281
+
productLineId: "teamLine",
282
+
prices: "include-by-default",
283
+
},
284
+
},
285
+
},{abortEarly: false})).rejects.toThrowErrorMatchingInlineSnapshot(`[ValidationError: Product "pro" has customer type "user" but its product line "teamLine" has customer type "team"]`);
},{abortEarly: false})).rejects.toThrowErrorMatchingInlineSnapshot(`[ValidationError: productLineId must contain only letters, numbers, underscores, and hyphens, and not start with a hyphen]`);
0 commit comments