@payloadcms/plugin-ecommerce 3.67.0-internal.8383bda → 3.67.0
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/dist/collections/addresses/createAddressesCollection.d.ts +1 -5
- package/dist/collections/addresses/createAddressesCollection.d.ts.map +1 -1
- package/dist/collections/addresses/createAddressesCollection.js +7 -6
- package/dist/collections/addresses/createAddressesCollection.js.map +1 -1
- package/dist/collections/carts/beforeChange.d.ts.map +1 -1
- package/dist/collections/carts/beforeChange.js +13 -1
- package/dist/collections/carts/beforeChange.js.map +1 -1
- package/dist/collections/carts/createCartsCollection.d.ts +6 -4
- package/dist/collections/carts/createCartsCollection.d.ts.map +1 -1
- package/dist/collections/carts/createCartsCollection.js +36 -5
- package/dist/collections/carts/createCartsCollection.js.map +1 -1
- package/dist/collections/carts/hasCartSecretAccess.d.ts +10 -0
- package/dist/collections/carts/hasCartSecretAccess.d.ts.map +1 -0
- package/dist/collections/carts/hasCartSecretAccess.js +24 -0
- package/dist/collections/carts/hasCartSecretAccess.js.map +1 -0
- package/dist/collections/orders/createOrdersCollection.d.ts +1 -5
- package/dist/collections/orders/createOrdersCollection.d.ts.map +1 -1
- package/dist/collections/orders/createOrdersCollection.js +9 -8
- package/dist/collections/orders/createOrdersCollection.js.map +1 -1
- package/dist/collections/products/createProductsCollection.d.ts +1 -4
- package/dist/collections/products/createProductsCollection.d.ts.map +1 -1
- package/dist/collections/products/createProductsCollection.js +5 -5
- package/dist/collections/products/createProductsCollection.js.map +1 -1
- package/dist/collections/transactions/createTransactionsCollection.d.ts +1 -3
- package/dist/collections/transactions/createTransactionsCollection.d.ts.map +1 -1
- package/dist/collections/transactions/createTransactionsCollection.js +5 -5
- package/dist/collections/transactions/createTransactionsCollection.js.map +1 -1
- package/dist/collections/variants/createVariantOptionsCollection.d.ts +1 -4
- package/dist/collections/variants/createVariantOptionsCollection.d.ts.map +1 -1
- package/dist/collections/variants/createVariantOptionsCollection.js +5 -5
- package/dist/collections/variants/createVariantOptionsCollection.js.map +1 -1
- package/dist/collections/variants/createVariantTypesCollection.d.ts +1 -4
- package/dist/collections/variants/createVariantTypesCollection.d.ts.map +1 -1
- package/dist/collections/variants/createVariantTypesCollection.js +5 -5
- package/dist/collections/variants/createVariantTypesCollection.js.map +1 -1
- package/dist/collections/variants/createVariantsCollection/index.d.ts +1 -4
- package/dist/collections/variants/createVariantsCollection/index.d.ts.map +1 -1
- package/dist/collections/variants/createVariantsCollection/index.js +5 -5
- package/dist/collections/variants/createVariantsCollection/index.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -33
- package/dist/index.js.map +1 -1
- package/dist/react/provider/index.d.ts +3 -0
- package/dist/react/provider/index.d.ts.map +1 -1
- package/dist/react/provider/index.js +261 -156
- package/dist/react/provider/index.js.map +1 -1
- package/dist/translations/en.d.ts.map +1 -1
- package/dist/translations/en.js +1 -0
- package/dist/translations/en.js.map +1 -1
- package/dist/types/index.d.ts +43 -25
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utilities/accessComposition.d.ts +55 -0
- package/dist/utilities/accessComposition.d.ts.map +1 -0
- package/dist/utilities/accessComposition.js +103 -0
- package/dist/utilities/accessComposition.js.map +1 -0
- package/dist/utilities/accessComposition.spec.js +803 -0
- package/dist/utilities/accessComposition.spec.js.map +1 -0
- package/dist/utilities/defaultProductsValidation.spec.js +383 -0
- package/dist/utilities/defaultProductsValidation.spec.js.map +1 -0
- package/dist/utilities/getCollectionSlugMap.spec.js +159 -0
- package/dist/utilities/getCollectionSlugMap.spec.js.map +1 -0
- package/dist/utilities/sanitizePluginConfig.d.ts.map +1 -1
- package/dist/utilities/sanitizePluginConfig.js +10 -2
- package/dist/utilities/sanitizePluginConfig.js.map +1 -1
- package/dist/utilities/sanitizePluginConfig.spec.js +515 -0
- package/dist/utilities/sanitizePluginConfig.spec.js.map +1 -0
- package/package.json +9 -9
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
import { accessAND, conditional, accessOR } from './accessComposition';
|
|
2
|
+
// Mock access args for testing
|
|
3
|
+
const mockArgs = {
|
|
4
|
+
req: {
|
|
5
|
+
user: null,
|
|
6
|
+
headers: new Headers(),
|
|
7
|
+
payload: {},
|
|
8
|
+
context: {}
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const mockArgsWithUser = {
|
|
12
|
+
req: {
|
|
13
|
+
user: {
|
|
14
|
+
id: '123',
|
|
15
|
+
email: 'test@example.com'
|
|
16
|
+
},
|
|
17
|
+
headers: new Headers(),
|
|
18
|
+
payload: {},
|
|
19
|
+
context: {}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
describe('Access Composition Utilities', ()=>{
|
|
23
|
+
describe('or', ()=>{
|
|
24
|
+
it('should return true when first checker returns true', async ()=>{
|
|
25
|
+
const checker1 = async ()=>true;
|
|
26
|
+
const checker2 = async ()=>false;
|
|
27
|
+
const result = await accessOR(checker1, checker2)(mockArgs);
|
|
28
|
+
expect(result).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
it('should return true when any checker returns true', async ()=>{
|
|
31
|
+
const checker1 = async ()=>false;
|
|
32
|
+
const checker2 = async ()=>true;
|
|
33
|
+
const checker3 = async ()=>false;
|
|
34
|
+
const result = await accessOR(checker1, checker2, checker3)(mockArgs);
|
|
35
|
+
expect(result).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
it('should return false when all checkers return false', async ()=>{
|
|
38
|
+
const checker1 = async ()=>false;
|
|
39
|
+
const checker2 = async ()=>false;
|
|
40
|
+
const checker3 = async ()=>false;
|
|
41
|
+
const result = await accessOR(checker1, checker2, checker3)(mockArgs);
|
|
42
|
+
expect(result).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
it('should combine Where queries with OR logic', async ()=>{
|
|
45
|
+
const checker1 = async ()=>({
|
|
46
|
+
customer: {
|
|
47
|
+
equals: '123'
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const checker2 = async ()=>({
|
|
51
|
+
status: {
|
|
52
|
+
equals: 'published'
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
const result = await accessOR(checker1, checker2)(mockArgs);
|
|
56
|
+
expect(result).toEqual({
|
|
57
|
+
or: [
|
|
58
|
+
{
|
|
59
|
+
customer: {
|
|
60
|
+
equals: '123'
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
status: {
|
|
65
|
+
equals: 'published'
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
it('should return true when one checker returns true and others return Where queries', async ()=>{
|
|
72
|
+
const checker1 = async ()=>({
|
|
73
|
+
customer: {
|
|
74
|
+
equals: '123'
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
const checker2 = async ()=>true;
|
|
78
|
+
const result = await accessOR(checker1, checker2)(mockArgs);
|
|
79
|
+
expect(result).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
it('should ignore false values when combining Where queries', async ()=>{
|
|
82
|
+
const checker1 = async ()=>false;
|
|
83
|
+
const checker2 = async ()=>({
|
|
84
|
+
customer: {
|
|
85
|
+
equals: '123'
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
const checker3 = async ()=>false;
|
|
89
|
+
const checker4 = async ()=>({
|
|
90
|
+
status: {
|
|
91
|
+
equals: 'published'
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const result = await accessOR(checker1, checker2, checker3, checker4)(mockArgs);
|
|
95
|
+
expect(result).toEqual({
|
|
96
|
+
or: [
|
|
97
|
+
{
|
|
98
|
+
customer: {
|
|
99
|
+
equals: '123'
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
status: {
|
|
104
|
+
equals: 'published'
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
it('should return a single Where query when only one checker returns a Where query', async ()=>{
|
|
111
|
+
const checker1 = async ()=>false;
|
|
112
|
+
const checker2 = async ()=>({
|
|
113
|
+
customer: {
|
|
114
|
+
equals: '123'
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
const checker3 = async ()=>false;
|
|
118
|
+
const result = await accessOR(checker1, checker2, checker3)(mockArgs);
|
|
119
|
+
expect(result).toEqual({
|
|
120
|
+
or: [
|
|
121
|
+
{
|
|
122
|
+
customer: {
|
|
123
|
+
equals: '123'
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
it('should short-circuit on first true result for performance', async ()=>{
|
|
130
|
+
let secondCheckerCalled = false;
|
|
131
|
+
const checker1 = async ()=>true;
|
|
132
|
+
const checker2 = async ()=>{
|
|
133
|
+
secondCheckerCalled = true;
|
|
134
|
+
return false;
|
|
135
|
+
};
|
|
136
|
+
await accessOR(checker1, checker2)(mockArgs);
|
|
137
|
+
expect(secondCheckerCalled).toBe(false);
|
|
138
|
+
});
|
|
139
|
+
it('should handle empty checkers array', async ()=>{
|
|
140
|
+
const result = await accessOR()(mockArgs);
|
|
141
|
+
expect(result).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
it('should handle complex nested Where queries', async ()=>{
|
|
144
|
+
const checker1 = async ()=>({
|
|
145
|
+
and: [
|
|
146
|
+
{
|
|
147
|
+
customer: {
|
|
148
|
+
equals: '123'
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
status: {
|
|
153
|
+
equals: 'active'
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
});
|
|
158
|
+
const checker2 = async ()=>({
|
|
159
|
+
role: {
|
|
160
|
+
equals: 'admin'
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
const result = await accessOR(checker1, checker2)(mockArgs);
|
|
164
|
+
expect(result).toEqual({
|
|
165
|
+
or: [
|
|
166
|
+
{
|
|
167
|
+
and: [
|
|
168
|
+
{
|
|
169
|
+
customer: {
|
|
170
|
+
equals: '123'
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
status: {
|
|
175
|
+
equals: 'active'
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
role: {
|
|
182
|
+
equals: 'admin'
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
describe('and', ()=>{
|
|
190
|
+
it('should return false when any checker returns false', async ()=>{
|
|
191
|
+
const checker1 = async ()=>true;
|
|
192
|
+
const checker2 = async ()=>false;
|
|
193
|
+
const result = await accessAND(checker1, checker2)(mockArgs);
|
|
194
|
+
expect(result).toBe(false);
|
|
195
|
+
});
|
|
196
|
+
it('should return true when all checkers return true', async ()=>{
|
|
197
|
+
const checker1 = async ()=>true;
|
|
198
|
+
const checker2 = async ()=>true;
|
|
199
|
+
const checker3 = async ()=>true;
|
|
200
|
+
const result = await accessAND(checker1, checker2, checker3)(mockArgs);
|
|
201
|
+
expect(result).toBe(true);
|
|
202
|
+
});
|
|
203
|
+
it('should combine Where queries with AND logic', async ()=>{
|
|
204
|
+
const checker1 = async ()=>({
|
|
205
|
+
customer: {
|
|
206
|
+
equals: '123'
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
const checker2 = async ()=>({
|
|
210
|
+
status: {
|
|
211
|
+
equals: 'published'
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
const result = await accessAND(checker1, checker2)(mockArgs);
|
|
215
|
+
expect(result).toEqual({
|
|
216
|
+
and: [
|
|
217
|
+
{
|
|
218
|
+
customer: {
|
|
219
|
+
equals: '123'
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
status: {
|
|
224
|
+
equals: 'published'
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
]
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
it('should return false when one checker returns false and others return Where queries', async ()=>{
|
|
231
|
+
const checker1 = async ()=>({
|
|
232
|
+
customer: {
|
|
233
|
+
equals: '123'
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
const checker2 = async ()=>false;
|
|
237
|
+
const result = await accessAND(checker1, checker2)(mockArgs);
|
|
238
|
+
expect(result).toBe(false);
|
|
239
|
+
});
|
|
240
|
+
it('should return Where query when all checkers return Where queries except one returns true', async ()=>{
|
|
241
|
+
const checker1 = async ()=>true;
|
|
242
|
+
const checker2 = async ()=>({
|
|
243
|
+
customer: {
|
|
244
|
+
equals: '123'
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
const checker3 = async ()=>({
|
|
248
|
+
status: {
|
|
249
|
+
equals: 'published'
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
const result = await accessAND(checker1, checker2, checker3)(mockArgs);
|
|
253
|
+
expect(result).toEqual({
|
|
254
|
+
and: [
|
|
255
|
+
{
|
|
256
|
+
customer: {
|
|
257
|
+
equals: '123'
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
status: {
|
|
262
|
+
equals: 'published'
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
]
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
it('should short-circuit on first false result for performance', async ()=>{
|
|
269
|
+
let secondCheckerCalled = false;
|
|
270
|
+
const checker1 = async ()=>false;
|
|
271
|
+
const checker2 = async ()=>{
|
|
272
|
+
secondCheckerCalled = true;
|
|
273
|
+
return true;
|
|
274
|
+
};
|
|
275
|
+
await accessAND(checker1, checker2)(mockArgs);
|
|
276
|
+
expect(secondCheckerCalled).toBe(false);
|
|
277
|
+
});
|
|
278
|
+
it('should handle empty checkers array', async ()=>{
|
|
279
|
+
const result = await accessAND()(mockArgs);
|
|
280
|
+
expect(result).toBe(true);
|
|
281
|
+
});
|
|
282
|
+
it('should return a single Where query when only one checker returns a Where query', async ()=>{
|
|
283
|
+
const checker1 = async ()=>true;
|
|
284
|
+
const checker2 = async ()=>({
|
|
285
|
+
customer: {
|
|
286
|
+
equals: '123'
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
const checker3 = async ()=>true;
|
|
290
|
+
const result = await accessAND(checker1, checker2, checker3)(mockArgs);
|
|
291
|
+
expect(result).toEqual({
|
|
292
|
+
and: [
|
|
293
|
+
{
|
|
294
|
+
customer: {
|
|
295
|
+
equals: '123'
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
]
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
it('should handle complex nested Where queries', async ()=>{
|
|
302
|
+
const checker1 = async ()=>({
|
|
303
|
+
or: [
|
|
304
|
+
{
|
|
305
|
+
customer: {
|
|
306
|
+
equals: '123'
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
customer: {
|
|
311
|
+
equals: '456'
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
]
|
|
315
|
+
});
|
|
316
|
+
const checker2 = async ()=>({
|
|
317
|
+
status: {
|
|
318
|
+
equals: 'active'
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
const result = await accessAND(checker1, checker2)(mockArgs);
|
|
322
|
+
expect(result).toEqual({
|
|
323
|
+
and: [
|
|
324
|
+
{
|
|
325
|
+
or: [
|
|
326
|
+
{
|
|
327
|
+
customer: {
|
|
328
|
+
equals: '123'
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
customer: {
|
|
333
|
+
equals: '456'
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
]
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
status: {
|
|
340
|
+
equals: 'active'
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
]
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
it('should return false immediately when first checker returns false', async ()=>{
|
|
347
|
+
const checker1 = async ()=>false;
|
|
348
|
+
const checker2 = async ()=>{
|
|
349
|
+
throw new Error('Should not be called');
|
|
350
|
+
};
|
|
351
|
+
const result = await accessAND(checker1, checker2)(mockArgs);
|
|
352
|
+
expect(result).toBe(false);
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
describe('conditional', ()=>{
|
|
356
|
+
it('should apply checker when condition is true', async ()=>{
|
|
357
|
+
const checker = async ()=>true;
|
|
358
|
+
const result = await conditional(true, checker)(mockArgs);
|
|
359
|
+
expect(result).toBe(true);
|
|
360
|
+
});
|
|
361
|
+
it('should return false when condition is false', async ()=>{
|
|
362
|
+
const checker = async ()=>true;
|
|
363
|
+
const result = await conditional(false, checker)(mockArgs);
|
|
364
|
+
expect(result).toBe(false);
|
|
365
|
+
});
|
|
366
|
+
it('should apply checker when condition function returns true', async ()=>{
|
|
367
|
+
const condition = ({ req })=>!!req.user;
|
|
368
|
+
const checker = async ()=>true;
|
|
369
|
+
const result = await conditional(condition, checker)(mockArgsWithUser);
|
|
370
|
+
expect(result).toBe(true);
|
|
371
|
+
});
|
|
372
|
+
it('should return false when condition function returns false', async ()=>{
|
|
373
|
+
const condition = ({ req })=>!!req.user;
|
|
374
|
+
const checker = async ()=>true;
|
|
375
|
+
const result = await conditional(condition, checker)(mockArgs);
|
|
376
|
+
expect(result).toBe(false);
|
|
377
|
+
});
|
|
378
|
+
it('should pass Where query through when condition is true', async ()=>{
|
|
379
|
+
const checker = async ()=>({
|
|
380
|
+
customer: {
|
|
381
|
+
equals: '123'
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
const result = await conditional(true, checker)(mockArgs);
|
|
385
|
+
expect(result).toEqual({
|
|
386
|
+
customer: {
|
|
387
|
+
equals: '123'
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
it('should not call checker when condition is false', async ()=>{
|
|
392
|
+
let checkerCalled = false;
|
|
393
|
+
const checker = async ()=>{
|
|
394
|
+
checkerCalled = true;
|
|
395
|
+
return true;
|
|
396
|
+
};
|
|
397
|
+
await conditional(false, checker)(mockArgs);
|
|
398
|
+
expect(checkerCalled).toBe(false);
|
|
399
|
+
});
|
|
400
|
+
it('should evaluate condition function each time', async ()=>{
|
|
401
|
+
const condition = ({ req })=>!!req.user;
|
|
402
|
+
const checker = async ()=>true;
|
|
403
|
+
// First call without user
|
|
404
|
+
const result1 = await conditional(condition, checker)(mockArgs);
|
|
405
|
+
expect(result1).toBe(false);
|
|
406
|
+
// Second call with user
|
|
407
|
+
const result2 = await conditional(condition, checker)(mockArgsWithUser);
|
|
408
|
+
expect(result2).toBe(true);
|
|
409
|
+
});
|
|
410
|
+
it('should work with false checker result when condition is true', async ()=>{
|
|
411
|
+
const checker = async ()=>false;
|
|
412
|
+
const result = await conditional(true, checker)(mockArgs);
|
|
413
|
+
expect(result).toBe(false);
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
describe('combined composition', ()=>{
|
|
417
|
+
it('should compose or, and, and conditional together', async ()=>{
|
|
418
|
+
const isAdmin = async ({ req })=>req.user?.role === 'admin';
|
|
419
|
+
const isOwner = async ({ req })=>({
|
|
420
|
+
customer: {
|
|
421
|
+
equals: req.user?.id
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
const isGuest = async ({ req })=>!req.user;
|
|
425
|
+
const allowGuestCarts = true;
|
|
426
|
+
const access = accessOR(isAdmin, accessAND(isOwner), conditional(allowGuestCarts, isGuest));
|
|
427
|
+
// Guest user (no user)
|
|
428
|
+
const guestResult = await access(mockArgs);
|
|
429
|
+
expect(guestResult).toBe(true);
|
|
430
|
+
// Admin user
|
|
431
|
+
const adminResult = await access({
|
|
432
|
+
req: {
|
|
433
|
+
user: {
|
|
434
|
+
id: '123',
|
|
435
|
+
role: 'admin'
|
|
436
|
+
},
|
|
437
|
+
headers: new Headers(),
|
|
438
|
+
payload: {},
|
|
439
|
+
context: {}
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
expect(adminResult).toBe(true);
|
|
443
|
+
// Regular user (owner)
|
|
444
|
+
const ownerResult = await access({
|
|
445
|
+
req: {
|
|
446
|
+
user: {
|
|
447
|
+
id: '123',
|
|
448
|
+
role: 'customer'
|
|
449
|
+
},
|
|
450
|
+
headers: new Headers(),
|
|
451
|
+
payload: {},
|
|
452
|
+
context: {}
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
expect(ownerResult).toEqual({
|
|
456
|
+
or: [
|
|
457
|
+
{
|
|
458
|
+
and: [
|
|
459
|
+
{
|
|
460
|
+
customer: {
|
|
461
|
+
equals: '123'
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
]
|
|
465
|
+
}
|
|
466
|
+
]
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
it('should handle complex nested compositions', async ()=>{
|
|
470
|
+
const checker1 = async ()=>({
|
|
471
|
+
status: {
|
|
472
|
+
equals: 'published'
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
const checker2 = async ()=>({
|
|
476
|
+
visibility: {
|
|
477
|
+
equals: 'public'
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
const checker3 = async ({ req })=>!!req.user;
|
|
481
|
+
const checker4 = async ()=>({
|
|
482
|
+
customer: {
|
|
483
|
+
equals: '123'
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
// ((published AND public) OR (authenticated AND customer=123))
|
|
487
|
+
const access = accessOR(accessAND(checker1, checker2), accessAND(checker3, checker4));
|
|
488
|
+
// Without user
|
|
489
|
+
const result1 = await access(mockArgs);
|
|
490
|
+
expect(result1).toEqual({
|
|
491
|
+
or: [
|
|
492
|
+
{
|
|
493
|
+
and: [
|
|
494
|
+
{
|
|
495
|
+
status: {
|
|
496
|
+
equals: 'published'
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
visibility: {
|
|
501
|
+
equals: 'public'
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
]
|
|
505
|
+
}
|
|
506
|
+
]
|
|
507
|
+
});
|
|
508
|
+
// With user
|
|
509
|
+
const result2 = await access(mockArgsWithUser);
|
|
510
|
+
expect(result2).toEqual({
|
|
511
|
+
or: [
|
|
512
|
+
{
|
|
513
|
+
and: [
|
|
514
|
+
{
|
|
515
|
+
status: {
|
|
516
|
+
equals: 'published'
|
|
517
|
+
}
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
visibility: {
|
|
521
|
+
equals: 'public'
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
]
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
and: [
|
|
528
|
+
{
|
|
529
|
+
customer: {
|
|
530
|
+
equals: '123'
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
]
|
|
534
|
+
}
|
|
535
|
+
]
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
it('should handle conditional inside or composition', async ()=>{
|
|
539
|
+
const isAdmin = async ()=>false;
|
|
540
|
+
const isGuest = async ()=>true;
|
|
541
|
+
const allowGuestAccess = true;
|
|
542
|
+
const access = accessOR(isAdmin, conditional(allowGuestAccess, isGuest));
|
|
543
|
+
const result = await access(mockArgs);
|
|
544
|
+
expect(result).toBe(true);
|
|
545
|
+
});
|
|
546
|
+
it('should handle conditional inside and composition', async ()=>{
|
|
547
|
+
const hasPermission = async ()=>({
|
|
548
|
+
permissions: {
|
|
549
|
+
contains: 'read'
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
const isActiveUser = async ()=>true;
|
|
553
|
+
const featureFlagEnabled = true;
|
|
554
|
+
const access = accessAND(hasPermission, conditional(featureFlagEnabled, isActiveUser));
|
|
555
|
+
const result = await access(mockArgs);
|
|
556
|
+
expect(result).toEqual({
|
|
557
|
+
and: [
|
|
558
|
+
{
|
|
559
|
+
permissions: {
|
|
560
|
+
contains: 'read'
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
]
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
it('should correctly handle multiple levels of nesting', async ()=>{
|
|
567
|
+
const a = async ()=>true;
|
|
568
|
+
const b = async ()=>false;
|
|
569
|
+
const c = async ()=>({
|
|
570
|
+
field1: {
|
|
571
|
+
equals: 'value1'
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
const d = async ()=>({
|
|
575
|
+
field2: {
|
|
576
|
+
equals: 'value2'
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
// (a AND (b OR (c AND d)))
|
|
580
|
+
const access = accessAND(a, accessOR(b, accessAND(c, d)));
|
|
581
|
+
const result = await access(mockArgs);
|
|
582
|
+
expect(result).toEqual({
|
|
583
|
+
and: [
|
|
584
|
+
{
|
|
585
|
+
or: [
|
|
586
|
+
{
|
|
587
|
+
and: [
|
|
588
|
+
{
|
|
589
|
+
field1: {
|
|
590
|
+
equals: 'value1'
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
field2: {
|
|
595
|
+
equals: 'value2'
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
]
|
|
599
|
+
}
|
|
600
|
+
]
|
|
601
|
+
}
|
|
602
|
+
]
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
});
|
|
606
|
+
describe('edge cases and failure scenarios', ()=>{
|
|
607
|
+
it('should handle checker that throws an error', async ()=>{
|
|
608
|
+
const checker1 = async ()=>{
|
|
609
|
+
throw new Error('Access check failed');
|
|
610
|
+
};
|
|
611
|
+
const checker2 = async ()=>true;
|
|
612
|
+
await expect(accessOR(checker1, checker2)(mockArgs)).rejects.toThrow('Access check failed');
|
|
613
|
+
});
|
|
614
|
+
it('should handle null or undefined returns gracefully', async ()=>{
|
|
615
|
+
const checker1 = async ()=>null;
|
|
616
|
+
const checker2 = async ()=>undefined;
|
|
617
|
+
const checker3 = async ()=>true;
|
|
618
|
+
const result = await accessOR(checker1, checker2, checker3)(mockArgs);
|
|
619
|
+
expect(result).toBe(true);
|
|
620
|
+
});
|
|
621
|
+
it('should handle deeply nested Where queries', async ()=>{
|
|
622
|
+
const checker = async ()=>({
|
|
623
|
+
and: [
|
|
624
|
+
{
|
|
625
|
+
or: [
|
|
626
|
+
{
|
|
627
|
+
field1: {
|
|
628
|
+
equals: 'value1'
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
and: [
|
|
633
|
+
{
|
|
634
|
+
field2: {
|
|
635
|
+
equals: 'value2'
|
|
636
|
+
}
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
field3: {
|
|
640
|
+
equals: 'value3'
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
]
|
|
644
|
+
}
|
|
645
|
+
]
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
field4: {
|
|
649
|
+
not_equals: 'value4'
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
]
|
|
653
|
+
});
|
|
654
|
+
const result = await accessOR(checker)(mockArgs);
|
|
655
|
+
expect(result).toEqual({
|
|
656
|
+
or: [
|
|
657
|
+
{
|
|
658
|
+
and: [
|
|
659
|
+
{
|
|
660
|
+
or: [
|
|
661
|
+
{
|
|
662
|
+
field1: {
|
|
663
|
+
equals: 'value1'
|
|
664
|
+
}
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
and: [
|
|
668
|
+
{
|
|
669
|
+
field2: {
|
|
670
|
+
equals: 'value2'
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
field3: {
|
|
675
|
+
equals: 'value3'
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
]
|
|
679
|
+
}
|
|
680
|
+
]
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
field4: {
|
|
684
|
+
not_equals: 'value4'
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
]
|
|
688
|
+
}
|
|
689
|
+
]
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
it('should handle async checker that takes time', async ()=>{
|
|
693
|
+
const checker1 = async ()=>{
|
|
694
|
+
await new Promise((resolve)=>setTimeout(resolve, 10));
|
|
695
|
+
return false;
|
|
696
|
+
};
|
|
697
|
+
const checker2 = async ()=>{
|
|
698
|
+
await new Promise((resolve)=>setTimeout(resolve, 10));
|
|
699
|
+
return true;
|
|
700
|
+
};
|
|
701
|
+
const start = Date.now();
|
|
702
|
+
const result = await accessOR(checker1, checker2)(mockArgs);
|
|
703
|
+
const duration = Date.now() - start;
|
|
704
|
+
expect(result).toBe(true);
|
|
705
|
+
expect(duration).toBeGreaterThanOrEqual(20);
|
|
706
|
+
});
|
|
707
|
+
it('should handle all checkers returning null/undefined', async ()=>{
|
|
708
|
+
const checker1 = async ()=>null;
|
|
709
|
+
const checker2 = async ()=>undefined;
|
|
710
|
+
const result = await accessOR(checker1, checker2)(mockArgs);
|
|
711
|
+
// null and undefined should be treated as false
|
|
712
|
+
expect(result).toBe(false);
|
|
713
|
+
});
|
|
714
|
+
it('should handle Where query with empty object', async ()=>{
|
|
715
|
+
const checker = async ()=>({});
|
|
716
|
+
const result = await accessOR(checker)(mockArgs);
|
|
717
|
+
expect(result).toEqual({});
|
|
718
|
+
});
|
|
719
|
+
it('should handle conditional with complex condition function', async ()=>{
|
|
720
|
+
const condition = ({ req })=>{
|
|
721
|
+
return !!(req.user && req.user.email && req.user.email.endsWith('@admin.com'));
|
|
722
|
+
};
|
|
723
|
+
const checker = async ()=>true;
|
|
724
|
+
// Non-admin email
|
|
725
|
+
const result1 = await conditional(condition, checker)({
|
|
726
|
+
req: {
|
|
727
|
+
user: {
|
|
728
|
+
id: '123',
|
|
729
|
+
email: 'user@example.com'
|
|
730
|
+
},
|
|
731
|
+
headers: new Headers(),
|
|
732
|
+
payload: {},
|
|
733
|
+
context: {}
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
expect(result1).toBe(false);
|
|
737
|
+
// Admin email
|
|
738
|
+
const result2 = await conditional(condition, checker)({
|
|
739
|
+
req: {
|
|
740
|
+
user: {
|
|
741
|
+
id: '123',
|
|
742
|
+
email: 'admin@admin.com'
|
|
743
|
+
},
|
|
744
|
+
headers: new Headers(),
|
|
745
|
+
payload: {},
|
|
746
|
+
context: {}
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
expect(result2).toBe(true);
|
|
750
|
+
});
|
|
751
|
+
it('should handle very large number of checkers in or', async ()=>{
|
|
752
|
+
const checkers = Array.from({
|
|
753
|
+
length: 100
|
|
754
|
+
}, (_, i)=>async ()=>({
|
|
755
|
+
field: {
|
|
756
|
+
equals: `value${i}`
|
|
757
|
+
}
|
|
758
|
+
}));
|
|
759
|
+
const result = await accessOR(...checkers)(mockArgs);
|
|
760
|
+
expect(result).toHaveProperty('or');
|
|
761
|
+
expect(result.or).toHaveLength(100);
|
|
762
|
+
});
|
|
763
|
+
it('should handle very large number of checkers in and', async ()=>{
|
|
764
|
+
const checkers = Array.from({
|
|
765
|
+
length: 100
|
|
766
|
+
}, (_, i)=>async ()=>({
|
|
767
|
+
field: {
|
|
768
|
+
equals: `value${i}`
|
|
769
|
+
}
|
|
770
|
+
}));
|
|
771
|
+
const result = await accessAND(...checkers)(mockArgs);
|
|
772
|
+
expect(result).toHaveProperty('and');
|
|
773
|
+
expect(result.and).toHaveLength(100);
|
|
774
|
+
});
|
|
775
|
+
it('should handle alternating true/false in or correctly', async ()=>{
|
|
776
|
+
const checkers = [
|
|
777
|
+
async ()=>false,
|
|
778
|
+
async ()=>false,
|
|
779
|
+
async ()=>false,
|
|
780
|
+
async ()=>true,
|
|
781
|
+
async ()=>{
|
|
782
|
+
throw new Error('Should not be called');
|
|
783
|
+
}
|
|
784
|
+
];
|
|
785
|
+
const result = await accessOR(...checkers)(mockArgs);
|
|
786
|
+
expect(result).toBe(true);
|
|
787
|
+
});
|
|
788
|
+
it('should handle alternating true/false in and correctly', async ()=>{
|
|
789
|
+
const checkers = [
|
|
790
|
+
async ()=>true,
|
|
791
|
+
async ()=>true,
|
|
792
|
+
async ()=>false,
|
|
793
|
+
async ()=>{
|
|
794
|
+
throw new Error('Should not be called');
|
|
795
|
+
}
|
|
796
|
+
];
|
|
797
|
+
const result = await accessAND(...checkers)(mockArgs);
|
|
798
|
+
expect(result).toBe(false);
|
|
799
|
+
});
|
|
800
|
+
});
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
//# sourceMappingURL=accessComposition.spec.js.map
|