@selvajs/server 1.2.0 → 1.3.0
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/dist/access/guards.d.ts +213 -0
- package/dist/access/guards.d.ts.map +1 -0
- package/dist/access/guards.js +417 -0
- package/dist/access/guards.js.map +1 -0
- package/dist/access/index.d.ts +2 -0
- package/dist/access/index.d.ts.map +1 -1
- package/dist/access/index.js +6 -0
- package/dist/access/index.js.map +1 -1
- package/dist/access/permissions-scope.d.ts +15 -0
- package/dist/access/permissions-scope.d.ts.map +1 -0
- package/dist/access/permissions-scope.js +23 -0
- package/dist/access/permissions-scope.js.map +1 -0
- package/dist/api/bodies.d.ts +104 -0
- package/dist/api/bodies.d.ts.map +1 -0
- package/dist/api/bodies.js +87 -0
- package/dist/api/bodies.js.map +1 -0
- package/dist/api/deps.d.ts +135 -0
- package/dist/api/deps.d.ts.map +1 -0
- package/dist/api/deps.js +55 -0
- package/dist/api/deps.js.map +1 -0
- package/dist/api/errors.d.ts +35 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +63 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/index.d.ts +12 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +15 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/map-core-error.d.ts +19 -0
- package/dist/api/map-core-error.d.ts.map +1 -0
- package/dist/api/map-core-error.js +49 -0
- package/dist/api/map-core-error.js.map +1 -0
- package/dist/api/pagination.d.ts +19 -0
- package/dist/api/pagination.d.ts.map +1 -0
- package/dist/api/pagination.js +40 -0
- package/dist/api/pagination.js.map +1 -0
- package/dist/api/request.d.ts +93 -0
- package/dist/api/request.d.ts.map +1 -0
- package/dist/api/request.js +150 -0
- package/dist/api/request.js.map +1 -0
- package/dist/api/respond.d.ts +29 -0
- package/dist/api/respond.d.ts.map +1 -0
- package/dist/api/respond.js +59 -0
- package/dist/api/respond.js.map +1 -0
- package/dist/api/responses-schema.d.ts +158 -0
- package/dist/api/responses-schema.d.ts.map +1 -0
- package/dist/api/responses-schema.js +104 -0
- package/dist/api/responses-schema.js.map +1 -0
- package/dist/api/responses.d.ts +19 -0
- package/dist/api/responses.d.ts.map +1 -0
- package/dist/api/responses.js +21 -0
- package/dist/api/responses.js.map +1 -0
- package/dist/api/shaped.d.ts +23 -0
- package/dist/api/shaped.d.ts.map +1 -0
- package/dist/api/shaped.js +29 -0
- package/dist/api/shaped.js.map +1 -0
- package/dist/api/types.d.ts +41 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +12 -0
- package/dist/api/types.js.map +1 -0
- package/dist/compute/errors.d.ts +18 -0
- package/dist/compute/errors.d.ts.map +1 -0
- package/dist/compute/errors.js +21 -0
- package/dist/compute/errors.js.map +1 -0
- package/dist/compute/evict-changed-servers.d.ts +22 -0
- package/dist/compute/evict-changed-servers.d.ts.map +1 -0
- package/dist/compute/evict-changed-servers.js +31 -0
- package/dist/compute/evict-changed-servers.js.map +1 -0
- package/dist/compute/idempotency-http.d.ts +47 -0
- package/dist/compute/idempotency-http.d.ts.map +1 -0
- package/dist/compute/idempotency-http.js +51 -0
- package/dist/compute/idempotency-http.js.map +1 -0
- package/dist/compute/index.d.ts +5 -0
- package/dist/compute/index.d.ts.map +1 -1
- package/dist/compute/index.js +5 -0
- package/dist/compute/index.js.map +1 -1
- package/dist/compute/resolve-server.d.ts +25 -0
- package/dist/compute/resolve-server.d.ts.map +1 -0
- package/dist/compute/resolve-server.js +35 -0
- package/dist/compute/resolve-server.js.map +1 -0
- package/dist/compute/server-config-write.d.ts +52 -0
- package/dist/compute/server-config-write.d.ts.map +1 -0
- package/dist/compute/server-config-write.js +100 -0
- package/dist/compute/server-config-write.js.map +1 -0
- package/dist/definitions/index.d.ts +1 -0
- package/dist/definitions/index.d.ts.map +1 -1
- package/dist/definitions/index.js +1 -0
- package/dist/definitions/index.js.map +1 -1
- package/dist/definitions/visibility.d.ts +74 -0
- package/dist/definitions/visibility.d.ts.map +1 -0
- package/dist/definitions/visibility.js +117 -0
- package/dist/definitions/visibility.js.map +1 -0
- package/dist/handlers/definitionVersions.d.ts +30 -0
- package/dist/handlers/definitionVersions.d.ts.map +1 -0
- package/dist/handlers/definitionVersions.js +93 -0
- package/dist/handlers/definitionVersions.js.map +1 -0
- package/dist/handlers/definitions.d.ts +35 -0
- package/dist/handlers/definitions.d.ts.map +1 -0
- package/dist/handlers/definitions.js +207 -0
- package/dist/handlers/definitions.js.map +1 -0
- package/dist/handlers/index.d.ts +27 -0
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/index.js +29 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/invite-lookup.d.ts +11 -0
- package/dist/handlers/invite-lookup.d.ts.map +1 -0
- package/dist/handlers/invite-lookup.js +20 -0
- package/dist/handlers/invite-lookup.js.map +1 -0
- package/dist/handlers/invites.d.ts +31 -0
- package/dist/handlers/invites.d.ts.map +1 -0
- package/dist/handlers/invites.js +148 -0
- package/dist/handlers/invites.js.map +1 -0
- package/dist/handlers/me.d.ts +8 -0
- package/dist/handlers/me.d.ts.map +1 -0
- package/dist/handlers/me.js +21 -0
- package/dist/handlers/me.js.map +1 -0
- package/dist/handlers/me.starred.d.ts +11 -0
- package/dist/handlers/me.starred.d.ts.map +1 -0
- package/dist/handlers/me.starred.js +21 -0
- package/dist/handlers/me.starred.js.map +1 -0
- package/dist/handlers/orgAssets.d.ts +13 -0
- package/dist/handlers/orgAssets.d.ts.map +1 -0
- package/dist/handlers/orgAssets.js +54 -0
- package/dist/handlers/orgAssets.js.map +1 -0
- package/dist/handlers/orgCompute.d.ts +16 -0
- package/dist/handlers/orgCompute.d.ts.map +1 -0
- package/dist/handlers/orgCompute.js +96 -0
- package/dist/handlers/orgCompute.js.map +1 -0
- package/dist/handlers/orgMembers.d.ts +29 -0
- package/dist/handlers/orgMembers.d.ts.map +1 -0
- package/dist/handlers/orgMembers.js +200 -0
- package/dist/handlers/orgMembers.js.map +1 -0
- package/dist/handlers/orgs.d.ts +16 -0
- package/dist/handlers/orgs.d.ts.map +1 -0
- package/dist/handlers/orgs.js +26 -0
- package/dist/handlers/orgs.js.map +1 -0
- package/dist/handlers/projectMembers.d.ts +15 -0
- package/dist/handlers/projectMembers.d.ts.map +1 -0
- package/dist/handlers/projectMembers.js +95 -0
- package/dist/handlers/projectMembers.js.map +1 -0
- package/dist/handlers/projects.d.ts +21 -0
- package/dist/handlers/projects.d.ts.map +1 -0
- package/dist/handlers/projects.js +164 -0
- package/dist/handlers/projects.js.map +1 -0
- package/dist/handlers/reclaim.d.ts +10 -0
- package/dist/handlers/reclaim.d.ts.map +1 -0
- package/dist/handlers/reclaim.js +38 -0
- package/dist/handlers/reclaim.js.map +1 -0
- package/dist/handlers/services.d.ts +22 -0
- package/dist/handlers/services.d.ts.map +1 -0
- package/dist/handlers/services.js +37 -0
- package/dist/handlers/services.js.map +1 -0
- package/dist/handlers/shareLinks.d.ts +23 -0
- package/dist/handlers/shareLinks.d.ts.map +1 -0
- package/dist/handlers/shareLinks.js +85 -0
- package/dist/handlers/shareLinks.js.map +1 -0
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +1 -0
- package/dist/http/index.js.map +1 -1
- package/dist/http/login-rate-limit.d.ts +72 -0
- package/dist/http/login-rate-limit.d.ts.map +1 -0
- package/dist/http/login-rate-limit.js +84 -0
- package/dist/http/login-rate-limit.js.map +1 -0
- package/dist/notifications/deliver-invite.d.ts +27 -0
- package/dist/notifications/deliver-invite.d.ts.map +1 -0
- package/dist/notifications/deliver-invite.js +32 -0
- package/dist/notifications/deliver-invite.js.map +1 -0
- package/dist/notifications/index.d.ts +1 -0
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +1 -0
- package/dist/notifications/index.js.map +1 -1
- package/dist/organizations/index.d.ts +3 -0
- package/dist/organizations/index.d.ts.map +1 -0
- package/dist/organizations/index.js +5 -0
- package/dist/organizations/index.js.map +1 -0
- package/dist/organizations/list-all-members.d.ts +10 -0
- package/dist/organizations/list-all-members.d.ts.map +1 -0
- package/dist/organizations/list-all-members.js +36 -0
- package/dist/organizations/list-all-members.js.map +1 -0
- package/dist/organizations/org-asset-service.d.ts +39 -0
- package/dist/organizations/org-asset-service.d.ts.map +1 -0
- package/dist/organizations/org-asset-service.js +71 -0
- package/dist/organizations/org-asset-service.js.map +1 -0
- package/dist/projects/create-project.d.ts +30 -0
- package/dist/projects/create-project.d.ts.map +1 -0
- package/dist/projects/create-project.js +56 -0
- package/dist/projects/create-project.js.map +1 -0
- package/dist/projects/index.d.ts +2 -0
- package/dist/projects/index.d.ts.map +1 -0
- package/dist/projects/index.js +3 -0
- package/dist/projects/index.js.map +1 -0
- package/dist/providers/auth-lookup.d.ts +14 -0
- package/dist/providers/auth-lookup.d.ts.map +1 -0
- package/dist/providers/auth-lookup.js +34 -0
- package/dist/providers/auth-lookup.js.map +1 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +1 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/testing/harness.d.ts +168 -0
- package/dist/testing/harness.d.ts.map +1 -0
- package/dist/testing/harness.js +314 -0
- package/dist/testing/harness.js.map +1 -0
- package/dist/testing/index.d.ts +18 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +15 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/scenarios.d.ts +67 -0
- package/dist/testing/scenarios.d.ts.map +1 -0
- package/dist/testing/scenarios.js +104 -0
- package/dist/testing/scenarios.js.map +1 -0
- package/dist/testing/seed-adapter.d.ts +31 -0
- package/dist/testing/seed-adapter.d.ts.map +1 -0
- package/dist/testing/seed-adapter.js +19 -0
- package/dist/testing/seed-adapter.js.map +1 -0
- package/package.json +30 -2
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A host-agnostic harness for testing API handlers.
|
|
3
|
+
*
|
|
4
|
+
* Handlers are transport-free, but the tests around them were not: they built
|
|
5
|
+
* a SvelteKit `RequestEvent` and imported the Selva app's fixtures, so a second
|
|
6
|
+
* host could run the handlers and inherit none of the tests proving they work.
|
|
7
|
+
* That is the duplication this removes — a host wires a `TestHarness` once and
|
|
8
|
+
* gets the shared suites.
|
|
9
|
+
*
|
|
10
|
+
* Two things stay injected because no interface supplies them:
|
|
11
|
+
* - {@link SeedAuthAdapter}, since identity has no portable creation method.
|
|
12
|
+
* - `mapError`, since folding a host's own domain errors into the envelope is
|
|
13
|
+
* by definition host-specific. Omitting it is not neutral: a host whose
|
|
14
|
+
* guards throw something `runHandler` does not recognize gets a 500 where
|
|
15
|
+
* production returns 403, and every status assertion silently weakens.
|
|
16
|
+
*/
|
|
17
|
+
import { randomUUID } from 'node:crypto';
|
|
18
|
+
import { SYSTEM_CONTEXT, DEFAULT_ORG_PERMISSIONS, DEFAULT_SHARE_LINK_MAX_SOLVES, emptyProfile } from '@selvajs/platform';
|
|
19
|
+
import { depsFromConfig, runHandler } from '../api/index.js';
|
|
20
|
+
/**
|
|
21
|
+
* Swallows output unless `LOUD=1`.
|
|
22
|
+
*
|
|
23
|
+
* `runHandler` logs through `req.log` on the 500 path. Without a logger a test
|
|
24
|
+
* that trips an unexpected 500 dies inside the error handler itself, which
|
|
25
|
+
* hides the error that caused it.
|
|
26
|
+
*/
|
|
27
|
+
export const silentLog = {
|
|
28
|
+
error: (...args) => {
|
|
29
|
+
if (process.env.LOUD)
|
|
30
|
+
console.error('[test log]', ...args);
|
|
31
|
+
},
|
|
32
|
+
warn: () => { },
|
|
33
|
+
info: () => { },
|
|
34
|
+
debug: () => { },
|
|
35
|
+
child: () => silentLog
|
|
36
|
+
};
|
|
37
|
+
export async function seedUser(h, email, platformPermissions = []) {
|
|
38
|
+
// Identity row then data-layer row, in that order — the sequence production
|
|
39
|
+
// runs through its auth hook. Reversing it leaves a data row keyed to an id
|
|
40
|
+
// no identity claims.
|
|
41
|
+
const u = await h.auth.createUser(email);
|
|
42
|
+
await h.config.data.ensureUser(SYSTEM_CONTEXT, u.id);
|
|
43
|
+
if (platformPermissions.length > 0) {
|
|
44
|
+
await h.config.data.permissions.set(SYSTEM_CONTEXT, u.id, platformPermissions);
|
|
45
|
+
}
|
|
46
|
+
// `AuthUser.email` is optional — a header-auth provider need not report one.
|
|
47
|
+
// The seeded email is the caller's own input, so hand that back rather than
|
|
48
|
+
// widening every test's `email` to `string | undefined`.
|
|
49
|
+
return { id: u.id, email };
|
|
50
|
+
}
|
|
51
|
+
export async function seedOrg(h, opts) {
|
|
52
|
+
const now = new Date().toISOString();
|
|
53
|
+
const org = {
|
|
54
|
+
id: randomUUID(),
|
|
55
|
+
name: opts.name,
|
|
56
|
+
slug: opts.slug,
|
|
57
|
+
ownerId: opts.ownerId,
|
|
58
|
+
createdBy: opts.ownerId,
|
|
59
|
+
updatedBy: opts.ownerId,
|
|
60
|
+
createdAt: now,
|
|
61
|
+
updatedAt: now,
|
|
62
|
+
deletedAt: null
|
|
63
|
+
};
|
|
64
|
+
await h.config.data.orgs.createOrg(SYSTEM_CONTEXT, org);
|
|
65
|
+
return org;
|
|
66
|
+
}
|
|
67
|
+
export async function seedOrgMember(h, opts) {
|
|
68
|
+
const now = new Date().toISOString();
|
|
69
|
+
const member = {
|
|
70
|
+
orgId: opts.orgId,
|
|
71
|
+
userId: opts.userId,
|
|
72
|
+
role: opts.role,
|
|
73
|
+
permissions: opts.permissions ?? [...DEFAULT_ORG_PERMISSIONS[opts.role]],
|
|
74
|
+
joinedAt: now,
|
|
75
|
+
updatedAt: now,
|
|
76
|
+
updatedBy: opts.userId,
|
|
77
|
+
deletedAt: null
|
|
78
|
+
};
|
|
79
|
+
await h.config.data.orgs.addOrgMember(SYSTEM_CONTEXT, member);
|
|
80
|
+
return member;
|
|
81
|
+
}
|
|
82
|
+
export async function seedProject(h, opts) {
|
|
83
|
+
const now = new Date().toISOString();
|
|
84
|
+
const project = {
|
|
85
|
+
id: randomUUID(),
|
|
86
|
+
orgId: opts.orgId,
|
|
87
|
+
name: opts.name,
|
|
88
|
+
slug: opts.slug,
|
|
89
|
+
visibility: opts.visibility ?? 'private',
|
|
90
|
+
ownerId: opts.ownerId,
|
|
91
|
+
createdBy: opts.ownerId,
|
|
92
|
+
updatedBy: opts.ownerId,
|
|
93
|
+
autoJoinOnUpload: opts.autoJoinOnUpload ?? false,
|
|
94
|
+
createdAt: now,
|
|
95
|
+
updatedAt: now,
|
|
96
|
+
deletedAt: null
|
|
97
|
+
};
|
|
98
|
+
await h.config.data.projects.createProject(SYSTEM_CONTEXT, project);
|
|
99
|
+
await h.config.data.projects.addProjectMember(SYSTEM_CONTEXT, {
|
|
100
|
+
projectId: project.id,
|
|
101
|
+
userId: opts.ownerId,
|
|
102
|
+
role: 'owner',
|
|
103
|
+
joinedAt: now,
|
|
104
|
+
updatedAt: now,
|
|
105
|
+
updatedBy: opts.ownerId,
|
|
106
|
+
deletedAt: null
|
|
107
|
+
});
|
|
108
|
+
return project;
|
|
109
|
+
}
|
|
110
|
+
export async function seedProjectMember(h, opts) {
|
|
111
|
+
const now = new Date().toISOString();
|
|
112
|
+
const member = {
|
|
113
|
+
projectId: opts.projectId,
|
|
114
|
+
userId: opts.userId,
|
|
115
|
+
role: opts.role,
|
|
116
|
+
joinedAt: now,
|
|
117
|
+
updatedAt: now,
|
|
118
|
+
updatedBy: opts.userId,
|
|
119
|
+
deletedAt: null
|
|
120
|
+
};
|
|
121
|
+
await h.config.data.projects.addProjectMember(SYSTEM_CONTEXT, member);
|
|
122
|
+
return member;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Grant platform-wide permissions to a seeded user.
|
|
126
|
+
*
|
|
127
|
+
* Replaces the whole set rather than adding to it, matching
|
|
128
|
+
* `IPermissionStore.set` — a test that means to add has to pass the existing
|
|
129
|
+
* permissions back in.
|
|
130
|
+
*/
|
|
131
|
+
export async function grantPlatformPermissions(h, userId, perms) {
|
|
132
|
+
await h.config.data.permissions.set(SYSTEM_CONTEXT, userId, perms);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A definition with one version, written through the host's `DefinitionService`
|
|
136
|
+
* rather than straight into the store.
|
|
137
|
+
*
|
|
138
|
+
* Going through the service is what makes the fixture match production: it is
|
|
139
|
+
* what allocates the version, writes the blob, and moves the channel pointer,
|
|
140
|
+
* and a test that seeded the record alone would exercise a definition no solve
|
|
141
|
+
* path can load.
|
|
142
|
+
*/
|
|
143
|
+
export async function seedDefinition(h, opts) {
|
|
144
|
+
const service = h.deps?.services?.definitions;
|
|
145
|
+
if (!service)
|
|
146
|
+
throw new Error('TestHarness.deps.services.definitions is required to seed one');
|
|
147
|
+
const input = {
|
|
148
|
+
guid: opts.guid ?? randomUUID(),
|
|
149
|
+
projectId: opts.projectId,
|
|
150
|
+
ownerId: opts.ownerId,
|
|
151
|
+
fileExt: 'gh',
|
|
152
|
+
displayName: opts.displayName ?? 'Test Definition',
|
|
153
|
+
originalFilename: 'test.gh'
|
|
154
|
+
};
|
|
155
|
+
const file = new TextEncoder().encode('FAKE_GH_BYTES');
|
|
156
|
+
// Admin-shaped ctx so the service's own tenancy checks don't refuse a seed.
|
|
157
|
+
const ctx = {
|
|
158
|
+
userId: opts.ownerId,
|
|
159
|
+
actingOrgId: undefined,
|
|
160
|
+
platformPermissions: ['instance_admin'],
|
|
161
|
+
orgPermissions: []
|
|
162
|
+
};
|
|
163
|
+
const schema = { name: 'Test', inputs: [], outputs: [] };
|
|
164
|
+
return service.create(ctx, input, file, schema);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* A share link plus the plaintext token behind it.
|
|
168
|
+
*
|
|
169
|
+
* Minted through the harness's own codec, so the stored hash verifies against
|
|
170
|
+
* the same secret a handler resolves from `deps.tokens.shareLinks` — a second
|
|
171
|
+
* codec would hash to something no handler could ever match.
|
|
172
|
+
*/
|
|
173
|
+
export async function seedShareLink(h, opts) {
|
|
174
|
+
const codec = h.deps?.tokens?.shareLinks;
|
|
175
|
+
if (!codec)
|
|
176
|
+
throw new Error('TestHarness.deps.tokens.shareLinks is required to seed a link');
|
|
177
|
+
const rawToken = codec.mintRawToken();
|
|
178
|
+
const link = {
|
|
179
|
+
id: randomUUID(),
|
|
180
|
+
definitionId: opts.definitionId,
|
|
181
|
+
channel: opts.channel ?? 'live',
|
|
182
|
+
tokenHash: codec.hashToken(rawToken),
|
|
183
|
+
createdBy: opts.createdBy,
|
|
184
|
+
createdAt: new Date().toISOString(),
|
|
185
|
+
expiresAt: opts.expiresAt ?? null,
|
|
186
|
+
revokedAt: null,
|
|
187
|
+
allowSolve: opts.allowSolve ?? true,
|
|
188
|
+
maxSolves: opts.maxSolves === undefined ? DEFAULT_SHARE_LINK_MAX_SOLVES : opts.maxSolves,
|
|
189
|
+
solveCount: 0
|
|
190
|
+
};
|
|
191
|
+
await h.config.data.shareLinks.create({
|
|
192
|
+
userId: opts.createdBy,
|
|
193
|
+
actingOrgId: undefined,
|
|
194
|
+
platformPermissions: ['instance_admin'],
|
|
195
|
+
orgPermissions: []
|
|
196
|
+
}, link);
|
|
197
|
+
return { link, rawToken };
|
|
198
|
+
}
|
|
199
|
+
// ============================================================================
|
|
200
|
+
// Acting identity
|
|
201
|
+
// ============================================================================
|
|
202
|
+
/**
|
|
203
|
+
* The harness a `callHandler` call should build its deps from, carried on the
|
|
204
|
+
* locals rather than passed alongside them.
|
|
205
|
+
*
|
|
206
|
+
* A test already threads `locals` from `actAs` into every call; making the
|
|
207
|
+
* harness a second argument would mean passing the same two values everywhere
|
|
208
|
+
* and would let them disagree — locals from one stack, deps from another, which
|
|
209
|
+
* type-checks and silently tests nothing.
|
|
210
|
+
*/
|
|
211
|
+
const HARNESS = Symbol.for('@selvajs/server/testing.harness');
|
|
212
|
+
/**
|
|
213
|
+
* Build the per-request identity a handler sees.
|
|
214
|
+
*
|
|
215
|
+
* Mirrors what a host's auth hook produces: one membership lookup, plus the
|
|
216
|
+
* instance-admin fallback to the first listed org for an admin who belongs to
|
|
217
|
+
* none. Keeping this aligned with the host is the point of testing against real
|
|
218
|
+
* stores rather than mocks — a fixture that invented its own `ctx` would prove
|
|
219
|
+
* the handler works under scope no request ever has.
|
|
220
|
+
*/
|
|
221
|
+
export async function actAs(h, userId) {
|
|
222
|
+
const user = await h.auth.findById(userId);
|
|
223
|
+
if (!user)
|
|
224
|
+
throw new Error(`actAs: user not found: ${userId}`);
|
|
225
|
+
const platformPermissions = await h.config.data.permissions.getFor(SYSTEM_CONTEXT, user.id);
|
|
226
|
+
const membership = await h.config.data.orgs.findUserMembership(SYSTEM_CONTEXT, user.id);
|
|
227
|
+
let actingOrgId = membership?.org.id;
|
|
228
|
+
const orgPermissions = membership ? [...membership.member.permissions] : [];
|
|
229
|
+
if (!actingOrgId && platformPermissions.includes('instance_admin')) {
|
|
230
|
+
const firstOrgPage = await h.config.data.orgs.listOrgs(SYSTEM_CONTEXT, { limit: 1 });
|
|
231
|
+
actingOrgId = firstOrgPage.items[0]?.id;
|
|
232
|
+
}
|
|
233
|
+
const profile = (await h.config.data.userProfile.getProfile(SYSTEM_CONTEXT, user.id)) ?? emptyProfile(user.id);
|
|
234
|
+
return {
|
|
235
|
+
user,
|
|
236
|
+
ctx: { userId: user.id, actingOrgId, platformPermissions, orgPermissions },
|
|
237
|
+
profile,
|
|
238
|
+
providers: h.config,
|
|
239
|
+
log: h.log ?? silentLog,
|
|
240
|
+
[HARNESS]: h
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
export function anon(h) {
|
|
244
|
+
return { providers: h.config, [HARNESS]: h };
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Invoke an `ApiHandler` with no host framework in the loop.
|
|
248
|
+
*
|
|
249
|
+
* Still goes through `runHandler`, because that is the contract: it is what
|
|
250
|
+
* turns a thrown `ApiError` — or a host error `mapError` recognizes — into a
|
|
251
|
+
* status. Calling the handler bare would make every failure an exception and
|
|
252
|
+
* every status assertion untestable.
|
|
253
|
+
*/
|
|
254
|
+
export async function callHandler(handler, opts) {
|
|
255
|
+
const url = new URL(opts.url ?? 'http://test.local/');
|
|
256
|
+
const init = {
|
|
257
|
+
method: opts.body !== undefined ? 'POST' : 'GET',
|
|
258
|
+
headers: opts.headers ?? {}
|
|
259
|
+
};
|
|
260
|
+
if (opts.body !== undefined) {
|
|
261
|
+
// FormData carries its own multipart content-type with a generated
|
|
262
|
+
// boundary; forcing JSON onto it makes `request.formData()` throw.
|
|
263
|
+
if (opts.body instanceof FormData) {
|
|
264
|
+
init.body = opts.body;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
init.headers['content-type'] ??= 'application/json';
|
|
268
|
+
init.body = typeof opts.body === 'string' ? opts.body : JSON.stringify(opts.body);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const locals = opts.locals;
|
|
272
|
+
const h = locals[HARNESS];
|
|
273
|
+
if (!h) {
|
|
274
|
+
// Without the harness there is no `mapError`, so every guard rejection
|
|
275
|
+
// would fall through to a 500 and each expected 403 would read as a
|
|
276
|
+
// passing-but-meaningless assertion. Fail loudly instead.
|
|
277
|
+
throw new Error('callHandler: locals carry no harness — build them with `actAs()` or `anon()`.');
|
|
278
|
+
}
|
|
279
|
+
const req = {
|
|
280
|
+
ctx: locals.ctx,
|
|
281
|
+
user: locals.user,
|
|
282
|
+
profile: locals.profile,
|
|
283
|
+
log: locals.log ?? h.log ?? silentLog,
|
|
284
|
+
params: opts.params ?? {},
|
|
285
|
+
url,
|
|
286
|
+
request: new Request(url.toString(), init),
|
|
287
|
+
deps: depsFromConfig(locals.providers, h.deps?.services ?? {}, {
|
|
288
|
+
tokens: h.deps?.tokens,
|
|
289
|
+
uploadLimits: h.deps?.uploadLimits
|
|
290
|
+
})
|
|
291
|
+
};
|
|
292
|
+
return readResponse(await runHandler(handler, req, { fallback: 'Test', mapError: h.mapError }));
|
|
293
|
+
}
|
|
294
|
+
async function readResponse(res) {
|
|
295
|
+
const contentType = res.headers.get('content-type') ?? '';
|
|
296
|
+
const out = { status: res.status, headers: res.headers };
|
|
297
|
+
if (res.headers.has('location'))
|
|
298
|
+
out.location = res.headers.get('location');
|
|
299
|
+
if (contentType.includes('application/json')) {
|
|
300
|
+
const text = await res.text();
|
|
301
|
+
out.text = text;
|
|
302
|
+
try {
|
|
303
|
+
out.json = text ? JSON.parse(text) : undefined;
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
// Leave it as text — a malformed body is the assertion's problem.
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
out.text = await res.text();
|
|
311
|
+
}
|
|
312
|
+
return out;
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=harness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../../src/testing/harness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test harness for the transport-free API handlers.
|
|
3
|
+
*
|
|
4
|
+
* A host builds a {@link TestHarness} around its own provider stack and gets
|
|
5
|
+
* the seeders, `actAs`, and `callHandler` that the handler tests are written
|
|
6
|
+
* against — so the tests travel with the handlers instead of being rewritten
|
|
7
|
+
* per host.
|
|
8
|
+
*
|
|
9
|
+
* This entry point constructs no provider. Which stack a test runs on is the
|
|
10
|
+
* host's decision, and a package that picked one would drag that provider into
|
|
11
|
+
* every consumer's dependency tree.
|
|
12
|
+
*/
|
|
13
|
+
export type { SeedAuthAdapter } from './seed-adapter.js';
|
|
14
|
+
export { seedUser, seedOrg, seedOrgMember, seedProject, seedProjectMember, seedDefinition, seedShareLink, grantPlatformPermissions, actAs, anon, callHandler, silentLog } from './harness.js';
|
|
15
|
+
export type { TestHarness, SeededUser, SeededDefinition, SeededShareLink, ActingLocals, CallHandlerOpts, CallResult } from './harness.js';
|
|
16
|
+
export { seedAcme, seedBigClient, seedThirdOrg, seedCommons } from './scenarios.js';
|
|
17
|
+
export type { AcmeFixture, BigClientFixture, ThirdOrgFixture, CommonsFixture } from './scenarios.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EACN,QAAQ,EACR,OAAO,EACP,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,wBAAwB,EACxB,KAAK,EACL,IAAI,EACJ,WAAW,EACX,SAAS,EACT,MAAM,cAAc,CAAC;AACtB,YAAY,EACX,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,eAAe,EACf,UAAU,EACV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACpF,YAAY,EACX,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test harness for the transport-free API handlers.
|
|
3
|
+
*
|
|
4
|
+
* A host builds a {@link TestHarness} around its own provider stack and gets
|
|
5
|
+
* the seeders, `actAs`, and `callHandler` that the handler tests are written
|
|
6
|
+
* against — so the tests travel with the handlers instead of being rewritten
|
|
7
|
+
* per host.
|
|
8
|
+
*
|
|
9
|
+
* This entry point constructs no provider. Which stack a test runs on is the
|
|
10
|
+
* host's decision, and a package that picked one would drag that provider into
|
|
11
|
+
* every consumer's dependency tree.
|
|
12
|
+
*/
|
|
13
|
+
export { seedUser, seedOrg, seedOrgMember, seedProject, seedProjectMember, seedDefinition, seedShareLink, grantPlatformPermissions, actAs, anon, callHandler, silentLog } from './harness.js';
|
|
14
|
+
export { seedAcme, seedBigClient, seedThirdOrg, seedCommons } from './scenarios.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EACN,QAAQ,EACR,OAAO,EACP,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,wBAAwB,EACxB,KAAK,EACL,IAAI,EACJ,WAAW,EACX,SAAS,EACT,MAAM,cAAc,CAAC;AAUtB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared cast the handler tests are written against.
|
|
3
|
+
*
|
|
4
|
+
* These compose only the seeders in `./harness.ts`, so they run on whatever
|
|
5
|
+
* provider stack the host built its {@link TestHarness} around. They live here
|
|
6
|
+
* rather than in a host's fixtures because a handler test that travels with its
|
|
7
|
+
* handler needs the same two tenants wherever it runs — a host that reseeds its
|
|
8
|
+
* own Acme with different roles gets different results from the same test.
|
|
9
|
+
*/
|
|
10
|
+
import type { Organization, Project } from '@selvajs/platform';
|
|
11
|
+
import type { SeededDefinition, SeededUser, TestHarness } from './harness.js';
|
|
12
|
+
export interface AcmeFixture {
|
|
13
|
+
acme: Organization;
|
|
14
|
+
alice: SeededUser;
|
|
15
|
+
bob: SeededUser;
|
|
16
|
+
alicesPrivate: Project;
|
|
17
|
+
acmeOrg: Project;
|
|
18
|
+
acmePublic: Project;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The primary tenant.
|
|
22
|
+
*
|
|
23
|
+
* **Alice is the org's `ownerId` but her membership row is `admin`.** Those are
|
|
24
|
+
* separate fields and this fixture deliberately makes them disagree, because
|
|
25
|
+
* production can too. Every org-role gate reads the **membership row**, so a
|
|
26
|
+
* test that treats `alice` as "the owner" will invert its own result and pass
|
|
27
|
+
* for the wrong reason — seed an explicit `role: 'owner'` member instead.
|
|
28
|
+
*/
|
|
29
|
+
export declare function seedAcme(h: TestHarness): Promise<AcmeFixture>;
|
|
30
|
+
export interface BigClientFixture {
|
|
31
|
+
bigClient: Organization;
|
|
32
|
+
carol: SeededUser;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Second tenant. Carol is a member of BigClient — used for cross-org rejection
|
|
36
|
+
* scenarios (Carol acting in BigClient context cannot view Acme data).
|
|
37
|
+
*/
|
|
38
|
+
export declare function seedBigClient(h: TestHarness): Promise<BigClientFixture>;
|
|
39
|
+
export interface ThirdOrgFixture {
|
|
40
|
+
initech: Organization;
|
|
41
|
+
dave: SeededUser;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Third tenant. Dave is a member of Initech — used for cross-org public-visibility
|
|
45
|
+
* scenarios (any authenticated user from any org can view a public project when
|
|
46
|
+
* cross-org public access is enabled).
|
|
47
|
+
*/
|
|
48
|
+
export declare function seedThirdOrg(h: TestHarness): Promise<ThirdOrgFixture>;
|
|
49
|
+
export interface CommonsFixture {
|
|
50
|
+
commonsProject: Project;
|
|
51
|
+
alicesCommonsDef: SeededDefinition;
|
|
52
|
+
peter: SeededUser;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Commons-mode project (`autoJoinOnUpload=true`) inside Acme with Alice's
|
|
56
|
+
* pre-existing definition. Peter is a separate authenticated user — *not* a
|
|
57
|
+
* project member — who is allowed to upload his own new definitions but cannot
|
|
58
|
+
* touch Alice's existing one.
|
|
59
|
+
*
|
|
60
|
+
* Requires {@link seedAcme} to have run first (uses Alice as project owner +
|
|
61
|
+
* existing-def owner).
|
|
62
|
+
*/
|
|
63
|
+
export declare function seedCommons(h: TestHarness, opts: {
|
|
64
|
+
acmeId: string;
|
|
65
|
+
aliceId: string;
|
|
66
|
+
}): Promise<CommonsFixture>;
|
|
67
|
+
//# sourceMappingURL=scenarios.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenarios.d.ts","sourceRoot":"","sources":["../../src/testing/scenarios.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE9E,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,GAAG,EAAE,UAAU,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CA8BnE;AAED,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,YAAY,CAAC;IACxB,KAAK,EAAE,UAAU,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAS7E;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAS3E;AAED,MAAM,WAAW,cAAc;IAC9B,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,KAAK,EAAE,UAAU,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAChC,CAAC,EAAE,WAAW,EACd,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC,OAAO,CAAC,cAAc,CAAC,CAgBzB"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared cast the handler tests are written against.
|
|
3
|
+
*
|
|
4
|
+
* These compose only the seeders in `./harness.ts`, so they run on whatever
|
|
5
|
+
* provider stack the host built its {@link TestHarness} around. They live here
|
|
6
|
+
* rather than in a host's fixtures because a handler test that travels with its
|
|
7
|
+
* handler needs the same two tenants wherever it runs — a host that reseeds its
|
|
8
|
+
* own Acme with different roles gets different results from the same test.
|
|
9
|
+
*/
|
|
10
|
+
import { seedDefinition, seedOrg, seedOrgMember, seedProject, seedUser } from './harness.js';
|
|
11
|
+
/**
|
|
12
|
+
* The primary tenant.
|
|
13
|
+
*
|
|
14
|
+
* **Alice is the org's `ownerId` but her membership row is `admin`.** Those are
|
|
15
|
+
* separate fields and this fixture deliberately makes them disagree, because
|
|
16
|
+
* production can too. Every org-role gate reads the **membership row**, so a
|
|
17
|
+
* test that treats `alice` as "the owner" will invert its own result and pass
|
|
18
|
+
* for the wrong reason — seed an explicit `role: 'owner'` member instead.
|
|
19
|
+
*/
|
|
20
|
+
export async function seedAcme(h) {
|
|
21
|
+
const alice = await seedUser(h, 'alice@acme.test');
|
|
22
|
+
const bob = await seedUser(h, 'bob@acme.test');
|
|
23
|
+
const acme = await seedOrg(h, { name: 'Acme', slug: 'acme', ownerId: alice.id });
|
|
24
|
+
await seedOrgMember(h, { orgId: acme.id, userId: alice.id, role: 'admin' });
|
|
25
|
+
await seedOrgMember(h, { orgId: acme.id, userId: bob.id, role: 'member' });
|
|
26
|
+
const alicesPrivate = await seedProject(h, {
|
|
27
|
+
orgId: acme.id,
|
|
28
|
+
name: 'Alice Private',
|
|
29
|
+
slug: 'alice-private',
|
|
30
|
+
ownerId: alice.id,
|
|
31
|
+
visibility: 'private'
|
|
32
|
+
});
|
|
33
|
+
const acmeOrg = await seedProject(h, {
|
|
34
|
+
orgId: acme.id,
|
|
35
|
+
name: 'Acme Org Project',
|
|
36
|
+
slug: 'acme-org',
|
|
37
|
+
ownerId: alice.id,
|
|
38
|
+
visibility: 'org'
|
|
39
|
+
});
|
|
40
|
+
const acmePublic = await seedProject(h, {
|
|
41
|
+
orgId: acme.id,
|
|
42
|
+
name: 'Acme Public',
|
|
43
|
+
slug: 'acme-public',
|
|
44
|
+
ownerId: alice.id,
|
|
45
|
+
visibility: 'public'
|
|
46
|
+
});
|
|
47
|
+
return { acme, alice, bob, alicesPrivate, acmeOrg, acmePublic };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Second tenant. Carol is a member of BigClient — used for cross-org rejection
|
|
51
|
+
* scenarios (Carol acting in BigClient context cannot view Acme data).
|
|
52
|
+
*/
|
|
53
|
+
export async function seedBigClient(h) {
|
|
54
|
+
const carol = await seedUser(h, 'carol@bigclient.test');
|
|
55
|
+
const bigClient = await seedOrg(h, {
|
|
56
|
+
name: 'BigClient',
|
|
57
|
+
slug: 'bigclient',
|
|
58
|
+
ownerId: carol.id
|
|
59
|
+
});
|
|
60
|
+
await seedOrgMember(h, { orgId: bigClient.id, userId: carol.id, role: 'member' });
|
|
61
|
+
return { bigClient, carol };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Third tenant. Dave is a member of Initech — used for cross-org public-visibility
|
|
65
|
+
* scenarios (any authenticated user from any org can view a public project when
|
|
66
|
+
* cross-org public access is enabled).
|
|
67
|
+
*/
|
|
68
|
+
export async function seedThirdOrg(h) {
|
|
69
|
+
const dave = await seedUser(h, 'dave@initech.test');
|
|
70
|
+
const initech = await seedOrg(h, {
|
|
71
|
+
name: 'Initech',
|
|
72
|
+
slug: 'initech',
|
|
73
|
+
ownerId: dave.id
|
|
74
|
+
});
|
|
75
|
+
await seedOrgMember(h, { orgId: initech.id, userId: dave.id, role: 'member' });
|
|
76
|
+
return { initech, dave };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Commons-mode project (`autoJoinOnUpload=true`) inside Acme with Alice's
|
|
80
|
+
* pre-existing definition. Peter is a separate authenticated user — *not* a
|
|
81
|
+
* project member — who is allowed to upload his own new definitions but cannot
|
|
82
|
+
* touch Alice's existing one.
|
|
83
|
+
*
|
|
84
|
+
* Requires {@link seedAcme} to have run first (uses Alice as project owner +
|
|
85
|
+
* existing-def owner).
|
|
86
|
+
*/
|
|
87
|
+
export async function seedCommons(h, opts) {
|
|
88
|
+
const peter = await seedUser(h, 'peter@elsewhere.test');
|
|
89
|
+
const commonsProject = await seedProject(h, {
|
|
90
|
+
orgId: opts.acmeId,
|
|
91
|
+
name: 'Acme Commons',
|
|
92
|
+
slug: 'acme-commons',
|
|
93
|
+
ownerId: opts.aliceId,
|
|
94
|
+
visibility: 'public',
|
|
95
|
+
autoJoinOnUpload: true
|
|
96
|
+
});
|
|
97
|
+
const alicesCommonsDef = await seedDefinition(h, {
|
|
98
|
+
projectId: commonsProject.id,
|
|
99
|
+
ownerId: opts.aliceId,
|
|
100
|
+
displayName: "Alice's Commons Def"
|
|
101
|
+
});
|
|
102
|
+
return { commonsProject, alicesCommonsDef, peter };
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=scenarios.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenarios.js","sourceRoot":"","sources":["../../src/testing/scenarios.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one thing test fixtures cannot get from `SelvaConfig`: creating and
|
|
3
|
+
* reading identities.
|
|
4
|
+
*
|
|
5
|
+
* Everything else a fixture needs — orgs, projects, definitions, invites —
|
|
6
|
+
* lives behind `IDataProvider` and is portable as-is. Identity is not.
|
|
7
|
+
* `IAuthProvider` exposes user *creation* only through the optional
|
|
8
|
+
* `passwordAuth.createUserWithPassword`, and a provider that authenticates by
|
|
9
|
+
* OAuth or forwarded headers implements none of it, so there is no interface
|
|
10
|
+
* method every provider answers. Each provider also stores identity its own
|
|
11
|
+
* way — the local one in `auth-users.json`, Supabase in its own `auth.users`
|
|
12
|
+
* table — and seeding has to reach that store directly, not through a login
|
|
13
|
+
* flow no test wants to perform.
|
|
14
|
+
*
|
|
15
|
+
* So the host supplies this. Two methods, because that is all the shared
|
|
16
|
+
* fixtures use: `seedUser` creates, `actAs` reads back.
|
|
17
|
+
*/
|
|
18
|
+
import type { AuthUser } from '@selvajs/platform';
|
|
19
|
+
export interface SeedAuthAdapter {
|
|
20
|
+
/**
|
|
21
|
+
* Create an identity and return it. Called before the data-layer row exists,
|
|
22
|
+
* so implementations must not assume `ensureUser` has run.
|
|
23
|
+
*
|
|
24
|
+
* The returned `id` is what every other seeder keys off, so it has to be the
|
|
25
|
+
* id the auth provider will report for this user later — not a fresh one.
|
|
26
|
+
*/
|
|
27
|
+
createUser(email: string): Promise<AuthUser>;
|
|
28
|
+
/** Read an identity back. `null` when absent, so `actAs` can fail loudly. */
|
|
29
|
+
findById(id: string): Promise<AuthUser | null>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=seed-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed-adapter.d.ts","sourceRoot":"","sources":["../../src/testing/seed-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,WAAW,eAAe;IAC/B;;;;;;OAMG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE7C,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;CAC/C"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one thing test fixtures cannot get from `SelvaConfig`: creating and
|
|
3
|
+
* reading identities.
|
|
4
|
+
*
|
|
5
|
+
* Everything else a fixture needs — orgs, projects, definitions, invites —
|
|
6
|
+
* lives behind `IDataProvider` and is portable as-is. Identity is not.
|
|
7
|
+
* `IAuthProvider` exposes user *creation* only through the optional
|
|
8
|
+
* `passwordAuth.createUserWithPassword`, and a provider that authenticates by
|
|
9
|
+
* OAuth or forwarded headers implements none of it, so there is no interface
|
|
10
|
+
* method every provider answers. Each provider also stores identity its own
|
|
11
|
+
* way — the local one in `auth-users.json`, Supabase in its own `auth.users`
|
|
12
|
+
* table — and seeding has to reach that store directly, not through a login
|
|
13
|
+
* flow no test wants to perform.
|
|
14
|
+
*
|
|
15
|
+
* So the host supplies this. Two methods, because that is all the shared
|
|
16
|
+
* fixtures use: `seedUser` creates, `actAs` reads back.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=seed-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed-adapter.js","sourceRoot":"","sources":["../../src/testing/seed-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}
|