feat: initial commit — storefront, convex backend, and shared packages
Completes the first milestone of The Pet Loft ecommerce platform: - apps/storefront: full customer-facing Next.js app with HeroUI (cart, checkout, orders, wishlist, product detail, shop, search, auth) - convex/: serverless backend with schema, queries, mutations, actions, HTTP routes, Stripe/Shippo integrations, and co-located tests - packages/types, packages/utils, packages/convex: shared workspace packages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
127
convex/model/checkout.test.ts
Normal file
127
convex/model/checkout.test.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { describe, it, expect, expectTypeOf } from "vitest";
|
||||
import type {
|
||||
ShippoConfidenceScore,
|
||||
ShippoValidationValue,
|
||||
ShippoAddressType,
|
||||
ShippoValidationReason,
|
||||
RecommendedAddress,
|
||||
AddressValidationResult,
|
||||
} from "./checkout";
|
||||
|
||||
describe("AddressValidationResult types", () => {
|
||||
it("ShippoConfidenceScore accepts valid literals", () => {
|
||||
expectTypeOf<"high">().toMatchTypeOf<ShippoConfidenceScore>();
|
||||
expectTypeOf<"medium">().toMatchTypeOf<ShippoConfidenceScore>();
|
||||
expectTypeOf<"low">().toMatchTypeOf<ShippoConfidenceScore>();
|
||||
});
|
||||
|
||||
it("ShippoValidationValue accepts valid literals", () => {
|
||||
expectTypeOf<"valid">().toMatchTypeOf<ShippoValidationValue>();
|
||||
expectTypeOf<"partially_valid">().toMatchTypeOf<ShippoValidationValue>();
|
||||
expectTypeOf<"invalid">().toMatchTypeOf<ShippoValidationValue>();
|
||||
});
|
||||
|
||||
it("ShippoAddressType accepts valid literals", () => {
|
||||
expectTypeOf<"residential">().toMatchTypeOf<ShippoAddressType>();
|
||||
expectTypeOf<"commercial">().toMatchTypeOf<ShippoAddressType>();
|
||||
expectTypeOf<"unknown">().toMatchTypeOf<ShippoAddressType>();
|
||||
expectTypeOf<"po_box">().toMatchTypeOf<ShippoAddressType>();
|
||||
expectTypeOf<"military">().toMatchTypeOf<ShippoAddressType>();
|
||||
});
|
||||
|
||||
it("ShippoValidationReason has code and description", () => {
|
||||
expectTypeOf<ShippoValidationReason>().toHaveProperty("code");
|
||||
expectTypeOf<ShippoValidationReason>().toHaveProperty("description");
|
||||
});
|
||||
|
||||
it("RecommendedAddress has required fields and no state", () => {
|
||||
expectTypeOf<RecommendedAddress>().toHaveProperty("addressLine1");
|
||||
expectTypeOf<RecommendedAddress>().toHaveProperty("city");
|
||||
expectTypeOf<RecommendedAddress>().toHaveProperty("postalCode");
|
||||
expectTypeOf<RecommendedAddress>().toHaveProperty("country");
|
||||
expectTypeOf<RecommendedAddress>().toHaveProperty("confidenceScore");
|
||||
expectTypeOf<RecommendedAddress>().toHaveProperty("confidenceCode");
|
||||
expectTypeOf<RecommendedAddress>().toHaveProperty("confidenceDescription");
|
||||
|
||||
type Keys = keyof RecommendedAddress;
|
||||
expectTypeOf<"state" extends Keys ? true : false>().toEqualTypeOf<false>();
|
||||
expectTypeOf<"addressLine2" extends Keys ? true : false>().toEqualTypeOf<false>();
|
||||
});
|
||||
|
||||
it("AddressValidationResult is structurally complete", () => {
|
||||
expectTypeOf<AddressValidationResult>().toHaveProperty("isValid");
|
||||
expectTypeOf<AddressValidationResult>().toHaveProperty("validationValue");
|
||||
expectTypeOf<AddressValidationResult>().toHaveProperty("reasons");
|
||||
expectTypeOf<AddressValidationResult>().toHaveProperty("addressType");
|
||||
expectTypeOf<AddressValidationResult>().toHaveProperty("changedAttributes");
|
||||
expectTypeOf<AddressValidationResult>().toHaveProperty("originalAddress");
|
||||
});
|
||||
|
||||
it("recommendedAddress is optional on AddressValidationResult", () => {
|
||||
const withoutRecommended: AddressValidationResult = {
|
||||
isValid: true,
|
||||
validationValue: "valid",
|
||||
reasons: [],
|
||||
addressType: "unknown",
|
||||
changedAttributes: [],
|
||||
originalAddress: {
|
||||
addressLine1: "10 Downing Street",
|
||||
city: "London",
|
||||
postalCode: "SW1A 2AA",
|
||||
country: "GB",
|
||||
},
|
||||
};
|
||||
expect(withoutRecommended.recommendedAddress).toBeUndefined();
|
||||
});
|
||||
|
||||
it("AddressValidationResult accepts a full object with recommended address", () => {
|
||||
const full: AddressValidationResult = {
|
||||
isValid: false,
|
||||
validationValue: "partially_valid",
|
||||
reasons: [{ code: "postal_data_match", description: "Postal code matched" }],
|
||||
addressType: "unknown",
|
||||
changedAttributes: ["postalCode"],
|
||||
recommendedAddress: {
|
||||
addressLine1: "10 Downing Street",
|
||||
city: "London",
|
||||
postalCode: "SW1A 2AA",
|
||||
country: "GB",
|
||||
completeAddress: "10 Downing Street;LONDON;SW1A 2AA;UNITED KINGDOM",
|
||||
confidenceScore: "high",
|
||||
confidenceCode: "postal_data_match",
|
||||
confidenceDescription: "Matched via postal data",
|
||||
},
|
||||
originalAddress: {
|
||||
addressLine1: "10 Downing St",
|
||||
city: "London",
|
||||
postalCode: "SW1A2AA",
|
||||
country: "GB",
|
||||
},
|
||||
};
|
||||
expect(full.isValid).toBe(false);
|
||||
expect(full.recommendedAddress).toBeDefined();
|
||||
expect(full.recommendedAddress!.confidenceScore).toBe("high");
|
||||
});
|
||||
|
||||
it("originalAddress uses additionalInformation instead of addressLine2", () => {
|
||||
const result: AddressValidationResult = {
|
||||
isValid: true,
|
||||
validationValue: "valid",
|
||||
reasons: [],
|
||||
addressType: "unknown",
|
||||
changedAttributes: [],
|
||||
originalAddress: {
|
||||
addressLine1: "10 Downing Street",
|
||||
additionalInformation: "Flat 1",
|
||||
city: "London",
|
||||
postalCode: "SW1A 2AA",
|
||||
country: "GB",
|
||||
},
|
||||
};
|
||||
expect(result.originalAddress.additionalInformation).toBe("Flat 1");
|
||||
|
||||
type OrigKeys = keyof AddressValidationResult["originalAddress"];
|
||||
expectTypeOf<"state" extends OrigKeys ? true : false>().toEqualTypeOf<false>();
|
||||
expectTypeOf<"addressLine2" extends OrigKeys ? true : false>().toEqualTypeOf<false>();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user