@rooguys/js 0.1.0 → 1.0.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/README.md +342 -141
- package/package.json +1 -1
- package/src/__tests__/fixtures/responses.js +249 -0
- package/src/__tests__/property/batch-event-validation.property.test.js +225 -0
- package/src/__tests__/property/email-validation.property.test.js +272 -0
- package/src/__tests__/property/error-mapping.property.test.js +506 -0
- package/src/__tests__/property/field-selection.property.test.js +297 -0
- package/src/__tests__/property/idempotency-key.property.test.js +350 -0
- package/src/__tests__/property/leaderboard-filter.property.test.js +585 -0
- package/src/__tests__/property/partial-update.property.test.js +251 -0
- package/src/__tests__/property/rate-limit-error.property.test.js +276 -0
- package/src/__tests__/property/rate-limit-extraction.property.test.js +193 -0
- package/src/__tests__/property/request-construction.property.test.js +20 -28
- package/src/__tests__/property/response-format.property.test.js +418 -0
- package/src/__tests__/property/response-parsing.property.test.js +16 -21
- package/src/__tests__/property/timestamp-validation.property.test.js +345 -0
- package/src/__tests__/unit/aha.test.js +57 -26
- package/src/__tests__/unit/config.test.js +7 -1
- package/src/__tests__/unit/errors.test.js +6 -8
- package/src/__tests__/unit/events.test.js +253 -14
- package/src/__tests__/unit/leaderboards.test.js +249 -0
- package/src/__tests__/unit/questionnaires.test.js +6 -6
- package/src/__tests__/unit/users.test.js +275 -12
- package/src/__tests__/utils/generators.js +87 -0
- package/src/__tests__/utils/mockClient.js +71 -5
- package/src/errors.js +156 -0
- package/src/http-client.js +276 -0
- package/src/index.js +856 -66
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Property-Based Test: Leaderboard Filter Query Construction
|
|
3
|
+
* Feature: sdk-documentation-update, Property 10: Leaderboard Filter Query Construction
|
|
4
|
+
* Validates: Requirements 6.1, 6.2, 6.3
|
|
5
|
+
*
|
|
6
|
+
* For any leaderboard request with filter parameters (persona, minLevel, maxLevel,
|
|
7
|
+
* startDate, endDate), the SDK SHALL include the corresponding query parameters
|
|
8
|
+
* with correctly formatted values (dates as ISO 8601).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fc from 'fast-check';
|
|
12
|
+
import { jest } from '@jest/globals';
|
|
13
|
+
import Rooguys from '../../index.js';
|
|
14
|
+
import { createMockFetch, createMockHeaders } from '../utils/mockClient.js';
|
|
15
|
+
|
|
16
|
+
describe('Property 10: Leaderboard Filter Query Construction', () => {
|
|
17
|
+
let mockFetch;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
mockFetch = createMockFetch();
|
|
21
|
+
global.fetch = mockFetch;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
jest.clearAllMocks();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const createSuccessResponse = (data) => ({
|
|
29
|
+
ok: true,
|
|
30
|
+
headers: createMockHeaders({
|
|
31
|
+
'X-RateLimit-Limit': '1000',
|
|
32
|
+
'X-RateLimit-Remaining': '950',
|
|
33
|
+
'X-RateLimit-Reset': '1704067200',
|
|
34
|
+
}),
|
|
35
|
+
json: async () => ({
|
|
36
|
+
success: true,
|
|
37
|
+
data,
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const leaderboardResponse = {
|
|
42
|
+
rankings: [],
|
|
43
|
+
pagination: { page: 1, limit: 50, total: 0, totalPages: 0 },
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Generator for valid persona names
|
|
47
|
+
const validPersona = fc.constantFrom(
|
|
48
|
+
'Achiever',
|
|
49
|
+
'Explorer',
|
|
50
|
+
'Competitor',
|
|
51
|
+
'Socializer'
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// Generator for level numbers
|
|
55
|
+
const levelNumber = fc.integer({ min: 1, max: 100 });
|
|
56
|
+
|
|
57
|
+
// Generator for valid dates within a reasonable range
|
|
58
|
+
const validDate = fc.date({
|
|
59
|
+
min: new Date('2020-01-01'),
|
|
60
|
+
max: new Date('2030-12-31'),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Generator for timeframe
|
|
64
|
+
const timeframe = fc.constantFrom('all-time', 'weekly', 'monthly');
|
|
65
|
+
|
|
66
|
+
// Generator for pagination
|
|
67
|
+
const pagination = fc.record({
|
|
68
|
+
page: fc.integer({ min: 1, max: 100 }),
|
|
69
|
+
limit: fc.integer({ min: 1, max: 100 }),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('Persona Filter (Requirement 6.1)', () => {
|
|
73
|
+
it('should include persona query parameter when provided', async () => {
|
|
74
|
+
await fc.assert(
|
|
75
|
+
fc.asyncProperty(
|
|
76
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
77
|
+
validPersona,
|
|
78
|
+
async (apiKey, persona) => {
|
|
79
|
+
// Arrange
|
|
80
|
+
mockFetch.mockClear();
|
|
81
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
82
|
+
const sdk = new Rooguys(apiKey);
|
|
83
|
+
|
|
84
|
+
// Act
|
|
85
|
+
await sdk.leaderboards.getGlobal({ persona });
|
|
86
|
+
|
|
87
|
+
// Assert
|
|
88
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
89
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
90
|
+
const url = new URL(callUrl);
|
|
91
|
+
|
|
92
|
+
expect(url.searchParams.get('persona')).toBe(persona);
|
|
93
|
+
}
|
|
94
|
+
),
|
|
95
|
+
{ numRuns: 100 }
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should not include persona parameter when not provided', async () => {
|
|
100
|
+
await fc.assert(
|
|
101
|
+
fc.asyncProperty(
|
|
102
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
103
|
+
async (apiKey) => {
|
|
104
|
+
// Arrange
|
|
105
|
+
mockFetch.mockClear();
|
|
106
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
107
|
+
const sdk = new Rooguys(apiKey);
|
|
108
|
+
|
|
109
|
+
// Act
|
|
110
|
+
await sdk.leaderboards.getGlobal({});
|
|
111
|
+
|
|
112
|
+
// Assert
|
|
113
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
114
|
+
const url = new URL(callUrl);
|
|
115
|
+
|
|
116
|
+
expect(url.searchParams.has('persona')).toBe(false);
|
|
117
|
+
}
|
|
118
|
+
),
|
|
119
|
+
{ numRuns: 100 }
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should include persona filter in custom leaderboard requests', async () => {
|
|
124
|
+
await fc.assert(
|
|
125
|
+
fc.asyncProperty(
|
|
126
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
127
|
+
fc.uuid(), // Leaderboard ID
|
|
128
|
+
validPersona,
|
|
129
|
+
async (apiKey, leaderboardId, persona) => {
|
|
130
|
+
// Arrange
|
|
131
|
+
mockFetch.mockClear();
|
|
132
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
133
|
+
const sdk = new Rooguys(apiKey);
|
|
134
|
+
|
|
135
|
+
// Act
|
|
136
|
+
await sdk.leaderboards.getCustom(leaderboardId, { persona });
|
|
137
|
+
|
|
138
|
+
// Assert
|
|
139
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
140
|
+
const url = new URL(callUrl);
|
|
141
|
+
|
|
142
|
+
expect(url.searchParams.get('persona')).toBe(persona);
|
|
143
|
+
}
|
|
144
|
+
),
|
|
145
|
+
{ numRuns: 100 }
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('Level Range Filter (Requirement 6.2)', () => {
|
|
151
|
+
it('should include minLevel query parameter when provided', async () => {
|
|
152
|
+
await fc.assert(
|
|
153
|
+
fc.asyncProperty(
|
|
154
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
155
|
+
levelNumber,
|
|
156
|
+
async (apiKey, minLevel) => {
|
|
157
|
+
// Arrange
|
|
158
|
+
mockFetch.mockClear();
|
|
159
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
160
|
+
const sdk = new Rooguys(apiKey);
|
|
161
|
+
|
|
162
|
+
// Act
|
|
163
|
+
await sdk.leaderboards.getGlobal({ minLevel });
|
|
164
|
+
|
|
165
|
+
// Assert
|
|
166
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
167
|
+
const url = new URL(callUrl);
|
|
168
|
+
|
|
169
|
+
expect(url.searchParams.get('min_level')).toBe(String(minLevel));
|
|
170
|
+
}
|
|
171
|
+
),
|
|
172
|
+
{ numRuns: 100 }
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should include maxLevel query parameter when provided', async () => {
|
|
177
|
+
await fc.assert(
|
|
178
|
+
fc.asyncProperty(
|
|
179
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
180
|
+
levelNumber,
|
|
181
|
+
async (apiKey, maxLevel) => {
|
|
182
|
+
// Arrange
|
|
183
|
+
mockFetch.mockClear();
|
|
184
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
185
|
+
const sdk = new Rooguys(apiKey);
|
|
186
|
+
|
|
187
|
+
// Act
|
|
188
|
+
await sdk.leaderboards.getGlobal({ maxLevel });
|
|
189
|
+
|
|
190
|
+
// Assert
|
|
191
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
192
|
+
const url = new URL(callUrl);
|
|
193
|
+
|
|
194
|
+
expect(url.searchParams.get('max_level')).toBe(String(maxLevel));
|
|
195
|
+
}
|
|
196
|
+
),
|
|
197
|
+
{ numRuns: 100 }
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('should include both minLevel and maxLevel when provided', async () => {
|
|
202
|
+
await fc.assert(
|
|
203
|
+
fc.asyncProperty(
|
|
204
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
205
|
+
levelNumber,
|
|
206
|
+
levelNumber,
|
|
207
|
+
async (apiKey, minLevel, maxLevel) => {
|
|
208
|
+
// Arrange
|
|
209
|
+
mockFetch.mockClear();
|
|
210
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
211
|
+
const sdk = new Rooguys(apiKey);
|
|
212
|
+
|
|
213
|
+
// Act
|
|
214
|
+
await sdk.leaderboards.getGlobal({ minLevel, maxLevel });
|
|
215
|
+
|
|
216
|
+
// Assert
|
|
217
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
218
|
+
const url = new URL(callUrl);
|
|
219
|
+
|
|
220
|
+
expect(url.searchParams.get('min_level')).toBe(String(minLevel));
|
|
221
|
+
expect(url.searchParams.get('max_level')).toBe(String(maxLevel));
|
|
222
|
+
}
|
|
223
|
+
),
|
|
224
|
+
{ numRuns: 100 }
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('should not include level parameters when not provided', async () => {
|
|
229
|
+
await fc.assert(
|
|
230
|
+
fc.asyncProperty(
|
|
231
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
232
|
+
async (apiKey) => {
|
|
233
|
+
// Arrange
|
|
234
|
+
mockFetch.mockClear();
|
|
235
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
236
|
+
const sdk = new Rooguys(apiKey);
|
|
237
|
+
|
|
238
|
+
// Act
|
|
239
|
+
await sdk.leaderboards.getGlobal({});
|
|
240
|
+
|
|
241
|
+
// Assert
|
|
242
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
243
|
+
const url = new URL(callUrl);
|
|
244
|
+
|
|
245
|
+
expect(url.searchParams.has('min_level')).toBe(false);
|
|
246
|
+
expect(url.searchParams.has('max_level')).toBe(false);
|
|
247
|
+
}
|
|
248
|
+
),
|
|
249
|
+
{ numRuns: 100 }
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('Date Range Filter (Requirement 6.3)', () => {
|
|
255
|
+
it('should format startDate as ISO 8601 string', async () => {
|
|
256
|
+
await fc.assert(
|
|
257
|
+
fc.asyncProperty(
|
|
258
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
259
|
+
validDate,
|
|
260
|
+
async (apiKey, startDate) => {
|
|
261
|
+
// Arrange
|
|
262
|
+
mockFetch.mockClear();
|
|
263
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
264
|
+
const sdk = new Rooguys(apiKey);
|
|
265
|
+
|
|
266
|
+
// Act
|
|
267
|
+
await sdk.leaderboards.getGlobal({ startDate });
|
|
268
|
+
|
|
269
|
+
// Assert
|
|
270
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
271
|
+
const url = new URL(callUrl);
|
|
272
|
+
const startDateParam = url.searchParams.get('start_date');
|
|
273
|
+
|
|
274
|
+
expect(startDateParam).toBe(startDate.toISOString());
|
|
275
|
+
|
|
276
|
+
// Verify it's a valid ISO 8601 date
|
|
277
|
+
const parsedDate = new Date(startDateParam);
|
|
278
|
+
expect(parsedDate.getTime()).toBe(startDate.getTime());
|
|
279
|
+
}
|
|
280
|
+
),
|
|
281
|
+
{ numRuns: 100 }
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('should format endDate as ISO 8601 string', async () => {
|
|
286
|
+
await fc.assert(
|
|
287
|
+
fc.asyncProperty(
|
|
288
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
289
|
+
validDate,
|
|
290
|
+
async (apiKey, endDate) => {
|
|
291
|
+
// Arrange
|
|
292
|
+
mockFetch.mockClear();
|
|
293
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
294
|
+
const sdk = new Rooguys(apiKey);
|
|
295
|
+
|
|
296
|
+
// Act
|
|
297
|
+
await sdk.leaderboards.getGlobal({ endDate });
|
|
298
|
+
|
|
299
|
+
// Assert
|
|
300
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
301
|
+
const url = new URL(callUrl);
|
|
302
|
+
const endDateParam = url.searchParams.get('end_date');
|
|
303
|
+
|
|
304
|
+
expect(endDateParam).toBe(endDate.toISOString());
|
|
305
|
+
|
|
306
|
+
// Verify it's a valid ISO 8601 date
|
|
307
|
+
const parsedDate = new Date(endDateParam);
|
|
308
|
+
expect(parsedDate.getTime()).toBe(endDate.getTime());
|
|
309
|
+
}
|
|
310
|
+
),
|
|
311
|
+
{ numRuns: 100 }
|
|
312
|
+
);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('should include both startDate and endDate when provided', async () => {
|
|
316
|
+
await fc.assert(
|
|
317
|
+
fc.asyncProperty(
|
|
318
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
319
|
+
validDate,
|
|
320
|
+
validDate,
|
|
321
|
+
async (apiKey, startDate, endDate) => {
|
|
322
|
+
// Arrange
|
|
323
|
+
mockFetch.mockClear();
|
|
324
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
325
|
+
const sdk = new Rooguys(apiKey);
|
|
326
|
+
|
|
327
|
+
// Act
|
|
328
|
+
await sdk.leaderboards.getGlobal({ startDate, endDate });
|
|
329
|
+
|
|
330
|
+
// Assert
|
|
331
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
332
|
+
const url = new URL(callUrl);
|
|
333
|
+
|
|
334
|
+
expect(url.searchParams.get('start_date')).toBe(startDate.toISOString());
|
|
335
|
+
expect(url.searchParams.get('end_date')).toBe(endDate.toISOString());
|
|
336
|
+
}
|
|
337
|
+
),
|
|
338
|
+
{ numRuns: 100 }
|
|
339
|
+
);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should accept ISO string dates and convert them correctly', async () => {
|
|
343
|
+
await fc.assert(
|
|
344
|
+
fc.asyncProperty(
|
|
345
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
346
|
+
validDate,
|
|
347
|
+
async (apiKey, date) => {
|
|
348
|
+
// Arrange
|
|
349
|
+
mockFetch.mockClear();
|
|
350
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
351
|
+
const sdk = new Rooguys(apiKey);
|
|
352
|
+
const isoString = date.toISOString();
|
|
353
|
+
|
|
354
|
+
// Act
|
|
355
|
+
await sdk.leaderboards.getGlobal({ startDate: isoString });
|
|
356
|
+
|
|
357
|
+
// Assert
|
|
358
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
359
|
+
const url = new URL(callUrl);
|
|
360
|
+
const startDateParam = url.searchParams.get('start_date');
|
|
361
|
+
|
|
362
|
+
// Should be a valid ISO 8601 date
|
|
363
|
+
const parsedDate = new Date(startDateParam);
|
|
364
|
+
expect(parsedDate.getTime()).toBe(date.getTime());
|
|
365
|
+
}
|
|
366
|
+
),
|
|
367
|
+
{ numRuns: 100 }
|
|
368
|
+
);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('should not include date parameters when not provided', async () => {
|
|
372
|
+
await fc.assert(
|
|
373
|
+
fc.asyncProperty(
|
|
374
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
375
|
+
async (apiKey) => {
|
|
376
|
+
// Arrange
|
|
377
|
+
mockFetch.mockClear();
|
|
378
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
379
|
+
const sdk = new Rooguys(apiKey);
|
|
380
|
+
|
|
381
|
+
// Act
|
|
382
|
+
await sdk.leaderboards.getGlobal({});
|
|
383
|
+
|
|
384
|
+
// Assert
|
|
385
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
386
|
+
const url = new URL(callUrl);
|
|
387
|
+
|
|
388
|
+
expect(url.searchParams.has('start_date')).toBe(false);
|
|
389
|
+
expect(url.searchParams.has('end_date')).toBe(false);
|
|
390
|
+
}
|
|
391
|
+
),
|
|
392
|
+
{ numRuns: 100 }
|
|
393
|
+
);
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
describe('Combined Filters', () => {
|
|
398
|
+
it('should include all filter parameters when all are provided', async () => {
|
|
399
|
+
await fc.assert(
|
|
400
|
+
fc.asyncProperty(
|
|
401
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
402
|
+
validPersona,
|
|
403
|
+
levelNumber,
|
|
404
|
+
levelNumber,
|
|
405
|
+
validDate,
|
|
406
|
+
validDate,
|
|
407
|
+
timeframe,
|
|
408
|
+
pagination,
|
|
409
|
+
async (apiKey, persona, minLevel, maxLevel, startDate, endDate, tf, pag) => {
|
|
410
|
+
// Arrange
|
|
411
|
+
mockFetch.mockClear();
|
|
412
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
413
|
+
const sdk = new Rooguys(apiKey);
|
|
414
|
+
|
|
415
|
+
// Act
|
|
416
|
+
await sdk.leaderboards.getGlobal({
|
|
417
|
+
timeframe: tf,
|
|
418
|
+
page: pag.page,
|
|
419
|
+
limit: pag.limit,
|
|
420
|
+
persona,
|
|
421
|
+
minLevel,
|
|
422
|
+
maxLevel,
|
|
423
|
+
startDate,
|
|
424
|
+
endDate,
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// Assert
|
|
428
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
429
|
+
const url = new URL(callUrl);
|
|
430
|
+
|
|
431
|
+
// Verify all parameters are present
|
|
432
|
+
expect(url.searchParams.get('timeframe')).toBe(tf);
|
|
433
|
+
expect(url.searchParams.get('page')).toBe(String(pag.page));
|
|
434
|
+
expect(url.searchParams.get('limit')).toBe(String(pag.limit));
|
|
435
|
+
expect(url.searchParams.get('persona')).toBe(persona);
|
|
436
|
+
expect(url.searchParams.get('min_level')).toBe(String(minLevel));
|
|
437
|
+
expect(url.searchParams.get('max_level')).toBe(String(maxLevel));
|
|
438
|
+
expect(url.searchParams.get('start_date')).toBe(startDate.toISOString());
|
|
439
|
+
expect(url.searchParams.get('end_date')).toBe(endDate.toISOString());
|
|
440
|
+
}
|
|
441
|
+
),
|
|
442
|
+
{ numRuns: 100 }
|
|
443
|
+
);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it('should work with custom leaderboard and all filters', async () => {
|
|
447
|
+
await fc.assert(
|
|
448
|
+
fc.asyncProperty(
|
|
449
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
450
|
+
fc.uuid(), // Leaderboard ID
|
|
451
|
+
validPersona,
|
|
452
|
+
levelNumber,
|
|
453
|
+
levelNumber,
|
|
454
|
+
validDate,
|
|
455
|
+
validDate,
|
|
456
|
+
async (apiKey, leaderboardId, persona, minLevel, maxLevel, startDate, endDate) => {
|
|
457
|
+
// Arrange
|
|
458
|
+
mockFetch.mockClear();
|
|
459
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
460
|
+
const sdk = new Rooguys(apiKey);
|
|
461
|
+
|
|
462
|
+
// Act
|
|
463
|
+
await sdk.leaderboards.getCustom(leaderboardId, {
|
|
464
|
+
persona,
|
|
465
|
+
minLevel,
|
|
466
|
+
maxLevel,
|
|
467
|
+
startDate,
|
|
468
|
+
endDate,
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// Assert
|
|
472
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
473
|
+
const url = new URL(callUrl);
|
|
474
|
+
|
|
475
|
+
// Verify endpoint contains leaderboard ID
|
|
476
|
+
expect(url.pathname).toContain(leaderboardId);
|
|
477
|
+
|
|
478
|
+
// Verify all filter parameters are present
|
|
479
|
+
expect(url.searchParams.get('persona')).toBe(persona);
|
|
480
|
+
expect(url.searchParams.get('min_level')).toBe(String(minLevel));
|
|
481
|
+
expect(url.searchParams.get('max_level')).toBe(String(maxLevel));
|
|
482
|
+
expect(url.searchParams.get('start_date')).toBe(startDate.toISOString());
|
|
483
|
+
expect(url.searchParams.get('end_date')).toBe(endDate.toISOString());
|
|
484
|
+
}
|
|
485
|
+
),
|
|
486
|
+
{ numRuns: 100 }
|
|
487
|
+
);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it('should only include provided filters (partial filter set)', async () => {
|
|
491
|
+
await fc.assert(
|
|
492
|
+
fc.asyncProperty(
|
|
493
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
494
|
+
fc.option(validPersona, { nil: undefined }),
|
|
495
|
+
fc.option(levelNumber, { nil: undefined }),
|
|
496
|
+
fc.option(levelNumber, { nil: undefined }),
|
|
497
|
+
fc.option(validDate, { nil: undefined }),
|
|
498
|
+
fc.option(validDate, { nil: undefined }),
|
|
499
|
+
async (apiKey, persona, minLevel, maxLevel, startDate, endDate) => {
|
|
500
|
+
// Arrange
|
|
501
|
+
mockFetch.mockClear();
|
|
502
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
503
|
+
const sdk = new Rooguys(apiKey);
|
|
504
|
+
|
|
505
|
+
const options = {};
|
|
506
|
+
if (persona !== undefined) options.persona = persona;
|
|
507
|
+
if (minLevel !== undefined) options.minLevel = minLevel;
|
|
508
|
+
if (maxLevel !== undefined) options.maxLevel = maxLevel;
|
|
509
|
+
if (startDate !== undefined) options.startDate = startDate;
|
|
510
|
+
if (endDate !== undefined) options.endDate = endDate;
|
|
511
|
+
|
|
512
|
+
// Act
|
|
513
|
+
await sdk.leaderboards.getGlobal(options);
|
|
514
|
+
|
|
515
|
+
// Assert
|
|
516
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
517
|
+
const url = new URL(callUrl);
|
|
518
|
+
|
|
519
|
+
// Verify only provided parameters are present
|
|
520
|
+
expect(url.searchParams.has('persona')).toBe(persona !== undefined);
|
|
521
|
+
expect(url.searchParams.has('min_level')).toBe(minLevel !== undefined);
|
|
522
|
+
expect(url.searchParams.has('max_level')).toBe(maxLevel !== undefined);
|
|
523
|
+
expect(url.searchParams.has('start_date')).toBe(startDate !== undefined);
|
|
524
|
+
expect(url.searchParams.has('end_date')).toBe(endDate !== undefined);
|
|
525
|
+
|
|
526
|
+
// Verify values are correct for provided parameters
|
|
527
|
+
if (persona !== undefined) {
|
|
528
|
+
expect(url.searchParams.get('persona')).toBe(persona);
|
|
529
|
+
}
|
|
530
|
+
if (minLevel !== undefined) {
|
|
531
|
+
expect(url.searchParams.get('min_level')).toBe(String(minLevel));
|
|
532
|
+
}
|
|
533
|
+
if (maxLevel !== undefined) {
|
|
534
|
+
expect(url.searchParams.get('max_level')).toBe(String(maxLevel));
|
|
535
|
+
}
|
|
536
|
+
if (startDate !== undefined) {
|
|
537
|
+
expect(url.searchParams.get('start_date')).toBe(startDate.toISOString());
|
|
538
|
+
}
|
|
539
|
+
if (endDate !== undefined) {
|
|
540
|
+
expect(url.searchParams.get('end_date')).toBe(endDate.toISOString());
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
),
|
|
544
|
+
{ numRuns: 100 }
|
|
545
|
+
);
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
describe('Null value handling', () => {
|
|
550
|
+
it('should not include parameters with null values', async () => {
|
|
551
|
+
await fc.assert(
|
|
552
|
+
fc.asyncProperty(
|
|
553
|
+
fc.string({ minLength: 10, maxLength: 100 }), // API key
|
|
554
|
+
async (apiKey) => {
|
|
555
|
+
// Arrange
|
|
556
|
+
mockFetch.mockClear();
|
|
557
|
+
mockFetch.mockResolvedValue(createSuccessResponse(leaderboardResponse));
|
|
558
|
+
const sdk = new Rooguys(apiKey);
|
|
559
|
+
|
|
560
|
+
// Act
|
|
561
|
+
await sdk.leaderboards.getGlobal({
|
|
562
|
+
persona: null,
|
|
563
|
+
minLevel: null,
|
|
564
|
+
maxLevel: null,
|
|
565
|
+
startDate: null,
|
|
566
|
+
endDate: null,
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
// Assert
|
|
570
|
+
const callUrl = mockFetch.mock.calls[0][0];
|
|
571
|
+
const url = new URL(callUrl);
|
|
572
|
+
|
|
573
|
+
// Verify null parameters are NOT included
|
|
574
|
+
expect(url.searchParams.has('persona')).toBe(false);
|
|
575
|
+
expect(url.searchParams.has('min_level')).toBe(false);
|
|
576
|
+
expect(url.searchParams.has('max_level')).toBe(false);
|
|
577
|
+
expect(url.searchParams.has('start_date')).toBe(false);
|
|
578
|
+
expect(url.searchParams.has('end_date')).toBe(false);
|
|
579
|
+
}
|
|
580
|
+
),
|
|
581
|
+
{ numRuns: 100 }
|
|
582
|
+
);
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
});
|