@jskit-ai/users-core 0.1.47 → 0.1.49

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.
Files changed (104) hide show
  1. package/package.descriptor.mjs +9 -46
  2. package/package.json +8 -19
  3. package/src/server/UsersCoreServiceProvider.js +0 -4
  4. package/src/server/common/registerCommonRepositories.js +0 -5
  5. package/src/server/common/services/authProfileSyncService.js +28 -7
  6. package/src/server/common/support/realtimeServiceEvents.js +1 -59
  7. package/src/server/profileSyncLifecycleContributorRegistry.js +56 -0
  8. package/src/server/registerUsersBootstrap.js +1 -3
  9. package/src/server/registerUsersCore.js +2 -14
  10. package/src/server/usersBootstrapContributor.js +10 -85
  11. package/src/shared/index.js +2 -99
  12. package/src/shared/settings.js +1 -119
  13. package/templates/migrations/users_core_generic_initial.cjs +0 -16
  14. package/test/authProfileSyncService.test.js +19 -10
  15. package/test/registerServiceRealtimeEvents.test.js +0 -94
  16. package/test/registerUsersCore.test.js +6 -19
  17. package/test/repositoryContracts.test.js +1 -11
  18. package/test/resourcesCanonical.test.js +1 -19
  19. package/test/settingsFieldRegistriesSingleton.test.js +0 -10
  20. package/test/usersBootstrapContributor.test.js +20 -38
  21. package/test/usersRouteRequestInputValidator.test.js +2 -43
  22. package/test/usersRouteResources.test.js +2 -20
  23. package/test-support/registerDefaultSettingsFields.js +0 -1
  24. package/src/server/UsersWorkspacesServiceProvider.js +0 -44
  25. package/src/server/common/contributors/workspaceActionContextContributor.js +0 -88
  26. package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +0 -34
  27. package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +0 -78
  28. package/src/server/common/formatters/workspaceFormatter.js +0 -53
  29. package/src/server/common/repositories/workspaceInvitesRepository.js +0 -208
  30. package/src/server/common/repositories/workspaceMembershipsRepository.js +0 -190
  31. package/src/server/common/repositories/workspacesRepository.js +0 -202
  32. package/src/server/common/services/workspaceContextService.js +0 -281
  33. package/src/server/common/support/workspaceRoutePaths.js +0 -17
  34. package/src/server/common/validators/routeParamsValidator.js +0 -62
  35. package/src/server/consoleSettings/bootConsoleSettingsRoutes.js +0 -63
  36. package/src/server/consoleSettings/consoleService.js +0 -36
  37. package/src/server/consoleSettings/consoleSettingsActions.js +0 -55
  38. package/src/server/consoleSettings/consoleSettingsRepository.js +0 -115
  39. package/src/server/consoleSettings/consoleSettingsService.js +0 -40
  40. package/src/server/consoleSettings/registerConsoleSettings.js +0 -56
  41. package/src/server/registerWorkspaceBootstrap.js +0 -27
  42. package/src/server/registerWorkspaceCore.js +0 -73
  43. package/src/server/registerWorkspaceRepositories.js +0 -26
  44. package/src/server/support/resolveWorkspace.js +0 -16
  45. package/src/server/support/workspaceActionSurfaces.js +0 -135
  46. package/src/server/support/workspaceInvitationsPolicy.js +0 -45
  47. package/src/server/support/workspaceRouteInput.js +0 -22
  48. package/src/server/workspaceBootstrapContributor.js +0 -211
  49. package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +0 -133
  50. package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +0 -19
  51. package/src/server/workspaceDirectory/workspaceDirectoryActions.js +0 -133
  52. package/src/server/workspaceMembers/bootWorkspaceMembers.js +0 -236
  53. package/src/server/workspaceMembers/registerWorkspaceMembers.js +0 -108
  54. package/src/server/workspaceMembers/workspaceMembersActions.js +0 -186
  55. package/src/server/workspaceMembers/workspaceMembersService.js +0 -222
  56. package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +0 -62
  57. package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +0 -119
  58. package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +0 -74
  59. package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +0 -138
  60. package/src/server/workspaceSettings/bootWorkspaceSettings.js +0 -76
  61. package/src/server/workspaceSettings/registerWorkspaceSettings.js +0 -62
  62. package/src/server/workspaceSettings/workspaceSettingsActions.js +0 -72
  63. package/src/server/workspaceSettings/workspaceSettingsRepository.js +0 -154
  64. package/src/server/workspaceSettings/workspaceSettingsService.js +0 -66
  65. package/src/shared/resources/consoleSettingsFields.js +0 -54
  66. package/src/shared/resources/consoleSettingsResource.js +0 -119
  67. package/src/shared/resources/workspaceMembersResource.js +0 -354
  68. package/src/shared/resources/workspacePendingInvitationsResource.js +0 -82
  69. package/src/shared/resources/workspaceResource.js +0 -176
  70. package/src/shared/resources/workspaceSettingsFields.js +0 -59
  71. package/src/shared/resources/workspaceSettingsResource.js +0 -169
  72. package/src/shared/roles.js +0 -161
  73. package/src/shared/support/usersApiPaths.js +0 -43
  74. package/src/shared/support/usersVisibility.js +0 -42
  75. package/src/shared/support/workspacePathModel.js +0 -145
  76. package/src/shared/tenancyMode.js +0 -35
  77. package/src/shared/tenancyProfile.js +0 -73
  78. package/templates/migrations/users_core_console_owner.cjs +0 -37
  79. package/templates/packages/main/src/shared/resources/consoleSettingsFields.js +0 -11
  80. package/test/consoleService.test.js +0 -57
  81. package/test/consoleSettingsService.test.js +0 -86
  82. package/test/registerWorkspaceDirectory.test.js +0 -31
  83. package/test/registerWorkspaceSettings.test.js +0 -40
  84. package/test/roles.test.js +0 -159
  85. package/test/tenancyProfile.test.js +0 -67
  86. package/test/usersApiPaths.test.js +0 -49
  87. package/test/usersRouteValidators.test.js +0 -49
  88. package/test/usersVisibility.test.js +0 -27
  89. package/test/workspaceActionContextContributor.test.js +0 -344
  90. package/test/workspaceActionSurfaces.test.js +0 -105
  91. package/test/workspaceAuthPolicyContextResolver.test.js +0 -119
  92. package/test/workspaceBootstrapContributor.test.js +0 -154
  93. package/test/workspaceInvitationsPolicy.test.js +0 -71
  94. package/test/workspaceInvitesRepository.test.js +0 -111
  95. package/test/workspaceMembersService.test.js +0 -398
  96. package/test/workspacePathModel.test.js +0 -93
  97. package/test/workspacePendingInvitationsResource.test.js +0 -38
  98. package/test/workspacePendingInvitationsService.test.js +0 -151
  99. package/test/workspaceRouteVisibilityResolver.test.js +0 -83
  100. package/test/workspaceService.test.js +0 -546
  101. package/test/workspaceSettingsActions.test.js +0 -52
  102. package/test/workspaceSettingsRepository.test.js +0 -202
  103. package/test/workspaceSettingsResource.test.js +0 -169
  104. package/test/workspaceSettingsService.test.js +0 -140
