@rooguys/sdk 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 +478 -113
- package/dist/__tests__/utils/mockClient.d.ts +65 -3
- package/dist/__tests__/utils/mockClient.js +144 -5
- package/dist/errors.d.ts +123 -0
- package/dist/errors.js +163 -0
- package/dist/http-client.d.ts +167 -0
- package/dist/http-client.js +250 -0
- package/dist/index.d.ts +160 -10
- package/dist/index.js +585 -146
- package/dist/types.d.ts +372 -50
- package/dist/types.js +21 -0
- package/package.json +1 -1
- package/src/__tests__/property/request-construction.property.test.ts +142 -91
- package/src/__tests__/property/response-parsing.property.test.ts +118 -67
- package/src/__tests__/property/sdk-modules.property.test.ts +450 -0
- package/src/__tests__/unit/aha.test.ts +61 -50
- package/src/__tests__/unit/badges.test.ts +27 -33
- package/src/__tests__/unit/config.test.ts +94 -126
- package/src/__tests__/unit/errors.test.ts +106 -150
- package/src/__tests__/unit/events.test.ts +119 -144
- package/src/__tests__/unit/leaderboards.test.ts +173 -40
- package/src/__tests__/unit/levels.test.ts +25 -33
- package/src/__tests__/unit/questionnaires.test.ts +33 -42
- package/src/__tests__/unit/users.test.ts +214 -99
- package/src/__tests__/utils/mockClient.ts +193 -6
- package/src/errors.ts +255 -0
- package/src/http-client.ts +433 -0
- package/src/index.ts +742 -150
- package/src/types.ts +429 -51
|
@@ -1,125 +1,104 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { Rooguys, ValidationError } from '../../index';
|
|
2
|
+
import {
|
|
3
|
+
createMockRooguysClient,
|
|
4
|
+
setupMockRequest,
|
|
5
|
+
setupMockRequestError,
|
|
6
|
+
expectRequestWith,
|
|
7
|
+
MockAxiosInstance,
|
|
8
|
+
} from '../utils/mockClient';
|
|
4
9
|
import { mockResponses, mockErrors } from '../fixtures/responses';
|
|
5
10
|
|
|
6
|
-
jest.mock('axios');
|
|
7
|
-
const mockedAxios = axios as jest.Mocked<typeof axios>;
|
|
8
|
-
|
|
9
11
|
describe('Events Resource', () => {
|
|
10
12
|
let client: Rooguys;
|
|
11
|
-
let
|
|
12
|
-
const apiKey = 'test-api-key';
|
|
13
|
+
let mockAxios: MockAxiosInstance;
|
|
13
14
|
|
|
14
15
|
beforeEach(() => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
jest.clearAllMocks();
|
|
16
|
+
const mock = createMockRooguysClient();
|
|
17
|
+
client = mock.client;
|
|
18
|
+
mockAxios = mock.mockAxios;
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
describe('track', () => {
|
|
22
22
|
it('should track an event with valid inputs', async () => {
|
|
23
|
-
|
|
24
|
-
mockSuccessResponse(mockResponses.trackEventResponse)
|
|
25
|
-
);
|
|
23
|
+
setupMockRequest(mockAxios, mockResponses.trackEventResponse);
|
|
26
24
|
|
|
27
|
-
const result = await client.events.track('
|
|
25
|
+
const result = await client.events.track('purchase_completed', 'user_123', {
|
|
28
26
|
amount: 50.0,
|
|
29
27
|
});
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
'
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
expectRequestWith(mockAxios, {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
url: '/events',
|
|
32
|
+
data: {
|
|
33
|
+
event_name: 'purchase_completed',
|
|
35
34
|
user_id: 'user_123',
|
|
36
35
|
properties: { amount: 50.0 },
|
|
37
36
|
},
|
|
38
|
-
|
|
39
|
-
);
|
|
37
|
+
});
|
|
40
38
|
expect(result).toEqual(mockResponses.trackEventResponse);
|
|
41
39
|
});
|
|
42
40
|
|
|
43
41
|
it('should track an event with empty properties', async () => {
|
|
44
|
-
|
|
45
|
-
mockSuccessResponse(mockResponses.trackEventResponse)
|
|
46
|
-
);
|
|
42
|
+
setupMockRequest(mockAxios, mockResponses.trackEventResponse);
|
|
47
43
|
|
|
48
|
-
const result = await client.events.track('
|
|
44
|
+
const result = await client.events.track('user_login', 'user_456');
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
'
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
expectRequestWith(mockAxios, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
url: '/events',
|
|
49
|
+
data: {
|
|
50
|
+
event_name: 'user_login',
|
|
54
51
|
user_id: 'user_456',
|
|
55
52
|
properties: {},
|
|
56
53
|
},
|
|
57
|
-
|
|
58
|
-
);
|
|
54
|
+
});
|
|
59
55
|
expect(result).toEqual(mockResponses.trackEventResponse);
|
|
60
56
|
});
|
|
61
57
|
|
|
62
58
|
it('should include profile when includeProfile is true', async () => {
|
|
63
|
-
|
|
64
|
-
mockSuccessResponse(mockResponses.trackEventWithProfileResponse)
|
|
65
|
-
);
|
|
59
|
+
setupMockRequest(mockAxios, mockResponses.trackEventWithProfileResponse);
|
|
66
60
|
|
|
67
61
|
const result = await client.events.track(
|
|
68
|
-
'
|
|
62
|
+
'purchase_completed',
|
|
69
63
|
'user_123',
|
|
70
64
|
{ amount: 50.0 },
|
|
71
65
|
{ includeProfile: true }
|
|
72
66
|
);
|
|
73
67
|
|
|
74
|
-
expect(mockAxiosInstance.post).toHaveBeenCalledWith(
|
|
75
|
-
'/event',
|
|
76
|
-
{
|
|
77
|
-
event_name: 'purchase-completed',
|
|
78
|
-
user_id: 'user_123',
|
|
79
|
-
properties: { amount: 50.0 },
|
|
80
|
-
},
|
|
81
|
-
{ params: { include_profile: true } }
|
|
82
|
-
);
|
|
83
68
|
expect(result).toEqual(mockResponses.trackEventWithProfileResponse);
|
|
84
69
|
expect(result.profile).toBeDefined();
|
|
85
70
|
});
|
|
86
71
|
|
|
87
72
|
it('should handle special characters in event name', async () => {
|
|
88
|
-
|
|
89
|
-
mockSuccessResponse(mockResponses.trackEventResponse)
|
|
90
|
-
);
|
|
73
|
+
setupMockRequest(mockAxios, mockResponses.trackEventResponse);
|
|
91
74
|
|
|
92
75
|
await client.events.track('user-signup_v2', 'user_123');
|
|
93
76
|
|
|
94
|
-
|
|
95
|
-
'
|
|
96
|
-
|
|
77
|
+
expectRequestWith(mockAxios, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
url: '/events',
|
|
80
|
+
data: expect.objectContaining({
|
|
97
81
|
event_name: 'user-signup_v2',
|
|
98
82
|
}),
|
|
99
|
-
|
|
100
|
-
);
|
|
83
|
+
});
|
|
101
84
|
});
|
|
102
85
|
|
|
103
86
|
it('should handle special characters in user ID', async () => {
|
|
104
|
-
|
|
105
|
-
mockSuccessResponse(mockResponses.trackEventResponse)
|
|
106
|
-
);
|
|
87
|
+
setupMockRequest(mockAxios, mockResponses.trackEventResponse);
|
|
107
88
|
|
|
108
|
-
await client.events.track('
|
|
89
|
+
await client.events.track('user_login', 'user@example.com');
|
|
109
90
|
|
|
110
|
-
|
|
111
|
-
'
|
|
112
|
-
|
|
91
|
+
expectRequestWith(mockAxios, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
url: '/events',
|
|
94
|
+
data: expect.objectContaining({
|
|
113
95
|
user_id: 'user@example.com',
|
|
114
96
|
}),
|
|
115
|
-
|
|
116
|
-
);
|
|
97
|
+
});
|
|
117
98
|
});
|
|
118
99
|
|
|
119
100
|
it('should handle complex nested properties', async () => {
|
|
120
|
-
|
|
121
|
-
mockSuccessResponse(mockResponses.trackEventResponse)
|
|
122
|
-
);
|
|
101
|
+
setupMockRequest(mockAxios, mockResponses.trackEventResponse);
|
|
123
102
|
|
|
124
103
|
const complexProperties = {
|
|
125
104
|
order: {
|
|
@@ -138,19 +117,17 @@ describe('Events Resource', () => {
|
|
|
138
117
|
|
|
139
118
|
await client.events.track('order_placed', 'user_123', complexProperties);
|
|
140
119
|
|
|
141
|
-
|
|
142
|
-
'
|
|
143
|
-
|
|
120
|
+
expectRequestWith(mockAxios, {
|
|
121
|
+
method: 'POST',
|
|
122
|
+
url: '/events',
|
|
123
|
+
data: expect.objectContaining({
|
|
144
124
|
properties: complexProperties,
|
|
145
125
|
}),
|
|
146
|
-
|
|
147
|
-
);
|
|
126
|
+
});
|
|
148
127
|
});
|
|
149
128
|
|
|
150
129
|
it('should throw error when API returns 400', async () => {
|
|
151
|
-
|
|
152
|
-
mockErrorResponse(400, 'Validation failed', mockErrors.validationError.details)
|
|
153
|
-
);
|
|
130
|
+
setupMockRequestError(mockAxios, 400, 'Validation failed', 'VALIDATION_ERROR');
|
|
154
131
|
|
|
155
132
|
await expect(
|
|
156
133
|
client.events.track('', 'user_123')
|
|
@@ -158,39 +135,23 @@ describe('Events Resource', () => {
|
|
|
158
135
|
});
|
|
159
136
|
|
|
160
137
|
it('should throw error when API returns 500', async () => {
|
|
161
|
-
|
|
162
|
-
mockErrorResponse(500, 'Internal server error')
|
|
163
|
-
);
|
|
138
|
+
setupMockRequestError(mockAxios, 500, 'Internal server error', 'SERVER_ERROR');
|
|
164
139
|
|
|
165
140
|
await expect(
|
|
166
|
-
client.events.track('
|
|
141
|
+
client.events.track('user_login', 'user_123')
|
|
167
142
|
).rejects.toThrow('Internal server error');
|
|
168
143
|
});
|
|
169
144
|
|
|
170
145
|
it('should throw error when API returns 503 (queue full)', async () => {
|
|
171
|
-
|
|
172
|
-
mockErrorResponse(503, mockErrors.queueFullError.message)
|
|
173
|
-
);
|
|
146
|
+
setupMockRequestError(mockAxios, 503, 'Event queue is full. Please retry later.', 'SERVICE_UNAVAILABLE');
|
|
174
147
|
|
|
175
148
|
await expect(
|
|
176
|
-
client.events.track('
|
|
149
|
+
client.events.track('user_login', 'user_123')
|
|
177
150
|
).rejects.toThrow('Event queue is full');
|
|
178
151
|
});
|
|
179
152
|
|
|
180
|
-
it('should handle network timeout', async () => {
|
|
181
|
-
const timeoutError = new Error('timeout of 10000ms exceeded');
|
|
182
|
-
(timeoutError as any).code = 'ECONNABORTED';
|
|
183
|
-
mockAxiosInstance.post.mockRejectedValue(timeoutError);
|
|
184
|
-
|
|
185
|
-
await expect(
|
|
186
|
-
client.events.track('user-login', 'user_123')
|
|
187
|
-
).rejects.toThrow('timeout');
|
|
188
|
-
});
|
|
189
|
-
|
|
190
153
|
it('should handle properties with null values', async () => {
|
|
191
|
-
|
|
192
|
-
mockSuccessResponse(mockResponses.trackEventResponse)
|
|
193
|
-
);
|
|
154
|
+
setupMockRequest(mockAxios, mockResponses.trackEventResponse);
|
|
194
155
|
|
|
195
156
|
await client.events.track('user_updated', 'user_123', {
|
|
196
157
|
email: 'user@example.com',
|
|
@@ -198,85 +159,99 @@ describe('Events Resource', () => {
|
|
|
198
159
|
address: null,
|
|
199
160
|
});
|
|
200
161
|
|
|
201
|
-
|
|
202
|
-
'
|
|
203
|
-
|
|
162
|
+
expectRequestWith(mockAxios, {
|
|
163
|
+
method: 'POST',
|
|
164
|
+
url: '/events',
|
|
165
|
+
data: expect.objectContaining({
|
|
204
166
|
properties: {
|
|
205
167
|
email: 'user@example.com',
|
|
206
168
|
phone: null,
|
|
207
169
|
address: null,
|
|
208
170
|
},
|
|
209
171
|
}),
|
|
210
|
-
|
|
211
|
-
);
|
|
172
|
+
});
|
|
212
173
|
});
|
|
213
174
|
|
|
214
175
|
it('should handle properties with boolean values', async () => {
|
|
215
|
-
|
|
216
|
-
mockSuccessResponse(mockResponses.trackEventResponse)
|
|
217
|
-
);
|
|
176
|
+
setupMockRequest(mockAxios, mockResponses.trackEventResponse);
|
|
218
177
|
|
|
219
|
-
await client.events.track('
|
|
178
|
+
await client.events.track('feature_toggled', 'user_123', {
|
|
220
179
|
feature_name: 'dark_mode',
|
|
221
180
|
enabled: true,
|
|
222
181
|
});
|
|
223
182
|
|
|
224
|
-
|
|
225
|
-
'
|
|
226
|
-
|
|
183
|
+
expectRequestWith(mockAxios, {
|
|
184
|
+
method: 'POST',
|
|
185
|
+
url: '/events',
|
|
186
|
+
data: expect.objectContaining({
|
|
227
187
|
properties: {
|
|
228
188
|
feature_name: 'dark_mode',
|
|
229
189
|
enabled: true,
|
|
230
190
|
},
|
|
231
191
|
}),
|
|
232
|
-
|
|
233
|
-
);
|
|
192
|
+
});
|
|
234
193
|
});
|
|
235
194
|
|
|
236
|
-
it('should handle
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
);
|
|
195
|
+
it('should handle custom timestamp', async () => {
|
|
196
|
+
setupMockRequest(mockAxios, mockResponses.trackEventResponse);
|
|
197
|
+
const timestamp = new Date();
|
|
240
198
|
|
|
241
|
-
await client.events.track('
|
|
242
|
-
score: 1500,
|
|
243
|
-
multiplier: 1.5,
|
|
244
|
-
rank: 42,
|
|
245
|
-
});
|
|
199
|
+
await client.events.track('user_login', 'user_123', {}, { timestamp });
|
|
246
200
|
|
|
247
|
-
|
|
248
|
-
'
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
multiplier: 1.5,
|
|
253
|
-
rank: 42,
|
|
254
|
-
},
|
|
201
|
+
expectRequestWith(mockAxios, {
|
|
202
|
+
method: 'POST',
|
|
203
|
+
url: '/events',
|
|
204
|
+
data: expect.objectContaining({
|
|
205
|
+
timestamp: timestamp.toISOString(),
|
|
255
206
|
}),
|
|
256
|
-
|
|
257
|
-
);
|
|
207
|
+
});
|
|
258
208
|
});
|
|
259
209
|
|
|
260
|
-
it('should
|
|
261
|
-
|
|
262
|
-
mockSuccessResponse(mockResponses.trackEventResponse)
|
|
263
|
-
);
|
|
210
|
+
it('should reject timestamp more than 7 days old', async () => {
|
|
211
|
+
const oldTimestamp = new Date(Date.now() - 8 * 24 * 60 * 60 * 1000);
|
|
264
212
|
|
|
265
|
-
await
|
|
266
|
-
|
|
267
|
-
|
|
213
|
+
await expect(
|
|
214
|
+
client.events.track('user_login', 'user_123', {}, { timestamp: oldTimestamp })
|
|
215
|
+
).rejects.toThrow(ValidationError);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('trackBatch', () => {
|
|
220
|
+
it('should track multiple events', async () => {
|
|
221
|
+
const batchResponse = { processed: 2, failed: 0 };
|
|
222
|
+
setupMockRequest(mockAxios, batchResponse);
|
|
223
|
+
|
|
224
|
+
const events = [
|
|
225
|
+
{ eventName: 'event1', userId: 'user1', properties: { a: 1 } },
|
|
226
|
+
{ eventName: 'event2', userId: 'user2', properties: { b: 2 } },
|
|
227
|
+
];
|
|
228
|
+
|
|
229
|
+
const result = await client.events.trackBatch(events);
|
|
230
|
+
|
|
231
|
+
expectRequestWith(mockAxios, {
|
|
232
|
+
method: 'POST',
|
|
233
|
+
url: '/events/batch',
|
|
268
234
|
});
|
|
235
|
+
expect(result).toEqual(batchResponse);
|
|
236
|
+
});
|
|
269
237
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
);
|
|
238
|
+
it('should throw error for empty events array', async () => {
|
|
239
|
+
await expect(client.events.trackBatch([])).rejects.toThrow(ValidationError);
|
|
240
|
+
await expect(client.events.trackBatch([])).rejects.toThrow('cannot be empty');
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('should throw error for more than 100 events', async () => {
|
|
244
|
+
const manyEvents = Array.from({ length: 101 }, (_, i) => ({
|
|
245
|
+
eventName: `event_${i}`,
|
|
246
|
+
userId: `user_${i}`,
|
|
247
|
+
}));
|
|
248
|
+
|
|
249
|
+
await expect(client.events.trackBatch(manyEvents)).rejects.toThrow(ValidationError);
|
|
250
|
+
await expect(client.events.trackBatch(manyEvents)).rejects.toThrow('maximum of 100');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should throw error for non-array input', async () => {
|
|
254
|
+
await expect(client.events.trackBatch('not an array' as any)).rejects.toThrow(ValidationError);
|
|
280
255
|
});
|
|
281
256
|
});
|
|
282
257
|
});
|
|
@@ -1,71 +1,66 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
1
|
import { Rooguys } from '../../index';
|
|
3
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
createMockRooguysClient,
|
|
4
|
+
setupMockRequest,
|
|
5
|
+
setupMockRequestError,
|
|
6
|
+
expectRequestWith,
|
|
7
|
+
MockAxiosInstance,
|
|
8
|
+
} from '../utils/mockClient';
|
|
4
9
|
import { mockResponses, mockErrors } from '../fixtures/responses';
|
|
5
10
|
|
|
6
|
-
jest.mock('axios');
|
|
7
|
-
const mockedAxios = axios as jest.Mocked<typeof axios>;
|
|
8
|
-
|
|
9
11
|
describe('Leaderboards Resource', () => {
|
|
10
12
|
let client: Rooguys;
|
|
11
|
-
let
|
|
12
|
-
const apiKey = 'test-api-key';
|
|
13
|
+
let mockAxios: MockAxiosInstance;
|
|
13
14
|
|
|
14
15
|
beforeEach(() => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
jest.clearAllMocks();
|
|
16
|
+
const mock = createMockRooguysClient();
|
|
17
|
+
client = mock.client;
|
|
18
|
+
mockAxios = mock.mockAxios;
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
describe('getGlobal', () => {
|
|
22
22
|
it('should get global leaderboard with default parameters', async () => {
|
|
23
|
-
|
|
24
|
-
mockSuccessResponse(mockResponses.leaderboardResponse)
|
|
25
|
-
);
|
|
23
|
+
setupMockRequest(mockAxios, mockResponses.leaderboardResponse);
|
|
26
24
|
|
|
27
25
|
const result = await client.leaderboards.getGlobal();
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
expectRequestWith(mockAxios, {
|
|
28
|
+
method: 'GET',
|
|
29
|
+
url: '/leaderboards/global',
|
|
31
30
|
});
|
|
32
|
-
expect(result).toEqual(mockResponses.leaderboardResponse);
|
|
33
31
|
expect(result.rankings).toHaveLength(2);
|
|
34
32
|
});
|
|
35
33
|
|
|
36
34
|
it('should get global leaderboard with weekly timeframe', async () => {
|
|
37
|
-
|
|
38
|
-
mockSuccessResponse(mockResponses.leaderboardResponse)
|
|
39
|
-
);
|
|
35
|
+
setupMockRequest(mockAxios, mockResponses.leaderboardResponse);
|
|
40
36
|
|
|
41
37
|
await client.leaderboards.getGlobal('weekly');
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
expectRequestWith(mockAxios, {
|
|
40
|
+
method: 'GET',
|
|
41
|
+
url: '/leaderboards/global',
|
|
45
42
|
});
|
|
46
43
|
});
|
|
47
44
|
|
|
48
45
|
it('should get global leaderboard with monthly timeframe', async () => {
|
|
49
|
-
|
|
50
|
-
mockSuccessResponse(mockResponses.leaderboardResponse)
|
|
51
|
-
);
|
|
46
|
+
setupMockRequest(mockAxios, mockResponses.leaderboardResponse);
|
|
52
47
|
|
|
53
48
|
await client.leaderboards.getGlobal('monthly');
|
|
54
49
|
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
expectRequestWith(mockAxios, {
|
|
51
|
+
method: 'GET',
|
|
52
|
+
url: '/leaderboards/global',
|
|
57
53
|
});
|
|
58
54
|
});
|
|
59
55
|
|
|
60
56
|
it('should get global leaderboard with custom pagination', async () => {
|
|
61
|
-
|
|
62
|
-
mockSuccessResponse(mockResponses.leaderboardResponse)
|
|
63
|
-
);
|
|
57
|
+
setupMockRequest(mockAxios, mockResponses.leaderboardResponse);
|
|
64
58
|
|
|
65
59
|
await client.leaderboards.getGlobal('all-time', 2, 25);
|
|
66
60
|
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
expectRequestWith(mockAxios, {
|
|
62
|
+
method: 'GET',
|
|
63
|
+
url: '/leaderboards/global',
|
|
69
64
|
});
|
|
70
65
|
});
|
|
71
66
|
|
|
@@ -75,7 +70,7 @@ describe('Leaderboards Resource', () => {
|
|
|
75
70
|
rankings: [],
|
|
76
71
|
total: 0,
|
|
77
72
|
};
|
|
78
|
-
|
|
73
|
+
setupMockRequest(mockAxios, emptyLeaderboard);
|
|
79
74
|
|
|
80
75
|
const result = await client.leaderboards.getGlobal();
|
|
81
76
|
|
|
@@ -84,9 +79,7 @@ describe('Leaderboards Resource', () => {
|
|
|
84
79
|
});
|
|
85
80
|
|
|
86
81
|
it('should throw error for invalid timeframe', async () => {
|
|
87
|
-
|
|
88
|
-
mockErrorResponse(400, mockErrors.invalidTimeframeError.message)
|
|
89
|
-
);
|
|
82
|
+
setupMockRequestError(mockAxios, 400, "Timeframe must be one of: all-time, weekly, monthly");
|
|
90
83
|
|
|
91
84
|
await expect(
|
|
92
85
|
client.leaderboards.getGlobal('invalid' as any)
|
|
@@ -94,9 +87,7 @@ describe('Leaderboards Resource', () => {
|
|
|
94
87
|
});
|
|
95
88
|
|
|
96
89
|
it('should throw error for invalid pagination', async () => {
|
|
97
|
-
|
|
98
|
-
mockErrorResponse(400, mockErrors.invalidPaginationError.message)
|
|
99
|
-
);
|
|
90
|
+
setupMockRequestError(mockAxios, 400, 'Limit must be between 1 and 100');
|
|
100
91
|
|
|
101
92
|
await expect(
|
|
102
93
|
client.leaderboards.getGlobal('all-time', 1, 150)
|
|
@@ -112,7 +103,7 @@ describe('Leaderboards Resource', () => {
|
|
|
112
103
|
{ rank: 3, user_id: 'user3', points: 900, level: null },
|
|
113
104
|
],
|
|
114
105
|
};
|
|
115
|
-
|
|
106
|
+
setupMockRequest(mockAxios, leaderboardWithTies);
|
|
116
107
|
|
|
117
108
|
const result = await client.leaderboards.getGlobal();
|
|
118
109
|
|
|
@@ -120,5 +111,147 @@ describe('Leaderboards Resource', () => {
|
|
|
120
111
|
expect(result.rankings[1].rank).toBe(1);
|
|
121
112
|
expect(result.rankings[2].rank).toBe(3);
|
|
122
113
|
});
|
|
114
|
+
|
|
115
|
+
it('should support filter options object', async () => {
|
|
116
|
+
setupMockRequest(mockAxios, mockResponses.leaderboardResponse);
|
|
117
|
+
|
|
118
|
+
await client.leaderboards.getGlobal({
|
|
119
|
+
timeframe: 'weekly',
|
|
120
|
+
page: 2,
|
|
121
|
+
limit: 25,
|
|
122
|
+
persona: 'Achiever',
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
expectRequestWith(mockAxios, {
|
|
126
|
+
method: 'GET',
|
|
127
|
+
url: '/leaderboards/global',
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('list', () => {
|
|
133
|
+
it('should list all leaderboards', async () => {
|
|
134
|
+
setupMockRequest(mockAxios, mockResponses.leaderboardsListResponse);
|
|
135
|
+
|
|
136
|
+
const result = await client.leaderboards.list();
|
|
137
|
+
|
|
138
|
+
expectRequestWith(mockAxios, {
|
|
139
|
+
method: 'GET',
|
|
140
|
+
url: '/leaderboards',
|
|
141
|
+
});
|
|
142
|
+
expect(result.leaderboards).toHaveLength(1);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should list leaderboards with pagination', async () => {
|
|
146
|
+
setupMockRequest(mockAxios, mockResponses.leaderboardsListResponse);
|
|
147
|
+
|
|
148
|
+
await client.leaderboards.list(2, 25);
|
|
149
|
+
|
|
150
|
+
expectRequestWith(mockAxios, {
|
|
151
|
+
method: 'GET',
|
|
152
|
+
url: '/leaderboards',
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should list leaderboards with search', async () => {
|
|
157
|
+
setupMockRequest(mockAxios, mockResponses.leaderboardsListResponse);
|
|
158
|
+
|
|
159
|
+
await client.leaderboards.list(1, 50, 'top');
|
|
160
|
+
|
|
161
|
+
expectRequestWith(mockAxios, {
|
|
162
|
+
method: 'GET',
|
|
163
|
+
url: '/leaderboards',
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should support options object', async () => {
|
|
168
|
+
setupMockRequest(mockAxios, mockResponses.leaderboardsListResponse);
|
|
169
|
+
|
|
170
|
+
await client.leaderboards.list({ page: 2, limit: 25, search: 'top' });
|
|
171
|
+
|
|
172
|
+
expectRequestWith(mockAxios, {
|
|
173
|
+
method: 'GET',
|
|
174
|
+
url: '/leaderboards',
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('getCustom', () => {
|
|
180
|
+
it('should get custom leaderboard by ID', async () => {
|
|
181
|
+
setupMockRequest(mockAxios, mockResponses.customLeaderboardResponse);
|
|
182
|
+
|
|
183
|
+
const result = await client.leaderboards.getCustom('lb1');
|
|
184
|
+
|
|
185
|
+
expectRequestWith(mockAxios, {
|
|
186
|
+
method: 'GET',
|
|
187
|
+
url: '/leaderboards/lb1',
|
|
188
|
+
});
|
|
189
|
+
expect(result.rankings).toBeDefined();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should get custom leaderboard with pagination', async () => {
|
|
193
|
+
setupMockRequest(mockAxios, mockResponses.customLeaderboardResponse);
|
|
194
|
+
|
|
195
|
+
await client.leaderboards.getCustom('lb1', 2, 25);
|
|
196
|
+
|
|
197
|
+
expectRequestWith(mockAxios, {
|
|
198
|
+
method: 'GET',
|
|
199
|
+
url: '/leaderboards/lb1',
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should support filter options object', async () => {
|
|
204
|
+
setupMockRequest(mockAxios, mockResponses.customLeaderboardResponse);
|
|
205
|
+
|
|
206
|
+
await client.leaderboards.getCustom('lb1', {
|
|
207
|
+
page: 2,
|
|
208
|
+
limit: 25,
|
|
209
|
+
persona: 'Achiever',
|
|
210
|
+
minLevel: 5,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
expectRequestWith(mockAxios, {
|
|
214
|
+
method: 'GET',
|
|
215
|
+
url: '/leaderboards/lb1',
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should throw error for non-existent leaderboard', async () => {
|
|
220
|
+
setupMockRequestError(mockAxios, 404, 'Leaderboard not found');
|
|
221
|
+
|
|
222
|
+
await expect(client.leaderboards.getCustom('nonexistent')).rejects.toThrow('Leaderboard not found');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe('getUserRank', () => {
|
|
227
|
+
it('should get user rank in leaderboard', async () => {
|
|
228
|
+
setupMockRequest(mockAxios, mockResponses.userRankResponse);
|
|
229
|
+
|
|
230
|
+
const result = await client.leaderboards.getUserRank('lb1', 'user123');
|
|
231
|
+
|
|
232
|
+
expectRequestWith(mockAxios, {
|
|
233
|
+
method: 'GET',
|
|
234
|
+
url: '/leaderboards/lb1/users/user123/rank',
|
|
235
|
+
});
|
|
236
|
+
expect(result.rank).toBe(42);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('getAroundUser', () => {
|
|
241
|
+
it('should get leaderboard entries around user', async () => {
|
|
242
|
+
const aroundResponse = {
|
|
243
|
+
...mockResponses.leaderboardResponse,
|
|
244
|
+
user_rank: 42,
|
|
245
|
+
};
|
|
246
|
+
setupMockRequest(mockAxios, aroundResponse);
|
|
247
|
+
|
|
248
|
+
const result = await client.leaderboards.getAroundUser('lb1', 'user123', 5);
|
|
249
|
+
|
|
250
|
+
expectRequestWith(mockAxios, {
|
|
251
|
+
method: 'GET',
|
|
252
|
+
url: '/leaderboards/lb1/users/user123/around',
|
|
253
|
+
});
|
|
254
|
+
expect(result.rankings).toBeDefined();
|
|
255
|
+
});
|
|
123
256
|
});
|
|
124
257
|
});
|