@rooguys/js 0.1.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 +277 -0
- package/jest.config.js +22 -0
- package/package.json +38 -0
- package/src/__tests__/fixtures/responses.js +330 -0
- package/src/__tests__/property/request-construction.property.test.js +211 -0
- package/src/__tests__/property/response-parsing.property.test.js +217 -0
- package/src/__tests__/setup.js +25 -0
- package/src/__tests__/unit/aha.test.js +191 -0
- package/src/__tests__/unit/badges.test.js +68 -0
- package/src/__tests__/unit/config.test.js +97 -0
- package/src/__tests__/unit/errors.test.js +141 -0
- package/src/__tests__/unit/events.test.js +223 -0
- package/src/__tests__/unit/leaderboards.test.js +113 -0
- package/src/__tests__/unit/levels.test.js +70 -0
- package/src/__tests__/unit/questionnaires.test.js +68 -0
- package/src/__tests__/unit/users.test.js +102 -0
- package/src/__tests__/utils/generators.js +80 -0
- package/src/__tests__/utils/mockClient.js +42 -0
- package/src/index.js +138 -0
package/README.md
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Rooguys Javascript SDK
|
|
2
|
+
|
|
3
|
+
The official Browser SDK for the Rooguys Gamification API. Lightweight and dependency-free (uses native `fetch`).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### NPM
|
|
8
|
+
```bash
|
|
9
|
+
npm install @rooguys/js
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### Direct Import (ES Modules)
|
|
13
|
+
```javascript
|
|
14
|
+
import Rooguys from './path/to/sdk/index.js';
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Initialization
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
import Rooguys from '@rooguys/js';
|
|
21
|
+
|
|
22
|
+
const client = new Rooguys('YOUR_API_KEY', {
|
|
23
|
+
baseUrl: 'https://api.rooguys.com/v1',
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage Examples
|
|
28
|
+
|
|
29
|
+
### 1. Track an Event
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
client.events.track('level_completed', 'user_123', { difficulty: 'hard' })
|
|
33
|
+
.then(response => {
|
|
34
|
+
console.log('Event tracked:', response);
|
|
35
|
+
})
|
|
36
|
+
.catch(console.error);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 2. Get User Profile
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
async function showProfile(userId) {
|
|
43
|
+
try {
|
|
44
|
+
const user = await client.users.get(userId);
|
|
45
|
+
document.getElementById('points').innerText = user.points;
|
|
46
|
+
document.getElementById('level').innerText = user.level?.name || 'No Level';
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error('Failed to load profile:', error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 3. Display Leaderboard
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
async function loadLeaderboard() {
|
|
57
|
+
const data = await client.leaderboards.getGlobal('weekly');
|
|
58
|
+
|
|
59
|
+
const list = document.getElementById('leaderboard');
|
|
60
|
+
list.innerHTML = data.rankings
|
|
61
|
+
.map(r => `<li>#${r.rank} ${r.user_id}: ${r.points}</li>`)
|
|
62
|
+
.join('');
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 4. List Custom Leaderboards
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
// Get all available leaderboards
|
|
70
|
+
const leaderboards = await client.leaderboards.list(1, 20);
|
|
71
|
+
|
|
72
|
+
leaderboards.data.forEach(lb => {
|
|
73
|
+
console.log(`${lb.name}: ${lb.description}`);
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 5. Get Custom Leaderboard Rankings
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
// Get rankings for a specific leaderboard
|
|
81
|
+
const customLb = await client.leaderboards.getCustom('leaderboard_id', 1, 10);
|
|
82
|
+
|
|
83
|
+
console.log(`Leaderboard: ${customLb.name}`);
|
|
84
|
+
customLb.rankings.forEach(entry => {
|
|
85
|
+
console.log(`#${entry.rank} - ${entry.user_id}: ${entry.points} pts`);
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 6. Get User Rank in Custom Leaderboard
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
const rank = await client.leaderboards.getUserRank('leaderboard_id', 'user_123');
|
|
93
|
+
|
|
94
|
+
console.log(`User rank: #${rank.rank} out of ${rank.total_users}`);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 7. List Badges
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
// Get all active badges
|
|
101
|
+
const badges = await client.badges.list(1, 50, true);
|
|
102
|
+
|
|
103
|
+
badges.data.forEach(badge => {
|
|
104
|
+
console.log(`${badge.name}: ${badge.description}`);
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 8. List Levels
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
const levels = await client.levels.list();
|
|
112
|
+
|
|
113
|
+
levels.data.forEach(level => {
|
|
114
|
+
console.log(`Level ${level.level_number}: ${level.name} (${level.points_required} pts)`);
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 9. Get Questionnaire
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
// Get questionnaire by slug
|
|
122
|
+
const questionnaire = await client.questionnaires.get('onboarding-survey');
|
|
123
|
+
|
|
124
|
+
console.log(`Title: ${questionnaire.title}`);
|
|
125
|
+
questionnaire.questions.forEach(q => {
|
|
126
|
+
console.log(`Q: ${q.text}`);
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 10. Get Active Questionnaire
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
const activeQuestionnaire = await client.questionnaires.getActive();
|
|
134
|
+
|
|
135
|
+
if (activeQuestionnaire) {
|
|
136
|
+
console.log(`Active: ${activeQuestionnaire.title}`);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 11. Aha Score - Declare User Activation
|
|
141
|
+
|
|
142
|
+
Track when users reach their "Aha Moment" with declarative scores (1-5).
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
// Declare that a user has reached an activation milestone
|
|
146
|
+
const result = await client.aha.declare('user_123', 4);
|
|
147
|
+
|
|
148
|
+
console.log(result.message); // "Aha score declared successfully"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 12. Aha Score - Get User Score
|
|
152
|
+
|
|
153
|
+
Retrieve a user's Aha Score, including declarative and inferred scores.
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
const ahaScore = await client.aha.getUserScore('user_123');
|
|
157
|
+
|
|
158
|
+
console.log(`Current Score: ${ahaScore.data.current_score}`);
|
|
159
|
+
console.log(`Status: ${ahaScore.data.status}`); // 'not_started', 'progressing', or 'activated'
|
|
160
|
+
console.log(`Declarative Score: ${ahaScore.data.declarative_score}`);
|
|
161
|
+
console.log(`Inferred Score: ${ahaScore.data.inferred_score}`);
|
|
162
|
+
|
|
163
|
+
// Access history
|
|
164
|
+
if (ahaScore.data.history.initial) {
|
|
165
|
+
console.log(`Initial Score: ${ahaScore.data.history.initial}`);
|
|
166
|
+
console.log(`Initial Date: ${ahaScore.data.history.initial_date}`);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## API Reference
|
|
171
|
+
|
|
172
|
+
### Events
|
|
173
|
+
|
|
174
|
+
- `track(eventName, userId, properties, options)` - Track a user event
|
|
175
|
+
|
|
176
|
+
### Users
|
|
177
|
+
|
|
178
|
+
- `get(userId)` - Get user profile
|
|
179
|
+
- `getBulk(userIds)` - Get multiple user profiles
|
|
180
|
+
- `getBadges(userId)` - Get user's badges
|
|
181
|
+
- `getRank(userId, timeframe)` - Get user's rank in global leaderboard
|
|
182
|
+
- `submitAnswers(userId, questionnaireId, answers)` - Submit questionnaire answers
|
|
183
|
+
|
|
184
|
+
### Leaderboards
|
|
185
|
+
|
|
186
|
+
- `getGlobal(timeframe, page, limit)` - Get global leaderboard rankings
|
|
187
|
+
- `list(page, limit, search)` - List all available leaderboards
|
|
188
|
+
- `getCustom(leaderboardId, page, limit, search)` - Get custom leaderboard rankings
|
|
189
|
+
- `getUserRank(leaderboardId, userId)` - Get user's rank in a custom leaderboard
|
|
190
|
+
|
|
191
|
+
### Badges
|
|
192
|
+
|
|
193
|
+
- `list(page, limit, activeOnly)` - List all badges
|
|
194
|
+
|
|
195
|
+
### Levels
|
|
196
|
+
|
|
197
|
+
- `list(page, limit)` - List all levels
|
|
198
|
+
|
|
199
|
+
### Questionnaires
|
|
200
|
+
|
|
201
|
+
- `get(slug)` - Get questionnaire by slug
|
|
202
|
+
- `getActive()` - Get the currently active questionnaire
|
|
203
|
+
|
|
204
|
+
### Aha Score
|
|
205
|
+
|
|
206
|
+
- `declare(userId, value)` - Declare a user's Aha Moment score (value must be between 1-5)
|
|
207
|
+
- `getUserScore(userId)` - Retrieve a user's current Aha Score and history
|
|
208
|
+
|
|
209
|
+
## Error Handling
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
try {
|
|
213
|
+
await client.users.get('unknown_user');
|
|
214
|
+
} catch (error) {
|
|
215
|
+
console.error('API Error:', error.message);
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Validation Errors
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
try {
|
|
223
|
+
// Invalid Aha Score value (must be 1-5)
|
|
224
|
+
await client.aha.declare('user_123', 10);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error(error.message); // "Aha score value must be between 1 and 5"
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Testing
|
|
231
|
+
|
|
232
|
+
The SDK includes comprehensive test coverage with both unit tests and property-based tests.
|
|
233
|
+
|
|
234
|
+
### Running Tests
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
npm test
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Test Coverage
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
npm run test:coverage
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The SDK maintains >90% test coverage across all modules, including:
|
|
247
|
+
- Unit tests for all API methods
|
|
248
|
+
- Property-based tests using fast-check
|
|
249
|
+
- Error handling and edge case validation
|
|
250
|
+
- Concurrent request handling
|
|
251
|
+
|
|
252
|
+
### Property-Based Testing
|
|
253
|
+
|
|
254
|
+
The SDK uses [fast-check](https://github.com/dubzzz/fast-check) for property-based testing to verify correctness across a wide range of inputs:
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
// Example: Verifying response parsing preservation
|
|
258
|
+
fc.assert(
|
|
259
|
+
fc.property(
|
|
260
|
+
fc.jsonValue(),
|
|
261
|
+
async (responseData) => {
|
|
262
|
+
// Test that any valid JSON response is parsed correctly
|
|
263
|
+
// and data structure is preserved
|
|
264
|
+
}
|
|
265
|
+
),
|
|
266
|
+
{ numRuns: 100 }
|
|
267
|
+
);
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Requirements
|
|
271
|
+
|
|
272
|
+
This SDK requires a browser environment with support for:
|
|
273
|
+
- `fetch` API
|
|
274
|
+
- `Promise`
|
|
275
|
+
- `AbortController` (for timeouts)
|
|
276
|
+
|
|
277
|
+
Most modern browsers support these features.
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
testEnvironment: 'jsdom',
|
|
3
|
+
roots: ['<rootDir>/src'],
|
|
4
|
+
testMatch: ['**/__tests__/**/*.test.js'],
|
|
5
|
+
transform: {},
|
|
6
|
+
collectCoverageFrom: [
|
|
7
|
+
'src/**/*.js',
|
|
8
|
+
'!src/**/*.test.js',
|
|
9
|
+
'!src/**/__tests__/**',
|
|
10
|
+
],
|
|
11
|
+
coverageThreshold: {
|
|
12
|
+
global: {
|
|
13
|
+
lines: 90,
|
|
14
|
+
branches: 85,
|
|
15
|
+
functions: 90,
|
|
16
|
+
statements: 90,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
coverageDirectory: 'coverage',
|
|
20
|
+
coverageReporters: ['text', 'lcov', 'html'],
|
|
21
|
+
setupFilesAfterEnv: ['<rootDir>/src/__tests__/setup.js'],
|
|
22
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rooguys/js",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official Browser SDK for Rooguys API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"module": "src/index.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
10
|
+
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch",
|
|
11
|
+
"test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --coverage"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"rooguys",
|
|
15
|
+
"sdk",
|
|
16
|
+
"browser",
|
|
17
|
+
"gamification"
|
|
18
|
+
],
|
|
19
|
+
"author": "mowamed",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/jest": "^29.5.0",
|
|
26
|
+
"fast-check": "^3.15.0",
|
|
27
|
+
"jest": "^29.7.0",
|
|
28
|
+
"jest-environment-jsdom": "^29.7.0"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/Rooguys/rooguys-browser-sdk.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/Rooguys/rooguys-browser-sdk/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/Rooguys/rooguys-browser-sdk#readme"
|
|
38
|
+
}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock API response fixtures for testing
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const mockResponses = {
|
|
6
|
+
userProfile: {
|
|
7
|
+
user_id: 'user123',
|
|
8
|
+
points: 100,
|
|
9
|
+
persona: 'Achiever',
|
|
10
|
+
level: {
|
|
11
|
+
id: 'level1',
|
|
12
|
+
name: 'Bronze',
|
|
13
|
+
level_number: 1,
|
|
14
|
+
points_required: 0,
|
|
15
|
+
},
|
|
16
|
+
next_level: {
|
|
17
|
+
id: 'level2',
|
|
18
|
+
name: 'Silver',
|
|
19
|
+
level_number: 2,
|
|
20
|
+
points_required: 500,
|
|
21
|
+
points_needed: 400,
|
|
22
|
+
},
|
|
23
|
+
metrics: {
|
|
24
|
+
logins: 10,
|
|
25
|
+
purchases: 2,
|
|
26
|
+
},
|
|
27
|
+
badges: [
|
|
28
|
+
{
|
|
29
|
+
id: 'badge1',
|
|
30
|
+
name: 'First Steps',
|
|
31
|
+
description: 'Completed first action',
|
|
32
|
+
icon_url: 'https://example.com/badge1.png',
|
|
33
|
+
earned_at: '2024-01-01T00:00:00Z',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
trackEventResponse: {
|
|
39
|
+
status: 'queued',
|
|
40
|
+
message: 'Event accepted for processing',
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
trackEventWithProfileResponse: {
|
|
44
|
+
status: 'queued',
|
|
45
|
+
message: 'Event accepted for processing',
|
|
46
|
+
profile: {
|
|
47
|
+
user_id: 'user123',
|
|
48
|
+
points: 110,
|
|
49
|
+
persona: 'Achiever',
|
|
50
|
+
level: {
|
|
51
|
+
id: 'level1',
|
|
52
|
+
name: 'Bronze',
|
|
53
|
+
level_number: 1,
|
|
54
|
+
points_required: 0,
|
|
55
|
+
},
|
|
56
|
+
next_level: {
|
|
57
|
+
id: 'level2',
|
|
58
|
+
name: 'Silver',
|
|
59
|
+
level_number: 2,
|
|
60
|
+
points_required: 500,
|
|
61
|
+
points_needed: 390,
|
|
62
|
+
},
|
|
63
|
+
metrics: {},
|
|
64
|
+
badges: [],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
leaderboardResponse: {
|
|
69
|
+
timeframe: 'all-time',
|
|
70
|
+
page: 1,
|
|
71
|
+
limit: 50,
|
|
72
|
+
total: 100,
|
|
73
|
+
rankings: [
|
|
74
|
+
{
|
|
75
|
+
rank: 1,
|
|
76
|
+
user_id: 'user1',
|
|
77
|
+
points: 1000,
|
|
78
|
+
level: {
|
|
79
|
+
id: 'level3',
|
|
80
|
+
name: 'Gold',
|
|
81
|
+
level_number: 3,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
rank: 2,
|
|
86
|
+
user_id: 'user2',
|
|
87
|
+
points: 900,
|
|
88
|
+
level: {
|
|
89
|
+
id: 'level2',
|
|
90
|
+
name: 'Silver',
|
|
91
|
+
level_number: 2,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
userRankResponse: {
|
|
98
|
+
user_id: 'user123',
|
|
99
|
+
rank: 42,
|
|
100
|
+
points: 850,
|
|
101
|
+
total_users: 1000,
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
badgesListResponse: {
|
|
105
|
+
badges: [
|
|
106
|
+
{
|
|
107
|
+
id: 'badge1',
|
|
108
|
+
name: 'First Steps',
|
|
109
|
+
description: 'Completed first action',
|
|
110
|
+
icon_url: 'https://example.com/badge1.png',
|
|
111
|
+
is_active: true,
|
|
112
|
+
unlock_criteria: {
|
|
113
|
+
metric: 'actions',
|
|
114
|
+
value: 1,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
pagination: {
|
|
119
|
+
page: 1,
|
|
120
|
+
limit: 50,
|
|
121
|
+
total: 10,
|
|
122
|
+
totalPages: 1,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
levelsListResponse: {
|
|
127
|
+
levels: [
|
|
128
|
+
{
|
|
129
|
+
id: 'level1',
|
|
130
|
+
name: 'Bronze',
|
|
131
|
+
level_number: 1,
|
|
132
|
+
points_required: 0,
|
|
133
|
+
description: 'Starting level',
|
|
134
|
+
icon_url: 'https://example.com/bronze.png',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'level2',
|
|
138
|
+
name: 'Silver',
|
|
139
|
+
level_number: 2,
|
|
140
|
+
points_required: 500,
|
|
141
|
+
description: 'Intermediate level',
|
|
142
|
+
icon_url: 'https://example.com/silver.png',
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
pagination: {
|
|
146
|
+
page: 1,
|
|
147
|
+
limit: 50,
|
|
148
|
+
total: 5,
|
|
149
|
+
totalPages: 1,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
questionnaireResponse: {
|
|
154
|
+
id: 'questionnaire1',
|
|
155
|
+
slug: 'user-persona',
|
|
156
|
+
title: 'User Persona Quiz',
|
|
157
|
+
description: 'Determine your user persona',
|
|
158
|
+
is_active: true,
|
|
159
|
+
questions: [
|
|
160
|
+
{
|
|
161
|
+
id: 'q1',
|
|
162
|
+
text: 'What motivates you?',
|
|
163
|
+
order: 1,
|
|
164
|
+
answer_options: [
|
|
165
|
+
{
|
|
166
|
+
id: 'a1',
|
|
167
|
+
text: 'Competition',
|
|
168
|
+
persona_weights: { Competitor: 1 },
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: 'a2',
|
|
172
|
+
text: 'Exploration',
|
|
173
|
+
persona_weights: { Explorer: 1 },
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
bulkUsersResponse: {
|
|
181
|
+
users: [
|
|
182
|
+
{
|
|
183
|
+
user_id: 'user1',
|
|
184
|
+
points: 100,
|
|
185
|
+
persona: null,
|
|
186
|
+
level: null,
|
|
187
|
+
next_level: null,
|
|
188
|
+
metrics: {},
|
|
189
|
+
badges: [],
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
user_id: 'user2',
|
|
193
|
+
points: 200,
|
|
194
|
+
persona: 'Achiever',
|
|
195
|
+
level: {
|
|
196
|
+
id: 'level1',
|
|
197
|
+
name: 'Bronze',
|
|
198
|
+
level_number: 1,
|
|
199
|
+
points_required: 0,
|
|
200
|
+
},
|
|
201
|
+
next_level: null,
|
|
202
|
+
metrics: {},
|
|
203
|
+
badges: [],
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
ahaScoreResponse: {
|
|
209
|
+
success: true,
|
|
210
|
+
data: {
|
|
211
|
+
user_id: 'user123',
|
|
212
|
+
current_score: 75,
|
|
213
|
+
declarative_score: 80,
|
|
214
|
+
inferred_score: 70,
|
|
215
|
+
status: 'activated',
|
|
216
|
+
history: {
|
|
217
|
+
initial: 50,
|
|
218
|
+
initial_date: '2024-01-01T00:00:00Z',
|
|
219
|
+
previous: 70,
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
ahaDeclarationResponse: {
|
|
225
|
+
success: true,
|
|
226
|
+
message: 'Declarative score recorded. Calculation in progress.',
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
answerSubmissionResponse: {
|
|
230
|
+
status: 'accepted',
|
|
231
|
+
message: 'Answers queued for processing',
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
leaderboardsListResponse: {
|
|
235
|
+
leaderboards: [
|
|
236
|
+
{
|
|
237
|
+
id: 'lb1',
|
|
238
|
+
name: 'Top Players',
|
|
239
|
+
description: 'Overall top players',
|
|
240
|
+
type: 'POINTS',
|
|
241
|
+
timeframe: 'all-time',
|
|
242
|
+
is_active: true,
|
|
243
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
pagination: {
|
|
247
|
+
page: 1,
|
|
248
|
+
limit: 50,
|
|
249
|
+
total: 5,
|
|
250
|
+
totalPages: 1,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
customLeaderboardResponse: {
|
|
255
|
+
leaderboard: {
|
|
256
|
+
id: 'lb1',
|
|
257
|
+
name: 'Top Players',
|
|
258
|
+
description: 'Overall top players',
|
|
259
|
+
type: 'POINTS',
|
|
260
|
+
timeframe: 'all-time',
|
|
261
|
+
is_active: true,
|
|
262
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
263
|
+
},
|
|
264
|
+
rankings: [
|
|
265
|
+
{
|
|
266
|
+
rank: 1,
|
|
267
|
+
user_id: 'user1',
|
|
268
|
+
points: 1000,
|
|
269
|
+
level: null,
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
pagination: {
|
|
273
|
+
page: 1,
|
|
274
|
+
limit: 50,
|
|
275
|
+
total: 10,
|
|
276
|
+
totalPages: 1,
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
export const mockErrors = {
|
|
282
|
+
validationError: {
|
|
283
|
+
error: 'Validation failed',
|
|
284
|
+
details: [
|
|
285
|
+
{
|
|
286
|
+
field: 'user_id',
|
|
287
|
+
message: 'User ID is required',
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
notFoundError: {
|
|
293
|
+
error: 'User not found',
|
|
294
|
+
message: "User 'user123' does not exist in this project",
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
serverError: {
|
|
298
|
+
error: 'Internal server error',
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
queueFullError: {
|
|
302
|
+
error: 'Service unavailable',
|
|
303
|
+
message: 'Event queue is full. Please retry later.',
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
unauthorizedError: {
|
|
307
|
+
error: 'Unauthorized',
|
|
308
|
+
message: 'Invalid or missing API key',
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
invalidTimeframeError: {
|
|
312
|
+
error: 'Invalid timeframe',
|
|
313
|
+
message: "Timeframe must be one of: all-time, weekly, monthly",
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
invalidPaginationError: {
|
|
317
|
+
error: 'Invalid limit parameter',
|
|
318
|
+
message: 'Limit must be between 1 and 100',
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
ahaValueError: {
|
|
322
|
+
error: 'Validation failed',
|
|
323
|
+
details: [
|
|
324
|
+
{
|
|
325
|
+
field: 'value',
|
|
326
|
+
message: 'value must be an integer between 1 and 5',
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
},
|
|
330
|
+
};
|