@mohasinac/appkit 3.0.6 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +42 -0
  2. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +86 -0
  3. package/dist/_internal/client/features/lottery/LotteryAdminSlotView.d.ts +14 -0
  4. package/dist/_internal/client/features/lottery/LotteryAdminSlotView.js +46 -0
  5. package/dist/_internal/client/features/lottery/LotteryDetailView.d.ts +28 -0
  6. package/dist/_internal/client/features/lottery/LotteryDetailView.js +17 -0
  7. package/dist/_internal/client/features/lottery/LotteryEntriesView.d.ts +23 -0
  8. package/dist/_internal/client/features/lottery/LotteryEntriesView.js +58 -0
  9. package/dist/_internal/client/features/lottery/LotteryListView.d.ts +21 -0
  10. package/dist/_internal/client/features/lottery/LotteryListView.js +25 -0
  11. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +19 -0
  12. package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -0
  13. package/dist/_internal/client/features/lottery/LotterySlotGrid.d.ts +13 -0
  14. package/dist/_internal/client/features/lottery/LotterySlotGrid.js +21 -0
  15. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.d.ts +28 -0
  16. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +18 -0
  17. package/dist/_internal/server/features/auctions/__tests__/service.test.d.ts +1 -0
  18. package/dist/_internal/server/features/auctions/__tests__/service.test.js +140 -0
  19. package/dist/_internal/server/features/checkout/__tests__/actions.test.d.ts +1 -0
  20. package/dist/_internal/server/features/checkout/__tests__/actions.test.js +600 -0
  21. package/dist/_internal/server/features/lottery/actions.d.ts +43 -0
  22. package/dist/_internal/server/features/lottery/actions.js +243 -0
  23. package/dist/_internal/server/features/lottery/adapters.d.ts +9 -0
  24. package/dist/_internal/server/features/lottery/adapters.js +21 -0
  25. package/dist/_internal/server/features/lottery/data.d.ts +20 -0
  26. package/dist/_internal/server/features/lottery/data.js +41 -0
  27. package/dist/_internal/server/features/lottery/metadata.d.ts +6 -0
  28. package/dist/_internal/server/features/lottery/metadata.js +31 -0
  29. package/dist/_internal/server/features/refunds/__tests__/actions.test.d.ts +1 -0
  30. package/dist/_internal/server/features/refunds/__tests__/actions.test.js +263 -0
  31. package/dist/_internal/server/jobs/core/__tests__/auctionSettlement.test.d.ts +1 -0
  32. package/dist/_internal/server/jobs/core/__tests__/auctionSettlement.test.js +249 -0
  33. package/dist/_internal/server/jobs/core/__tests__/onBidPlaced.test.d.ts +1 -0
  34. package/dist/_internal/server/jobs/core/__tests__/onBidPlaced.test.js +201 -0
  35. package/dist/_internal/server/jobs/core/__tests__/onOrderCreate.test.d.ts +1 -0
  36. package/dist/_internal/server/jobs/core/__tests__/onOrderCreate.test.js +169 -0
  37. package/dist/_internal/server/jobs/core/__tests__/onOrderStatusChange.test.d.ts +1 -0
  38. package/dist/_internal/server/jobs/core/__tests__/onOrderStatusChange.test.js +148 -0
  39. package/dist/_internal/server/jobs/core/__tests__/onProductStockChange.test.d.ts +1 -0
  40. package/dist/_internal/server/jobs/core/__tests__/onProductStockChange.test.js +210 -0
  41. package/dist/_internal/server/jobs/core/__tests__/onReviewWrite.test.d.ts +1 -0
  42. package/dist/_internal/server/jobs/core/__tests__/onReviewWrite.test.js +190 -0
  43. package/dist/_internal/server/jobs/core/__tests__/onUserBanChange.test.d.ts +1 -0
  44. package/dist/_internal/server/jobs/core/__tests__/onUserBanChange.test.js +161 -0
  45. package/dist/_internal/server/jobs/core/__tests__/payoutBatch.test.d.ts +1 -0
  46. package/dist/_internal/server/jobs/core/__tests__/payoutBatch.test.js +302 -0
  47. package/dist/_internal/server/jobs/core/__tests__/triggerEventRaffle.test.d.ts +1 -0
  48. package/dist/_internal/server/jobs/core/__tests__/triggerEventRaffle.test.js +252 -0
  49. package/dist/_internal/server/jobs/core/__tests__/wrapJobHandler.test.d.ts +1 -0
  50. package/dist/_internal/server/jobs/core/__tests__/wrapJobHandler.test.js +129 -0
  51. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  52. package/dist/_internal/shared/actions/action-registry.js +46 -0
  53. package/dist/_internal/shared/features/products/schema.d.ts +8 -8
  54. package/dist/client.d.ts +9 -0
  55. package/dist/client.js +9 -0
  56. package/dist/constants/field-names.d.ts +5 -1
  57. package/dist/constants/field-names.js +5 -1
  58. package/dist/features/addresses/repository/__tests__/addresses.repository.test.d.ts +1 -0
  59. package/dist/features/addresses/repository/__tests__/addresses.repository.test.js +386 -0
  60. package/dist/features/admin/api/bids/route.d.ts +2 -2
  61. package/dist/features/admin/api/coupons/route.d.ts +2 -2
  62. package/dist/features/admin/api/products/route.d.ts +2 -2
  63. package/dist/features/admin/api/reviews/route.d.ts +2 -2
  64. package/dist/features/admin/repository/__tests__/notification.repository.test.d.ts +1 -0
  65. package/dist/features/admin/repository/__tests__/notification.repository.test.js +356 -0
  66. package/dist/features/admin/repository/__tests__/site-settings.repository.test.d.ts +1 -0
  67. package/dist/features/admin/repository/__tests__/site-settings.repository.test.js +298 -0
  68. package/dist/features/auctions/repository/__tests__/bid.repository.test.d.ts +1 -0
  69. package/dist/features/auctions/repository/__tests__/bid.repository.test.js +290 -0
  70. package/dist/features/auth/api/route.d.ts +2 -2
  71. package/dist/features/auth/repository/__tests__/session.repository.test.d.ts +1 -0
  72. package/dist/features/auth/repository/__tests__/session.repository.test.js +310 -0
  73. package/dist/features/auth/repository/__tests__/session.test.d.ts +1 -0
  74. package/dist/features/auth/repository/__tests__/session.test.js +146 -0
  75. package/dist/features/auth/repository/__tests__/sms-counter.repository.test.d.ts +1 -0
  76. package/dist/features/auth/repository/__tests__/sms-counter.repository.test.js +171 -0
  77. package/dist/features/auth/repository/__tests__/sms-counter.test.d.ts +1 -0
  78. package/dist/features/auth/repository/__tests__/sms-counter.test.js +88 -0
  79. package/dist/features/auth/repository/__tests__/token.repository.test.d.ts +1 -0
  80. package/dist/features/auth/repository/__tests__/token.repository.test.js +260 -0
  81. package/dist/features/auth/repository/__tests__/token.test.d.ts +1 -0
  82. package/dist/features/auth/repository/__tests__/token.test.js +140 -0
  83. package/dist/features/auth/repository/__tests__/user.repository.test.d.ts +1 -0
  84. package/dist/features/auth/repository/__tests__/user.repository.test.js +352 -0
  85. package/dist/features/auth/repository/__tests__/user.test.d.ts +1 -0
  86. package/dist/features/auth/repository/__tests__/user.test.js +226 -0
  87. package/dist/features/auth/repository/session.repository.js +10 -1
  88. package/dist/features/cart/repository/__tests__/cart.repository.test.d.ts +1 -0
  89. package/dist/features/cart/repository/__tests__/cart.repository.test.js +371 -0
  90. package/dist/features/cart/repository/cart.repository.js +2 -2
  91. package/dist/features/categories/api/[id]/route.d.ts +4 -4
  92. package/dist/features/categories/api/route.d.ts +2 -2
  93. package/dist/features/categories/repository/__tests__/categories.repository.test.d.ts +1 -0
  94. package/dist/features/categories/repository/__tests__/categories.repository.test.js +813 -0
  95. package/dist/features/events/components/EventCard.js +1 -0
  96. package/dist/features/events/repository/__tests__/event.repository.test.d.ts +1 -0
  97. package/dist/features/events/repository/__tests__/event.repository.test.js +299 -0
  98. package/dist/features/events/schemas/firestore.d.ts +3 -0
  99. package/dist/features/events/schemas/index.d.ts +834 -14
  100. package/dist/features/events/schemas/index.js +80 -1
  101. package/dist/features/events/types/index.d.ts +4 -1
  102. package/dist/features/faq/repository/__tests__/faqs.repository.test.d.ts +1 -0
  103. package/dist/features/faq/repository/__tests__/faqs.repository.test.js +311 -0
  104. package/dist/features/history/repository/__tests__/user-history.repository.test.d.ts +1 -0
  105. package/dist/features/history/repository/__tests__/user-history.repository.test.js +180 -0
  106. package/dist/features/homepage/api/[id]/route.d.ts +4 -4
  107. package/dist/features/homepage/api/carousel/[id]/route.d.ts +4 -4
  108. package/dist/features/homepage/api/carousel/route.d.ts +2 -2
  109. package/dist/features/homepage/api/route.d.ts +2 -2
  110. package/dist/features/homepage/repository/__tests__/carousel.repository.test.d.ts +1 -0
  111. package/dist/features/homepage/repository/__tests__/carousel.repository.test.js +182 -0
  112. package/dist/features/homepage/repository/__tests__/homepage-sections.repository.test.d.ts +1 -0
  113. package/dist/features/homepage/repository/__tests__/homepage-sections.repository.test.js +185 -0
  114. package/dist/features/lottery/repository/lottery-entry.repository.d.ts +33 -0
  115. package/dist/features/lottery/repository/lottery-entry.repository.js +130 -0
  116. package/dist/features/lottery/schemas/firestore.d.ts +49 -0
  117. package/dist/features/lottery/schemas/firestore.js +25 -0
  118. package/dist/features/lottery/schemas/zod.d.ts +113 -0
  119. package/dist/features/lottery/schemas/zod.js +39 -0
  120. package/dist/features/lottery/types/index.d.ts +67 -0
  121. package/dist/features/lottery/types/index.js +55 -0
  122. package/dist/features/messages/repository/__tests__/conversations.repository.test.d.ts +1 -0
  123. package/dist/features/messages/repository/__tests__/conversations.repository.test.js +257 -0
  124. package/dist/features/orders/repository/__tests__/orders.repository.test.d.ts +1 -0
  125. package/dist/features/orders/repository/__tests__/orders.repository.test.js +211 -0
  126. package/dist/features/payments/repository/__tests__/payout.repository.test.d.ts +1 -0
  127. package/dist/features/payments/repository/__tests__/payout.repository.test.js +292 -0
  128. package/dist/features/products/api/[id]/route.d.ts +4 -4
  129. package/dist/features/products/api/route.d.ts +2 -2
  130. package/dist/features/products/repository/__tests__/product-features.repository.test.d.ts +1 -0
  131. package/dist/features/products/repository/__tests__/product-features.repository.test.js +329 -0
  132. package/dist/features/products/repository/__tests__/products.repository.test.d.ts +1 -0
  133. package/dist/features/products/repository/__tests__/products.repository.test.js +607 -0
  134. package/dist/features/products/schemas/firestore.d.ts +15 -1
  135. package/dist/features/products/schemas/index.d.ts +12 -12
  136. package/dist/features/products/schemas/index.js +4 -2
  137. package/dist/features/products/types/index.d.ts +1 -1
  138. package/dist/features/promotions/repository/__tests__/claimed-coupons.repository.test.d.ts +1 -0
  139. package/dist/features/promotions/repository/__tests__/claimed-coupons.repository.test.js +219 -0
  140. package/dist/features/promotions/repository/__tests__/coupons.repository.test.d.ts +1 -0
  141. package/dist/features/promotions/repository/__tests__/coupons.repository.test.js +480 -0
  142. package/dist/features/reviews/api/[id]/route.d.ts +4 -4
  143. package/dist/features/reviews/api/route.d.ts +2 -2
  144. package/dist/features/reviews/repository/__tests__/reviews.repository.test.d.ts +1 -0
  145. package/dist/features/reviews/repository/__tests__/reviews.repository.test.js +327 -0
  146. package/dist/features/scams/repository/__tests__/scammer.repository.test.d.ts +1 -0
  147. package/dist/features/scams/repository/__tests__/scammer.repository.test.js +385 -0
  148. package/dist/features/search/repository/__tests__/search.repository.test.d.ts +1 -0
  149. package/dist/features/search/repository/__tests__/search.repository.test.js +369 -0
  150. package/dist/features/seller/api/coupons/route.d.ts +2 -2
  151. package/dist/features/seller/api/offers/route.d.ts +2 -2
  152. package/dist/features/seller/api/products/route.d.ts +2 -2
  153. package/dist/features/seller/api/store/route.d.ts +2 -2
  154. package/dist/features/seller/repository/__tests__/offer.repository.test.d.ts +1 -0
  155. package/dist/features/seller/repository/__tests__/offer.repository.test.js +458 -0
  156. package/dist/features/stores/repository/__tests__/store.repository.test.d.ts +1 -0
  157. package/dist/features/stores/repository/__tests__/store.repository.test.js +261 -0
  158. package/dist/features/support/repository/__tests__/support.repository.test.d.ts +1 -0
  159. package/dist/features/support/repository/__tests__/support.repository.test.js +312 -0
  160. package/dist/features/wishlist/repository/__tests__/user-wishlist.repository.test.d.ts +1 -0
  161. package/dist/features/wishlist/repository/__tests__/user-wishlist.repository.test.js +139 -0
  162. package/dist/index.d.ts +14 -0
  163. package/dist/index.js +17 -0
  164. package/dist/next/api/routeHandler.d.ts +1 -1
  165. package/dist/next/routing/route-map.d.ts +13 -0
  166. package/dist/next/routing/route-map.js +6 -0
  167. package/dist/repositories/index.d.ts +1 -0
  168. package/dist/repositories/index.js +2 -0
  169. package/dist/security/index.d.ts +1 -1
  170. package/dist/security/index.js +1 -1
  171. package/dist/security/pii-schemas.d.ts +2 -0
  172. package/dist/security/pii-schemas.js +6 -0
  173. package/dist/seed/actions/demo-seed-actions.d.ts +1 -1
  174. package/dist/seed/events-seed-data.js +85 -2
  175. package/dist/seed/grouped-listings-seed-data.d.ts +3 -1
  176. package/dist/seed/index.d.ts +1 -0
  177. package/dist/seed/index.js +1 -0
  178. package/dist/seed/lottery-entries-seed-data.d.ts +2 -0
  179. package/dist/seed/lottery-entries-seed-data.js +108 -0
  180. package/dist/seed/manifest.js +1 -0
  181. package/dist/seed/products-preorders-seed-data.d.ts +3 -1
  182. package/dist/server.d.ts +6 -0
  183. package/dist/server.js +6 -0
  184. package/dist/styles.css +1 -1
  185. package/dist/tailwind-utilities.css +1 -1
  186. package/package.json +1 -1
