@edifice.io/communities-tests 1.0.0-develop-pedago.20250725171105
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/it/scenarios/api-community-oauth.spec.ts +106 -0
- package/it/scenarios/api-community-role-check.spec.ts +868 -0
- package/it/scenarios/api-community.spec.ts +89 -0
- package/it/scenarios/api-invitations-search-sort.spec.ts +858 -0
- package/it/scenarios/api-invitations.spec.ts +582 -0
- package/it/scenarios/api-membership.spec.ts +411 -0
- package/it/scenarios/api-permission-check.spec.ts +424 -0
- package/it/scenarios/api-resources-search-sort.spec.ts +600 -0
- package/it/scenarios/api-resources.spec.ts +183 -0
- package/it/scenarios/utils/_community-api.utils.ts +317 -0
- package/it/scenarios/utils/_community-tests.utils.ts +83 -0
- package/it/scenarios/utils/_invitation-api.utils.ts +453 -0
- package/it/scenarios/utils/_membership-api.utils.ts +184 -0
- package/it/scenarios/utils/_resource-api.utils.ts +292 -0
- package/it/scenarios/utils/_resource-ent.utils.ts +415 -0
- package/it/scenarios/utils/_resource-tests.utils.ts +396 -0
- package/it/scenarios/utils/_role.utils.ts +33 -0
- package/loadtest/index.ts +6 -0
- package/package.json +48 -0
- package/vite.config.ts +23 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { check, group } from "k6";
|
|
2
|
+
// @ts-ignore
|
|
3
|
+
import { chai, describe } from "https://jslib.k6.io/k6chaijs/4.3.4.0/index.js";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
authenticateWeb,
|
|
7
|
+
createAndSetRole,
|
|
8
|
+
initStructure,
|
|
9
|
+
getUsersOfSchool,
|
|
10
|
+
getRandomUserWithProfile,
|
|
11
|
+
Structure,
|
|
12
|
+
logout,
|
|
13
|
+
UserInfo,
|
|
14
|
+
Role,
|
|
15
|
+
linkRoleToUsers,
|
|
16
|
+
getRolesOfStructure,
|
|
17
|
+
} from "../../node_modules/edifice-k6-commons/dist/index.js";
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
createCommunity,
|
|
21
|
+
listCommunities,
|
|
22
|
+
getCommunity,
|
|
23
|
+
getSecretCode,
|
|
24
|
+
getCommunityStats,
|
|
25
|
+
updateCommunity,
|
|
26
|
+
updateWelcomeNote,
|
|
27
|
+
deleteCommunity,
|
|
28
|
+
createCommunityOrFail,
|
|
29
|
+
} from "./utils/_community-api.utils.ts";
|
|
30
|
+
|
|
31
|
+
import {
|
|
32
|
+
listCommunityMembers,
|
|
33
|
+
MembershipRole,
|
|
34
|
+
leaveCommunity,
|
|
35
|
+
removeMember,
|
|
36
|
+
updateMemberRole,
|
|
37
|
+
removeMembers,
|
|
38
|
+
} from "./utils/_membership-api.utils.ts";
|
|
39
|
+
|
|
40
|
+
import {
|
|
41
|
+
createInvitations,
|
|
42
|
+
listCommunityInvitations,
|
|
43
|
+
deleteInvitation,
|
|
44
|
+
deleteInvitationsBatch,
|
|
45
|
+
listMyInvitations,
|
|
46
|
+
updateInvitationStatus,
|
|
47
|
+
InvitationStatus,
|
|
48
|
+
joinCommunity,
|
|
49
|
+
} from "./utils/_invitation-api.utils.ts";
|
|
50
|
+
|
|
51
|
+
import {
|
|
52
|
+
createResource,
|
|
53
|
+
listCommunityResources,
|
|
54
|
+
getResource,
|
|
55
|
+
updateResource,
|
|
56
|
+
deleteResource,
|
|
57
|
+
countResources,
|
|
58
|
+
processMarkedResources,
|
|
59
|
+
ResourceType,
|
|
60
|
+
AppName,
|
|
61
|
+
} from "./utils/_resource-api.utils.ts";
|
|
62
|
+
|
|
63
|
+
interface StructureWithRole extends Structure {
|
|
64
|
+
communityRole?: Role;
|
|
65
|
+
}
|
|
66
|
+
// Add chai configuration
|
|
67
|
+
chai.config.logFailures = true;
|
|
68
|
+
|
|
69
|
+
// Add K6 options configuration
|
|
70
|
+
export const options = {
|
|
71
|
+
setupTimeout: "1h",
|
|
72
|
+
maxRedirects: 0,
|
|
73
|
+
thresholds: {
|
|
74
|
+
checks: ["rate == 1.00"],
|
|
75
|
+
},
|
|
76
|
+
scenarios: {
|
|
77
|
+
permissionGuardTest: {
|
|
78
|
+
exec: "testPermissionGuards",
|
|
79
|
+
executor: "per-vu-iterations",
|
|
80
|
+
vus: 1,
|
|
81
|
+
maxDuration: "1m",
|
|
82
|
+
gracefulStop: "5s",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const schoolName = `IT Community Permissions Tests V2`;
|
|
88
|
+
// Setup function to create structure and configure roles
|
|
89
|
+
export function setup(): StructureWithRole {
|
|
90
|
+
let structure: StructureWithRole = {} as StructureWithRole;
|
|
91
|
+
describe("[Community Permissions] Initialize data", () => {
|
|
92
|
+
authenticateWeb(__ENV.ADMC_LOGIN, __ENV.ADMC_PASSWORD);
|
|
93
|
+
structure = initStructure(schoolName, "tiny");
|
|
94
|
+
|
|
95
|
+
// Create Communities role WITH SPECIFIC PERMISSIONS
|
|
96
|
+
// Change this line to add explicit permissions
|
|
97
|
+
const role = createAndSetRole("Communities");
|
|
98
|
+
|
|
99
|
+
// Store role directly in the structure object
|
|
100
|
+
structure.communityRole = role;
|
|
101
|
+
|
|
102
|
+
logout();
|
|
103
|
+
});
|
|
104
|
+
return structure;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Update the main test function to work similarly to the role-check test
|
|
108
|
+
export function testPermissionGuards(structure: StructureWithRole) {
|
|
109
|
+
let normalUser: UserInfo;
|
|
110
|
+
let noPermissionsUser: UserInfo;
|
|
111
|
+
let communityId: number;
|
|
112
|
+
|
|
113
|
+
describe("[Community API Permission Guards Test]", () => {
|
|
114
|
+
// First authenticate as admin to get users - same approach as in role-check
|
|
115
|
+
authenticateWeb(__ENV.ADMC_LOGIN, __ENV.ADMC_PASSWORD);
|
|
116
|
+
|
|
117
|
+
const users = getUsersOfSchool(structure);
|
|
118
|
+
// Get test users while still authenticated as admin
|
|
119
|
+
normalUser = getRandomUserWithProfile(users, "Teacher");
|
|
120
|
+
noPermissionsUser = getRandomUserWithProfile(users, "Student", [
|
|
121
|
+
normalUser,
|
|
122
|
+
]);
|
|
123
|
+
const roles = getRolesOfStructure(structure.id);
|
|
124
|
+
// Assign Communities role only to normal user
|
|
125
|
+
linkRoleToUsers(
|
|
126
|
+
structure,
|
|
127
|
+
structure.communityRole!,
|
|
128
|
+
roles
|
|
129
|
+
.filter((r: Role) => r.name.includes("Teacher"))
|
|
130
|
+
.map((r: Role) => r.name),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
logout();
|
|
134
|
+
|
|
135
|
+
// We need a test community
|
|
136
|
+
group("Admin creates a test community", () => {
|
|
137
|
+
// Login as admin
|
|
138
|
+
authenticateWeb(__ENV.ADMC_LOGIN, __ENV.ADMC_PASSWORD);
|
|
139
|
+
|
|
140
|
+
// Create a community to test with
|
|
141
|
+
communityId = Number(
|
|
142
|
+
createCommunityOrFail({
|
|
143
|
+
title: "Permission Test Community",
|
|
144
|
+
type: "FREE",
|
|
145
|
+
schoolYearEnd: 2023,
|
|
146
|
+
schoolYearStart: 2023,
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// No logout needed
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Test access with the user who has no permissions
|
|
154
|
+
group("Test API access with user without permissions", () => {
|
|
155
|
+
authenticateWeb(noPermissionsUser.login, "password");
|
|
156
|
+
|
|
157
|
+
// Using utility functions directly with expectations of failure
|
|
158
|
+
const communities = listCommunities();
|
|
159
|
+
check(communities, {
|
|
160
|
+
"List communities fails for user without permissions": (r) =>
|
|
161
|
+
r === null,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Try to create a community
|
|
165
|
+
const communityResponse = createCommunity({
|
|
166
|
+
title: "Should Fail Community",
|
|
167
|
+
type: "FREE",
|
|
168
|
+
schoolYearEnd: 2023,
|
|
169
|
+
schoolYearStart: 2023,
|
|
170
|
+
});
|
|
171
|
+
check(communityResponse, {
|
|
172
|
+
"Create community fails for user without permissions": (r) =>
|
|
173
|
+
r.status === 401 || r.status === 403,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Try to get a community
|
|
177
|
+
const community = getCommunity(communityId);
|
|
178
|
+
check(community, {
|
|
179
|
+
"Get community fails for user without permissions": (r) => r === null,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Try to get secret code
|
|
183
|
+
const secretCode = getSecretCode(communityId);
|
|
184
|
+
check(secretCode, {
|
|
185
|
+
"Get secret code fails for user without permissions": (r) => r === null,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Try to get community stats
|
|
189
|
+
const stats = getCommunityStats(String(communityId));
|
|
190
|
+
check(stats, {
|
|
191
|
+
"Get community stats fails for user without permissions": (r) =>
|
|
192
|
+
r === null,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Try to update a community
|
|
196
|
+
const updatedCommunity = updateCommunity(String(communityId), {
|
|
197
|
+
title: "Updated Title",
|
|
198
|
+
});
|
|
199
|
+
check(updatedCommunity, {
|
|
200
|
+
"Update community fails for user without permissions": (r) =>
|
|
201
|
+
r === null,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Try to update welcome note
|
|
205
|
+
const welcomeNote = updateWelcomeNote(
|
|
206
|
+
String(communityId),
|
|
207
|
+
"New welcome note",
|
|
208
|
+
);
|
|
209
|
+
check(welcomeNote, {
|
|
210
|
+
"Update welcome note fails for user without permissions": (r) =>
|
|
211
|
+
r === null,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Try to list members
|
|
215
|
+
const members = listCommunityMembers(communityId);
|
|
216
|
+
check(members, {
|
|
217
|
+
"List members fails for user without permissions": (r) => r === null,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Try to create invitations
|
|
221
|
+
const invitations = createInvitations(communityId, {
|
|
222
|
+
users: [{ userId: normalUser.id, role: MembershipRole.MEMBER }],
|
|
223
|
+
});
|
|
224
|
+
check(invitations, {
|
|
225
|
+
"Create invitations fails for user without permissions": (r) =>
|
|
226
|
+
r === null,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Try to list invitations
|
|
230
|
+
const invitationsList = listCommunityInvitations(communityId);
|
|
231
|
+
check(invitationsList, {
|
|
232
|
+
"List invitations fails for user without permissions": (r) =>
|
|
233
|
+
r === null,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Try to join a community
|
|
237
|
+
const joinResult = joinCommunity("DUMMY_SECRET_CODE");
|
|
238
|
+
check(joinResult, {
|
|
239
|
+
"Join community fails for user without permissions": (r) => r === null,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Try to leave a community (would fail even if they were a member)
|
|
243
|
+
const leaveResult = leaveCommunity(communityId);
|
|
244
|
+
check(leaveResult, {
|
|
245
|
+
"Leave community fails for user without permissions": (r) => r !== true,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Try to remove a member
|
|
249
|
+
const removeResult = removeMember(communityId, normalUser.id);
|
|
250
|
+
check(removeResult, {
|
|
251
|
+
"Remove member fails for user without permissions": (r) => r !== true,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Try to remove members in batch
|
|
255
|
+
const removeMembersResult = removeMembers(communityId, [normalUser.id]);
|
|
256
|
+
check(removeMembersResult, {
|
|
257
|
+
"Remove members in batch fails for user without permissions": (r) =>
|
|
258
|
+
r === null,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Try to update a member's role
|
|
262
|
+
const roleUpdateResult = updateMemberRole(
|
|
263
|
+
communityId,
|
|
264
|
+
normalUser.id,
|
|
265
|
+
MembershipRole.ADMIN,
|
|
266
|
+
);
|
|
267
|
+
check(roleUpdateResult, {
|
|
268
|
+
"Update member role fails for user without permissions": (r) =>
|
|
269
|
+
r === null,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// Try to delete an invitation (using a dummy ID)
|
|
273
|
+
const deleteInvResult = deleteInvitation(communityId, 9999);
|
|
274
|
+
check(deleteInvResult, {
|
|
275
|
+
"Delete invitation fails for user without permissions": (r) =>
|
|
276
|
+
r !== true,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Try to delete invitations in batch
|
|
280
|
+
const batchDeleteResult = deleteInvitationsBatch(communityId, [9999]);
|
|
281
|
+
check(batchDeleteResult, {
|
|
282
|
+
"Batch delete invitations fails for user without permissions": (r) =>
|
|
283
|
+
r !== true,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Try to list personal invitations
|
|
287
|
+
// This might actually succeed as it's a personal endpoint, but include for completeness
|
|
288
|
+
const myInvitations = listMyInvitations();
|
|
289
|
+
check(myInvitations, {
|
|
290
|
+
"List personal invitations result is consistent with permissions": (
|
|
291
|
+
r,
|
|
292
|
+
) => r === null || r.items.length > 0,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Try to update an invitation status (using a dummy ID)
|
|
296
|
+
const updateStatusResult = updateInvitationStatus(
|
|
297
|
+
9999,
|
|
298
|
+
InvitationStatus.ACCEPTED,
|
|
299
|
+
);
|
|
300
|
+
check(updateStatusResult, {
|
|
301
|
+
"Update invitation status fails for user without permissions": (r) =>
|
|
302
|
+
r === null,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// Try to list resources
|
|
306
|
+
const resources = listCommunityResources(communityId);
|
|
307
|
+
check(resources, {
|
|
308
|
+
"List resources fails for user without permissions": (r) => r === null,
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Try to get resource count
|
|
312
|
+
const resourceCount = countResources(communityId);
|
|
313
|
+
check(resourceCount, {
|
|
314
|
+
"Count resources fails for user without permissions": (r) => r === null,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Try to create a resource
|
|
318
|
+
const newResource = createResource(communityId, {
|
|
319
|
+
type: ResourceType.ENT,
|
|
320
|
+
appName: AppName.EXTERNAL_LINK,
|
|
321
|
+
title: "Test Resource",
|
|
322
|
+
resourceUrl: "https://example.com",
|
|
323
|
+
openInNewTab: true,
|
|
324
|
+
});
|
|
325
|
+
check(newResource, {
|
|
326
|
+
"Create resource fails for user without permissions": (r) => r === null,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Try to get a resource (using dummy ID)
|
|
330
|
+
const resource = getResource(communityId, 9999);
|
|
331
|
+
check(resource, {
|
|
332
|
+
"Get resource fails for user without permissions": (r) => r === null,
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Try to update a resource (using dummy ID)
|
|
336
|
+
const updatedResource = updateResource(communityId, 9999, {
|
|
337
|
+
title: "Updated Title",
|
|
338
|
+
});
|
|
339
|
+
check(updatedResource, {
|
|
340
|
+
"Update resource fails for user without permissions": (r) => r === null,
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Try to delete a resource (using dummy ID)
|
|
344
|
+
const deleteResult = deleteResource(communityId, 9999);
|
|
345
|
+
check(deleteResult, {
|
|
346
|
+
"Delete resource fails for user without permissions": (r) =>
|
|
347
|
+
r === false,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Try to access ADMC-only endpoint for cleanup
|
|
351
|
+
const cleanupResult = processMarkedResources(communityId);
|
|
352
|
+
check(cleanupResult, {
|
|
353
|
+
"Resource cleanup fails for user without permissions": (r) =>
|
|
354
|
+
r === null,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
logout();
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Test ADMC-specific features
|
|
361
|
+
group("Test ADMC-specific resource operations", () => {
|
|
362
|
+
authenticateWeb(__ENV.ADMC_LOGIN, __ENV.ADMC_PASSWORD);
|
|
363
|
+
|
|
364
|
+
// Full flow - Create community, add resource, mark for deletion, cleanup, delete community
|
|
365
|
+
const testCommunityId = Number(
|
|
366
|
+
createCommunityOrFail({
|
|
367
|
+
title: "ADMC Cleanup Test Community",
|
|
368
|
+
type: "FREE",
|
|
369
|
+
schoolYearStart: 2025,
|
|
370
|
+
schoolYearEnd: 2026,
|
|
371
|
+
}),
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
console.log(
|
|
375
|
+
`Created test community for ADMC cleanup test with ID: ${testCommunityId}`,
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
// Create a test resource
|
|
379
|
+
const testResource = createResource(testCommunityId, {
|
|
380
|
+
type: ResourceType.ENT,
|
|
381
|
+
appName: AppName.EXTERNAL_LINK,
|
|
382
|
+
title: "Resource for Cleanup Test",
|
|
383
|
+
resourceUrl: "https://example.com/cleanup-test",
|
|
384
|
+
openInNewTab: true,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
check(testResource, {
|
|
388
|
+
"ADMC created resource for cleanup test": (r) => r !== null,
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// In a real scenario, we would mark the resource for deletion here
|
|
392
|
+
// Since we can't directly call the API to mark a resource for deletion in this test,
|
|
393
|
+
// we'll just test the cleanup endpoint functionality
|
|
394
|
+
|
|
395
|
+
// Run cleanup on the test community
|
|
396
|
+
const testCleanupResult = processMarkedResources(testCommunityId);
|
|
397
|
+
check(testCleanupResult, {
|
|
398
|
+
"ADMC can run cleanup on test community": (r) => r !== null,
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Delete the test community
|
|
402
|
+
const deleteTestCommunity = deleteCommunity(testCommunityId);
|
|
403
|
+
check(deleteTestCommunity, {
|
|
404
|
+
"ADMC successfully deleted test community": (r) => r === true,
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
logout();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// Cleanup
|
|
411
|
+
group("Cleanup test data", () => {
|
|
412
|
+
authenticateWeb(__ENV.ADMC_LOGIN, __ENV.ADMC_PASSWORD);
|
|
413
|
+
|
|
414
|
+
if (communityId) {
|
|
415
|
+
const deleted = deleteCommunity(communityId);
|
|
416
|
+
check(deleted, {
|
|
417
|
+
"Test community was deleted": (r) => r === true,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
logout();
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
}
|