@jskit-ai/users-core 0.1.48 → 0.1.50

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 (87) hide show
  1. package/package.descriptor.mjs +7 -7
  2. package/package.json +7 -17
  3. package/src/server/common/services/authProfileSyncService.js +28 -7
  4. package/src/server/common/support/realtimeServiceEvents.js +1 -59
  5. package/src/server/profileSyncLifecycleContributorRegistry.js +56 -0
  6. package/src/server/registerUsersBootstrap.js +0 -1
  7. package/src/server/registerUsersCore.js +2 -14
  8. package/src/server/usersBootstrapContributor.js +2 -64
  9. package/src/shared/index.js +2 -99
  10. package/src/shared/settings.js +1 -119
  11. package/test/authProfileSyncService.test.js +19 -10
  12. package/test/registerServiceRealtimeEvents.test.js +0 -86
  13. package/test/registerUsersCore.test.js +6 -15
  14. package/test/repositoryContracts.test.js +1 -9
  15. package/test/resourcesCanonical.test.js +0 -16
  16. package/test/settingsFieldRegistriesSingleton.test.js +0 -5
  17. package/test/usersBootstrapContributor.test.js +2 -26
  18. package/test/usersRouteResources.test.js +0 -16
  19. package/src/server/UsersWorkspacesServiceProvider.js +0 -44
  20. package/src/server/common/contributors/workspaceActionContextContributor.js +0 -88
  21. package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +0 -34
  22. package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +0 -78
  23. package/src/server/common/formatters/workspaceFormatter.js +0 -53
  24. package/src/server/common/repositories/workspaceInvitesRepository.js +0 -208
  25. package/src/server/common/repositories/workspaceMembershipsRepository.js +0 -190
  26. package/src/server/common/repositories/workspacesRepository.js +0 -202
  27. package/src/server/common/services/workspaceContextService.js +0 -281
  28. package/src/server/common/support/workspaceRoutePaths.js +0 -17
  29. package/src/server/common/validators/routeParamsValidator.js +0 -62
  30. package/src/server/registerWorkspaceBootstrap.js +0 -27
  31. package/src/server/registerWorkspaceCore.js +0 -73
  32. package/src/server/registerWorkspaceRepositories.js +0 -26
  33. package/src/server/support/resolveWorkspace.js +0 -16
  34. package/src/server/support/workspaceActionSurfaces.js +0 -118
  35. package/src/server/support/workspaceInvitationsPolicy.js +0 -45
  36. package/src/server/support/workspaceRouteInput.js +0 -22
  37. package/src/server/workspaceBootstrapContributor.js +0 -212
  38. package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +0 -133
  39. package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +0 -19
  40. package/src/server/workspaceDirectory/workspaceDirectoryActions.js +0 -133
  41. package/src/server/workspaceMembers/bootWorkspaceMembers.js +0 -236
  42. package/src/server/workspaceMembers/registerWorkspaceMembers.js +0 -108
  43. package/src/server/workspaceMembers/workspaceMembersActions.js +0 -186
  44. package/src/server/workspaceMembers/workspaceMembersService.js +0 -222
  45. package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +0 -62
  46. package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +0 -119
  47. package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +0 -74
  48. package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +0 -138
  49. package/src/server/workspaceSettings/bootWorkspaceSettings.js +0 -76
  50. package/src/server/workspaceSettings/registerWorkspaceSettings.js +0 -62
  51. package/src/server/workspaceSettings/workspaceSettingsActions.js +0 -72
  52. package/src/server/workspaceSettings/workspaceSettingsRepository.js +0 -154
  53. package/src/server/workspaceSettings/workspaceSettingsService.js +0 -66
  54. package/src/shared/resources/workspaceMembersResource.js +0 -354
  55. package/src/shared/resources/workspacePendingInvitationsResource.js +0 -82
  56. package/src/shared/resources/workspaceResource.js +0 -176
  57. package/src/shared/resources/workspaceSettingsFields.js +0 -59
  58. package/src/shared/resources/workspaceSettingsResource.js +0 -169
  59. package/src/shared/roles.js +0 -161
  60. package/src/shared/support/usersApiPaths.js +0 -43
  61. package/src/shared/support/usersVisibility.js +0 -42
  62. package/src/shared/support/workspacePathModel.js +0 -145
  63. package/src/shared/tenancyMode.js +0 -35
  64. package/src/shared/tenancyProfile.js +0 -73
  65. package/test/registerWorkspaceDirectory.test.js +0 -31
  66. package/test/registerWorkspaceSettings.test.js +0 -40
  67. package/test/roles.test.js +0 -159
  68. package/test/tenancyProfile.test.js +0 -67
  69. package/test/usersApiPaths.test.js +0 -49
  70. package/test/usersRouteValidators.test.js +0 -49
  71. package/test/usersVisibility.test.js +0 -27
  72. package/test/workspaceActionContextContributor.test.js +0 -344
  73. package/test/workspaceActionSurfaces.test.js +0 -85
  74. package/test/workspaceAuthPolicyContextResolver.test.js +0 -119
  75. package/test/workspaceBootstrapContributor.test.js +0 -154
  76. package/test/workspaceInvitationsPolicy.test.js +0 -71
  77. package/test/workspaceInvitesRepository.test.js +0 -111
  78. package/test/workspaceMembersService.test.js +0 -398
  79. package/test/workspacePathModel.test.js +0 -93
  80. package/test/workspacePendingInvitationsResource.test.js +0 -38
  81. package/test/workspacePendingInvitationsService.test.js +0 -151
  82. package/test/workspaceRouteVisibilityResolver.test.js +0 -83
  83. package/test/workspaceService.test.js +0 -546
  84. package/test/workspaceSettingsActions.test.js +0 -52
  85. package/test/workspaceSettingsRepository.test.js +0 -202
  86. package/test/workspaceSettingsResource.test.js +0 -169
  87. 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
- });