@@ -1,83 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { createWorkspaceRouteVisibilityResolver } from "../src/server/common/contributors/workspaceRouteVisibilityResolver.js";
4
-
5
- test("workspace route visibility resolver contributes workspace_user scope and actor ownership", async () => {
6
- const resolver = createWorkspaceRouteVisibilityResolver({
7
- workspaceService: {
8
- async resolveWorkspaceContextForUserBySlug() {
9
- throw new Error("should not be called");
10
- }
11
- }
12
- });
13
-
14
- const contribution = await resolver.resolve({
15
- visibility: "workspace_user",
16
- context: {
17
- actor: {
18
- id: "user_42"
19
- },
20
- workspace: {
21
- id: "11"
22
- }
23
- }
24
- });
25
-
26
- assert.deepEqual(contribution, {
27
- scopeKind: "workspace_user",
28
- requiresActorScope: true,
29
- scopeOwnerId: "11",
30
- userId: "user_42"
31
- });
32
- });
33
-
34
- test("workspace route visibility resolver keeps workspace-only visibility actor-agnostic", async () => {
35
- const resolver = createWorkspaceRouteVisibilityResolver({
36
- workspaceService: {
37
- async resolveWorkspaceContextForUserBySlug() {
38
- throw new Error("should not be called");
39
- }
40
- }
41
- });
42
-
43
- const contribution = await resolver.resolve({
44
- visibility: "workspace",
45
- context: {
46
- workspace: {
47
- id: "11"
48
- }
49
- }
50
- });
51
-
52
- assert.deepEqual(contribution, {
53
- scopeKind: "workspace",
54
- requiresActorScope: false,
55
- scopeOwnerId: "11"
56
- });
57
- });
58
-
59
- test("workspace route visibility resolver still marks workspace_user as actor-scoped when workspace is unresolved", async () => {
60
- const resolver = createWorkspaceRouteVisibilityResolver({
61
- workspaceService: {
62
- async resolveWorkspaceContextForUserBySlug() {
63
- return {};
64
- }
65
- }
66
- });
67
-
68
- const contribution = await resolver.resolve({
69
- visibility: "workspace_user",
70
- context: {
71
- actor: {
72
- id: "user_99"
73
- }
74
- },
75
- input: {}
76
- });
77
-
78
- assert.deepEqual(contribution, {
79
- scopeKind: "workspace_user",
80
- requiresActorScope: true,
81
- userId: "user_99"
82
- });
83
- });
@@ -1,546 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { createService } from "../src/server/common/services/workspaceContextService.js";
4
-
5
- function createRoleCatalog() {
6
- return {
7
- workspace: {
8
- defaultInviteRole: "member"
9
- },
10
- roles: {
11
- owner: {
12
- assignable: false,
13
- permissions: ["*"]
14
- },
15
- member: {
16
- assignable: true,
17
- permissions: ["workspace.settings.view"]
18
- }
19
- }
20
- };
21
- }
22
-
23
- function createWorkspaceServiceFixture({
24
- tenancyMode = "workspaces",
25
- tenancyPolicy = {},
26
- roleCatalog = createRoleCatalog(),
27
- additionalWorkspaces = [],
28
- userWorkspaceRows = null,
29
- membershipResolver = null,
30
- personalWorkspace = {
31
- id: "1",
32
- slug: "tonymobily3",
33
- name: "TonyMobily3",
34
- ownerUserId: "7",
35
- isPersonal: true,
36
- avatarUrl: ""
37
- }
38
- } = {}) {
39
- const calls = {
40
- findPersonalByOwnerUserId: 0,
41
- listForUserId: 0,
42
- insert: 0,
43
- updateById: 0,
44
- ensureOwnerMembership: 0
45
- };
46
- let nextWorkspaceId = 10;
47
- const personalWorkspaceState =
48
- personalWorkspace && typeof personalWorkspace === "object" ? { ...personalWorkspace } : null;
49
- const insertedPayloads = [];
50
-
51
- const workspaceBySlug = new Map();
52
- if (personalWorkspaceState?.slug) {
53
- workspaceBySlug.set(String(personalWorkspaceState.slug).trim().toLowerCase(), {
54
- ...personalWorkspaceState
55
- });
56
- }
57
- for (const workspace of Array.isArray(additionalWorkspaces) ? additionalWorkspaces : []) {
58
- if (!workspace || typeof workspace !== "object") {
59
- continue;
60
- }
61
- const slug = String(workspace.slug || "").trim().toLowerCase();
62
- if (!slug) {
63
- continue;
64
- }
65
- workspaceBySlug.set(slug, {
66
- ...workspace
67
- });
68
- }
69
-
70
- const service = createService({
71
- appConfig: {
72
- tenancyMode,
73
- tenancyPolicy,
74
- roleCatalog: roleCatalog && typeof roleCatalog === "object" ? { ...roleCatalog } : roleCatalog
75
- },
76
- workspacesRepository: {
77
- async findBySlug(slug) {
78
- const normalizedSlug = String(slug || "").trim().toLowerCase();
79
- const workspace = workspaceBySlug.get(normalizedSlug);
80
- if (!workspace) {
81
- return null;
82
- }
83
- return { ...workspace };
84
- },
85
- async findPersonalByOwnerUserId() {
86
- calls.findPersonalByOwnerUserId += 1;
87
- return personalWorkspaceState ? { ...personalWorkspaceState } : null;
88
- },
89
- async listForUserId() {
90
- calls.listForUserId += 1;
91
- if (Array.isArray(userWorkspaceRows)) {
92
- return userWorkspaceRows;
93
- }
94
- return [
95
- {
96
- id: "1",
97
- slug: "tonymobily3",
98
- name: "TonyMobily3",
99
- avatarUrl: "",
100
- roleSid: "owner",
101
- membershipStatus: "active"
102
- },
103
- {
104
- id: "2",
105
- slug: "pending-workspace",
106
- name: "Pending Workspace",
107
- avatarUrl: "",
108
- roleSid: "member",
109
- membershipStatus: "pending"
110
- }
111
- ];
112
- },
113
- async insert(payload) {
114
- calls.insert += 1;
115
- insertedPayloads.push(payload);
116
- const workspaceId = String(nextWorkspaceId++);
117
- const inserted = {
118
- id: workspaceId,
119
- slug: String(payload.slug || ""),
120
- name: String(payload.name || ""),
121
- ownerUserId: String(payload.ownerUserId || ""),
122
- isPersonal: payload.isPersonal === true,
123
- avatarUrl: String(payload.avatarUrl || "")
124
- };
125
- workspaceBySlug.set(String(inserted.slug).trim().toLowerCase(), inserted);
126
- return inserted;
127
- },
128
- async updateById(workspaceId, patch) {
129
- calls.updateById += 1;
130
- const targetId = String(workspaceId || "").trim();
131
- for (const [slug, workspace] of workspaceBySlug.entries()) {
132
- if (String(workspace.id || "").trim() !== targetId) {
133
- continue;
134
- }
135
- const updated = {
136
- ...workspace
137
- };
138
- if (Object.hasOwn(patch, "name")) {
139
- updated.name = String(patch.name || "");
140
- }
141
- if (Object.hasOwn(patch, "avatarUrl")) {
142
- updated.avatarUrl = String(patch.avatarUrl || "");
143
- }
144
- workspaceBySlug.set(slug, updated);
145
- return {
146
- ...updated
147
- };
148
- }
149
- return null;
150
- }
151
- },
152
- workspaceMembershipsRepository: {
153
- async ensureOwnerMembership() {
154
- calls.ensureOwnerMembership += 1;
155
- },
156
- async findByWorkspaceIdAndUserId(workspaceId, userId) {
157
- if (typeof membershipResolver === "function") {
158
- return membershipResolver(workspaceId, userId);
159
- }
160
- return {
161
- workspaceId,
162
- userId,
163
- roleSid: "owner",
164
- status: "active"
165
- };
166
- }
167
- },
168
- workspaceSettingsRepository: {
169
- async ensureForWorkspaceId() {
170
- return {
171
- invitesEnabled: true
172
- };
173
- }
174
- }
175
- });
176
-
177
- return { service, calls, insertedPayloads };
178
- }
179
-
180
- test("workspaceService no longer exposes bootstrap payload assembly", () => {
181
- const { service } = createWorkspaceServiceFixture();
182
- assert.equal(service.buildBootstrapPayload, undefined);
183
- });
184
-
185
- test("workspaceService.listWorkspacesForUser returns only accessible workspaces", async () => {
186
- const { service, calls } = createWorkspaceServiceFixture();
187
- const workspaces = await service.listWorkspacesForUser({
188
- id: "7",
189
- email: "chiaramobily@gmail.com",
190
- displayName: "Chiara"
191
- });
192
-
193
- assert.equal(workspaces.length, 1);
194
- assert.equal(workspaces[0].slug, "tonymobily3");
195
- assert.equal(workspaces[0].roleSid, "owner");
196
- assert.equal(calls.listForUserId, 1);
197
- assert.equal(calls.insert, 0);
198
- });
199
-
200
- test("workspaceService.listWorkspacesForUser no longer provisions personal workspace in workspace mode", async () => {
201
- const { service, calls } = createWorkspaceServiceFixture({
202
- tenancyMode: "workspaces",
203
- personalWorkspace: null
204
- });
205
-
206
- await service.listWorkspacesForUser({
207
- id: "7",
208
- email: "chiaramobily@gmail.com",
209
- displayName: "Chiara"
210
- });
211
-
212
- assert.equal(calls.findPersonalByOwnerUserId, 0);
213
- assert.equal(calls.insert, 0);
214
- });
215
-
216
- test("workspaceService.listWorkspacesForUser returns all active memberships in personal tenancy", async () => {
217
- const { service, calls } = createWorkspaceServiceFixture({
218
- tenancyMode: "personal",
219
- userWorkspaceRows: [
220
- {
221
- id: "1",
222
- slug: "chiaramobily",
223
- name: "Chiara Personal",
224
- avatarUrl: "",
225
- roleSid: "owner",
226
- membershipStatus: "active"
227
- },
228
- {
229
- id: "2",
230
- slug: "tonymobily",
231
- name: "Tony Workspace",
232
- avatarUrl: "",
233
- roleSid: "member",
234
- membershipStatus: "active"
235
- },
236
- {
237
- id: "3",
238
- slug: "pending-workspace",
239
- name: "Pending Workspace",
240
- avatarUrl: "",
241
- roleSid: "member",
242
- membershipStatus: "pending"
243
- }
244
- ]
245
- });
246
-
247
- const workspaces = await service.listWorkspacesForUser({
248
- id: "7",
249
- email: "chiaramobily@gmail.com",
250
- displayName: "Chiara"
251
- });
252
-
253
- assert.deepEqual(
254
- workspaces.map((workspace) => workspace.slug),
255
- ["chiaramobily", "tonymobily"]
256
- );
257
- assert.equal(calls.findPersonalByOwnerUserId, 0);
258
- assert.equal(calls.listForUserId, 1);
259
- });
260
-
261
- test("workspaceService.provisionWorkspaceForNewUser provisions personal workspace only in personal tenancy", async () => {
262
- const { service, calls, insertedPayloads } = createWorkspaceServiceFixture({
263
- tenancyMode: "personal",
264
- personalWorkspace: null
265
- });
266
-
267
- const workspace = await service.provisionWorkspaceForNewUser({
268
- id: "7",
269
- email: "chiaramobily@gmail.com",
270
- displayName: "Chiara"
271
- });
272
-
273
- assert.equal(Number(workspace.ownerUserId), 7);
274
- assert.equal(calls.findPersonalByOwnerUserId, 1);
275
- assert.equal(calls.insert, 1);
276
- assert.equal(calls.ensureOwnerMembership, 1);
277
- assert.equal(insertedPayloads[0].isPersonal, true);
278
- });
279
-
280
- test("workspaceService.provisionWorkspaceForNewUser is a no-op outside personal tenancy", async () => {
281
- const { service, calls } = createWorkspaceServiceFixture({
282
- tenancyMode: "workspaces"
283
- });
284
-
285
- const result = await service.provisionWorkspaceForNewUser({
286
- id: "7",
287
- email: "chiaramobily@gmail.com",
288
- displayName: "Chiara"
289
- });
290
-
291
- assert.equal(result, null);
292
- assert.equal(calls.insert, 0);
293
- });
294
-
295
- test("workspaceService.createWorkspaceForAuthenticatedUser creates non-personal workspace in workspace tenancy", async () => {
296
- const { service, calls, insertedPayloads } = createWorkspaceServiceFixture({
297
- tenancyMode: "workspaces",
298
- tenancyPolicy: {
299
- workspace: {
300
- allowSelfCreate: true
301
- }
302
- }
303
- });
304
-
305
- const workspace = await service.createWorkspaceForAuthenticatedUser(
306
- {
307
- id: "7",
308
- email: "chiaramobily@gmail.com",
309
- displayName: "Chiara"
310
- },
311
- {
312
- name: "Operations Team",
313
- slug: "ops-team"
314
- }
315
- );
316
-
317
- assert.equal(workspace.slug, "ops-team");
318
- assert.equal(calls.insert, 1);
319
- assert.equal(calls.ensureOwnerMembership, 1);
320
- assert.equal(insertedPayloads[0].isPersonal, false);
321
- assert.equal(insertedPayloads[0].ownerUserId, "7");
322
- });
323
-
324
- test("workspaceService.createWorkspaceForAuthenticatedUser rejects creation when self-create policy is disabled", async () => {
325
- const { service } = createWorkspaceServiceFixture({
326
- tenancyMode: "workspaces"
327
- });
328
-
329
- await assert.rejects(
330
- () =>
331
- service.createWorkspaceForAuthenticatedUser(
332
- {
333
- id: "7",
334
- email: "chiaramobily@gmail.com",
335
- displayName: "Chiara"
336
- },
337
- {
338
- name: "Operations Team"
339
- }
340
- ),
341
- /Workspace creation is disabled for this tenancy mode/
342
- );
343
- });
344
-
345
- test("workspaceService.resolveWorkspaceContextForUserBySlug returns workspace-not-found when requested slug does not exist", async () => {
346
- const { service } = createWorkspaceServiceFixture({
347
- tenancyMode: "personal",
348
- personalWorkspace: null
349
- });
350
-
351
- await assert.rejects(
352
- () =>
353
- service.resolveWorkspaceContextForUserBySlug(
354
- {
355
- id: "7",
356
- email: "chiaramobily@gmail.com",
357
- displayName: "Chiara"
358
- },
359
- "tonymobily3"
360
- ),
361
- /Workspace not found/
362
- );
363
- });
364
-
365
- test("workspaceService.resolveWorkspaceContextForUserBySlug allows personal tenancy access when membership is active", async () => {
366
- const { service } = createWorkspaceServiceFixture({
367
- tenancyMode: "personal",
368
- personalWorkspace: {
369
- id: "1",
370
- slug: "my-personal",
371
- name: "My Personal",
372
- ownerUserId: "7",
373
- isPersonal: true,
374
- avatarUrl: ""
375
- },
376
- additionalWorkspaces: [
377
- {
378
- id: "42",
379
- slug: "team-alpha",
380
- name: "Team Alpha",
381
- ownerUserId: "99",
382
- isPersonal: false,
383
- avatarUrl: ""
384
- }
385
- ]
386
- });
387
-
388
- const context = await service.resolveWorkspaceContextForUserBySlug(
389
- {
390
- id: "7",
391
- email: "chiaramobily@gmail.com",
392
- displayName: "Chiara"
393
- },
394
- "team-alpha"
395
- );
396
-
397
- assert.equal(context.workspace.slug, "team-alpha");
398
- assert.equal(context.membership.roleSid, "owner");
399
- assert.deepEqual(context.permissions, ["*"]);
400
- });
401
-
402
- test("workspaceService.resolveWorkspaceContextForUserBySlug grants owner access even when membership row is missing", async () => {
403
- let ensuredMembershipCount = 0;
404
- let membershipRecord = null;
405
-
406
- const service = createService({
407
- appConfig: {
408
- tenancyMode: "personal",
409
- roleCatalog: createRoleCatalog()
410
- },
411
- workspacesRepository: {
412
- async findBySlug(slug) {
413
- if (String(slug) !== "tonymobily") {
414
- return null;
415
- }
416
- return {
417
- id: "1",
418
- slug: "tonymobily",
419
- name: "TonyMobily",
420
- ownerUserId: "7",
421
- isPersonal: true,
422
- avatarUrl: ""
423
- };
424
- },
425
- async findPersonalByOwnerUserId() {
426
- return null;
427
- },
428
- async listForUserId() {
429
- return [];
430
- },
431
- async insert() {
432
- throw new Error("not implemented");
433
- }
434
- },
435
- workspaceMembershipsRepository: {
436
- async findByWorkspaceIdAndUserId() {
437
- return membershipRecord;
438
- },
439
- async ensureOwnerMembership(workspaceId, userId) {
440
- ensuredMembershipCount += 1;
441
- membershipRecord = {
442
- workspaceId,
443
- userId,
444
- roleSid: "owner",
445
- status: "active"
446
- };
447
- return membershipRecord;
448
- }
449
- },
450
- workspaceSettingsRepository: {
451
- async ensureForWorkspaceId() {
452
- return {
453
- invitesEnabled: true
454
- };
455
- }
456
- }
457
- });
458
-
459
- const context = await service.resolveWorkspaceContextForUserBySlug(
460
- {
461
- id: "7",
462
- email: "chiaramobily@gmail.com",
463
- displayName: "Chiara"
464
- },
465
- "tonymobily"
466
- );
467
-
468
- assert.equal(ensuredMembershipCount, 1);
469
- assert.equal(context.membership.roleSid, "owner");
470
- assert.deepEqual(context.permissions, ["*"]);
471
- });
472
-
473
- test("workspaceService.resolveWorkspaceContextForUserBySlug resolves permissions from appConfig.roleCatalog", async () => {
474
- const { service } = createWorkspaceServiceFixture({
475
- roleCatalog: {
476
- workspace: {
477
- defaultInviteRole: "member"
478
- },
479
- roles: {
480
- owner: {
481
- assignable: false,
482
- permissions: ["workspace.settings.update"]
483
- }
484
- }
485
- }
486
- });
487
-
488
- const context = await service.resolveWorkspaceContextForUserBySlug(
489
- {
490
- id: "7",
491
- email: "chiaramobily@gmail.com",
492
- displayName: "Chiara"
493
- },
494
- "tonymobily3"
495
- );
496
-
497
- assert.deepEqual(context.permissions, ["workspace.settings.update"]);
498
- });
499
-
500
- test("workspaceService.getWorkspaceForAuthenticatedUser resolves workspace from slug context", async () => {
501
- const { service } = createWorkspaceServiceFixture({
502
- additionalWorkspaces: [
503
- {
504
- id: "42",
505
- slug: "team-alpha",
506
- name: "Team Alpha",
507
- ownerUserId: "99",
508
- isPersonal: false,
509
- avatarUrl: ""
510
- }
511
- ]
512
- });
513
-
514
- const workspace = await service.getWorkspaceForAuthenticatedUser(
515
- {
516
- id: "7",
517
- email: "chiaramobily@gmail.com",
518
- displayName: "Chiara"
519
- },
520
- "team-alpha"
521
- );
522
-
523
- assert.equal(workspace.slug, "team-alpha");
524
- assert.equal(workspace.name, "Team Alpha");
525
- });
526
-
527
- test("workspaceService.updateWorkspaceForAuthenticatedUser updates workspace profile fields", async () => {
528
- const { service, calls } = createWorkspaceServiceFixture();
529
-
530
- const workspace = await service.updateWorkspaceForAuthenticatedUser(
531
- {
532
- id: "7",
533
- email: "chiaramobily@gmail.com",
534
- displayName: "Chiara"
535
- },
536
- "tonymobily3",
537
- {
538
- name: "Updated Workspace",
539
- avatarUrl: "https://example.com/acme.png"
540
- }
541
- );
542
-
543
- assert.equal(calls.updateById, 1);
544
- assert.equal(workspace.name, "Updated Workspace");
545
- assert.equal(workspace.avatarUrl, "https://example.com/acme.png");
546
- });
@@ -1,52 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import "../test-support/registerDefaultSettingsFields.js";
4
- import { workspaceDirectoryActions } from "../src/server/workspaceDirectory/workspaceDirectoryActions.js";
5
- import { workspacePendingInvitationsActions } from "../src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js";
6
- import { workspaceMembersActions } from "../src/server/workspaceMembers/workspaceMembersActions.js";
7
- import { workspaceSettingsActions } from "../src/server/workspaceSettings/workspaceSettingsActions.js";
8
- import { workspaceResource } from "../src/shared/resources/workspaceResource.js";
9
-
10
- test("workspace settings actions live in their own action array", () => {
11
- assert.deepEqual(
12
- workspaceSettingsActions.map((action) => action.id),
13
- ["workspace.settings.read", "workspace.settings.update"]
14
- );
15
- assert.equal(workspaceSettingsActions[0].surfacesFrom, "workspace");
16
- assert.equal(workspaceSettingsActions[1].surfacesFrom, "workspace");
17
- assert.deepEqual(workspaceSettingsActions[1].channels, ["api", "assistant_tool", "automation", "internal"]);
18
- assert.equal(workspaceSettingsActions[1].extensions?.assistant?.description, "Update workspace settings.");
19
- });
20
-
21
- test("workspace actions array no longer owns workspace settings actions", () => {
22
- const otherWorkspaceActionIds = [
23
- ...workspaceDirectoryActions,
24
- ...workspacePendingInvitationsActions,
25
- ...workspaceMembersActions
26
- ].map((action) => action.id);
27
-
28
- assert.equal(
29
- otherWorkspaceActionIds.includes("workspace.settings.read"),
30
- false
31
- );
32
- assert.equal(
33
- otherWorkspaceActionIds.includes("workspace.settings.update"),
34
- false
35
- );
36
- });
37
-
38
- test("workspace directory actions use the canonical workspace list resource output", () => {
39
- const listAction = workspaceDirectoryActions.find((action) => action.id === "workspace.workspaces.list");
40
- assert.ok(listAction);
41
- assert.equal(listAction.outputValidator, workspaceResource.operations.list.outputValidator);
42
- });
43
-
44
- test("workspace directory read/update actions use canonical workspace resource validators", () => {
45
- const readAction = workspaceDirectoryActions.find((action) => action.id === "workspace.workspaces.read");
46
- const updateAction = workspaceDirectoryActions.find((action) => action.id === "workspace.workspaces.update");
47
-
48
- assert.ok(readAction);
49
- assert.ok(updateAction);
50
- assert.equal(readAction.outputValidator, workspaceResource.operations.view.outputValidator);
51
- assert.equal(updateAction.outputValidator, workspaceResource.operations.patch.outputValidator);
52
- });