@stacksjs/commerce 0.70.53 → 0.70.55
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.
- package/package.json +3 -2
- package/src/carts/destroy.ts +54 -0
- package/src/carts/fetch.ts +20 -0
- package/src/carts/index.ts +13 -0
- package/src/carts/store.ts +75 -0
- package/src/carts/update.ts +78 -0
- package/src/coupons/destroy.ts +49 -0
- package/src/coupons/fetch.ts +510 -0
- package/src/coupons/index.ts +30 -0
- package/src/coupons/store.ts +46 -0
- package/src/coupons/update.ts +147 -0
- package/src/customers/destroy.ts +62 -0
- package/src/customers/fetch.ts +20 -0
- package/src/customers/index.ts +17 -0
- package/src/customers/store.ts +45 -0
- package/src/customers/update.ts +44 -0
- package/src/devices/destroy.ts +54 -0
- package/src/devices/export.ts +98 -0
- package/src/devices/fetch.ts +125 -0
- package/src/devices/index.ts +31 -0
- package/src/devices/store.ts +82 -0
- package/src/devices/update.ts +125 -0
- package/src/errors/destroy.ts +54 -0
- package/src/errors/fetch.ts +197 -0
- package/src/errors/index.ts +28 -0
- package/src/errors/update.ts +116 -0
- package/src/gift-cards/destroy.ts +87 -0
- package/src/gift-cards/fetch.ts +431 -0
- package/src/gift-cards/index.ts +26 -0
- package/src/gift-cards/store.ts +56 -0
- package/src/gift-cards/update.ts +102 -0
- package/src/index.ts +78 -0
- package/src/orders/destroy.ts +111 -0
- package/src/orders/events.ts +121 -0
- package/src/orders/export.ts +158 -0
- package/src/orders/fetch.ts +379 -0
- package/src/orders/guards.ts +261 -0
- package/src/orders/index.ts +91 -0
- package/src/orders/place-order.ts +274 -0
- package/src/orders/store.ts +51 -0
- package/src/orders/totals.ts +190 -0
- package/src/orders/update.ts +164 -0
- package/src/orders/webhook.ts +312 -0
- package/src/payments/destroy.ts +56 -0
- package/src/payments/fetch.ts +288 -0
- package/src/payments/index.ts +16 -0
- package/src/payments/store.ts +63 -0
- package/src/payments/update.ts +52 -0
- package/src/product.ts +0 -0
- package/src/products/categories/destroy.ts +103 -0
- package/src/products/categories/fetch.ts +285 -0
- package/src/products/categories/index.ts +36 -0
- package/src/products/categories/store.ts +87 -0
- package/src/products/categories/update.ts +251 -0
- package/src/products/index.ts +35 -0
- package/src/products/items/destroy.ts +54 -0
- package/src/products/items/fetch.ts +282 -0
- package/src/products/items/index.ts +26 -0
- package/src/products/items/store.ts +84 -0
- package/src/products/items/update.ts +324 -0
- package/src/products/manufacturers/destroy.ts +56 -0
- package/src/products/manufacturers/fetch.ts +117 -0
- package/src/products/manufacturers/index.ts +24 -0
- package/src/products/manufacturers/store.ts +82 -0
- package/src/products/manufacturers/update.ts +113 -0
- package/src/products/reviews/destroy.ts +56 -0
- package/src/products/reviews/fetch.ts +63 -0
- package/src/products/reviews/index.ts +25 -0
- package/src/products/reviews/store.ts +46 -0
- package/src/products/reviews/update.ts +82 -0
- package/src/products/units/destroy.ts +56 -0
- package/src/products/units/fetch.ts +20 -0
- package/src/products/units/index.ts +13 -0
- package/src/products/units/store.ts +162 -0
- package/src/products/units/update.ts +159 -0
- package/src/products/variants/destroy.ts +56 -0
- package/src/products/variants/fetch.ts +20 -0
- package/src/products/variants/index.ts +21 -0
- package/src/products/variants/store.ts +136 -0
- package/src/products/variants/update.ts +104 -0
- package/src/receipts/destroy.ts +54 -0
- package/src/receipts/export.ts +0 -0
- package/src/receipts/fetch.ts +222 -0
- package/src/receipts/index.ts +24 -0
- package/src/receipts/printer.ts +69 -0
- package/src/receipts/store.ts +69 -0
- package/src/receipts/update.ts +124 -0
- package/src/shippings/delivery-routes/destroy.ts +54 -0
- package/src/shippings/delivery-routes/fetch.ts +42 -0
- package/src/shippings/delivery-routes/index.ts +26 -0
- package/src/shippings/delivery-routes/store.ts +69 -0
- package/src/shippings/delivery-routes/update.ts +120 -0
- package/src/shippings/digital-deliveries/destroy.ts +111 -0
- package/src/shippings/digital-deliveries/fetch.ts +20 -0
- package/src/shippings/digital-deliveries/index.ts +26 -0
- package/src/shippings/digital-deliveries/store.ts +76 -0
- package/src/shippings/digital-deliveries/update.ts +129 -0
- package/src/shippings/drivers/destroy.ts +78 -0
- package/src/shippings/drivers/fetch.ts +20 -0
- package/src/shippings/drivers/index.ts +24 -0
- package/src/shippings/drivers/store.ts +69 -0
- package/src/shippings/drivers/update.ts +117 -0
- package/src/shippings/index.ts +39 -0
- package/src/shippings/license-keys/destroy.ts +111 -0
- package/src/shippings/license-keys/fetch.ts +20 -0
- package/src/shippings/license-keys/index.ts +26 -0
- package/src/shippings/license-keys/store.ts +77 -0
- package/src/shippings/license-keys/update.ts +117 -0
- package/src/shippings/shipping-methods/destroy.ts +111 -0
- package/src/shippings/shipping-methods/fetch.ts +62 -0
- package/src/shippings/shipping-methods/index.ts +28 -0
- package/src/shippings/shipping-methods/store.ts +123 -0
- package/src/shippings/shipping-methods/update.ts +120 -0
- package/src/shippings/shipping-rates/destroy.ts +102 -0
- package/src/shippings/shipping-rates/fetch.ts +320 -0
- package/src/shippings/shipping-rates/index.ts +36 -0
- package/src/shippings/shipping-rates/store.ts +102 -0
- package/src/shippings/shipping-rates/update.ts +139 -0
- package/src/shippings/shipping-zones/destroy.ts +111 -0
- package/src/shippings/shipping-zones/fetch.ts +96 -0
- package/src/shippings/shipping-zones/index.ts +26 -0
- package/src/shippings/shipping-zones/store.ts +74 -0
- package/src/shippings/shipping-zones/update.ts +156 -0
- package/src/tax/destroy.ts +54 -0
- package/src/tax/fetch.ts +20 -0
- package/src/tax/index.ts +24 -0
- package/src/tax/store.ts +69 -0
- package/src/tax/update.ts +119 -0
- package/src/tests/coupons.test.ts +90 -0
- package/src/tests/customers.test.ts +39 -0
- package/src/tests/device.test.ts +111 -0
- package/src/tests/digitals.test.ts +31 -0
- package/src/tests/drivers.test.ts +25 -0
- package/src/tests/gift-cards.test.ts +36 -0
- package/src/tests/license.test.ts +31 -0
- package/src/tests/manufacturers.test.ts +80 -0
- package/src/tests/orders.test.ts +25 -0
- package/src/tests/payments.test.ts +75 -0
- package/src/tests/rates.test.ts +367 -0
- package/src/tests/receipts.test.ts +287 -0
- package/src/tests/restaurant.test.ts +522 -0
- package/src/tests/reviews.test.ts +165 -0
- package/src/tests/routes.test.ts +51 -0
- package/src/tests/setup.ts +149 -0
- package/src/tests/shipping.test.ts +88 -0
- package/src/tests/tax.test.ts +25 -0
- package/src/tests/units.test.ts +110 -0
- package/src/tests/variants.test.ts +133 -0
- package/src/tests/waitlist.test.ts +303 -0
- package/src/tests/zones.test.ts +86 -0
- package/src/types.ts +312 -0
- package/src/utils/typed-stats.ts +67 -0
- package/src/waitlists/index.ts +19 -0
- package/src/waitlists/products/destroy.ts +65 -0
- package/src/waitlists/products/export.ts +113 -0
- package/src/waitlists/products/fetch.ts +362 -0
- package/src/waitlists/products/index.ts +33 -0
- package/src/waitlists/products/store.ts +112 -0
- package/src/waitlists/products/update.ts +125 -0
- package/src/waitlists/restaurant/destroy.ts +65 -0
- package/src/waitlists/restaurant/export.ts +107 -0
- package/src/waitlists/restaurant/fetch.ts +538 -0
- package/src/waitlists/restaurant/index.ts +43 -0
- package/src/waitlists/restaurant/store.ts +109 -0
- package/src/waitlists/restaurant/update.ts +198 -0
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
type WaitlistRestaurantJsonResponse = ModelRow<typeof WaitlistRestaurant>
|
|
2
|
+
import { beforeEach, describe, expect, it } from 'bun:test'
|
|
3
|
+
import { refreshDatabase } from './setup'
|
|
4
|
+
import { bulkDestroy } from '../waitlists/restaurant/destroy'
|
|
5
|
+
import {
|
|
6
|
+
fetchBetweenDates,
|
|
7
|
+
fetchSeatedBetweenDates,
|
|
8
|
+
fetchTablesTurnedToday,
|
|
9
|
+
fetchWaiting,
|
|
10
|
+
fetchWaitingWithPartySizes,
|
|
11
|
+
fetchWaitingWithQuotedTimes,
|
|
12
|
+
} from '../waitlists/restaurant/fetch'
|
|
13
|
+
import { bulkStore } from '../waitlists/restaurant/store'
|
|
14
|
+
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
await refreshDatabase()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('Restaurant Waitlist Module', () => {
|
|
20
|
+
describe('store', () => {
|
|
21
|
+
it('should return 0 when trying to bulk store an empty array', async () => {
|
|
22
|
+
const count = await bulkStore([])
|
|
23
|
+
expect(count).toBe(0)
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
describe('destroy', () => {
|
|
28
|
+
it('should return 0 when trying to delete an empty array of waitlist entries', async () => {
|
|
29
|
+
// Try to delete with an empty array
|
|
30
|
+
const deletedCount = await bulkDestroy([])
|
|
31
|
+
expect(deletedCount).toBe(0)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('fetch', () => {
|
|
36
|
+
it('should fetch waitlist entries between two dates', async () => {
|
|
37
|
+
// Create test dates using today
|
|
38
|
+
const today = new Date()
|
|
39
|
+
const startDate = new Date(today.setHours(0, 0, 0, 0))
|
|
40
|
+
const endDate = new Date(today.setHours(23, 59, 59, 999))
|
|
41
|
+
|
|
42
|
+
// Create test waitlist entries
|
|
43
|
+
const requests = [
|
|
44
|
+
{
|
|
45
|
+
name: 'John Doe',
|
|
46
|
+
email: 'john@example.com',
|
|
47
|
+
party_size: 4,
|
|
48
|
+
check_in_time: Date.now(),
|
|
49
|
+
table_preference: 'indoor',
|
|
50
|
+
status: 'waiting',
|
|
51
|
+
quoted_wait_time: 30,
|
|
52
|
+
customer_id: 1,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'Jane Smith',
|
|
56
|
+
email: 'jane@example.com',
|
|
57
|
+
party_size: 2,
|
|
58
|
+
check_in_time: Date.now(),
|
|
59
|
+
table_preference: 'bar',
|
|
60
|
+
status: 'waiting',
|
|
61
|
+
quoted_wait_time: 15,
|
|
62
|
+
customer_id: 2,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'Bob Wilson',
|
|
66
|
+
email: 'bob@example.com',
|
|
67
|
+
party_size: 6,
|
|
68
|
+
check_in_time: Date.now(),
|
|
69
|
+
table_preference: 'booth',
|
|
70
|
+
status: 'waiting',
|
|
71
|
+
quoted_wait_time: 45,
|
|
72
|
+
customer_id: 3,
|
|
73
|
+
},
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
// Create the waitlist entries
|
|
77
|
+
await bulkStore(requests)
|
|
78
|
+
|
|
79
|
+
// Fetch entries between dates
|
|
80
|
+
const entries = await fetchBetweenDates(startDate, endDate)
|
|
81
|
+
expect(entries).toBeDefined()
|
|
82
|
+
expect(entries.length).toBe(3) // All entries should be within the date range
|
|
83
|
+
expect(entries.map((w: WaitlistRestaurantJsonResponse) => w.name)).toContain('John Doe')
|
|
84
|
+
expect(entries.map((w: WaitlistRestaurantJsonResponse) => w.name)).toContain('Jane Smith')
|
|
85
|
+
expect(entries.map((w: WaitlistRestaurantJsonResponse) => w.name)).toContain('Bob Wilson')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('should fetch seated waitlist entries between two dates', async () => {
|
|
89
|
+
// Create test dates using today
|
|
90
|
+
const today = new Date()
|
|
91
|
+
const startDate = new Date(today.setHours(0, 0, 0, 0))
|
|
92
|
+
const endDate = new Date(today.setHours(23, 59, 59, 999))
|
|
93
|
+
|
|
94
|
+
// Create test waitlist entries with different statuses
|
|
95
|
+
const requests = [
|
|
96
|
+
{
|
|
97
|
+
name: 'John Doe',
|
|
98
|
+
email: 'john@example.com',
|
|
99
|
+
party_size: 4,
|
|
100
|
+
check_in_time: Date.now(),
|
|
101
|
+
table_preference: 'indoor',
|
|
102
|
+
status: 'seated',
|
|
103
|
+
quoted_wait_time: 30,
|
|
104
|
+
customer_id: 1,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'Jane Smith',
|
|
108
|
+
email: 'jane@example.com',
|
|
109
|
+
party_size: 2,
|
|
110
|
+
check_in_time: Date.now(),
|
|
111
|
+
table_preference: 'bar',
|
|
112
|
+
status: 'seated',
|
|
113
|
+
quoted_wait_time: 15,
|
|
114
|
+
customer_id: 2,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'Bob Wilson',
|
|
118
|
+
email: 'bob@example.com',
|
|
119
|
+
party_size: 6,
|
|
120
|
+
check_in_time: Date.now(),
|
|
121
|
+
table_preference: 'booth',
|
|
122
|
+
status: 'waiting',
|
|
123
|
+
quoted_wait_time: 45,
|
|
124
|
+
customer_id: 3,
|
|
125
|
+
},
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
// Create the waitlist entries
|
|
129
|
+
await bulkStore(requests)
|
|
130
|
+
|
|
131
|
+
// Fetch seated entries between dates
|
|
132
|
+
const seatedEntries = await fetchSeatedBetweenDates(startDate, endDate)
|
|
133
|
+
expect(seatedEntries).toBeDefined()
|
|
134
|
+
expect(seatedEntries.length).toBe(2)
|
|
135
|
+
expect(seatedEntries.map((w: WaitlistRestaurantJsonResponse) => w.name)).toContain('John Doe')
|
|
136
|
+
expect(seatedEntries.map((w: WaitlistRestaurantJsonResponse) => w.name)).toContain('Jane Smith')
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('should fetch all waitlist entries with waiting status', async () => {
|
|
140
|
+
// Create test waitlist entries with different statuses
|
|
141
|
+
const requests = [
|
|
142
|
+
{
|
|
143
|
+
name: 'John Doe',
|
|
144
|
+
email: 'john@example.com',
|
|
145
|
+
party_size: 4,
|
|
146
|
+
check_in_time: Date.now(),
|
|
147
|
+
table_preference: 'indoor',
|
|
148
|
+
status: 'waiting',
|
|
149
|
+
quoted_wait_time: 30,
|
|
150
|
+
customer_id: 1,
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'Jane Smith',
|
|
154
|
+
email: 'jane@example.com',
|
|
155
|
+
party_size: 2,
|
|
156
|
+
check_in_time: Date.now(),
|
|
157
|
+
table_preference: 'bar',
|
|
158
|
+
status: 'waiting',
|
|
159
|
+
quoted_wait_time: 15,
|
|
160
|
+
customer_id: 2,
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: 'Bob Wilson',
|
|
164
|
+
email: 'bob@example.com',
|
|
165
|
+
party_size: 6,
|
|
166
|
+
check_in_time: Date.now(),
|
|
167
|
+
table_preference: 'booth',
|
|
168
|
+
status: 'seated',
|
|
169
|
+
quoted_wait_time: 45,
|
|
170
|
+
customer_id: 3,
|
|
171
|
+
},
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
// Create the waitlist entries
|
|
175
|
+
await bulkStore(requests)
|
|
176
|
+
|
|
177
|
+
// Fetch entries with waiting status
|
|
178
|
+
const waitingEntries = await fetchWaiting()
|
|
179
|
+
expect(waitingEntries).toBeDefined()
|
|
180
|
+
expect(waitingEntries.length).toBe(2)
|
|
181
|
+
expect(waitingEntries.map((w: WaitlistRestaurantJsonResponse) => w.name)).toContain('John Doe')
|
|
182
|
+
expect(waitingEntries.map((w: WaitlistRestaurantJsonResponse) => w.name)).toContain('Jane Smith')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('should fetch waiting entries with quoted wait times', async () => {
|
|
186
|
+
// Create test waitlist entries with different wait times and statuses
|
|
187
|
+
const requests = [
|
|
188
|
+
{
|
|
189
|
+
name: 'John Doe',
|
|
190
|
+
email: 'john@example.com',
|
|
191
|
+
party_size: 4,
|
|
192
|
+
check_in_time: Date.now(),
|
|
193
|
+
table_preference: 'indoor',
|
|
194
|
+
status: 'waiting',
|
|
195
|
+
quoted_wait_time: 30,
|
|
196
|
+
customer_id: 1,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: 'Jane Smith',
|
|
200
|
+
email: 'jane@example.com',
|
|
201
|
+
party_size: 2,
|
|
202
|
+
check_in_time: Date.now(),
|
|
203
|
+
table_preference: 'bar',
|
|
204
|
+
status: 'waiting',
|
|
205
|
+
quoted_wait_time: 15,
|
|
206
|
+
customer_id: 2,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'Bob Wilson',
|
|
210
|
+
email: 'bob@example.com',
|
|
211
|
+
party_size: 6,
|
|
212
|
+
check_in_time: Date.now(),
|
|
213
|
+
table_preference: 'booth',
|
|
214
|
+
status: 'seated',
|
|
215
|
+
quoted_wait_time: 45,
|
|
216
|
+
customer_id: 3,
|
|
217
|
+
},
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
// Create the waitlist entries
|
|
221
|
+
await bulkStore(requests)
|
|
222
|
+
|
|
223
|
+
// Fetch waiting entries with quoted times
|
|
224
|
+
const result = await fetchWaitingWithQuotedTimes()
|
|
225
|
+
|
|
226
|
+
// Verify the results
|
|
227
|
+
expect(result).toBeDefined()
|
|
228
|
+
expect(result.entries).toBeDefined()
|
|
229
|
+
expect(result.entries.length).toBe(2) // Only waiting entries
|
|
230
|
+
expect(result.totalQuotedWaitTime).toBe(45) // 30 + 15
|
|
231
|
+
expect(result.averageQuotedWaitTime).toBe(22.5) // (30 + 15) / 2
|
|
232
|
+
|
|
233
|
+
// Verify individual entries
|
|
234
|
+
expect(result.entries.map(e => e.name)).toContain('John Doe')
|
|
235
|
+
expect(result.entries.map(e => e.name)).toContain('Jane Smith')
|
|
236
|
+
expect(result.entries.map(e => e.quoted_wait_time)).toContain(30)
|
|
237
|
+
expect(result.entries.map(e => e.quoted_wait_time)).toContain(15)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('should handle empty waiting list for quoted times', async () => {
|
|
241
|
+
// Create only seated entries
|
|
242
|
+
const requests = [
|
|
243
|
+
{
|
|
244
|
+
name: 'John Doe',
|
|
245
|
+
email: 'john@example.com',
|
|
246
|
+
party_size: 4,
|
|
247
|
+
check_in_time: Date.now(),
|
|
248
|
+
table_preference: 'indoor',
|
|
249
|
+
status: 'seated',
|
|
250
|
+
quoted_wait_time: 30,
|
|
251
|
+
customer_id: 1,
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
name: 'Jane Smith',
|
|
255
|
+
email: 'jane@example.com',
|
|
256
|
+
party_size: 2,
|
|
257
|
+
check_in_time: Date.now(),
|
|
258
|
+
table_preference: 'bar',
|
|
259
|
+
status: 'seated',
|
|
260
|
+
quoted_wait_time: 15,
|
|
261
|
+
customer_id: 2,
|
|
262
|
+
},
|
|
263
|
+
]
|
|
264
|
+
|
|
265
|
+
// Create the waitlist entries
|
|
266
|
+
await bulkStore(requests)
|
|
267
|
+
|
|
268
|
+
// Fetch waiting entries with quoted times
|
|
269
|
+
const result = await fetchWaitingWithQuotedTimes()
|
|
270
|
+
|
|
271
|
+
// Verify the results
|
|
272
|
+
expect(result).toBeDefined()
|
|
273
|
+
expect(result.entries).toBeDefined()
|
|
274
|
+
expect(result.entries.length).toBe(0)
|
|
275
|
+
expect(result.totalQuotedWaitTime).toBe(0)
|
|
276
|
+
expect(result.averageQuotedWaitTime).toBe(0)
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('should fetch waiting entries with party size calculations', async () => {
|
|
280
|
+
// Create test waitlist entries with different party sizes and statuses
|
|
281
|
+
const requests = [
|
|
282
|
+
{
|
|
283
|
+
name: 'John Doe',
|
|
284
|
+
email: 'john@example.com',
|
|
285
|
+
party_size: 4,
|
|
286
|
+
check_in_time: Date.now(),
|
|
287
|
+
table_preference: 'indoor',
|
|
288
|
+
status: 'waiting',
|
|
289
|
+
quoted_wait_time: 30,
|
|
290
|
+
customer_id: 1,
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
name: 'Jane Smith',
|
|
294
|
+
email: 'jane@example.com',
|
|
295
|
+
party_size: 4,
|
|
296
|
+
check_in_time: Date.now(),
|
|
297
|
+
table_preference: 'bar',
|
|
298
|
+
status: 'waiting',
|
|
299
|
+
quoted_wait_time: 15,
|
|
300
|
+
customer_id: 2,
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: 'Bob Wilson',
|
|
304
|
+
email: 'bob@example.com',
|
|
305
|
+
party_size: 2,
|
|
306
|
+
check_in_time: Date.now(),
|
|
307
|
+
table_preference: 'booth',
|
|
308
|
+
status: 'waiting',
|
|
309
|
+
quoted_wait_time: 45,
|
|
310
|
+
customer_id: 3,
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: 'Alice Brown',
|
|
314
|
+
email: 'alice@example.com',
|
|
315
|
+
party_size: 6,
|
|
316
|
+
check_in_time: Date.now(),
|
|
317
|
+
table_preference: 'indoor',
|
|
318
|
+
status: 'seated',
|
|
319
|
+
quoted_wait_time: 60,
|
|
320
|
+
customer_id: 4,
|
|
321
|
+
},
|
|
322
|
+
]
|
|
323
|
+
|
|
324
|
+
// Create the waitlist entries
|
|
325
|
+
await bulkStore(requests)
|
|
326
|
+
|
|
327
|
+
// Fetch waiting entries with party sizes
|
|
328
|
+
const result = await fetchWaitingWithPartySizes()
|
|
329
|
+
|
|
330
|
+
// Verify the results
|
|
331
|
+
expect(result).toBeDefined()
|
|
332
|
+
expect(result.entries).toBeDefined()
|
|
333
|
+
expect(result.entries.length).toBe(3) // Only waiting entries
|
|
334
|
+
expect(result.totalPartySize).toBe(10) // 4 + 4 + 2
|
|
335
|
+
expect(result.averagePartySize).toBeCloseTo(3.33, 2) // 10 / 3
|
|
336
|
+
|
|
337
|
+
// Verify party size breakdown
|
|
338
|
+
expect(result.partySizeBreakdown).toBeDefined()
|
|
339
|
+
expect(Object.keys(result.partySizeBreakdown).length).toBe(2) // 2 different party sizes
|
|
340
|
+
expect(result.partySizeBreakdown[4]).toBe(2) // Two parties of 4
|
|
341
|
+
expect(result.partySizeBreakdown[2]).toBe(1) // One party of 2
|
|
342
|
+
|
|
343
|
+
// Verify individual entries
|
|
344
|
+
expect(result.entries.map(e => e.name)).toContain('John Doe')
|
|
345
|
+
expect(result.entries.map(e => e.name)).toContain('Jane Smith')
|
|
346
|
+
expect(result.entries.map(e => e.name)).toContain('Bob Wilson')
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
it('should handle empty waiting list for party sizes', async () => {
|
|
350
|
+
// Create only seated entries
|
|
351
|
+
const requests = [
|
|
352
|
+
{
|
|
353
|
+
name: 'John Doe',
|
|
354
|
+
email: 'john@example.com',
|
|
355
|
+
party_size: 4,
|
|
356
|
+
check_in_time: Date.now(),
|
|
357
|
+
table_preference: 'indoor',
|
|
358
|
+
status: 'seated',
|
|
359
|
+
quoted_wait_time: 30,
|
|
360
|
+
customer_id: 1,
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
name: 'Jane Smith',
|
|
364
|
+
email: 'jane@example.com',
|
|
365
|
+
party_size: 2,
|
|
366
|
+
check_in_time: Date.now(),
|
|
367
|
+
table_preference: 'bar',
|
|
368
|
+
status: 'seated',
|
|
369
|
+
quoted_wait_time: 15,
|
|
370
|
+
customer_id: 2,
|
|
371
|
+
},
|
|
372
|
+
]
|
|
373
|
+
|
|
374
|
+
// Create the waitlist entries
|
|
375
|
+
await bulkStore(requests)
|
|
376
|
+
|
|
377
|
+
// Fetch waiting entries with party sizes
|
|
378
|
+
const result = await fetchWaitingWithPartySizes()
|
|
379
|
+
|
|
380
|
+
// Verify the results
|
|
381
|
+
expect(result).toBeDefined()
|
|
382
|
+
expect(result.entries).toBeDefined()
|
|
383
|
+
expect(result.entries.length).toBe(0)
|
|
384
|
+
expect(result.totalPartySize).toBe(0)
|
|
385
|
+
expect(result.averagePartySize).toBe(0)
|
|
386
|
+
expect(result.partySizeBreakdown).toEqual({})
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('should fetch tables turned statistics for today', async () => {
|
|
390
|
+
// Create test dates using today
|
|
391
|
+
const today = new Date()
|
|
392
|
+
const startOfDay = new Date(today)
|
|
393
|
+
startOfDay.setHours(0, 0, 0, 0)
|
|
394
|
+
const endOfDay = new Date(today)
|
|
395
|
+
endOfDay.setHours(23, 59, 59, 999)
|
|
396
|
+
|
|
397
|
+
// Create test waitlist entries with different statuses and table preferences
|
|
398
|
+
const requests = [
|
|
399
|
+
{
|
|
400
|
+
name: 'John Doe',
|
|
401
|
+
email: 'john@example.com',
|
|
402
|
+
party_size: 4,
|
|
403
|
+
check_in_time: startOfDay.getTime(),
|
|
404
|
+
table_preference: 'indoor',
|
|
405
|
+
status: 'seated',
|
|
406
|
+
quoted_wait_time: 30,
|
|
407
|
+
customer_id: 1,
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
name: 'Jane Smith',
|
|
411
|
+
email: 'jane@example.com',
|
|
412
|
+
party_size: 2,
|
|
413
|
+
check_in_time: endOfDay.getTime(),
|
|
414
|
+
table_preference: 'bar',
|
|
415
|
+
status: 'seated',
|
|
416
|
+
quoted_wait_time: 15,
|
|
417
|
+
customer_id: 2,
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
name: 'Bob Wilson',
|
|
421
|
+
email: 'bob@example.com',
|
|
422
|
+
party_size: 6,
|
|
423
|
+
check_in_time: Date.now(),
|
|
424
|
+
table_preference: 'booth',
|
|
425
|
+
status: 'seated',
|
|
426
|
+
quoted_wait_time: 45,
|
|
427
|
+
customer_id: 3,
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
name: 'Alice Brown',
|
|
431
|
+
email: 'alice@example.com',
|
|
432
|
+
party_size: 3,
|
|
433
|
+
check_in_time: Date.now(),
|
|
434
|
+
table_preference: 'indoor',
|
|
435
|
+
status: 'waiting',
|
|
436
|
+
quoted_wait_time: 20,
|
|
437
|
+
customer_id: 4,
|
|
438
|
+
},
|
|
439
|
+
]
|
|
440
|
+
|
|
441
|
+
// Create the waitlist entries
|
|
442
|
+
await bulkStore(requests)
|
|
443
|
+
|
|
444
|
+
// Fetch tables turned statistics
|
|
445
|
+
const stats = await fetchTablesTurnedToday()
|
|
446
|
+
|
|
447
|
+
// Verify the results
|
|
448
|
+
expect(stats).toBeDefined()
|
|
449
|
+
expect(stats.totalTablesTurned).toBe(3) // 3 seated entries
|
|
450
|
+
expect(stats.totalCustomersSeated).toBe(12) // 4 + 2 + 6
|
|
451
|
+
expect(stats.averagePartySize).toBe(4) // 12 / 3
|
|
452
|
+
|
|
453
|
+
// Verify table preference breakdown
|
|
454
|
+
expect(stats.breakdownByTablePreference).toBeDefined()
|
|
455
|
+
expect(Object.keys(stats.breakdownByTablePreference).length).toBe(3) // indoor, bar, booth
|
|
456
|
+
expect(stats.breakdownByTablePreference.indoor).toBe(1)
|
|
457
|
+
expect(stats.breakdownByTablePreference.bar).toBe(1)
|
|
458
|
+
expect(stats.breakdownByTablePreference.booth).toBe(1)
|
|
459
|
+
|
|
460
|
+
// Verify party size breakdown
|
|
461
|
+
expect(stats.breakdownByPartySize).toBeDefined()
|
|
462
|
+
expect(Object.keys(stats.breakdownByPartySize).length).toBe(3) // 2, 3, 4, 6
|
|
463
|
+
expect(stats.breakdownByPartySize[2]).toBe(1)
|
|
464
|
+
expect(stats.breakdownByPartySize[4]).toBe(1)
|
|
465
|
+
expect(stats.breakdownByPartySize[6]).toBe(1)
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
it('should handle no tables turned today', async () => {
|
|
469
|
+
// Create test entries with different dates
|
|
470
|
+
const today = new Date()
|
|
471
|
+
const yesterday = new Date(today)
|
|
472
|
+
yesterday.setDate(yesterday.getDate() - 1)
|
|
473
|
+
|
|
474
|
+
const requests = [
|
|
475
|
+
{
|
|
476
|
+
name: 'John Doe',
|
|
477
|
+
email: 'john@example.com',
|
|
478
|
+
party_size: 4,
|
|
479
|
+
check_in_time: yesterday.getTime(),
|
|
480
|
+
table_preference: 'indoor',
|
|
481
|
+
status: 'seated',
|
|
482
|
+
quoted_wait_time: 30,
|
|
483
|
+
customer_id: 1,
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
name: 'Jane Smith',
|
|
487
|
+
email: 'jane@example.com',
|
|
488
|
+
party_size: 2,
|
|
489
|
+
check_in_time: yesterday.getTime(),
|
|
490
|
+
table_preference: 'bar',
|
|
491
|
+
status: 'seated',
|
|
492
|
+
quoted_wait_time: 15,
|
|
493
|
+
customer_id: 2,
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
name: 'Bob Wilson',
|
|
497
|
+
email: 'bob@example.com',
|
|
498
|
+
party_size: 6,
|
|
499
|
+
check_in_time: Date.now(),
|
|
500
|
+
table_preference: 'booth',
|
|
501
|
+
status: 'waiting',
|
|
502
|
+
quoted_wait_time: 45,
|
|
503
|
+
customer_id: 3,
|
|
504
|
+
},
|
|
505
|
+
]
|
|
506
|
+
|
|
507
|
+
// Create the waitlist entries
|
|
508
|
+
await bulkStore(requests)
|
|
509
|
+
|
|
510
|
+
// Fetch tables turned statistics
|
|
511
|
+
const stats = await fetchTablesTurnedToday()
|
|
512
|
+
|
|
513
|
+
// Verify the results
|
|
514
|
+
expect(stats).toBeDefined()
|
|
515
|
+
expect(stats.totalTablesTurned).toBe(0)
|
|
516
|
+
expect(stats.totalCustomersSeated).toBe(0)
|
|
517
|
+
expect(stats.averagePartySize).toBe(0)
|
|
518
|
+
expect(stats.breakdownByTablePreference).toEqual({})
|
|
519
|
+
expect(stats.breakdownByPartySize).toEqual({})
|
|
520
|
+
})
|
|
521
|
+
})
|
|
522
|
+
})
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from 'bun:test'
|
|
2
|
+
import { refreshDatabase } from './setup'
|
|
3
|
+
import { bulkDestroy } from '../products/reviews/destroy'
|
|
4
|
+
import { fetchApprovedByProductId, fetchByProductId, fetchByUserId, fetchMostHelpfulByProductId } from '../products/reviews/fetch'
|
|
5
|
+
import { store } from '../products/reviews/store'
|
|
6
|
+
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
await refreshDatabase()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
describe('Product Review Module', () => {
|
|
12
|
+
describe('fetch methods', () => {
|
|
13
|
+
it('should fetch reviews by product ID', async () => {
|
|
14
|
+
// Create several reviews for the same product
|
|
15
|
+
const productId = 2
|
|
16
|
+
|
|
17
|
+
// Create first review
|
|
18
|
+
const request1 = {
|
|
19
|
+
product_id: productId,
|
|
20
|
+
customer_id: 1,
|
|
21
|
+
rating: 5,
|
|
22
|
+
title: 'First review',
|
|
23
|
+
content: 'First review content',
|
|
24
|
+
is_approved: true,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
await store(request1)
|
|
28
|
+
|
|
29
|
+
// Create second review
|
|
30
|
+
const request2 = {
|
|
31
|
+
product_id: productId,
|
|
32
|
+
customer_id: 2,
|
|
33
|
+
rating: 4,
|
|
34
|
+
title: 'Second review',
|
|
35
|
+
content: 'Second review content',
|
|
36
|
+
is_approved: true,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
await store(request2)
|
|
40
|
+
|
|
41
|
+
// Now fetch reviews by product ID
|
|
42
|
+
const result = await fetchByProductId(productId)
|
|
43
|
+
|
|
44
|
+
expect(result).toBeDefined()
|
|
45
|
+
expect(result.length).toBe(2)
|
|
46
|
+
expect(result[0].product_id).toBe(productId)
|
|
47
|
+
expect(result[1].product_id).toBe(productId)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('should fetch reviews by user ID', async () => {
|
|
51
|
+
// Create several reviews for the same user
|
|
52
|
+
const customerId = 3
|
|
53
|
+
|
|
54
|
+
// Create first review
|
|
55
|
+
const request1 = {
|
|
56
|
+
product_id: 1,
|
|
57
|
+
customer_id: customerId,
|
|
58
|
+
rating: 5,
|
|
59
|
+
title: 'First user review',
|
|
60
|
+
content: 'First user review content',
|
|
61
|
+
}
|
|
62
|
+
await store(request1)
|
|
63
|
+
|
|
64
|
+
// Create second review
|
|
65
|
+
const request2 = {
|
|
66
|
+
product_id: 2,
|
|
67
|
+
customer_id: customerId,
|
|
68
|
+
rating: 3,
|
|
69
|
+
title: 'Second user review',
|
|
70
|
+
content: 'Second user review content',
|
|
71
|
+
}
|
|
72
|
+
await store(request2)
|
|
73
|
+
|
|
74
|
+
// Now fetch reviews by user ID
|
|
75
|
+
const result = await fetchByUserId(customerId)
|
|
76
|
+
|
|
77
|
+
expect(result).toBeDefined()
|
|
78
|
+
expect(result.length).toBe(2)
|
|
79
|
+
expect(result[0].customer_id).toBe(customerId)
|
|
80
|
+
expect(result[1].customer_id).toBe(customerId)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should fetch approved reviews for a product', async () => {
|
|
84
|
+
const productId = 3
|
|
85
|
+
|
|
86
|
+
// Create approved review
|
|
87
|
+
const request1 = {
|
|
88
|
+
product_id: productId,
|
|
89
|
+
customer_id: 1,
|
|
90
|
+
rating: 5,
|
|
91
|
+
title: 'Approved review',
|
|
92
|
+
content: 'Approved review content',
|
|
93
|
+
is_approved: true,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
await store(request1)
|
|
97
|
+
|
|
98
|
+
// Create unapproved review
|
|
99
|
+
const request2 = {
|
|
100
|
+
product_id: productId,
|
|
101
|
+
customer_id: 2,
|
|
102
|
+
rating: 2,
|
|
103
|
+
title: 'Unapproved review',
|
|
104
|
+
content: 'Unapproved review content',
|
|
105
|
+
is_approved: false,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
await store(request2)
|
|
109
|
+
|
|
110
|
+
// Now fetch only approved reviews
|
|
111
|
+
const result = await fetchApprovedByProductId(productId)
|
|
112
|
+
|
|
113
|
+
expect(result).toBeDefined()
|
|
114
|
+
expect(result.length).toBe(1)
|
|
115
|
+
expect(Boolean(result[0].is_approved)).toBe(true)
|
|
116
|
+
expect(result[0].title).toBe('Approved review')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('should fetch most helpful reviews', async () => {
|
|
120
|
+
const productId = 5
|
|
121
|
+
|
|
122
|
+
// Create review with many helpful votes
|
|
123
|
+
const request1 = {
|
|
124
|
+
product_id: productId,
|
|
125
|
+
customer_id: 1,
|
|
126
|
+
rating: 5,
|
|
127
|
+
title: 'Most helpful review',
|
|
128
|
+
content: 'Very helpful content',
|
|
129
|
+
is_approved: true,
|
|
130
|
+
helpful_votes: 10,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
await store(request1)
|
|
134
|
+
|
|
135
|
+
// Create review with fewer helpful votes
|
|
136
|
+
const request2 = {
|
|
137
|
+
product_id: productId,
|
|
138
|
+
customer_id: 2,
|
|
139
|
+
rating: 4,
|
|
140
|
+
title: 'Less helpful review',
|
|
141
|
+
content: 'Still helpful content',
|
|
142
|
+
is_approved: true,
|
|
143
|
+
helpful_votes: 5,
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await store(request2)
|
|
147
|
+
|
|
148
|
+
// Now fetch most helpful reviews
|
|
149
|
+
const helpfulReviews = await fetchMostHelpfulByProductId(productId)
|
|
150
|
+
|
|
151
|
+
expect(helpfulReviews).toBeDefined()
|
|
152
|
+
expect(helpfulReviews.length).toBe(2)
|
|
153
|
+
expect(helpfulReviews[0].helpful_votes).toBe(10)
|
|
154
|
+
expect(helpfulReviews[1].helpful_votes).toBe(5)
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
describe('destroy', () => {
|
|
159
|
+
it('should return 0 when trying to delete an empty array of reviews', async () => {
|
|
160
|
+
// Try to delete with an empty array
|
|
161
|
+
const deletedCount = await bulkDestroy([])
|
|
162
|
+
expect(deletedCount).toBe(0)
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
})
|