@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,396 @@
1
+ import { check, group, fail } from "k6";
2
+
3
+ import {
4
+ authenticateWeb,
5
+ logout,
6
+ UserInfo,
7
+ } from "../../../node_modules/edifice-k6-commons/dist/index.js";
8
+
9
+ import {
10
+ createResource,
11
+ listCommunityResources,
12
+ getResource,
13
+ updateResource,
14
+ deleteResource,
15
+ countResources,
16
+ ResourceType,
17
+ AppName,
18
+ } from "./_resource-api.utils.ts";
19
+ import { createRealEntResource } from "./_resource-ent.utils.ts";
20
+
21
+ /**
22
+ * Configuration options for resource tests
23
+ */
24
+ export interface ResourceTestConfig {
25
+ appName: AppName; // App name to test (EXTERNAL_LINK, WORKSPACE, etc.)
26
+ resourceType: ResourceType; // Resource type (ENT, IMAGE, VIDEO, etc.)
27
+ sampleResourceEntId?: string; // Sample resource ID (required for non-external-link resources)
28
+ sampleUrl?: string; // Sample URL (required for external links)
29
+ sampleTitle?: string; // Sample title
30
+ sampleUpdatedTitle?: string; // Sample updated title for testing updates
31
+ sampleUpdatedUrl?: string; // Sample updated URL for external links
32
+ }
33
+
34
+ /**
35
+ * Default configurations for different app types
36
+ */
37
+ export const DEFAULT_TEST_CONFIGS: Record<AppName, ResourceTestConfig> = {
38
+ [AppName.EXTERNAL_LINK]: {
39
+ appName: AppName.EXTERNAL_LINK,
40
+ resourceType: ResourceType.EXTERNAL_LINK,
41
+ sampleUrl: "https://example.com/test",
42
+ sampleTitle: "Test External Link",
43
+ sampleUpdatedTitle: "Updated External Link",
44
+ sampleUpdatedUrl: "https://example.com/updated",
45
+ },
46
+ [AppName.WORKSPACE]: {
47
+ appName: AppName.WORKSPACE,
48
+ resourceType: ResourceType.ENT,
49
+ sampleResourceEntId: "workspace-document-123",
50
+ sampleTitle: "Test Workspace Document",
51
+ sampleUpdatedTitle: "Updated Workspace Document",
52
+ },
53
+ [AppName.BLOG]: {
54
+ appName: AppName.BLOG,
55
+ resourceType: ResourceType.ENT,
56
+ sampleResourceEntId: "blog-post-123",
57
+ sampleTitle: "Test Blog Post",
58
+ sampleUpdatedTitle: "Updated Blog Post",
59
+ },
60
+ [AppName.WIKI]: {
61
+ appName: AppName.WIKI,
62
+ resourceType: ResourceType.ENT,
63
+ sampleResourceEntId: "wiki-page-123",
64
+ sampleTitle: "Test Wiki Page",
65
+ sampleUpdatedTitle: "Updated Wiki Page",
66
+ },
67
+ [AppName.EXERCIZER]: {
68
+ appName: AppName.EXERCIZER,
69
+ resourceType: ResourceType.ENT,
70
+ sampleResourceEntId: "exercizer-123",
71
+ sampleTitle: "Test Exercizer",
72
+ sampleUpdatedTitle: "Updated Exercizer",
73
+ },
74
+ [AppName.SCRAPBOOK]: {
75
+ appName: AppName.SCRAPBOOK,
76
+ resourceType: ResourceType.ENT,
77
+ sampleResourceEntId: "scrapbook-123",
78
+ sampleTitle: "Test Scrapbook",
79
+ sampleUpdatedTitle: "Updated Scrapbook",
80
+ },
81
+ [AppName.MINDMAP]: {
82
+ appName: AppName.MINDMAP,
83
+ resourceType: ResourceType.ENT,
84
+ sampleResourceEntId: "mindmap-123",
85
+ sampleTitle: "Test Mindmap",
86
+ sampleUpdatedTitle: "Updated Mindmap",
87
+ },
88
+ [AppName.FORM]: {
89
+ appName: AppName.FORM,
90
+ resourceType: ResourceType.ENT,
91
+ sampleResourceEntId: "form-123",
92
+ sampleTitle: "Test Form",
93
+ sampleUpdatedTitle: "Updated Form",
94
+ },
95
+ [AppName.PAD]: {
96
+ appName: AppName.PAD,
97
+ resourceType: ResourceType.ENT,
98
+ sampleResourceEntId: "pad-123",
99
+ sampleTitle: "Test Pad",
100
+ sampleUpdatedTitle: "Updated Pad",
101
+ },
102
+ [AppName.COLLABORATIVEWALL]: {
103
+ appName: AppName.COLLABORATIVEWALL,
104
+ resourceType: ResourceType.ENT,
105
+ sampleResourceEntId: "wall-123",
106
+ sampleTitle: "Test Collaborative Wall",
107
+ sampleUpdatedTitle: "Updated Collaborative Wall",
108
+ },
109
+ [AppName.TIMELINEGENERATOR]: {
110
+ appName: AppName.TIMELINEGENERATOR,
111
+ resourceType: ResourceType.ENT,
112
+ sampleResourceEntId: "timeline-123",
113
+ sampleTitle: "Test Timeline",
114
+ sampleUpdatedTitle: "Updated Timeline",
115
+ },
116
+ [AppName.COLLABORATIVEDITOR]: {
117
+ appName: AppName.COLLABORATIVEDITOR,
118
+ resourceType: ResourceType.ENT,
119
+ sampleResourceEntId: "collaborative-editor-123",
120
+ sampleTitle: "Test Collaborative Editor",
121
+ sampleUpdatedTitle: "Updated Collaborative Editor",
122
+ },
123
+ };
124
+
125
+ /**
126
+ * Runs comprehensive tests for resource management with a specific app type
127
+ *
128
+ * @param communityId ID of the community to test in
129
+ * @param config Resource test configuration
130
+ * @param adminUser Admin user for testing admin operations
131
+ * @param memberUser Regular user for testing permission restrictions
132
+ * @returns Resource ID created during the test
133
+ */
134
+ export function testResourceManagement(
135
+ communityId: number,
136
+ config: ResourceTestConfig,
137
+ adminUser: UserInfo,
138
+ memberUser: UserInfo,
139
+ ): number | null {
140
+ let resourceId: number | null = null;
141
+
142
+ // Prepare update data based on app type
143
+ const updateData: Record<string, any> = {
144
+ title: config.sampleUpdatedTitle,
145
+ };
146
+
147
+ if (config.appName === AppName.EXTERNAL_LINK && config.sampleUpdatedUrl) {
148
+ updateData.resourceUrl = config.sampleUpdatedUrl;
149
+ updateData.openInNewTab = false;
150
+ }
151
+
152
+ // Test as admin user
153
+ group(`Admin creates and manages ${config.appName} resources`, () => {
154
+ const adminAuthenticated = authenticateWeb(adminUser.login, "password");
155
+ if (!adminAuthenticated) {
156
+ fail("Admin authentication failed");
157
+ }
158
+ // Create a resource entity for ENT apps
159
+ const resourceEntId = createRealEntResource(config.appName, {
160
+ title: config.sampleTitle,
161
+ });
162
+ check(resourceEntId, {
163
+ [`[${config.appName}] resourceId created successfully`]: (id) =>
164
+ id !== null,
165
+ });
166
+ // Assign the resourceEntId to the config for later use
167
+ config.sampleResourceEntId = resourceEntId!;
168
+
169
+ // Prepare create data based on app type
170
+ const createData = {
171
+ type: config.resourceType,
172
+ appName: config.appName,
173
+ title: config.sampleTitle,
174
+ };
175
+
176
+ // Add specific required fields based on app type
177
+ if (config.appName === AppName.EXTERNAL_LINK) {
178
+ Object.assign(createData, {
179
+ resourceUrl: config.sampleUrl,
180
+ openInNewTab: true,
181
+ });
182
+ } else {
183
+ Object.assign(createData, {
184
+ resourceEntId: config.sampleResourceEntId,
185
+ });
186
+ }
187
+ // Create another resource for deletion
188
+ const deleteTestData = {
189
+ ...createData,
190
+ title: `${config.sampleTitle} to be deleted`,
191
+ };
192
+ // Test resource creation
193
+ group(`Create ${config.appName} resource`, () => {
194
+ const resource = createResource(communityId, createData);
195
+
196
+ check(resource, {
197
+ [`[${config.appName}] resource created successfully`]: (r) =>
198
+ r !== null,
199
+ [`[${config.appName}] resource has correct type`]: (r) =>
200
+ r !== null && r.type === config.resourceType,
201
+ [`[${config.appName}] resource has correct app name`]: (r) =>
202
+ r !== null && r.appName === config.appName,
203
+ });
204
+
205
+ // For External Links, check URL and openInNewTab
206
+ if (config.appName === AppName.EXTERNAL_LINK) {
207
+ check(resource, {
208
+ [`[${config.appName}] resource has correct URL`]: (r) =>
209
+ r !== null && r.url === config.sampleUrl,
210
+ [`[${config.appName}] resource has correct openInNewTab`]: (r) =>
211
+ r !== null && r.openInNewTab === true,
212
+ });
213
+ }
214
+
215
+ // Check title for all types
216
+ check(resource, {
217
+ [`[${config.appName}] resource has correct title`]: (r) =>
218
+ r !== null && r.title === config.sampleTitle,
219
+ });
220
+
221
+ if (resource) {
222
+ resourceId = resource.id;
223
+ console.log(
224
+ `Created ${config.appName} resource with ID: ${resourceId}`,
225
+ );
226
+ }
227
+ });
228
+
229
+ // Test resource listing
230
+ group(`List ${config.appName} resources in community`, () => {
231
+ const resources = listCommunityResources(communityId);
232
+
233
+ check(resources, {
234
+ [`[${config.appName}] listing resources successful`]: (r) => r !== null,
235
+ [`[${config.appName}] resource list contains items`]: (r) =>
236
+ r !== null && r.items && r.items.length > 0,
237
+ [`[${config.appName}] created resource is in the list`]: (r) => {
238
+ return r !== null && r.items.some((res) => res.id === resourceId);
239
+ },
240
+ });
241
+ });
242
+
243
+ // Test resource counting
244
+ group(`Count ${config.appName} resources in community`, () => {
245
+ const countResult = countResources(communityId);
246
+
247
+ check(countResult, {
248
+ [`[${config.appName}] count successful`]: (r) => r !== null,
249
+ [`[${config.appName}] count returns positive number`]: (r) =>
250
+ r !== null && r.count > 0,
251
+ });
252
+ });
253
+
254
+ // Test resource retrieval
255
+ group(`Get ${config.appName} resource by ID`, () => {
256
+ if (!resourceId) return;
257
+
258
+ const resource = getResource(communityId, resourceId);
259
+
260
+ check(resource, {
261
+ [`[${config.appName}] resource retrieval successful`]: (r) =>
262
+ r !== null,
263
+ [`[${config.appName}] retrieved resource has correct ID`]: (r) =>
264
+ r !== null && r.id === resourceId,
265
+ [`[${config.appName}] resource has correct title`]: (r) =>
266
+ r !== null && r.title === config.sampleTitle,
267
+ });
268
+
269
+ // Check URL for external links
270
+ if (config.appName === AppName.EXTERNAL_LINK) {
271
+ check(resource, {
272
+ [`[${config.appName}] resource has correct URL`]: (r) =>
273
+ r !== null && r.url === config.sampleUrl,
274
+ });
275
+ }
276
+ });
277
+
278
+ // Test resource update
279
+ group(`Update ${config.appName} resource`, () => {
280
+ if (!resourceId) return;
281
+
282
+ const updated = updateResource(communityId, resourceId, updateData);
283
+
284
+ check(updated, {
285
+ [`[${config.appName}] resource update successful`]: (r) => r !== null,
286
+ [`[${config.appName}] resource has updated title`]: (r) =>
287
+ r !== null && r.title === config.sampleUpdatedTitle,
288
+ });
289
+
290
+ // Check updated URL and openInNewTab for external links
291
+ if (config.appName === AppName.EXTERNAL_LINK && config.sampleUpdatedUrl) {
292
+ check(updated, {
293
+ [`[${config.appName}] resource has updated URL`]: (r) =>
294
+ r !== null && r.url === config.sampleUpdatedUrl,
295
+ [`[${config.appName}] resource has updated openInNewTab`]: (r) =>
296
+ r !== null && r.openInNewTab === false,
297
+ });
298
+ }
299
+
300
+ // Verify the update was persisted
301
+ const verified = getResource(communityId, resourceId);
302
+ check(verified, {
303
+ [`[${config.appName}] updated resource persisted correctly`]: (r) =>
304
+ r !== null && r.title === config.sampleUpdatedTitle,
305
+ });
306
+ });
307
+
308
+ // Test resource deletion
309
+ group(`Delete ${config.appName} resource`, () => {
310
+ const deleteTestResource = createResource(communityId, deleteTestData);
311
+
312
+ if (deleteTestResource) {
313
+ const deleteResult = deleteResource(communityId, deleteTestResource.id);
314
+
315
+ check(deleteResult, {
316
+ [`[${config.appName}] resource deletion successful`]: (r) =>
317
+ r === true,
318
+ });
319
+
320
+ // Verify the resource was deleted
321
+ const verifyDeleted = getResource(communityId, deleteTestResource.id);
322
+ check(verifyDeleted, {
323
+ [`[${config.appName}] deleted resource is no longer accessible`]: (
324
+ r,
325
+ ) => r === null,
326
+ });
327
+ }
328
+ });
329
+
330
+ logout();
331
+ });
332
+
333
+ // Test as regular member
334
+ group(`Member can view but not modify ${config.appName} resources`, () => {
335
+ if (!resourceId) return;
336
+
337
+ const memberAuthenticated = authenticateWeb(memberUser.login, "password");
338
+ if (!memberAuthenticated) {
339
+ fail("Member authentication failed");
340
+ }
341
+
342
+ // Test resource viewing as member
343
+ group(`Member can view ${config.appName} resources`, () => {
344
+ const resources = listCommunityResources(communityId);
345
+ check(resources, {
346
+ [`[${config.appName}] member can list resources`]: (r) => r !== null,
347
+ });
348
+
349
+ const resource = getResource(communityId, resourceId!);
350
+ check(resource, {
351
+ [`[${config.appName}] member can get individual resource`]: (r) =>
352
+ r !== null,
353
+ });
354
+ });
355
+
356
+ // Test that member cannot update resources
357
+ group(`Member cannot update ${config.appName} resources`, () => {
358
+ const updateResult = updateResource(communityId, resourceId!, {
359
+ title: "Member should not be able to update",
360
+ });
361
+
362
+ check(updateResult, {
363
+ [`[${config.appName}] member cannot update resources`]: (r) =>
364
+ r === null,
365
+ });
366
+ });
367
+
368
+ // Test that member cannot delete resources
369
+ group(`Member cannot delete ${config.appName} resources`, () => {
370
+ const deleteResult = deleteResource(communityId, resourceId!);
371
+
372
+ check(deleteResult, {
373
+ [`[${config.appName}] member cannot delete resources`]: (r) =>
374
+ r === false,
375
+ });
376
+ });
377
+
378
+ // Test that member cannot create resources
379
+ group(`Member cannot create ${config.appName} resources`, () => {
380
+ const createResult = createResource(communityId, {
381
+ type: config.resourceType,
382
+ appName: config.appName,
383
+ title: config.sampleTitle,
384
+ });
385
+
386
+ check(createResult, {
387
+ [`[${config.appName}] member cannot create resources`]: (r) =>
388
+ r === null,
389
+ });
390
+ });
391
+
392
+ logout();
393
+ });
394
+
395
+ return resourceId;
396
+ }
@@ -0,0 +1,33 @@
1
+ import http from "k6/http";
2
+ import { getHeaders, getRoleByName, Role } from "../../../node_modules/edifice-k6-commons/dist/index.js";
3
+ import { check } from "k6";
4
+
5
+ const rootUrl = __ENV.ROOT_URL || "";
6
+ //TODO move to k6-commons
7
+ export function createAndSetAllRoles(): Role {
8
+ const roleName = `All - Stress Test`;
9
+ let role = getRoleByName(roleName);
10
+ if (role) {
11
+ console.log(`Role ${roleName} already existed`);
12
+ } else {
13
+ let res = http.get(
14
+ `${rootUrl}/appregistry/applications/actions?actionType=WORKFLOW`,
15
+ { headers: getHeaders() },
16
+ );
17
+ check(res, { "get workflow actions": (r) => r.status == 200 });
18
+ const applications = JSON.parse(<string>res.body) as Array<{actions: string[]}>;
19
+ const actions = applications.flatMap(application => application.actions.map((entries) => entries[0]));
20
+ const headers = getHeaders();
21
+ headers["content-type"] = "application/json";
22
+ const payload = {
23
+ role: roleName,
24
+ actions,
25
+ };
26
+ res = http.post(`${rootUrl}/appregistry/role`, JSON.stringify(payload), {
27
+ headers,
28
+ });
29
+ check(res, { "save role ok": (r) => r.status == 201 });
30
+ role = getRoleByName(roleName);
31
+ }
32
+ return role;
33
+ }
@@ -0,0 +1,6 @@
1
+ import { UserSetupData } from "../node_modules/edifice-k6-commons/dist/index.js";
2
+ import { group } from "k6";
3
+
4
+ export function community(_: UserSetupData) {
5
+ group("[community]", () => {});
6
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@edifice.io/communities-tests",
3
+ "version": "1.0.0-develop-pedago.20250725171105",
4
+ "description": "Common functions for testing module community with K6.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+ssh://git@github.com/edificeio/communities"
8
+ },
9
+ "author": "Edifice",
10
+ "license": "AGPL-3.0",
11
+ "bugs": {
12
+ "url": "https://github.com/edificeio/communities/issues"
13
+ },
14
+ "homepage": "https://github.com/edificeio/communities#readme",
15
+ "engines": {
16
+ "node": ">=22"
17
+ },
18
+ "engineStrict": true,
19
+ "type": "module",
20
+ "files": [
21
+ "**/*.ts"
22
+ ],
23
+ "devDependencies": {
24
+ "@types/k6": "^0.49.3",
25
+ "@types/node": "^20.11.21",
26
+ "edifice-k6-commons": "2.0.1-develop",
27
+ "husky": "^9.0.11",
28
+ "prettier": "^3.2.5",
29
+ "semantic-release": "^23.0.2",
30
+ "typescript": "^5.2.2",
31
+ "vite": "^5.1.4",
32
+ "vite-plugin-dts": "^4.3.0"
33
+ },
34
+ "lint-staged": {
35
+ "*.{js,ts,jsx,tsx,json,css,md}": "pnpm format"
36
+ },
37
+ "scripts": {
38
+ "dev": "vite",
39
+ "build": "tsc && vite build",
40
+ "build:types": "tsc --build",
41
+ "preview": "vite preview",
42
+ "clean": "rm -Rf dist",
43
+ "format": "pnpm run format:write && pnpm run format:check",
44
+ "format:check": "npx prettier --check \"src/**/*.ts\"",
45
+ "format:write": "npx prettier --write \"src/**/*.ts\"",
46
+ "pre-commit": "pnpm run format"
47
+ }
48
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,23 @@
1
+ // vite.config.ts
2
+ import { resolve } from "path";
3
+ import { defineConfig } from "vite";
4
+ import dts from "vite-plugin-dts";
5
+
6
+ export default defineConfig({
7
+ build: {
8
+ lib: {
9
+ entry: resolve(__dirname, "loadtest/index.ts"),
10
+ name: "community-k6-tests",
11
+ fileName: "index",
12
+ },
13
+ rollupOptions: {
14
+ external: ["k6", "k6/http"],
15
+ },
16
+ },
17
+ plugins: [
18
+ dts({
19
+ insertTypesEntry: true, // Ensures a types entry is added to package.json
20
+ outDir: "dist", // Directory for type definitions
21
+ }),
22
+ ],
23
+ });