@@ -0,0 +1,386 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { makeMockDb, makeSnap, makeQuerySnap } from "../../../../../tests/helpers/mock-firestore";
3
+ import { DatabaseError } from "../../../../errors";
4
+ const { db, mockDocRef, mockCollection, mockQuery, mockBatch } = makeMockDb();
5
+ const { mockEncryptPiiFields, mockDecryptPiiFields } = vi.hoisted(() => ({
6
+ mockEncryptPiiFields: vi.fn((d) => d),
7
+ mockDecryptPiiFields: vi.fn((d) => d),
8
+ }));
9
+ const { mockGetFirestoreCount } = vi.hoisted(() => ({
10
+ mockGetFirestoreCount: vi.fn().mockResolvedValue(0),
11
+ }));
12
+ vi.mock("../../../../providers/db-firebase/admin", () => ({
13
+ getAdminDb: () => db,
14
+ }));
15
+ vi.mock("../../../../providers/db-firebase", async (importOriginal) => {
16
+ const actual = await importOriginal();
17
+ return {
18
+ ...actual,
19
+ prepareForFirestore: (d) => d,
20
+ getFirestoreCount: mockGetFirestoreCount,
21
+ };
22
+ });
23
+ vi.mock("../../../../security", () => ({
24
+ encryptPiiFields: mockEncryptPiiFields,
25
+ decryptPiiFields: mockDecryptPiiFields,
26
+ ADDRESS_PII_FIELDS: ["fullName", "phone", "addressLine1"],
27
+ }));
28
+ vi.mock("../../../../errors/normalize", () => ({
29
+ normalizeError: vi.fn(),
30
+ }));
31
+ vi.mock("../../../../monitoring", () => ({
32
+ serverLogger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
33
+ }));
34
+ import { AddressesRepository } from "../addresses.repository";
35
+ const repo = new AddressesRepository();
36
+ function makeAddressDoc(overrides = {}) {
37
+ return {
38
+ id: "addr-1",
39
+ ownerType: "user",
40
+ ownerId: "user-1",
41
+ label: "Home",
42
+ fullName: "Ravi Kumar",
43
+ phone: "+91-9876543210",
44
+ addressLine1: "123 Main St",
45
+ city: "Mumbai",
46
+ state: "Maharashtra",
47
+ postalCode: "400001",
48
+ country: "IN",
49
+ isDefault: false,
50
+ createdAt: new Date(),
51
+ updatedAt: new Date(),
52
+ ...overrides,
53
+ };
54
+ }
55
+ function makeAddressInput(overrides = {}) {
56
+ return {
57
+ label: "Home",
58
+ fullName: "Ravi Kumar",
59
+ phone: "+91-9876543210",
60
+ addressLine1: "123 Main St",
61
+ city: "Mumbai",
62
+ state: "Maharashtra",
63
+ postalCode: "400001",
64
+ country: "IN",
65
+ isDefault: false,
66
+ ...overrides,
67
+ };
68
+ }
69
+ beforeEach(() => {
70
+ vi.clearAllMocks();
71
+ // Restore passthrough encryption mocks after clearAllMocks
72
+ mockEncryptPiiFields.mockImplementation((d) => d);
73
+ mockDecryptPiiFields.mockImplementation((d) => d);
74
+ db.collection.mockReturnValue(mockCollection);
75
+ mockCollection.doc.mockReturnValue(mockDocRef);
76
+ mockCollection.where.mockReturnValue(mockQuery);
77
+ mockQuery.where.mockReturnValue(mockQuery);
78
+ mockQuery.orderBy.mockReturnValue(mockQuery);
79
+ mockQuery.limit.mockReturnValue(mockQuery);
80
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
81
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
82
+ mockDocRef.set.mockResolvedValue(undefined);
83
+ mockDocRef.update.mockResolvedValue(undefined);
84
+ mockDocRef.delete.mockResolvedValue(undefined);
85
+ db.batch.mockReturnValue(mockBatch);
86
+ mockGetFirestoreCount.mockResolvedValue(0);
87
+ });
88
+ // ---------------------------------------------------------------------------
89
+ // listByOwner
90
+ // ---------------------------------------------------------------------------
91
+ describe("AddressesRepository.listByOwner", () => {
92
+ it("returns addresses for the owner", async () => {
93
+ const addr = makeAddressDoc();
94
+ mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: addr.id, data: addr }]));
95
+ const results = await repo.listByOwner("user", "user-1");
96
+ expect(results).toHaveLength(1);
97
+ });
98
+ it("queries with ownerType AND ownerId filters", async () => {
99
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
100
+ await repo.listByOwner("user", "user-1");
101
+ expect(mockCollection.where).toHaveBeenCalledWith("ownerType", "==", "user");
102
+ expect(mockQuery.where).toHaveBeenCalledWith("ownerId", "==", "user-1");
103
+ });
104
+ it("orders results by createdAt descending", async () => {
105
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
106
+ await repo.listByOwner("user", "user-1");
107
+ expect(mockQuery.orderBy).toHaveBeenCalledWith("createdAt", "desc");
108
+ });
109
+ it("returns empty array when no addresses found", async () => {
110
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
111
+ const results = await repo.listByOwner("user", "nobody");
112
+ expect(results).toHaveLength(0);
113
+ });
114
+ it("returns store addresses when ownerType=store", async () => {
115
+ const addr = makeAddressDoc({ ownerType: "store", ownerId: "store-1" });
116
+ mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: addr.id, data: addr }]));
117
+ const results = await repo.listByOwner("store", "store-1");
118
+ expect(results).toHaveLength(1);
119
+ expect(mockCollection.where).toHaveBeenCalledWith("ownerType", "==", "store");
120
+ });
121
+ });
122
+ // ---------------------------------------------------------------------------
123
+ // listByOwnerType
124
+ // ---------------------------------------------------------------------------
125
+ describe("AddressesRepository.listByOwnerType", () => {
126
+ it("queries only by ownerType (no ownerId filter)", async () => {
127
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
128
+ await repo.listByOwnerType("user");
129
+ expect(mockCollection.where).toHaveBeenCalledWith("ownerType", "==", "user");
130
+ // Ensure ownerId filter NOT applied (only one where call at collection level)
131
+ const collectionWhereCalls = mockCollection.where.mock.calls.filter((c) => c[0] === "ownerId");
132
+ expect(collectionWhereCalls).toHaveLength(0);
133
+ });
134
+ it("applies the limit (default 500)", async () => {
135
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
136
+ await repo.listByOwnerType("store");
137
+ expect(mockQuery.limit).toHaveBeenCalledWith(500);
138
+ });
139
+ it("applies custom limit when provided", async () => {
140
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
141
+ await repo.listByOwnerType("store", 100);
142
+ expect(mockQuery.limit).toHaveBeenCalledWith(100);
143
+ });
144
+ });
145
+ // ---------------------------------------------------------------------------
146
+ // countByOwner
147
+ // ---------------------------------------------------------------------------
148
+ describe("AddressesRepository.countByOwner", () => {
149
+ it("returns count from getFirestoreCount", async () => {
150
+ mockGetFirestoreCount.mockResolvedValue(3);
151
+ const count = await repo.countByOwner("user", "user-1");
152
+ expect(count).toBe(3);
153
+ });
154
+ it("queries by ownerType AND ownerId", async () => {
155
+ await repo.countByOwner("user", "user-1");
156
+ expect(mockCollection.where).toHaveBeenCalledWith("ownerType", "==", "user");
157
+ expect(mockQuery.where).toHaveBeenCalledWith("ownerId", "==", "user-1");
158
+ });
159
+ it("returns 0 when no addresses exist", async () => {
160
+ mockGetFirestoreCount.mockResolvedValue(0);
161
+ const count = await repo.countByOwner("user", "ghost-user");
162
+ expect(count).toBe(0);
163
+ });
164
+ });
165
+ // ---------------------------------------------------------------------------
166
+ // createForOwner
167
+ // ---------------------------------------------------------------------------
168
+ describe("AddressesRepository.createForOwner", () => {
169
+ it("isDefault:false — no batch operations, docRef.set called", async () => {
170
+ const address = makeAddressDoc();
171
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "mock-doc-id"));
172
+ await repo.createForOwner("user", "user-1", makeAddressInput({ isDefault: false }));
173
+ expect(mockDocRef.set).toHaveBeenCalledOnce();
174
+ expect(mockBatch.update).not.toHaveBeenCalled();
175
+ expect(mockBatch.commit).not.toHaveBeenCalled();
176
+ });
177
+ it("isDefault:true with no existing defaults — no batch commit, docRef.set still called", async () => {
178
+ // clearDefaultFlag: empty snap → early return without batch
179
+ mockQuery.get.mockResolvedValue({ docs: [], empty: true, size: 0 });
180
+ const address = makeAddressDoc({ isDefault: true });
181
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "mock-doc-id"));
182
+ await repo.createForOwner("user", "user-1", makeAddressInput({ isDefault: true }));
183
+ expect(mockDocRef.set).toHaveBeenCalledOnce();
184
+ expect(mockBatch.commit).not.toHaveBeenCalled();
185
+ });
186
+ it("isDefault:true with existing defaults — clears them via batch before creating", async () => {
187
+ const existingDefault = makeAddressDoc({ isDefault: true, id: "addr-old" });
188
+ const existingSnap = makeSnap(existingDefault, "addr-old");
189
+ mockQuery.get.mockResolvedValue({ docs: [existingSnap], empty: false, size: 1 });
190
+ const newAddress = makeAddressDoc({ isDefault: true });
191
+ mockDocRef.get.mockResolvedValue(makeSnap(newAddress, "mock-doc-id"));
192
+ await repo.createForOwner("user", "user-1", makeAddressInput({ isDefault: true }));
193
+ expect(mockBatch.update).toHaveBeenCalledWith(existingSnap.ref, expect.objectContaining({ isDefault: false }));
194
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
195
+ expect(mockDocRef.set).toHaveBeenCalledOnce();
196
+ });
197
+ it("sets ownerType and ownerId on the written document", async () => {
198
+ const address = makeAddressDoc();
199
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "mock-doc-id"));
200
+ await repo.createForOwner("store", "store-abc", makeAddressInput());
201
+ expect(mockDocRef.set).toHaveBeenCalledWith(expect.objectContaining({ ownerType: "store", ownerId: "store-abc" }));
202
+ });
203
+ it("re-fetches the address after write to return consistent data", async () => {
204
+ const address = makeAddressDoc();
205
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "mock-doc-id"));
206
+ const result = await repo.createForOwner("user", "user-1", makeAddressInput());
207
+ // The re-fetch (findById) uses mockDocRef.get — result reflects that
208
+ expect(result).toBeDefined();
209
+ expect(mockDocRef.get).toHaveBeenCalled();
210
+ });
211
+ it("throws DatabaseError when re-fetch returns null (doc not readable after write)", async () => {
212
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
213
+ await expect(repo.createForOwner("user", "user-1", makeAddressInput())).rejects.toThrow(DatabaseError);
214
+ });
215
+ it("calls encryptPiiFields with the address data on write", async () => {
216
+ const address = makeAddressDoc();
217
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "mock-doc-id"));
218
+ await repo.createForOwner("user", "user-1", makeAddressInput({ fullName: "Jane Doe" }));
219
+ expect(mockEncryptPiiFields).toHaveBeenCalledWith(expect.objectContaining({ fullName: "Jane Doe" }), expect.arrayContaining(["fullName", "phone", "addressLine1"]));
220
+ });
221
+ it("uses an auto-generated Firestore ID (getCollection().doc() called without args)", async () => {
222
+ const address = makeAddressDoc();
223
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "mock-doc-id"));
224
+ await repo.createForOwner("user", "user-1", makeAddressInput());
225
+ // doc() called with no argument = auto-ID
226
+ const callsWithNoArg = mockCollection.doc.mock.calls.filter((args) => args.length === 0);
227
+ expect(callsWithNoArg).not.toHaveLength(0);
228
+ });
229
+ });
230
+ // ---------------------------------------------------------------------------
231
+ // updateForOwner
232
+ // ---------------------------------------------------------------------------
233
+ describe("AddressesRepository.updateForOwner", () => {
234
+ it("throws DatabaseError when address not found", async () => {
235
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
236
+ await expect(repo.updateForOwner("user", "user-1", "addr-x", { label: "Work" })).rejects.toThrow(DatabaseError);
237
+ });
238
+ it("throws DatabaseError when ownerType does not match", async () => {
239
+ const address = makeAddressDoc({ ownerType: "store", ownerId: "store-1" });
240
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
241
+ await expect(repo.updateForOwner("user", "user-1", "addr-1", { label: "Work" })).rejects.toThrow(DatabaseError);
242
+ });
243
+ it("throws DatabaseError when ownerId does not match", async () => {
244
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-other" });
245
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
246
+ await expect(repo.updateForOwner("user", "user-1", "addr-1", { label: "Work" })).rejects.toThrow(DatabaseError);
247
+ });
248
+ it("updates the address when ownership matches", async () => {
249
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
250
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
251
+ mockQuery.get.mockResolvedValue({ docs: [], empty: true, size: 0 });
252
+ await repo.updateForOwner("user", "user-1", "addr-1", { label: "Office" });
253
+ expect(mockDocRef.update).toHaveBeenCalled();
254
+ });
255
+ it("isDefault:true — clears existing defaults before updating", async () => {
256
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
257
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
258
+ const existingDefault = makeAddressDoc({ isDefault: true, id: "addr-old" });
259
+ const existingSnap = makeSnap(existingDefault, "addr-old");
260
+ mockQuery.get.mockResolvedValue({ docs: [existingSnap], empty: false, size: 1 });
261
+ await repo.updateForOwner("user", "user-1", "addr-1", { isDefault: true });
262
+ expect(mockBatch.update).toHaveBeenCalledWith(existingSnap.ref, expect.objectContaining({ isDefault: false }));
263
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
264
+ });
265
+ it("isDefault:false — does NOT clear existing defaults", async () => {
266
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
267
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
268
+ await repo.updateForOwner("user", "user-1", "addr-1", { isDefault: false });
269
+ expect(mockBatch.commit).not.toHaveBeenCalled();
270
+ });
271
+ it("encrypts PII fields on update", async () => {
272
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
273
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
274
+ await repo.updateForOwner("user", "user-1", "addr-1", { fullName: "New Name" });
275
+ expect(mockEncryptPiiFields).toHaveBeenCalledWith(expect.objectContaining({ fullName: "New Name" }), expect.arrayContaining(["fullName", "phone", "addressLine1"]));
276
+ });
277
+ });
278
+ // ---------------------------------------------------------------------------
279
+ // deleteForOwner
280
+ // ---------------------------------------------------------------------------
281
+ describe("AddressesRepository.deleteForOwner", () => {
282
+ it("throws DatabaseError when address not found", async () => {
283
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
284
+ await expect(repo.deleteForOwner("user", "user-1", "addr-x")).rejects.toThrow(DatabaseError);
285
+ });
286
+ it("throws DatabaseError when address belongs to a different ownerId", async () => {
287
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-other" });
288
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
289
+ await expect(repo.deleteForOwner("user", "user-1", "addr-1")).rejects.toThrow(DatabaseError);
290
+ });
291
+ it("throws DatabaseError when address belongs to a different ownerType", async () => {
292
+ const address = makeAddressDoc({ ownerType: "store", ownerId: "user-1" });
293
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
294
+ await expect(repo.deleteForOwner("user", "user-1", "addr-1")).rejects.toThrow(DatabaseError);
295
+ });
296
+ it("deletes the address when ownership matches", async () => {
297
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
298
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
299
+ await repo.deleteForOwner("user", "user-1", "addr-1");
300
+ expect(mockDocRef.delete).toHaveBeenCalledOnce();
301
+ });
302
+ it("does NOT delete address for a different store-owner", async () => {
303
+ const storeAddr = makeAddressDoc({ ownerType: "store", ownerId: "store-2" });
304
+ mockDocRef.get.mockResolvedValue(makeSnap(storeAddr, "addr-1"));
305
+ await expect(repo.deleteForOwner("store", "store-1", "addr-1")).rejects.toThrow(DatabaseError);
306
+ expect(mockDocRef.delete).not.toHaveBeenCalled();
307
+ });
308
+ });
309
+ // ---------------------------------------------------------------------------
310
+ // setDefault
311
+ // ---------------------------------------------------------------------------
312
+ describe("AddressesRepository.setDefault", () => {
313
+ it("throws DatabaseError when address not found", async () => {
314
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
315
+ await expect(repo.setDefault("user", "user-1", "addr-x")).rejects.toThrow(DatabaseError);
316
+ });
317
+ it("throws DatabaseError when address belongs to a different owner", async () => {
318
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-other" });
319
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
320
+ await expect(repo.setDefault("user", "user-1", "addr-1")).rejects.toThrow(DatabaseError);
321
+ });
322
+ it("clears all existing defaults for the owner before setting the new one", async () => {
323
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
324
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
325
+ const existingDefault = makeAddressDoc({ isDefault: true, id: "addr-old" });
326
+ const existingSnap = makeSnap(existingDefault, "addr-old");
327
+ mockQuery.get.mockResolvedValue({ docs: [existingSnap], empty: false, size: 1 });
328
+ await repo.setDefault("user", "user-1", "addr-1");
329
+ // clearDefaultFlag ran and batched the update
330
+ expect(mockBatch.update).toHaveBeenCalledWith(existingSnap.ref, expect.objectContaining({ isDefault: false }));
331
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
332
+ });
333
+ it("sets isDefault:true on the target address", async () => {
334
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
335
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
336
+ mockQuery.get.mockResolvedValue({ docs: [], empty: true, size: 0 });
337
+ await repo.setDefault("user", "user-1", "addr-1");
338
+ expect(mockDocRef.update).toHaveBeenCalledWith(expect.objectContaining({ isDefault: true }));
339
+ });
340
+ it("uses a batch for atomicity (clear + set in single WriteBatch)", async () => {
341
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
342
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
343
+ // Two existing defaults to clear
344
+ const s1 = makeSnap(makeAddressDoc({ isDefault: true, id: "old-1" }), "old-1");
345
+ const s2 = makeSnap(makeAddressDoc({ isDefault: true, id: "old-2" }), "old-2");
346
+ mockQuery.get.mockResolvedValue({ docs: [s1, s2], empty: false, size: 2 });
347
+ await repo.setDefault("user", "user-1", "addr-1");
348
+ expect(mockBatch.update).toHaveBeenCalledTimes(2);
349
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
350
+ });
351
+ });
352
+ // ---------------------------------------------------------------------------
353
+ // deleteAllForOwner
354
+ // ---------------------------------------------------------------------------
355
+ describe("AddressesRepository.deleteAllForOwner", () => {
356
+ it("returns 0 and does not start a batch when no addresses found", async () => {
357
+ mockQuery.get.mockResolvedValue({ docs: [], empty: true, size: 0 });
358
+ const count = await repo.deleteAllForOwner("user", "user-1");
359
+ expect(count).toBe(0);
360
+ expect(mockBatch.delete).not.toHaveBeenCalled();
361
+ expect(mockBatch.commit).not.toHaveBeenCalled();
362
+ });
363
+ it("batch-deletes all matching addresses and returns the count", async () => {
364
+ const s1 = makeSnap(makeAddressDoc({ id: "addr-1" }), "addr-1");
365
+ const s2 = makeSnap(makeAddressDoc({ id: "addr-2" }), "addr-2");
366
+ mockQuery.get.mockResolvedValue({ docs: [s1, s2], empty: false, size: 2 });
367
+ const count = await repo.deleteAllForOwner("user", "user-1");
368
+ expect(count).toBe(2);
369
+ expect(mockBatch.delete).toHaveBeenCalledWith(s1.ref);
370
+ expect(mockBatch.delete).toHaveBeenCalledWith(s2.ref);
371
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
372
+ });
373
+ it("queries by ownerType AND ownerId", async () => {
374
+ mockQuery.get.mockResolvedValue({ docs: [], empty: true, size: 0 });
375
+ await repo.deleteAllForOwner("store", "store-xyz");
376
+ expect(mockCollection.where).toHaveBeenCalledWith("ownerType", "==", "store");
377
+ expect(mockQuery.where).toHaveBeenCalledWith("ownerId", "==", "store-xyz");
378
+ });
379
+ it("returns count matching the number of docs deleted", async () => {
380
+ const snaps = Array.from({ length: 5 }, (_, i) => makeSnap(makeAddressDoc({ id: `addr-${i}` }), `addr-${i}`));
381
+ mockQuery.get.mockResolvedValue({ docs: snaps, empty: false, size: 5 });
382
+ const count = await repo.deleteAllForOwner("user", "user-1");
383
+ expect(count).toBe(5);
384
+ expect(mockBatch.delete).toHaveBeenCalledTimes(5);
385
+ });
386
+ });
@@ -9,6 +9,6 @@
9
9
  * export { adminBidsGET as GET } from "@mohasinac/feat-admin";
