@jskit-ai/workspaces-core 0.1.196 → 0.1.198

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.
@@ -81,6 +81,8 @@ function createFixture() {
81
81
  }
82
82
  },
83
83
  workspaceInvitesRepository: {
84
+ async withTransaction(work) { return work({}); },
85
+ async lockWorkspaceForInvitations() {},
84
86
  async listPendingByWorkspaceIdWithWorkspace(workspaceId) {
85
87
  assert.equal(Number(workspaceId), 7);
86
88
  return [];
@@ -101,6 +103,7 @@ function createFixture() {
101
103
 
102
104
  test("workspaceMembersService.createInvite uses configured inviteExpiresInMs", async () => {
103
105
  const expiresAtValues = [];
106
+ let savedInvite;
104
107
  const service = createService({
105
108
  workspaceMembershipsRepository: {
106
109
  async listActiveByWorkspaceId() {
@@ -108,18 +111,19 @@ test("workspaceMembersService.createInvite uses configured inviteExpiresInMs", a
108
111
  }
109
112
  },
110
113
  workspaceInvitesRepository: {
114
+ async withTransaction(work) { return work({}); },
115
+ async lockWorkspaceForInvitations() {},
111
116
  async expirePendingByWorkspaceIdAndEmail() {},
112
117
  async insert(payload) {
113
118
  expiresAtValues.push(payload.expiresAt);
114
- return {
115
- id: "31"
116
- };
119
+ savedInvite = { ...payload, id: "31" };
120
+ return savedInvite;
117
121
  },
118
122
  async listPendingByWorkspaceIdWithWorkspace() {
119
123
  return [];
120
124
  },
121
125
  async findPendingByIdForWorkspace() {
122
- return null;
126
+ return savedInvite;
123
127
  },
124
128
  async revokeById() {}
125
129
  },
@@ -158,6 +162,7 @@ test("workspaceMembersService.createInvite uses configured inviteExpiresInMs", a
158
162
  test("workspaceMembersService.createInvite sends generated invite URLs through the configured mailer", async () => {
159
163
  const templateCalls = [];
160
164
  const mailerCalls = [];
165
+ let savedInvite;
161
166
  const service = createService({
162
167
  workspaceMembershipsRepository: {
163
168
  async listActiveByWorkspaceId() {
@@ -165,6 +170,8 @@ test("workspaceMembersService.createInvite sends generated invite URLs through t
165
170
  }
166
171
  },
167
172
  workspaceInvitesRepository: {
173
+ async withTransaction(work) { return work({}); },
174
+ async lockWorkspaceForInvitations() {},
168
175
  async expirePendingByWorkspaceIdAndEmail(workspaceId, email) {
169
176
  assert.equal(workspaceId, "7");
170
177
  assert.equal(email, "invitee@example.com");
@@ -173,16 +180,14 @@ test("workspaceMembersService.createInvite sends generated invite URLs through t
173
180
  assert.equal(payload.workspaceId, "7");
174
181
  assert.equal(payload.email, "invitee@example.com");
175
182
  assert.equal(payload.roleSid, "admin");
176
- return {
177
- id: "88",
178
- expiresAt: payload.expiresAt
179
- };
183
+ savedInvite = { ...payload, id: "88", workspaceSlug: "acme", workspaceName: "Acme" };
184
+ return savedInvite;
180
185
  },
181
186
  async listPendingByWorkspaceIdWithWorkspace() {
182
187
  return [];
183
188
  },
184
189
  async findPendingByIdForWorkspace() {
185
- return null;
190
+ return savedInvite;
186
191
  },
187
192
  async revokeById() {}
188
193
  },
@@ -253,6 +258,8 @@ test("workspaceMembersService.revokeInvite returns the revoked invite id", async
253
258
  }
254
259
  },
255
260
  workspaceInvitesRepository: {
261
+ async withTransaction(work) { return work({}); },
262
+ async lockWorkspaceForInvitations() {},
256
263
  async listPendingByWorkspaceIdWithWorkspace() {
257
264
  return [];
258
265
  },
@@ -300,6 +307,8 @@ test("workspaceMembersService rejects invite operations when invitations are dis
300
307
  }
301
308
  },
302
309
  workspaceInvitesRepository: {
310
+ async withTransaction(work) { return work({}); },
311
+ async lockWorkspaceForInvitations() {},
303
312
  async listPendingByWorkspaceIdWithWorkspace() {
304
313
  throw new Error("invite repository should not be called when invitations are disabled");
305
314
  },
@@ -440,6 +449,8 @@ test("workspaceMembersService.removeMember marks membership revoked and returns
440
449
  }
441
450
  },
442
451
  workspaceInvitesRepository: {
452
+ async withTransaction(work) { return work({}); },
453
+ async lockWorkspaceForInvitations() {},
443
454
  async listPendingByWorkspaceIdWithWorkspace() {
444
455
  return [];
445
456
  },
@@ -493,6 +504,8 @@ test("workspaceMembersService.removeMember rejects removing the owner", async ()
493
504
  }
494
505
  },
495
506
  workspaceInvitesRepository: {
507
+ async withTransaction(work) { return work({}); },
508
+ async lockWorkspaceForInvitations() {},
496
509
  async listPendingByWorkspaceIdWithWorkspace() {
497
510
  return [];
498
511
  },
@@ -16,6 +16,7 @@ function createFixture({
16
16
 
17
17
  const service = createService({
18
18
  workspaceInvitesRepository: {
19
+ async withTransaction(work) { return work({}); },
19
20
  async listPendingByEmail() {
20
21
  return Array.isArray(pendingInvitesByEmail) ? [...pendingInvitesByEmail] : [];
21
22
  },
@@ -234,7 +235,7 @@ test("acceptInviteByToken accepts opaque invite token and resolves invite by dec
234
235
  token: encodedToken
235
236
  });
236
237
 
237
- assert.deepEqual(calls.tokenHashCalls, [tokenHash]);
238
+ assert.deepEqual(calls.tokenHashCalls, [tokenHash, tokenHash]);
238
239
  assert.equal(calls.upsertCalls.length, 1);
239
240
  assert.deepEqual(calls.acceptCalls, [44]);
240
241
  assert.deepEqual(calls.revokeCalls, []);
@@ -268,10 +269,37 @@ test("refuseInviteByToken revokes the invite and returns refused", async () => {
268
269
  token: encodedToken
269
270
  });
270
271
 
271
- assert.deepEqual(calls.tokenHashCalls, [tokenHash]);
272
+ assert.deepEqual(calls.tokenHashCalls, [tokenHash, tokenHash]);
272
273
  assert.deepEqual(calls.acceptCalls, []);
273
274
  assert.deepEqual(calls.revokeCalls, [45]);
274
275
  assert.equal(calls.upsertCalls.length, 0);
275
276
  assert.equal(response.decision, "refused");
276
277
  assert.equal(response.workspaceId, "1");
277
278
  });
279
+
280
+
281
+ test("participant registration rejects invalid, duplicate and late registration", async () => {
282
+ const { service } = createFixture();
283
+ assert.throws(() => service.registerAcceptanceParticipant(null), /must be a function/);
284
+ service.registerAcceptanceParticipant(async () => {});
285
+ assert.throws(() => service.registerAcceptanceParticipant(async () => {}), /already registered/);
286
+ const { service: late } = createFixture();
287
+ await assert.rejects(late.acceptInviteByToken(), /Authentication required/);
288
+ assert.throws(() => late.registerAcceptanceParticipant(async () => {}), /registration is closed/);
289
+ });
290
+
291
+ for (const transactionOutcome of ["committed", "unknown", "rolledBack"]) {
292
+ test(`acceptance preserves a ${transactionOutcome} managed completion failure`, async () => {
293
+ const cause = new Error("Completion failed");
294
+ const failure = Object.assign(new Error("Managed completion failed"), { transactionOutcome, cause });
295
+ const service = createService({
296
+ workspaceInvitesRepository: {
297
+ async findPendingByTokenHash() { return { workspaceId: "1", email: "a@example.test" }; },
298
+ async withTransaction() { throw failure; }
299
+ },
300
+ workspaceMembershipsRepository: {}
301
+ });
302
+ await assert.rejects(service.acceptInviteByToken({ user: { id: "1", email: "a@example.test" }, token: "valid-token" }),
303
+ (error) => error === failure && error.cause === cause);
304
+ });
305
+ }