@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.
@@ -1,125 +1,104 @@
1
- import axios from 'axios';
2
- import { Rooguys } from '../../index';
3
- import { createMockAxiosInstance, mockSuccessResponse, mockErrorResponse } from '../utils/mockClient';
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 mockAxiosInstance: ReturnType<typeof createMockAxiosInstance>;
12
- const apiKey = 'test-api-key';
13
+ let mockAxios: MockAxiosInstance;
13
14
 
14
15
  beforeEach(() => {
15
- mockAxiosInstance = createMockAxiosInstance();
16
- mockedAxios.create.mockReturnValue(mockAxiosInstance as any);
17
- client = new Rooguys(apiKey);
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
- mockAxiosInstance.post.mockResolvedValue(
24
- mockSuccessResponse(mockResponses.trackEventResponse)
25
- );
23
+ setupMockRequest(mockAxios, mockResponses.trackEventResponse);
26
24
 
27
- const result = await client.events.track('purchase-completed', 'user_123', {
25
+ const result = await client.events.track('purchase_completed', 'user_123', {
28
26
  amount: 50.0,
29
27
  });
30
28
 
31
- expect(mockAxiosInstance.post).toHaveBeenCalledWith(
32
- '/event',
33
- {
34
- event_name: 'purchase-completed',
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
- { params: { include_profile: undefined } }
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
- mockAxiosInstance.post.mockResolvedValue(
45
- mockSuccessResponse(mockResponses.trackEventResponse)
46
- );
42
+ setupMockRequest(mockAxios, mockResponses.trackEventResponse);
47
43
 
48
- const result = await client.events.track('user-login', 'user_456');
44
+ const result = await client.events.track('user_login', 'user_456');
49
45
 
50
- expect(mockAxiosInstance.post).toHaveBeenCalledWith(
51
- '/event',
52
- {
53
- event_name: 'user-login',
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
- { params: { include_profile: undefined } }
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
- mockAxiosInstance.post.mockResolvedValue(
64
- mockSuccessResponse(mockResponses.trackEventWithProfileResponse)
65
- );
59
+ setupMockRequest(mockAxios, mockResponses.trackEventWithProfileResponse);
66
60
 
67
61
  const result = await client.events.track(
68
- 'purchase-completed',
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
- mockAxiosInstance.post.mockResolvedValue(
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
- expect(mockAxiosInstance.post).toHaveBeenCalledWith(
95
- '/event',
96
- expect.objectContaining({
77
+ expectRequestWith(mockAxios, {
78
+ method: 'POST',
79
+ url: '/events',
80
+ data: expect.objectContaining({
97
81
  event_name: 'user-signup_v2',
98
82
  }),
99
- expect.any(Object)
100
- );
83
+ });
101
84
  });
102
85
 
103
86
  it('should handle special characters in user ID', async () => {
104
- mockAxiosInstance.post.mockResolvedValue(
105
- mockSuccessResponse(mockResponses.trackEventResponse)
106
- );
87
+ setupMockRequest(mockAxios, mockResponses.trackEventResponse);
107
88
 
108
- await client.events.track('user-login', 'user@example.com');
89
+ await client.events.track('user_login', 'user@example.com');
109
90
 
110
- expect(mockAxiosInstance.post).toHaveBeenCalledWith(
111
- '/event',
112
- expect.objectContaining({
91
+ expectRequestWith(mockAxios, {
92
+ method: 'POST',
93
+ url: '/events',
94
+ data: expect.objectContaining({
113
95
  user_id: 'user@example.com',
114
96
  }),
115
- expect.any(Object)
116
- );
97
+ });
117
98
  });
118
99
 
119
100
  it('should handle complex nested properties', async () => {
120
- mockAxiosInstance.post.mockResolvedValue(
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
- expect(mockAxiosInstance.post).toHaveBeenCalledWith(
142
- '/event',
143
- expect.objectContaining({
120
+ expectRequestWith(mockAxios, {
121
+ method: 'POST',
122
+ url: '/events',
123
+ data: expect.objectContaining({
144
124
  properties: complexProperties,
145
125
  }),
146
- expect.any(Object)
147
- );
126
+ });
148
127
  });
149
128
 
150
129
  it('should throw error when API returns 400', async () => {
151
- mockAxiosInstance.post.mockRejectedValue(
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
- mockAxiosInstance.post.mockRejectedValue(
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('user-login', 'user_123')
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
- mockAxiosInstance.post.mockRejectedValue(
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('user-login', 'user_123')
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
- mockAxiosInstance.post.mockResolvedValue(
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
- expect(mockAxiosInstance.post).toHaveBeenCalledWith(
202
- '/event',
203
- expect.objectContaining({
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
- expect.any(Object)
211
- );
172
+ });
212
173
  });
213
174
 
214
175
  it('should handle properties with boolean values', async () => {
215
- mockAxiosInstance.post.mockResolvedValue(
216
- mockSuccessResponse(mockResponses.trackEventResponse)
217
- );
176
+ setupMockRequest(mockAxios, mockResponses.trackEventResponse);
218
177
 
219
- await client.events.track('feature-toggled', 'user_123', {
178
+ await client.events.track('feature_toggled', 'user_123', {
220
179
  feature_name: 'dark_mode',
221
180
  enabled: true,
222
181
  });
223
182
 
224
- expect(mockAxiosInstance.post).toHaveBeenCalledWith(
225
- '/event',
226
- expect.objectContaining({
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
- expect.any(Object)
233
- );
192
+ });
234
193
  });
235
194
 
236
- it('should handle properties with numeric values', async () => {
237
- mockAxiosInstance.post.mockResolvedValue(
238
- mockSuccessResponse(mockResponses.trackEventResponse)
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('score-updated', 'user_123', {
242
- score: 1500,
243
- multiplier: 1.5,
244
- rank: 42,
245
- });
199
+ await client.events.track('user_login', 'user_123', {}, { timestamp });
246
200
 
247
- expect(mockAxiosInstance.post).toHaveBeenCalledWith(
248
- '/event',
249
- expect.objectContaining({
250
- properties: {
251
- score: 1500,
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
- expect.any(Object)
257
- );
207
+ });
258
208
  });
259
209
 
260
- it('should handle empty string properties', async () => {
261
- mockAxiosInstance.post.mockResolvedValue(
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 client.events.track('form-submitted', 'user_123', {
266
- name: 'John Doe',
267
- comment: '',
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
- expect(mockAxiosInstance.post).toHaveBeenCalledWith(
271
- '/event',
272
- expect.objectContaining({
273
- properties: {
274
- name: 'John Doe',
275
- comment: '',
276
- },
277
- }),
278
- expect.any(Object)
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 { createMockAxiosInstance, mockSuccessResponse, mockErrorResponse } from '../utils/mockClient';
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 mockAxiosInstance: ReturnType<typeof createMockAxiosInstance>;
12
- const apiKey = 'test-api-key';
13
+ let mockAxios: MockAxiosInstance;
13
14
 
14
15
  beforeEach(() => {
15
- mockAxiosInstance = createMockAxiosInstance();
16
- mockedAxios.create.mockReturnValue(mockAxiosInstance as any);
17
- client = new Rooguys(apiKey);
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
- mockAxiosInstance.get.mockResolvedValue(
24
- mockSuccessResponse(mockResponses.leaderboardResponse)
25
- );
23
+ setupMockRequest(mockAxios, mockResponses.leaderboardResponse);
26
24
 
27
25
  const result = await client.leaderboards.getGlobal();
28
26
 
29
- expect(mockAxiosInstance.get).toHaveBeenCalledWith('/leaderboard', {
30
- params: { timeframe: 'all-time', page: 1, limit: 50 },
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
- mockAxiosInstance.get.mockResolvedValue(
38
- mockSuccessResponse(mockResponses.leaderboardResponse)
39
- );
35
+ setupMockRequest(mockAxios, mockResponses.leaderboardResponse);
40
36
 
41
37
  await client.leaderboards.getGlobal('weekly');
42
38
 
43
- expect(mockAxiosInstance.get).toHaveBeenCalledWith('/leaderboard', {
44
- params: { timeframe: 'weekly', page: 1, limit: 50 },
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
- mockAxiosInstance.get.mockResolvedValue(
50
- mockSuccessResponse(mockResponses.leaderboardResponse)
51
- );
46
+ setupMockRequest(mockAxios, mockResponses.leaderboardResponse);
52
47
 
53
48
  await client.leaderboards.getGlobal('monthly');
54
49
 
55
- expect(mockAxiosInstance.get).toHaveBeenCalledWith('/leaderboard', {
56
- params: { timeframe: 'monthly', page: 1, limit: 50 },
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
- mockAxiosInstance.get.mockResolvedValue(
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
- expect(mockAxiosInstance.get).toHaveBeenCalledWith('/leaderboard', {
68
- params: { timeframe: 'all-time', page: 2, limit: 25 },
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
- mockAxiosInstance.get.mockResolvedValue(mockSuccessResponse(emptyLeaderboard));
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
- mockAxiosInstance.get.mockRejectedValue(
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
- mockAxiosInstance.get.mockRejectedValue(
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
- mockAxiosInstance.get.mockResolvedValue(mockSuccessResponse(leaderboardWithTies));
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
  });