@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,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Property-Based Test: HTTP Request Construction
|
|
3
3
|
* Feature: sdk-testing-enhancement, Property 1: HTTP Request Construction
|
|
4
|
+
* Validates: Requirements 1.1, 3.1
|
|
4
5
|
*
|
|
5
6
|
* Tests that any valid SDK method call constructs correct HTTP request
|
|
6
7
|
* with proper method, URL, headers, and body structure.
|
|
@@ -8,20 +9,22 @@
|
|
|
8
9
|
|
|
9
10
|
import fc from 'fast-check';
|
|
10
11
|
import { Rooguys } from '../../index';
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
createMockRooguysClient,
|
|
14
|
+
mockAxiosResponse,
|
|
15
|
+
getLastRequestConfig,
|
|
16
|
+
MockAxiosInstance,
|
|
17
|
+
} from '../utils/mockClient';
|
|
12
18
|
import { arbitraries } from '../utils/generators';
|
|
13
|
-
import axios from 'axios';
|
|
14
|
-
|
|
15
|
-
// Mock axios
|
|
16
|
-
jest.mock('axios');
|
|
17
|
-
const mockedAxios = axios as jest.Mocked<typeof axios>;
|
|
18
19
|
|
|
19
20
|
describe('Property: HTTP Request Construction', () => {
|
|
20
|
-
let
|
|
21
|
+
let client: Rooguys;
|
|
22
|
+
let mockAxios: MockAxiosInstance;
|
|
21
23
|
|
|
22
24
|
beforeEach(() => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
const mock = createMockRooguysClient();
|
|
26
|
+
client = mock.client;
|
|
27
|
+
mockAxios = mock.mockAxios;
|
|
25
28
|
});
|
|
26
29
|
|
|
27
30
|
afterEach(() => {
|
|
@@ -31,32 +34,28 @@ describe('Property: HTTP Request Construction', () => {
|
|
|
31
34
|
it('should construct valid POST request for event tracking', async () => {
|
|
32
35
|
await fc.assert(
|
|
33
36
|
fc.asyncProperty(
|
|
34
|
-
arbitraries.apiKey(),
|
|
35
37
|
arbitraries.eventName(),
|
|
36
38
|
arbitraries.userId(),
|
|
37
39
|
arbitraries.properties(),
|
|
38
|
-
async (
|
|
40
|
+
async (eventName, userId, properties) => {
|
|
39
41
|
// Arrange
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse({
|
|
43
|
+
status: 'queued',
|
|
44
|
+
message: 'Event accepted'
|
|
45
|
+
}));
|
|
44
46
|
|
|
45
47
|
// Act
|
|
46
|
-
await
|
|
48
|
+
await client.events.track(eventName, userId, properties);
|
|
47
49
|
|
|
48
50
|
// Assert
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
params: expect.any(Object)
|
|
58
|
-
})
|
|
59
|
-
);
|
|
51
|
+
const config = getLastRequestConfig(mockAxios);
|
|
52
|
+
expect(config.method).toBe('POST');
|
|
53
|
+
expect(config.url).toBe('/events');
|
|
54
|
+
expect(config.data).toEqual({
|
|
55
|
+
event_name: eventName,
|
|
56
|
+
user_id: userId,
|
|
57
|
+
properties,
|
|
58
|
+
});
|
|
60
59
|
}
|
|
61
60
|
),
|
|
62
61
|
{ numRuns: 100 }
|
|
@@ -66,20 +65,21 @@ describe('Property: HTTP Request Construction', () => {
|
|
|
66
65
|
it('should construct valid GET request for user profile', async () => {
|
|
67
66
|
await fc.assert(
|
|
68
67
|
fc.asyncProperty(
|
|
69
|
-
arbitraries.apiKey(),
|
|
70
68
|
arbitraries.userId(),
|
|
71
|
-
async (
|
|
69
|
+
async (userId) => {
|
|
72
70
|
// Arrange
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse({
|
|
72
|
+
user_id: userId,
|
|
73
|
+
points: 100
|
|
74
|
+
}));
|
|
77
75
|
|
|
78
76
|
// Act
|
|
79
|
-
await
|
|
77
|
+
await client.users.get(userId);
|
|
80
78
|
|
|
81
79
|
// Assert
|
|
82
|
-
|
|
80
|
+
const config = getLastRequestConfig(mockAxios);
|
|
81
|
+
expect(config.method).toBe('GET');
|
|
82
|
+
expect(config.url).toBe(`/users/${encodeURIComponent(userId)}`);
|
|
83
83
|
}
|
|
84
84
|
),
|
|
85
85
|
{ numRuns: 100 }
|
|
@@ -89,23 +89,21 @@ describe('Property: HTTP Request Construction', () => {
|
|
|
89
89
|
it('should construct valid POST request for bulk user fetch', async () => {
|
|
90
90
|
await fc.assert(
|
|
91
91
|
fc.asyncProperty(
|
|
92
|
-
arbitraries.apiKey(),
|
|
93
92
|
arbitraries.userIds(),
|
|
94
|
-
async (
|
|
93
|
+
async (userIds) => {
|
|
95
94
|
// Arrange
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
});
|
|
99
|
-
const sdk = new Rooguys(apiKey);
|
|
95
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse({
|
|
96
|
+
users: []
|
|
97
|
+
}));
|
|
100
98
|
|
|
101
99
|
// Act
|
|
102
|
-
await
|
|
100
|
+
await client.users.getBulk(userIds);
|
|
103
101
|
|
|
104
102
|
// Assert
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
);
|
|
103
|
+
const config = getLastRequestConfig(mockAxios);
|
|
104
|
+
expect(config.method).toBe('POST');
|
|
105
|
+
expect(config.url).toBe('/users/bulk');
|
|
106
|
+
expect(config.data).toEqual({ user_ids: userIds });
|
|
109
107
|
}
|
|
110
108
|
),
|
|
111
109
|
{ numRuns: 100 }
|
|
@@ -115,26 +113,29 @@ describe('Property: HTTP Request Construction', () => {
|
|
|
115
113
|
it('should construct valid GET request with query parameters for leaderboard', async () => {
|
|
116
114
|
await fc.assert(
|
|
117
115
|
fc.asyncProperty(
|
|
118
|
-
arbitraries.apiKey(),
|
|
119
116
|
arbitraries.timeframe(),
|
|
120
117
|
arbitraries.pagination(),
|
|
121
|
-
async (
|
|
118
|
+
async (timeframe, { page, limit }) => {
|
|
122
119
|
// Arrange
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse({
|
|
121
|
+
rankings: [],
|
|
122
|
+
page,
|
|
123
|
+
limit,
|
|
124
|
+
total: 0
|
|
125
|
+
}));
|
|
127
126
|
|
|
128
127
|
// Act
|
|
129
|
-
await
|
|
128
|
+
await client.leaderboards.getGlobal(timeframe as any, page, limit);
|
|
130
129
|
|
|
131
130
|
// Assert
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
131
|
+
const config = getLastRequestConfig(mockAxios);
|
|
132
|
+
expect(config.method).toBe('GET');
|
|
133
|
+
expect(config.url).toBe('/leaderboards/global');
|
|
134
|
+
expect(config.params).toEqual(expect.objectContaining({
|
|
135
|
+
timeframe,
|
|
136
|
+
page,
|
|
137
|
+
limit
|
|
138
|
+
}));
|
|
138
139
|
}
|
|
139
140
|
),
|
|
140
141
|
{ numRuns: 100 }
|
|
@@ -144,57 +145,107 @@ describe('Property: HTTP Request Construction', () => {
|
|
|
144
145
|
it('should construct valid POST request for Aha score declaration', async () => {
|
|
145
146
|
await fc.assert(
|
|
146
147
|
fc.asyncProperty(
|
|
147
|
-
arbitraries.apiKey(),
|
|
148
148
|
arbitraries.userId(),
|
|
149
149
|
arbitraries.ahaValue(),
|
|
150
|
-
async (
|
|
150
|
+
async (userId, value) => {
|
|
151
151
|
// Arrange
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse({
|
|
153
|
+
success: true,
|
|
154
|
+
message: 'Score declared'
|
|
155
|
+
}));
|
|
156
156
|
|
|
157
157
|
// Act
|
|
158
|
-
await
|
|
158
|
+
await client.aha.declare(userId, value);
|
|
159
159
|
|
|
160
160
|
// Assert
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
);
|
|
161
|
+
const config = getLastRequestConfig(mockAxios);
|
|
162
|
+
expect(config.method).toBe('POST');
|
|
163
|
+
expect(config.url).toBe('/aha/declare');
|
|
164
|
+
expect(config.data).toEqual({
|
|
165
|
+
user_id: userId,
|
|
166
|
+
value,
|
|
167
|
+
});
|
|
168
168
|
}
|
|
169
169
|
),
|
|
170
170
|
{ numRuns: 100 }
|
|
171
171
|
);
|
|
172
172
|
});
|
|
173
173
|
|
|
174
|
-
it('should
|
|
174
|
+
it('should construct valid GET request for badges list', async () => {
|
|
175
175
|
await fc.assert(
|
|
176
176
|
fc.asyncProperty(
|
|
177
|
-
arbitraries.
|
|
178
|
-
|
|
179
|
-
async (
|
|
177
|
+
arbitraries.pagination(),
|
|
178
|
+
fc.boolean(),
|
|
179
|
+
async ({ page, limit }, activeOnly) => {
|
|
180
180
|
// Arrange
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse({
|
|
182
|
+
badges: [],
|
|
183
|
+
pagination: { page, limit, total: 0, totalPages: 0 }
|
|
184
|
+
}));
|
|
185
|
+
|
|
186
|
+
// Act
|
|
187
|
+
await client.badges.list(page, limit, activeOnly);
|
|
188
|
+
|
|
189
|
+
// Assert
|
|
190
|
+
const config = getLastRequestConfig(mockAxios);
|
|
191
|
+
expect(config.method).toBe('GET');
|
|
192
|
+
expect(config.url).toBe('/badges');
|
|
193
|
+
expect(config.params).toEqual({
|
|
194
|
+
page,
|
|
195
|
+
limit,
|
|
196
|
+
active_only: activeOnly
|
|
183
197
|
});
|
|
198
|
+
}
|
|
199
|
+
),
|
|
200
|
+
{ numRuns: 100 }
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should construct valid GET request for levels list', async () => {
|
|
205
|
+
await fc.assert(
|
|
206
|
+
fc.asyncProperty(
|
|
207
|
+
arbitraries.pagination(),
|
|
208
|
+
async ({ page, limit }) => {
|
|
209
|
+
// Arrange
|
|
210
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse({
|
|
211
|
+
levels: [],
|
|
212
|
+
pagination: { page, limit, total: 0, totalPages: 0 }
|
|
213
|
+
}));
|
|
184
214
|
|
|
185
215
|
// Act
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
expect(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
216
|
+
await client.levels.list(page, limit);
|
|
217
|
+
|
|
218
|
+
// Assert
|
|
219
|
+
const config = getLastRequestConfig(mockAxios);
|
|
220
|
+
expect(config.method).toBe('GET');
|
|
221
|
+
expect(config.url).toBe('/levels');
|
|
222
|
+
expect(config.params).toEqual({ page, limit });
|
|
223
|
+
}
|
|
224
|
+
),
|
|
225
|
+
{ numRuns: 100 }
|
|
226
|
+
);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should construct valid GET request for questionnaire by slug', async () => {
|
|
230
|
+
await fc.assert(
|
|
231
|
+
fc.asyncProperty(
|
|
232
|
+
fc.string({ minLength: 1, maxLength: 50 }).filter(s => /^[a-z0-9-]+$/.test(s)),
|
|
233
|
+
async (slug) => {
|
|
234
|
+
// Arrange
|
|
235
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse({
|
|
236
|
+
id: 'q1',
|
|
237
|
+
slug,
|
|
238
|
+
title: 'Test',
|
|
239
|
+
questions: []
|
|
240
|
+
}));
|
|
241
|
+
|
|
242
|
+
// Act
|
|
243
|
+
await client.questionnaires.get(slug);
|
|
244
|
+
|
|
245
|
+
// Assert
|
|
246
|
+
const config = getLastRequestConfig(mockAxios);
|
|
247
|
+
expect(config.method).toBe('GET');
|
|
248
|
+
expect(config.url).toBe(`/questionnaires/${slug}`);
|
|
198
249
|
}
|
|
199
250
|
),
|
|
200
251
|
{ numRuns: 100 }
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Property-Based Test: Response Parsing
|
|
3
|
-
*
|
|
2
|
+
* Property-Based Test: Response Parsing Round-Trip
|
|
3
|
+
* Task 7.2: Property test for response parsing
|
|
4
|
+
* Validates: Requirements 2.1, 2.3, 2.4, 2.5
|
|
4
5
|
*
|
|
5
6
|
* Tests that any successful response is parsed correctly and data structure
|
|
6
7
|
* is preserved including nested objects, arrays, and null values.
|
|
@@ -8,19 +9,21 @@
|
|
|
8
9
|
|
|
9
10
|
import fc from 'fast-check';
|
|
10
11
|
import { Rooguys } from '../../index';
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
12
|
+
import { HttpClient } from '../../http-client';
|
|
13
|
+
import {
|
|
14
|
+
createMockRooguysClient,
|
|
15
|
+
mockAxiosResponse,
|
|
16
|
+
MockAxiosInstance,
|
|
17
|
+
} from '../utils/mockClient';
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
describe('Property: Response Parsing Preservation', () => {
|
|
19
|
-
let mockClient: any;
|
|
19
|
+
describe('Property: Response Parsing Round-Trip', () => {
|
|
20
|
+
let client: Rooguys;
|
|
21
|
+
let mockAxios: MockAxiosInstance;
|
|
20
22
|
|
|
21
23
|
beforeEach(() => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
const mock = createMockRooguysClient();
|
|
25
|
+
client = mock.client;
|
|
26
|
+
mockAxios = mock.mockAxios;
|
|
24
27
|
});
|
|
25
28
|
|
|
26
29
|
afterEach(() => {
|
|
@@ -30,7 +33,6 @@ describe('Property: Response Parsing Preservation', () => {
|
|
|
30
33
|
it('should preserve nested object structures in responses', async () => {
|
|
31
34
|
await fc.assert(
|
|
32
35
|
fc.asyncProperty(
|
|
33
|
-
fc.string({ minLength: 10, maxLength: 100 }),
|
|
34
36
|
fc.string({ minLength: 1, maxLength: 255 }),
|
|
35
37
|
fc.record({
|
|
36
38
|
user_id: fc.string(),
|
|
@@ -47,16 +49,16 @@ describe('Property: Response Parsing Preservation', () => {
|
|
|
47
49
|
}), { nil: null }),
|
|
48
50
|
metrics: fc.dictionary(fc.string(), fc.integer()),
|
|
49
51
|
}),
|
|
50
|
-
async (
|
|
52
|
+
async (userId, responseData) => {
|
|
51
53
|
// Arrange
|
|
52
|
-
|
|
53
|
-
const sdk = new Rooguys(apiKey);
|
|
54
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse(responseData));
|
|
54
55
|
|
|
55
56
|
// Act
|
|
56
|
-
const result = await
|
|
57
|
+
const result = await client.users.get(userId);
|
|
57
58
|
|
|
58
|
-
// Assert
|
|
59
|
-
expect(result).toEqual(responseData);
|
|
59
|
+
// Assert - SDK preserves the response structure
|
|
60
|
+
expect(result.user_id).toEqual(responseData.user_id);
|
|
61
|
+
expect(result.points).toEqual(responseData.points);
|
|
60
62
|
expect(result.level).toEqual(responseData.level);
|
|
61
63
|
expect(result.next_level).toEqual(responseData.next_level);
|
|
62
64
|
expect(result.metrics).toEqual(responseData.metrics);
|
|
@@ -69,62 +71,63 @@ describe('Property: Response Parsing Preservation', () => {
|
|
|
69
71
|
it('should preserve arrays in responses', async () => {
|
|
70
72
|
await fc.assert(
|
|
71
73
|
fc.asyncProperty(
|
|
72
|
-
fc.string({ minLength: 10, maxLength: 100 }),
|
|
73
74
|
fc.array(fc.string({ minLength: 1, maxLength: 255 }), { minLength: 1, maxLength: 10 }),
|
|
74
75
|
fc.array(fc.record({
|
|
75
76
|
user_id: fc.string(),
|
|
76
77
|
points: fc.integer(),
|
|
77
78
|
}), { minLength: 0, maxLength: 20 }),
|
|
78
|
-
async (
|
|
79
|
+
async (userIds, usersData) => {
|
|
79
80
|
// Arrange
|
|
80
81
|
const responseData = { users: usersData };
|
|
81
|
-
|
|
82
|
-
const sdk = new Rooguys(apiKey);
|
|
82
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse(responseData));
|
|
83
83
|
|
|
84
84
|
// Act
|
|
85
|
-
const result = await
|
|
85
|
+
const result = await client.users.getBulk(userIds);
|
|
86
86
|
|
|
87
87
|
// Assert
|
|
88
|
-
expect(result).toEqual(responseData);
|
|
89
88
|
expect(Array.isArray(result.users)).toBe(true);
|
|
90
89
|
expect(result.users).toHaveLength(usersData.length);
|
|
91
|
-
|
|
90
|
+
result.users.forEach((user, index) => {
|
|
91
|
+
expect(user.user_id).toEqual(usersData[index].user_id);
|
|
92
|
+
expect(user.points).toEqual(usersData[index].points);
|
|
93
|
+
});
|
|
92
94
|
}
|
|
93
95
|
),
|
|
94
96
|
{ numRuns: 100 }
|
|
95
97
|
);
|
|
96
98
|
});
|
|
97
99
|
|
|
98
|
-
it('should preserve null values in responses', async () => {
|
|
100
|
+
it('should preserve null values in aha score responses', async () => {
|
|
99
101
|
await fc.assert(
|
|
100
102
|
fc.asyncProperty(
|
|
101
|
-
fc.string({ minLength: 10, maxLength: 100 }),
|
|
102
103
|
fc.string({ minLength: 1, maxLength: 255 }),
|
|
103
104
|
fc.record({
|
|
104
105
|
user_id: fc.string(),
|
|
106
|
+
current_score: fc.integer({ min: 0, max: 100 }),
|
|
105
107
|
declarative_score: fc.option(fc.integer({ min: 1, max: 5 }), { nil: null }),
|
|
106
108
|
inferred_score: fc.option(fc.integer({ min: 0, max: 100 }), { nil: null }),
|
|
109
|
+
status: fc.constantFrom('not_started', 'progressing', 'activated'),
|
|
107
110
|
history: fc.record({
|
|
108
111
|
initial: fc.option(fc.integer(), { nil: null }),
|
|
109
112
|
initial_date: fc.option(fc.string(), { nil: null }),
|
|
110
113
|
previous: fc.option(fc.integer(), { nil: null }),
|
|
111
114
|
}),
|
|
112
115
|
}),
|
|
113
|
-
async (
|
|
114
|
-
// Arrange
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
async (userId, ahaData) => {
|
|
117
|
+
// Arrange - mock the wrapped response { success: true, data: {...} }
|
|
118
|
+
const responseData = { success: true, data: ahaData };
|
|
119
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse(responseData));
|
|
117
120
|
|
|
118
121
|
// Act
|
|
119
|
-
const result = await
|
|
120
|
-
|
|
121
|
-
// Assert
|
|
122
|
-
|
|
123
|
-
expect(
|
|
124
|
-
expect(
|
|
125
|
-
expect(
|
|
126
|
-
expect(
|
|
127
|
-
expect(
|
|
122
|
+
const result = await client.aha.getUserScore(userId);
|
|
123
|
+
|
|
124
|
+
// Assert - SDK unwraps { success: true, data: {...} } to just the data part
|
|
125
|
+
const data = result as any;
|
|
126
|
+
expect(data.declarative_score).toBe(ahaData.declarative_score);
|
|
127
|
+
expect(data.inferred_score).toBe(ahaData.inferred_score);
|
|
128
|
+
expect(data.history.initial).toBe(ahaData.history.initial);
|
|
129
|
+
expect(data.history.initial_date).toBe(ahaData.history.initial_date);
|
|
130
|
+
expect(data.history.previous).toBe(ahaData.history.previous);
|
|
128
131
|
}
|
|
129
132
|
),
|
|
130
133
|
{ numRuns: 100 }
|
|
@@ -134,9 +137,8 @@ describe('Property: Response Parsing Preservation', () => {
|
|
|
134
137
|
it('should handle empty objects and arrays', async () => {
|
|
135
138
|
await fc.assert(
|
|
136
139
|
fc.asyncProperty(
|
|
137
|
-
fc.string({ minLength: 10, maxLength: 100 }),
|
|
138
140
|
fc.constantFrom('all-time', 'weekly', 'monthly'),
|
|
139
|
-
async (
|
|
141
|
+
async (timeframe) => {
|
|
140
142
|
// Arrange
|
|
141
143
|
const responseData = {
|
|
142
144
|
timeframe,
|
|
@@ -145,53 +147,102 @@ describe('Property: Response Parsing Preservation', () => {
|
|
|
145
147
|
total: 0,
|
|
146
148
|
rankings: [],
|
|
147
149
|
};
|
|
148
|
-
|
|
149
|
-
const sdk = new Rooguys(apiKey);
|
|
150
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse(responseData));
|
|
150
151
|
|
|
151
152
|
// Act
|
|
152
|
-
const result = await
|
|
153
|
+
const result = await client.leaderboards.getGlobal(timeframe as any);
|
|
153
154
|
|
|
154
155
|
// Assert
|
|
155
|
-
expect(result).toEqual(responseData);
|
|
156
156
|
expect(Array.isArray(result.rankings)).toBe(true);
|
|
157
157
|
expect(result.rankings).toHaveLength(0);
|
|
158
|
+
expect(result.timeframe).toBe(timeframe);
|
|
158
159
|
}
|
|
159
160
|
),
|
|
160
161
|
{ numRuns: 100 }
|
|
161
162
|
);
|
|
162
163
|
});
|
|
163
164
|
|
|
164
|
-
it('should preserve complex nested structures', async () => {
|
|
165
|
+
it('should preserve complex nested structures with cache metadata', async () => {
|
|
165
166
|
await fc.assert(
|
|
166
167
|
fc.asyncProperty(
|
|
167
|
-
fc.string({ minLength: 10, maxLength: 100 }),
|
|
168
168
|
fc.record({
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
timeframe: fc.constantFrom('all-time', 'weekly', 'monthly'),
|
|
170
|
+
page: fc.integer({ min: 1, max: 100 }),
|
|
171
|
+
limit: fc.integer({ min: 1, max: 100 }),
|
|
172
|
+
total: fc.integer({ min: 0, max: 10000 }),
|
|
173
|
+
rankings: fc.array(fc.record({
|
|
174
|
+
rank: fc.integer({ min: 1, max: 1000 }),
|
|
171
175
|
user_id: fc.string(),
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
176
|
+
points: fc.integer({ min: 0, max: 100000 }),
|
|
177
|
+
percentile: fc.option(fc.float({ min: 0, max: 100 }), { nil: null }),
|
|
178
|
+
}), { minLength: 0, maxLength: 10 }),
|
|
179
|
+
cache_metadata: fc.option(fc.record({
|
|
180
|
+
cached_at: fc.string(),
|
|
181
|
+
ttl: fc.integer({ min: 0, max: 3600 }),
|
|
182
|
+
}), { nil: undefined }),
|
|
183
|
+
}),
|
|
184
|
+
async (responseData) => {
|
|
185
|
+
// Arrange
|
|
186
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse(responseData));
|
|
187
|
+
|
|
188
|
+
// Act
|
|
189
|
+
const result = await client.leaderboards.getGlobal();
|
|
190
|
+
|
|
191
|
+
// Assert
|
|
192
|
+
expect(result.timeframe).toBe(responseData.timeframe);
|
|
193
|
+
expect(result.page).toBe(responseData.page);
|
|
194
|
+
expect(result.limit).toBe(responseData.limit);
|
|
195
|
+
expect(result.total).toBe(responseData.total);
|
|
196
|
+
expect(result.rankings).toHaveLength(responseData.rankings.length);
|
|
197
|
+
|
|
198
|
+
// Verify each ranking entry
|
|
199
|
+
result.rankings.forEach((entry, index) => {
|
|
200
|
+
expect(entry.rank).toBe(responseData.rankings[index].rank);
|
|
201
|
+
expect(entry.user_id).toBe(responseData.rankings[index].user_id);
|
|
202
|
+
expect(entry.points).toBe(responseData.rankings[index].points);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
),
|
|
206
|
+
{ numRuns: 100 }
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('should preserve badge list structures', async () => {
|
|
211
|
+
await fc.assert(
|
|
212
|
+
fc.asyncProperty(
|
|
213
|
+
fc.record({
|
|
214
|
+
badges: fc.array(fc.record({
|
|
215
|
+
id: fc.string(),
|
|
216
|
+
name: fc.string(),
|
|
217
|
+
description: fc.option(fc.string(), { nil: null }),
|
|
218
|
+
icon_url: fc.option(fc.string(), { nil: null }),
|
|
219
|
+
is_active: fc.boolean(),
|
|
220
|
+
}), { minLength: 0, maxLength: 10 }),
|
|
221
|
+
pagination: fc.record({
|
|
222
|
+
page: fc.integer({ min: 1, max: 100 }),
|
|
223
|
+
limit: fc.integer({ min: 1, max: 100 }),
|
|
224
|
+
total: fc.integer({ min: 0, max: 1000 }),
|
|
225
|
+
totalPages: fc.integer({ min: 0, max: 100 }),
|
|
181
226
|
}),
|
|
182
227
|
}),
|
|
183
|
-
async (
|
|
228
|
+
async (responseData) => {
|
|
184
229
|
// Arrange
|
|
185
|
-
|
|
186
|
-
const sdk = new Rooguys(apiKey);
|
|
230
|
+
mockAxios.request.mockResolvedValue(mockAxiosResponse(responseData));
|
|
187
231
|
|
|
188
232
|
// Act
|
|
189
|
-
const result = await
|
|
233
|
+
const result = await client.badges.list();
|
|
190
234
|
|
|
191
235
|
// Assert
|
|
192
|
-
expect(result).
|
|
193
|
-
expect(result.
|
|
194
|
-
|
|
236
|
+
expect(result.badges).toHaveLength(responseData.badges.length);
|
|
237
|
+
expect(result.pagination).toEqual(responseData.pagination);
|
|
238
|
+
|
|
239
|
+
result.badges.forEach((badge, index) => {
|
|
240
|
+
expect(badge.id).toBe(responseData.badges[index].id);
|
|
241
|
+
expect(badge.name).toBe(responseData.badges[index].name);
|
|
242
|
+
expect(badge.description).toBe(responseData.badges[index].description);
|
|
243
|
+
expect(badge.icon_url).toBe(responseData.badges[index].icon_url);
|
|
244
|
+
expect(badge.is_active).toBe(responseData.badges[index].is_active);
|
|
245
|
+
});
|
|
195
246
|
}
|
|
196
247
|
),
|
|
197
248
|
{ numRuns: 100 }
|