@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.
@@ -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
+ }