@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.
- package/package.descriptor.mjs +9 -46
- package/package.json +8 -19
- package/src/server/UsersCoreServiceProvider.js +0 -4
- package/src/server/common/registerCommonRepositories.js +0 -5
- package/src/server/common/services/authProfileSyncService.js +28 -7
- package/src/server/common/support/realtimeServiceEvents.js +1 -59
- package/src/server/profileSyncLifecycleContributorRegistry.js +56 -0
- package/src/server/registerUsersBootstrap.js +1 -3
- package/src/server/registerUsersCore.js +2 -14
- package/src/server/usersBootstrapContributor.js +10 -85
- package/src/shared/index.js +2 -99
- package/src/shared/settings.js +1 -119
- package/templates/migrations/users_core_generic_initial.cjs +0 -16
- package/test/authProfileSyncService.test.js +19 -10
- package/test/registerServiceRealtimeEvents.test.js +0 -94
- package/test/registerUsersCore.test.js +6 -19
- package/test/repositoryContracts.test.js +1 -11
- package/test/resourcesCanonical.test.js +1 -19
- package/test/settingsFieldRegistriesSingleton.test.js +0 -10
- package/test/usersBootstrapContributor.test.js +20 -38
- package/test/usersRouteRequestInputValidator.test.js +2 -43
- package/test/usersRouteResources.test.js +2 -20
- package/test-support/registerDefaultSettingsFields.js +0 -1
- package/src/server/UsersWorkspacesServiceProvider.js +0 -44
- package/src/server/common/contributors/workspaceActionContextContributor.js +0 -88
- package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +0 -34
- package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +0 -78
- package/src/server/common/formatters/workspaceFormatter.js +0 -53
- package/src/server/common/repositories/workspaceInvitesRepository.js +0 -208
- package/src/server/common/repositories/workspaceMembershipsRepository.js +0 -190
- package/src/server/common/repositories/workspacesRepository.js +0 -202
- package/src/server/common/services/workspaceContextService.js +0 -281
- package/src/server/common/support/workspaceRoutePaths.js +0 -17
- package/src/server/common/validators/routeParamsValidator.js +0 -62
- package/src/server/consoleSettings/bootConsoleSettingsRoutes.js +0 -63
- package/src/server/consoleSettings/consoleService.js +0 -36
- package/src/server/consoleSettings/consoleSettingsActions.js +0 -55
- package/src/server/consoleSettings/consoleSettingsRepository.js +0 -115
- package/src/server/consoleSettings/consoleSettingsService.js +0 -40
- package/src/server/consoleSettings/registerConsoleSettings.js +0 -56
- package/src/server/registerWorkspaceBootstrap.js +0 -27
- package/src/server/registerWorkspaceCore.js +0 -73
- package/src/server/registerWorkspaceRepositories.js +0 -26
- package/src/server/support/resolveWorkspace.js +0 -16
- package/src/server/support/workspaceActionSurfaces.js +0 -135
- package/src/server/support/workspaceInvitationsPolicy.js +0 -45
- package/src/server/support/workspaceRouteInput.js +0 -22
- package/src/server/workspaceBootstrapContributor.js +0 -211
- package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +0 -133
- package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +0 -19
- package/src/server/workspaceDirectory/workspaceDirectoryActions.js +0 -133
- package/src/server/workspaceMembers/bootWorkspaceMembers.js +0 -236
- package/src/server/workspaceMembers/registerWorkspaceMembers.js +0 -108
- package/src/server/workspaceMembers/workspaceMembersActions.js +0 -186
- package/src/server/workspaceMembers/workspaceMembersService.js +0 -222
- package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +0 -62
- package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +0 -119
- package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +0 -74
- package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +0 -138
- package/src/server/workspaceSettings/bootWorkspaceSettings.js +0 -76
- package/src/server/workspaceSettings/registerWorkspaceSettings.js +0 -62
- package/src/server/workspaceSettings/workspaceSettingsActions.js +0 -72
- package/src/server/workspaceSettings/workspaceSettingsRepository.js +0 -154
- package/src/server/workspaceSettings/workspaceSettingsService.js +0 -66
- package/src/shared/resources/consoleSettingsFields.js +0 -54
- package/src/shared/resources/consoleSettingsResource.js +0 -119
- package/src/shared/resources/workspaceMembersResource.js +0 -354
- package/src/shared/resources/workspacePendingInvitationsResource.js +0 -82
- package/src/shared/resources/workspaceResource.js +0 -176
- package/src/shared/resources/workspaceSettingsFields.js +0 -59
- package/src/shared/resources/workspaceSettingsResource.js +0 -169
- package/src/shared/roles.js +0 -161
- package/src/shared/support/usersApiPaths.js +0 -43
- package/src/shared/support/usersVisibility.js +0 -42
- package/src/shared/support/workspacePathModel.js +0 -145
- package/src/shared/tenancyMode.js +0 -35
- package/src/shared/tenancyProfile.js +0 -73
- package/templates/migrations/users_core_console_owner.cjs +0 -37
- package/templates/packages/main/src/shared/resources/consoleSettingsFields.js +0 -11
- package/test/consoleService.test.js +0 -57
- package/test/consoleSettingsService.test.js +0 -86
- package/test/registerWorkspaceDirectory.test.js +0 -31
- package/test/registerWorkspaceSettings.test.js +0 -40
- package/test/roles.test.js +0 -159
- package/test/tenancyProfile.test.js +0 -67
- package/test/usersApiPaths.test.js +0 -49
- package/test/usersRouteValidators.test.js +0 -49
- package/test/usersVisibility.test.js +0 -27
- package/test/workspaceActionContextContributor.test.js +0 -344
- package/test/workspaceActionSurfaces.test.js +0 -105
- package/test/workspaceAuthPolicyContextResolver.test.js +0 -119
- package/test/workspaceBootstrapContributor.test.js +0 -154
- package/test/workspaceInvitationsPolicy.test.js +0 -71
- package/test/workspaceInvitesRepository.test.js +0 -111
- package/test/workspaceMembersService.test.js +0 -398
- package/test/workspacePathModel.test.js +0 -93
- package/test/workspacePendingInvitationsResource.test.js +0 -38
- package/test/workspacePendingInvitationsService.test.js +0 -151
- package/test/workspaceRouteVisibilityResolver.test.js +0 -83
- package/test/workspaceService.test.js +0 -546
- package/test/workspaceSettingsActions.test.js +0 -52
- package/test/workspaceSettingsRepository.test.js +0 -202
- package/test/workspaceSettingsResource.test.js +0 -169
- package/test/workspaceSettingsService.test.js +0 -140
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { createWorkspaceAuthPolicyContextResolver } from "../src/server/common/contributors/workspaceAuthPolicyContextResolver.js";
|
|
4
|
-
|
|
5
|
-
test("workspace auth policy context resolver returns empty context when policy does not require workspace context", async () => {
|
|
6
|
-
const resolver = createWorkspaceAuthPolicyContextResolver({
|
|
7
|
-
workspaceService: {
|
|
8
|
-
async resolveWorkspaceContextForUserBySlug() {
|
|
9
|
-
throw new Error("must not be called");
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
const resolved = await resolver({
|
|
15
|
-
request: {
|
|
16
|
-
params: {
|
|
17
|
-
workspaceSlug: "acme"
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
actor: {
|
|
21
|
-
id: 7
|
|
22
|
-
},
|
|
23
|
-
meta: {
|
|
24
|
-
contextPolicy: "none",
|
|
25
|
-
permission: ""
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
assert.deepEqual(resolved, {});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("workspace auth policy context resolver resolves workspace context from users workspace service", async () => {
|
|
33
|
-
const calls = [];
|
|
34
|
-
const resolver = createWorkspaceAuthPolicyContextResolver({
|
|
35
|
-
workspaceService: {
|
|
36
|
-
async resolveWorkspaceContextForUserBySlug(actor, workspaceSlug, options) {
|
|
37
|
-
calls.push({
|
|
38
|
-
actor,
|
|
39
|
-
workspaceSlug,
|
|
40
|
-
options
|
|
41
|
-
});
|
|
42
|
-
return {
|
|
43
|
-
workspace: {
|
|
44
|
-
id: 11,
|
|
45
|
-
slug: workspaceSlug
|
|
46
|
-
},
|
|
47
|
-
membership: {
|
|
48
|
-
roleSid: "owner"
|
|
49
|
-
},
|
|
50
|
-
permissions: ["projects.read"]
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const request = {
|
|
57
|
-
params: {
|
|
58
|
-
workspaceSlug: "ACME"
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
const actor = {
|
|
62
|
-
id: 7
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const resolved = await resolver({
|
|
66
|
-
request,
|
|
67
|
-
actor,
|
|
68
|
-
meta: {
|
|
69
|
-
contextPolicy: "required"
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
assert.deepEqual(calls, [
|
|
74
|
-
{
|
|
75
|
-
actor,
|
|
76
|
-
workspaceSlug: "acme",
|
|
77
|
-
options: {
|
|
78
|
-
request
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
]);
|
|
82
|
-
assert.deepEqual(resolved, {
|
|
83
|
-
workspace: {
|
|
84
|
-
id: 11,
|
|
85
|
-
slug: "acme"
|
|
86
|
-
},
|
|
87
|
-
membership: {
|
|
88
|
-
roleSid: "owner"
|
|
89
|
-
},
|
|
90
|
-
permissions: ["projects.read"]
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test("workspace auth policy context resolver skips workspace lookup when workspace slug is absent", async () => {
|
|
95
|
-
let called = false;
|
|
96
|
-
const resolver = createWorkspaceAuthPolicyContextResolver({
|
|
97
|
-
workspaceService: {
|
|
98
|
-
async resolveWorkspaceContextForUserBySlug() {
|
|
99
|
-
called = true;
|
|
100
|
-
return {};
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const resolved = await resolver({
|
|
106
|
-
request: {
|
|
107
|
-
params: {}
|
|
108
|
-
},
|
|
109
|
-
actor: {
|
|
110
|
-
id: 7
|
|
111
|
-
},
|
|
112
|
-
meta: {
|
|
113
|
-
contextPolicy: "required"
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
assert.equal(called, false);
|
|
118
|
-
assert.deepEqual(resolved, {});
|
|
119
|
-
});
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { createWorkspaceBootstrapContributor } from "../src/server/workspaceBootstrapContributor.js";
|
|
4
|
-
|
|
5
|
-
function createAuthenticatedProfile(overrides = {}) {
|
|
6
|
-
return {
|
|
7
|
-
id: "7",
|
|
8
|
-
authProvider: "local",
|
|
9
|
-
authProviderUserSid: "user-7",
|
|
10
|
-
username: "tester",
|
|
11
|
-
displayName: "Test User",
|
|
12
|
-
email: "test@example.com",
|
|
13
|
-
...overrides
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
test("workspace bootstrap contributor passes actor context to pending invites service", async () => {
|
|
18
|
-
const profile = createAuthenticatedProfile();
|
|
19
|
-
const pendingServiceCalls = [];
|
|
20
|
-
const contributor = createWorkspaceBootstrapContributor({
|
|
21
|
-
workspaceService: {
|
|
22
|
-
async listWorkspacesForUser() {
|
|
23
|
-
return [];
|
|
24
|
-
},
|
|
25
|
-
async resolveWorkspaceContextForUserBySlug() {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
workspacePendingInvitationsService: {
|
|
30
|
-
async listPendingInvitesForUser(user, options = {}) {
|
|
31
|
-
pendingServiceCalls.push({
|
|
32
|
-
user,
|
|
33
|
-
options
|
|
34
|
-
});
|
|
35
|
-
return [];
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
usersRepository: {
|
|
39
|
-
async findById() {
|
|
40
|
-
return profile;
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
workspaceInvitationsEnabled: true,
|
|
44
|
-
appConfig: {
|
|
45
|
-
tenancyMode: "workspaces"
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
await contributor.contribute({
|
|
50
|
-
payload: {
|
|
51
|
-
session: {
|
|
52
|
-
authenticated: true,
|
|
53
|
-
userId: profile.id
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
assert.equal(pendingServiceCalls.length, 1);
|
|
59
|
-
assert.equal(pendingServiceCalls[0].user.id, profile.id);
|
|
60
|
-
assert.equal(pendingServiceCalls[0].options?.context?.actor?.id, profile.id);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("workspace bootstrap contributor resolves workspace slug from bootstrap query", async () => {
|
|
64
|
-
const profile = createAuthenticatedProfile();
|
|
65
|
-
const calls = [];
|
|
66
|
-
const contributor = createWorkspaceBootstrapContributor({
|
|
67
|
-
workspaceService: {
|
|
68
|
-
async listWorkspacesForUser() {
|
|
69
|
-
return [];
|
|
70
|
-
},
|
|
71
|
-
async resolveWorkspaceContextForUserBySlug(_user, workspaceSlug) {
|
|
72
|
-
calls.push(workspaceSlug);
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
workspacePendingInvitationsService: {
|
|
77
|
-
async listPendingInvitesForUser() {
|
|
78
|
-
return [];
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
usersRepository: {
|
|
82
|
-
async findById() {
|
|
83
|
-
return profile;
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
workspaceInvitationsEnabled: true,
|
|
87
|
-
appConfig: {
|
|
88
|
-
tenancyMode: "workspaces"
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const payload = await contributor.contribute({
|
|
93
|
-
query: {
|
|
94
|
-
workspaceSlug: " AcMe "
|
|
95
|
-
},
|
|
96
|
-
payload: {
|
|
97
|
-
session: {
|
|
98
|
-
authenticated: true,
|
|
99
|
-
userId: profile.id
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
assert.deepEqual(calls, ["acme"]);
|
|
105
|
-
assert.deepEqual(payload.requestedWorkspace, {
|
|
106
|
-
slug: "acme",
|
|
107
|
-
status: "resolved"
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test("workspace bootstrap contributor reports unauthenticated requested workspace without generic bootstrap work", async () => {
|
|
112
|
-
const contributor = createWorkspaceBootstrapContributor({
|
|
113
|
-
workspaceService: {
|
|
114
|
-
async listWorkspacesForUser() {
|
|
115
|
-
assert.fail("listWorkspacesForUser should not run for unauthenticated payloads");
|
|
116
|
-
},
|
|
117
|
-
async resolveWorkspaceContextForUserBySlug() {
|
|
118
|
-
assert.fail("resolveWorkspaceContextForUserBySlug should not run for unauthenticated payloads");
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
workspacePendingInvitationsService: {
|
|
122
|
-
async listPendingInvitesForUser() {
|
|
123
|
-
assert.fail("listPendingInvitesForUser should not run for unauthenticated payloads");
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
usersRepository: {
|
|
127
|
-
async findById() {
|
|
128
|
-
assert.fail("findById should not run for unauthenticated payloads");
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
workspaceInvitationsEnabled: true,
|
|
132
|
-
appConfig: {
|
|
133
|
-
tenancyMode: "workspaces"
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
const payload = await contributor.contribute({
|
|
138
|
-
query: {
|
|
139
|
-
workspaceSlug: "AcMe"
|
|
140
|
-
},
|
|
141
|
-
payload: {
|
|
142
|
-
session: {
|
|
143
|
-
authenticated: false
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
assert.deepEqual(payload, {
|
|
149
|
-
requestedWorkspace: {
|
|
150
|
-
slug: "acme",
|
|
151
|
-
status: "unauthenticated"
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
});
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { resolveWorkspaceInvitationsPolicy } from "../src/server/support/workspaceInvitationsPolicy.js";
|
|
4
|
-
|
|
5
|
-
test("workspace invitations policy enables invitations by default in personal mode", () => {
|
|
6
|
-
const policy = resolveWorkspaceInvitationsPolicy({
|
|
7
|
-
appConfig: {},
|
|
8
|
-
tenancyProfile: {
|
|
9
|
-
mode: "personal",
|
|
10
|
-
workspace: {
|
|
11
|
-
enabled: true
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
assert.equal(policy.enabled, true);
|
|
17
|
-
assert.equal(policy.allowInPersonalMode, true);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test("workspace invitations policy disables invitations in personal mode when explicitly configured", () => {
|
|
21
|
-
const policy = resolveWorkspaceInvitationsPolicy({
|
|
22
|
-
appConfig: {
|
|
23
|
-
workspaceInvitations: {
|
|
24
|
-
allowInPersonalMode: false
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
tenancyProfile: {
|
|
28
|
-
mode: "personal",
|
|
29
|
-
workspace: {
|
|
30
|
-
enabled: true
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
assert.equal(policy.enabled, false);
|
|
36
|
-
assert.equal(policy.allowInPersonalMode, false);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("workspace invitations policy disables invitations when workspace mode is disabled", () => {
|
|
40
|
-
const policy = resolveWorkspaceInvitationsPolicy({
|
|
41
|
-
appConfig: {},
|
|
42
|
-
tenancyProfile: {
|
|
43
|
-
mode: "none",
|
|
44
|
-
workspace: {
|
|
45
|
-
enabled: false
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
assert.equal(policy.enabled, false);
|
|
51
|
-
assert.equal(policy.workspaceEnabled, false);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test("workspace invitations policy disables invitations when app config disables feature", () => {
|
|
55
|
-
const policy = resolveWorkspaceInvitationsPolicy({
|
|
56
|
-
appConfig: {
|
|
57
|
-
workspaceInvitations: {
|
|
58
|
-
enabled: false,
|
|
59
|
-
allowInPersonalMode: true
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
tenancyProfile: {
|
|
63
|
-
mode: "workspace",
|
|
64
|
-
workspace: {
|
|
65
|
-
enabled: true
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
assert.equal(policy.enabled, false);
|
|
71
|
-
});
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { createRepository } from "../src/server/common/repositories/workspaceInvitesRepository.js";
|
|
4
|
-
|
|
5
|
-
function createKnexStub() {
|
|
6
|
-
const state = {
|
|
7
|
-
insertPayload: null
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const row = {
|
|
11
|
-
id: 1,
|
|
12
|
-
workspace_id: 1,
|
|
13
|
-
email: "invitee@example.com",
|
|
14
|
-
role_sid: "member",
|
|
15
|
-
status: "pending",
|
|
16
|
-
token_hash: "hash",
|
|
17
|
-
invited_by_user_id: 1,
|
|
18
|
-
expires_at: "2026-03-16 00:26:35.709",
|
|
19
|
-
accepted_at: null,
|
|
20
|
-
revoked_at: null,
|
|
21
|
-
created_at: "2026-03-09 00:26:35.710",
|
|
22
|
-
updated_at: "2026-03-09 00:26:35.710"
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
function tableBuilder(tableName) {
|
|
26
|
-
assert.equal(tableName, "workspace_invites");
|
|
27
|
-
return {
|
|
28
|
-
insert(payload) {
|
|
29
|
-
state.insertPayload = payload;
|
|
30
|
-
return Promise.resolve([1]);
|
|
31
|
-
},
|
|
32
|
-
where(criteria) {
|
|
33
|
-
assert.equal(typeof criteria, "object");
|
|
34
|
-
return {
|
|
35
|
-
first() {
|
|
36
|
-
return Promise.resolve({ ...row });
|
|
37
|
-
},
|
|
38
|
-
orderBy() {
|
|
39
|
-
return {
|
|
40
|
-
first() {
|
|
41
|
-
return Promise.resolve({ ...row });
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return { knexStub: tableBuilder, state };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
test("workspaceInvitesRepository.insert normalizes expiresAt ISO input to database datetime", async () => {
|
|
54
|
-
const { knexStub, state } = createKnexStub();
|
|
55
|
-
const repository = createRepository(knexStub);
|
|
56
|
-
|
|
57
|
-
await repository.insert({
|
|
58
|
-
workspaceId: "1",
|
|
59
|
-
email: "invitee@example.com",
|
|
60
|
-
roleSid: "member",
|
|
61
|
-
status: "pending",
|
|
62
|
-
tokenHash: "hash",
|
|
63
|
-
invitedByUserId: "1",
|
|
64
|
-
expiresAt: "2026-03-16T00:26:35.709Z"
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
assert.equal(state.insertPayload.expires_at, "2026-03-16 00:26:35.709");
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("workspaceInvitesRepository.findPendingByTokenHash reads from invites table without workspace join", async () => {
|
|
71
|
-
const calls = {
|
|
72
|
-
tableName: "",
|
|
73
|
-
whereCriteria: null
|
|
74
|
-
};
|
|
75
|
-
const row = {
|
|
76
|
-
id: 44,
|
|
77
|
-
workspace_id: 9,
|
|
78
|
-
email: "invitee@example.com",
|
|
79
|
-
role_sid: "member",
|
|
80
|
-
status: "pending",
|
|
81
|
-
token_hash: "hash-token",
|
|
82
|
-
invited_by_user_id: 1,
|
|
83
|
-
expires_at: "2030-01-01 00:00:00.000",
|
|
84
|
-
accepted_at: null,
|
|
85
|
-
revoked_at: null,
|
|
86
|
-
created_at: "2026-03-09 00:26:35.710",
|
|
87
|
-
updated_at: "2026-03-09 00:26:35.710"
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const repository = createRepository((tableName) => {
|
|
91
|
-
calls.tableName = String(tableName || "");
|
|
92
|
-
return {
|
|
93
|
-
where(criteria) {
|
|
94
|
-
calls.whereCriteria = criteria;
|
|
95
|
-
return this;
|
|
96
|
-
},
|
|
97
|
-
first() {
|
|
98
|
-
return Promise.resolve({ ...row });
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
const invite = await repository.findPendingByTokenHash("hash-token");
|
|
104
|
-
assert.equal(calls.tableName, "workspace_invites");
|
|
105
|
-
assert.deepEqual(calls.whereCriteria, {
|
|
106
|
-
token_hash: "hash-token",
|
|
107
|
-
status: "pending"
|
|
108
|
-
});
|
|
109
|
-
assert.equal(invite?.workspaceId, "9");
|
|
110
|
-
assert.equal(invite?.workspaceSlug, undefined);
|
|
111
|
-
});
|