10
10
  * ```
11
11
  */
12
- export declare const GET: (request: Request, context: {
12
+ export declare const GET: (request: Request, context?: {
13
13
  params: Promise<Record<string, string>>;
14
- }) => Promise<Response>;
14
+ } | undefined) => Promise<Response>;
@@ -9,6 +9,6 @@
9
9
  * export { adminCouponsGET as GET } from "@mohasinac/feat-admin";
10
10
  * ```
11
11
  */
12
- export declare const GET: (request: Request, context: {
12
+ export declare const GET: (request: Request, context?: {
13
13
  params: Promise<Record<string, string>>;
14
- }) => Promise<Response>;
14
+ } | undefined) => Promise<Response>;
@@ -9,6 +9,6 @@
9
9
  * export { adminProductsGET as GET } from "@mohasinac/feat-admin";
10
10
  * ```
11
11
  */
12
- export declare const GET: (request: Request, context: {
12
+ export declare const GET: (request: Request, context?: {
13
13
  params: Promise<Record<string, string>>;
14
- }) => Promise<Response>;
14
+ } | undefined) => Promise<Response>;
@@ -9,6 +9,6 @@
9
9
  * export { adminReviewsGET as GET } from "@mohasinac/feat-admin";
10
10
  * ```
11
11
  */
12
- export declare const GET: (request: Request, context: {
12
+ export declare const GET: (request: Request, context?: {
13
13
  params: Promise<Record<string, string>>;
14
- }) => Promise<Response>;
14
+ } | undefined) => Promise<Response>;