@selvajs/platform 0.14.2 → 0.15.0-beta.1

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 (196) hide show
  1. package/LICENSE +1 -1
  2. package/dist/auth.d.ts +60 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +12 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/compute.d.ts +47 -0
  7. package/dist/compute.d.ts.map +1 -0
  8. package/dist/compute.js +11 -0
  9. package/dist/compute.js.map +1 -0
  10. package/dist/computeServer/index.d.ts +2 -0
  11. package/dist/computeServer/index.d.ts.map +1 -1
  12. package/dist/computeServer/index.js +6 -0
  13. package/dist/computeServer/index.js.map +1 -1
  14. package/dist/computeServer/interface.d.ts +10 -0
  15. package/dist/computeServer/interface.d.ts.map +1 -1
  16. package/dist/computeServer/secrets.d.ts +53 -0
  17. package/dist/computeServer/secrets.d.ts.map +1 -0
  18. package/dist/computeServer/secrets.js +67 -0
  19. package/dist/computeServer/secrets.js.map +1 -0
  20. package/dist/context.d.ts +7 -0
  21. package/dist/context.d.ts.map +1 -1
  22. package/dist/context.js +13 -0
  23. package/dist/context.js.map +1 -1
  24. package/dist/data/interface.d.ts +33 -0
  25. package/dist/data/interface.d.ts.map +1 -1
  26. package/dist/definitions/interface.d.ts +18 -0
  27. package/dist/definitions/interface.d.ts.map +1 -1
  28. package/dist/definitions/schemas.d.ts +1 -0
  29. package/dist/definitions/schemas.d.ts.map +1 -1
  30. package/dist/definitions/schemas.js +3 -0
  31. package/dist/definitions/schemas.js.map +1 -1
  32. package/dist/definitions/service.d.ts +62 -0
  33. package/dist/definitions/service.d.ts.map +1 -0
  34. package/dist/definitions/service.js +149 -0
  35. package/dist/definitions/service.js.map +1 -0
  36. package/dist/definitions/types.d.ts +24 -1
  37. package/dist/definitions/types.d.ts.map +1 -1
  38. package/dist/definitions.d.ts +146 -0
  39. package/dist/definitions.d.ts.map +1 -0
  40. package/dist/definitions.js +23 -0
  41. package/dist/definitions.js.map +1 -0
  42. package/dist/errors/interface.d.ts +44 -0
  43. package/dist/errors/interface.d.ts.map +1 -0
  44. package/dist/errors/interface.js +9 -0
  45. package/dist/errors/interface.js.map +1 -0
  46. package/dist/events/interface.d.ts +23 -0
  47. package/dist/events/interface.d.ts.map +1 -1
  48. package/dist/events/interface.js.map +1 -1
  49. package/dist/index.d.ts +14 -5
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +7 -2
  52. package/dist/index.js.map +1 -1
  53. package/dist/invites/schemas.d.ts +24 -0
  54. package/dist/invites/schemas.d.ts.map +1 -0
  55. package/dist/invites/schemas.js +17 -0
  56. package/dist/invites/schemas.js.map +1 -0
  57. package/dist/metrics/interface.d.ts +6 -0
  58. package/dist/metrics/interface.d.ts.map +1 -1
  59. package/dist/metrics/interface.js.map +1 -1
  60. package/dist/organizations/index.d.ts +4 -3
  61. package/dist/organizations/index.d.ts.map +1 -1
  62. package/dist/organizations/index.js +2 -1
  63. package/dist/organizations/index.js.map +1 -1
  64. package/dist/organizations/interface.d.ts +1 -1
  65. package/dist/organizations/interface.d.ts.map +1 -1
  66. package/dist/organizations/paths.d.ts +22 -0
  67. package/dist/organizations/paths.d.ts.map +1 -0
  68. package/dist/organizations/paths.js +43 -0
  69. package/dist/organizations/paths.js.map +1 -0
  70. package/dist/organizations/schemas.d.ts +10 -0
  71. package/dist/organizations/schemas.d.ts.map +1 -1
  72. package/dist/organizations/schemas.js +6 -0
  73. package/dist/organizations/schemas.js.map +1 -1
  74. package/dist/organizations/types.d.ts +10 -1
  75. package/dist/organizations/types.d.ts.map +1 -1
  76. package/dist/organizations.d.ts +93 -0
  77. package/dist/organizations.d.ts.map +1 -0
  78. package/dist/organizations.js +14 -0
  79. package/dist/organizations.js.map +1 -0
  80. package/dist/solveCache/interface.d.ts +86 -0
  81. package/dist/solveCache/interface.d.ts.map +1 -0
  82. package/dist/solveCache/interface.js +12 -0
  83. package/dist/solveCache/interface.js.map +1 -0
  84. package/dist/storage/assetClasses.d.ts +59 -0
  85. package/dist/storage/assetClasses.d.ts.map +1 -0
  86. package/dist/storage/assetClasses.js +86 -0
  87. package/dist/storage/assetClasses.js.map +1 -0
  88. package/dist/storage/cacheBust.d.ts +21 -0
  89. package/dist/storage/cacheBust.d.ts.map +1 -0
  90. package/dist/storage/cacheBust.js +29 -0
  91. package/dist/storage/cacheBust.js.map +1 -0
  92. package/dist/storage/image.d.ts +6 -1
  93. package/dist/storage/image.d.ts.map +1 -1
  94. package/dist/storage/image.js +8 -3
  95. package/dist/storage/image.js.map +1 -1
  96. package/dist/storage/index.d.ts +3 -0
  97. package/dist/storage/index.d.ts.map +1 -1
  98. package/dist/storage/index.js +2 -0
  99. package/dist/storage/index.js.map +1 -1
  100. package/dist/testing/adapterSuite.d.ts +33 -0
  101. package/dist/testing/adapterSuite.d.ts.map +1 -0
  102. package/dist/testing/adapterSuite.js +166 -0
  103. package/dist/testing/adapterSuite.js.map +1 -0
  104. package/dist/testing/suites/definitionStoreSuite.d.ts.map +1 -1
  105. package/dist/testing/suites/definitionStoreSuite.js +87 -0
  106. package/dist/testing/suites/definitionStoreSuite.js.map +1 -1
  107. package/dist/testing/suites/eventSinkSuite.js +1 -0
  108. package/dist/testing/suites/eventSinkSuite.js.map +1 -1
  109. package/dist/testing/suites/orgStoreSuite.d.ts.map +1 -1
  110. package/dist/testing/suites/orgStoreSuite.js +12 -0
  111. package/dist/testing/suites/orgStoreSuite.js.map +1 -1
  112. package/dist/testing/suites/platformPermissionStoreSuite.d.ts +9 -0
  113. package/dist/testing/suites/platformPermissionStoreSuite.d.ts.map +1 -1
  114. package/dist/testing/suites/platformPermissionStoreSuite.js +34 -1
  115. package/dist/testing/suites/platformPermissionStoreSuite.js.map +1 -1
  116. package/dist/testing/suites/runner.d.ts +21 -0
  117. package/dist/testing/suites/runner.d.ts.map +1 -0
  118. package/dist/testing/suites/runner.js +7 -0
  119. package/dist/testing/suites/runner.js.map +1 -0
  120. package/dist/testing/suites/storageProviderSuite.d.ts.map +1 -1
  121. package/dist/testing/suites/storageProviderSuite.js +18 -0
  122. package/dist/testing/suites/storageProviderSuite.js.map +1 -1
  123. package/package.json +9 -24
  124. package/src/access/index.ts +0 -23
  125. package/src/access/rules.ts +0 -276
  126. package/src/auth/index.ts +0 -8
  127. package/src/auth/interface.ts +0 -279
  128. package/src/auth/types.ts +0 -32
  129. package/src/bindings/index.ts +0 -2
  130. package/src/bindings/interface.ts +0 -61
  131. package/src/computeServer/index.ts +0 -17
  132. package/src/computeServer/interface.ts +0 -62
  133. package/src/computeServer/types.ts +0 -74
  134. package/src/computeServer/utils.ts +0 -112
  135. package/src/config.ts +0 -99
  136. package/src/context.ts +0 -66
  137. package/src/data/index.ts +0 -1
  138. package/src/data/interface.ts +0 -70
  139. package/src/definitions/index.ts +0 -26
  140. package/src/definitions/interface.ts +0 -78
  141. package/src/definitions/paths.ts +0 -31
  142. package/src/definitions/schemas.ts +0 -64
  143. package/src/definitions/types.ts +0 -111
  144. package/src/errors.ts +0 -16
  145. package/src/events/audit.ts +0 -67
  146. package/src/events/index.ts +0 -9
  147. package/src/events/interface.ts +0 -102
  148. package/src/index.ts +0 -219
  149. package/src/invites/index.ts +0 -2
  150. package/src/invites/interface.ts +0 -38
  151. package/src/invites/types.ts +0 -39
  152. package/src/metrics/interface.ts +0 -86
  153. package/src/organizations/index.ts +0 -14
  154. package/src/organizations/interface.ts +0 -68
  155. package/src/organizations/schemas.ts +0 -52
  156. package/src/organizations/types.ts +0 -31
  157. package/src/pagination.ts +0 -45
  158. package/src/permissions/index.ts +0 -3
  159. package/src/permissions/interface.ts +0 -60
  160. package/src/permissions/types.ts +0 -21
  161. package/src/platformProjects/index.ts +0 -3
  162. package/src/platformProjects/interface.ts +0 -27
  163. package/src/platformProjects/schemas.ts +0 -13
  164. package/src/platformProjects/types.ts +0 -22
  165. package/src/projects/index.ts +0 -4
  166. package/src/projects/interface.ts +0 -58
  167. package/src/projects/schemas.ts +0 -28
  168. package/src/projects/types.ts +0 -36
  169. package/src/shareLinks/index.ts +0 -5
  170. package/src/shareLinks/interface.ts +0 -35
  171. package/src/shareLinks/schemas.ts +0 -21
  172. package/src/shareLinks/types.ts +0 -34
  173. package/src/storage/image.ts +0 -77
  174. package/src/storage/index.ts +0 -9
  175. package/src/storage/interface.ts +0 -27
  176. package/src/testing/index.ts +0 -47
  177. package/src/testing/suites/authProviderSuite.ts +0 -207
  178. package/src/testing/suites/computeServerStoreSuite.ts +0 -195
  179. package/src/testing/suites/definitionStoreSuite.ts +0 -585
  180. package/src/testing/suites/emailLinkAuthSuite.ts +0 -83
  181. package/src/testing/suites/eventSinkSuite.ts +0 -379
  182. package/src/testing/suites/helpers.ts +0 -87
  183. package/src/testing/suites/inviteStoreSuite.ts +0 -143
  184. package/src/testing/suites/orgStoreSuite.ts +0 -412
  185. package/src/testing/suites/platformPermissionStoreSuite.ts +0 -196
  186. package/src/testing/suites/platformProjectGrantStoreSuite.ts +0 -166
  187. package/src/testing/suites/projectStoreSuite.ts +0 -381
  188. package/src/testing/suites/shareLinkStoreSuite.ts +0 -203
  189. package/src/testing/suites/solveMetricSinkSuite.ts +0 -135
  190. package/src/testing/suites/storageProviderSuite.ts +0 -175
  191. package/src/testing/suites/userProfileStoreSuite.ts +0 -134
  192. package/src/userProfile/index.ts +0 -3
  193. package/src/userProfile/interface.ts +0 -48
  194. package/src/userProfile/types.ts +0 -24
  195. package/src/utils/audit.ts +0 -46
  196. package/src/utils/index.ts +0 -2
@@ -1,207 +0,0 @@
1
- // Conformance suite for IAuthProvider: token round-trips, identity, password auth, admin management.
2
-
3
- import { describe, it, expect } from 'vitest';
4
- import type { IAuthProvider } from '../../auth/index.js';
5
- import { makeUuid } from './helpers.js';
6
-
7
- export interface AuthProviderConformanceOptions {
8
- /** Name to show in test output (e.g. "LocalAuthProvider"). */
9
- name: string;
10
- /**
11
- * Factory returning a fresh provider configured with a known password.
12
- * `adminPassword` must work with `passwordAuth.verifyLogin(adminEmail, adminPassword)`
13
- * for password-capable providers. The factory is responsible for seeding
14
- * the admin user (e.g. via `createUserWithPassword`) before returning.
15
- *
16
- * `adminEmail` defaults to `''` for legacy reasons; new providers should
17
- * always return the seeded admin's email.
18
- */
19
- createProvider: () =>
20
- | Promise<{ provider: IAuthProvider; adminPassword: string; adminEmail?: string }>
21
- | { provider: IAuthProvider; adminPassword: string; adminEmail?: string };
22
- /**
23
- * Set to true when the provider is wired with a backend so that
24
- * createUser / listUsers / updateUserPlatformPermissions / deleteUser tests run.
25
- * When false those methods are expected to return null / 'not_supported'.
26
- */
27
- userManagement?: boolean;
28
- }
29
-
30
- export function runAuthProviderConformance(opts: AuthProviderConformanceOptions): void {
31
- const { name, createProvider, userManagement = false } = opts;
32
-
33
- describe(`IAuthProvider conformance: ${name}`, () => {
34
- // Token round-trips
35
- it('verifyLogin returns a session token that verifyToken accepts', async () => {
36
- const { provider, adminPassword, adminEmail = '' } = await createProvider();
37
- const result = await provider.passwordAuth!.verifyLogin(adminEmail, adminPassword);
38
- expect(result.kind).toBe('success');
39
- if (result.kind !== 'success') return;
40
- const verified = await provider.verifyToken(result.sessionToken);
41
- expect(verified?.id).toBe(result.user.id);
42
- });
43
-
44
- it('verifyToken returns null for a garbage token', async () => {
45
- const { provider } = await createProvider();
46
- const result = await provider.verifyToken('not-a-valid-token');
47
- expect(result).toBeNull();
48
- });
49
-
50
- it('verifyToken returns null for an empty string', async () => {
51
- const { provider } = await createProvider();
52
- const result = await provider.verifyToken('');
53
- expect(result).toBeNull();
54
- });
55
-
56
- // Password auth (only runs when passwordAuth is present)
57
- it('verifyLogin returns success with correct password', async () => {
58
- const { provider, adminPassword, adminEmail = '' } = await createProvider();
59
- if (!provider.passwordAuth) return;
60
- const result = await provider.passwordAuth.verifyLogin(adminEmail, adminPassword);
61
- expect(result.kind).toBe('success');
62
- if (result.kind !== 'success') return;
63
- // Identity-only — platform permissions live on IPlatformPermissionStore now.
64
- expect(result.user.id).toBeTruthy();
65
- expect(typeof result.sessionToken).toBe('string');
66
- expect(result.sessionToken.length).toBeGreaterThan(0);
67
- });
68
-
69
- it('verifyLogin returns failed for wrong password', async () => {
70
- const { provider, adminEmail = '' } = await createProvider();
71
- if (!provider.passwordAuth) return;
72
- const result = await provider.passwordAuth.verifyLogin(adminEmail, 'wrong-password-xyz');
73
- expect(result.kind).toBe('failed');
74
- });
75
-
76
- // getUser
77
- it('getUser returns null for unknown id', async () => {
78
- const { provider } = await createProvider();
79
- // Valid UUID shape but not an actual user — stricter backends (Postgres
80
- // uuid columns) reject non-UUID ids outright, so we never send those.
81
- const result = await provider.getUser(makeUuid());
82
- expect(result).toBeNull();
83
- });
84
-
85
- // ============================================================================
86
- // User management (opt-in)
87
- // ============================================================================
88
-
89
- if (userManagement) {
90
- it('passwordAuth.createUserWithPassword + getUser returns the created user', async () => {
91
- const { provider } = await createProvider();
92
- const email = `test-${makeUuid()}@example.com`;
93
- const created = await provider.passwordAuth!.createUserWithPassword(email, 'password1234');
94
- expect(created).toBeTruthy();
95
- const fetched = await provider.getUser(created.id);
96
- expect(fetched?.id).toBe(created.id);
97
- expect(fetched?.email).toBe(email);
98
- });
99
-
100
- it('createUserWithPassword allows login with the new credentials', async () => {
101
- const { provider } = await createProvider();
102
- const email = `login-${makeUuid()}@example.com`;
103
- await provider.passwordAuth!.createUserWithPassword(email, 'secret9999');
104
- const result = await provider.passwordAuth!.verifyLogin(email, 'secret9999');
105
- expect(result.kind).toBe('success');
106
- if (result.kind !== 'success') return;
107
- expect(result.user.email).toBe(email);
108
- });
109
-
110
- it('listUsers returns created users', async () => {
111
- const { provider } = await createProvider();
112
- const a = `a-${makeUuid()}@example.com`;
113
- const b = `b-${makeUuid()}@example.com`;
114
- await provider.passwordAuth!.createUserWithPassword(a, 'pass12345678');
115
- await provider.passwordAuth!.createUserWithPassword(b, 'pass12345678');
116
- const page = await provider.listUsers({ limit: 200 });
117
- expect(page).toBeTruthy();
118
- const emails = page!.items.map((u) => u.email);
119
- expect(emails).toContain(a);
120
- expect(emails).toContain(b);
121
- });
122
-
123
- it('deleteUser removes the user', async () => {
124
- const { provider } = await createProvider();
125
- const email = `del-${makeUuid()}@example.com`;
126
- const created = await provider.passwordAuth!.createUserWithPassword(email, 'pass12345678');
127
- const result = await provider.deleteUser(created.id);
128
- expect(result).toBe('ok');
129
- const fetched = await provider.getUser(created.id);
130
- expect(fetched).toBeNull();
131
- });
132
-
133
- it('deleteUser returns not_found for unknown user', async () => {
134
- const { provider } = await createProvider();
135
- const result = await provider.deleteUser(makeUuid());
136
- expect(result).toBe('not_found');
137
- });
138
-
139
- // Note: the §2 sole-`instance_admin` invariant is no longer enforced
140
- // by IAuthProvider — it lives on IPlatformPermissionStore now and
141
- // callers consult `countInstanceAdminsExcluding` before destructive
142
- // ops. Auth-side delete/disable just deletes the identity and
143
- // trusts the caller. See `runPlatformPermissionStoreConformance`
144
- // for the invariant tests.
145
-
146
- it('createUserWithPassword returns an identity record without profile fields', async () => {
147
- const { provider } = await createProvider();
148
- const user = await provider.passwordAuth!.createUserWithPassword(
149
- `new-${makeUuid()}@example.com`,
150
- 'pass12345678'
151
- );
152
- // Profile state (starred, recentRuns, displayName) lives on
153
- // IUserProfileStore. Platform permissions live on
154
- // IPlatformPermissionStore. AuthUser is identity-only.
155
- expect(
156
- (user as unknown as { starredDefinitions?: unknown }).starredDefinitions
157
- ).toBeUndefined();
158
- expect((user as unknown as { recentRuns?: unknown }).recentRuns).toBeUndefined();
159
- expect((user as unknown as { displayName?: unknown }).displayName).toBeUndefined();
160
- expect(
161
- (user as unknown as { platformPermissions?: unknown }).platformPermissions
162
- ).toBeUndefined();
163
- });
164
-
165
- it('disableUser disables a user', async () => {
166
- const { provider } = await createProvider();
167
- const created = await provider.passwordAuth!.createUserWithPassword(
168
- `disable-${makeUuid()}@example.com`,
169
- 'pass12345678'
170
- );
171
- const result = await provider.disableUser(created.id);
172
- expect(result).toBe('ok');
173
- const fetched = await provider.getUser(created.id);
174
- expect(fetched?.disabled).toBe(true);
175
- });
176
-
177
- it('disableUser returns not_found for unknown user', async () => {
178
- const { provider } = await createProvider();
179
- const result = await provider.disableUser(makeUuid());
180
- expect(result).toBe('not_found');
181
- });
182
- } else {
183
- it('listUsers returns null when user management is not supported', async () => {
184
- const { provider } = await createProvider();
185
- const result = await provider.listUsers();
186
- expect(result).toBeNull();
187
- });
188
-
189
- it('createUser (allowlist) is undefined when user management is not supported', async () => {
190
- const { provider } = await createProvider();
191
- expect(provider.createUser).toBeUndefined();
192
- });
193
-
194
- it('deleteUser returns not_supported', async () => {
195
- const { provider } = await createProvider();
196
- const result = await provider.deleteUser(makeUuid());
197
- expect(result).toBe('not_supported');
198
- });
199
-
200
- it('disableUser returns not_supported', async () => {
201
- const { provider } = await createProvider();
202
- const result = await provider.disableUser(makeUuid());
203
- expect(result).toBe('not_supported');
204
- });
205
- }
206
- });
207
- }
@@ -1,195 +0,0 @@
1
- /**
2
- * Adapter conformance suite for IComputeServerStore.
3
- *
4
- * Architecture spec §4.8 — servers are scoped (platform vs. org-private), with a global
5
- * `defaultServerId` and per-org `orgDefaults`. The store surface exposes
6
- * scope-targeted mutations; visibility filtering is done by callers via
7
- * the pure helpers in `@selvajs/platform`.
8
- */
9
-
10
- import { describe, it, expect } from 'vitest';
11
- import type { IComputeServerStore } from '../../computeServer/interface.js';
12
- import type {
13
- ComputeServerConfig,
14
- OrgComputeServer,
15
- PlatformComputeServer
16
- } from '../../computeServer/types.js';
17
- import { isOrgServer, isPlatformServer } from '../../computeServer/types.js';
18
- import { SYSTEM_CONTEXT } from '../../context.js';
19
- import { makeUuid } from './helpers.js';
20
-
21
- export interface ComputeServerStoreConformanceOptions {
22
- name: string;
23
- createStore: () => Promise<IComputeServerStore> | IComputeServerStore;
24
- /**
25
- * Optional hook adapters with FK constraints use to seed an `orgs` row
26
- * before the suite writes an org-scoped compute config that FK-references
27
- * it. Adapters without org FKs (local JSON) can omit this.
28
- */
29
- seedOrg?: (orgId: string) => Promise<void>;
30
- }
31
-
32
- function platformServer(
33
- overrides: Partial<Omit<PlatformComputeServer, 'scope'>> = {}
34
- ): PlatformComputeServer {
35
- return {
36
- id: overrides.id ?? makeUuid(),
37
- scope: 'platform',
38
- sharedWith: overrides.sharedWith ?? 'all',
39
- label: overrides.label ?? 'Test Server',
40
- serverUrl: overrides.serverUrl ?? 'http://localhost:5000',
41
- apiKey: overrides.apiKey,
42
- timeoutMs: overrides.timeoutMs,
43
- retryCount: overrides.retryCount
44
- };
45
- }
46
-
47
- function orgServer(
48
- ownerOrgId: string,
49
- overrides: Partial<Omit<OrgComputeServer, 'scope' | 'ownerOrgId'>> = {}
50
- ): OrgComputeServer {
51
- return {
52
- id: overrides.id ?? makeUuid(),
53
- scope: 'org',
54
- ownerOrgId,
55
- label: overrides.label ?? 'Org Server',
56
- serverUrl: overrides.serverUrl ?? 'http://localhost:5001',
57
- apiKey: overrides.apiKey,
58
- timeoutMs: overrides.timeoutMs,
59
- retryCount: overrides.retryCount
60
- };
61
- }
62
-
63
- export function runComputeServerStoreConformance(opts: ComputeServerStoreConformanceOptions): void {
64
- const { name, createStore, seedOrg } = opts;
65
- const seed = async (): Promise<string> => {
66
- const id = makeUuid();
67
- if (seedOrg) await seedOrg(id);
68
- return id;
69
- };
70
-
71
- describe(`IComputeServerStore conformance: ${name}`, () => {
72
- it('savePlatformServers + getConfig round-trips servers and defaultServerId', async () => {
73
- const store = await createStore();
74
- const s1 = platformServer({ label: 'Primary' });
75
- const s2 = platformServer({ label: 'Secondary', apiKey: 'secret' });
76
- await store.savePlatformServers(SYSTEM_CONTEXT, [s1, s2], s1.id);
77
-
78
- const got = await store.getConfig(SYSTEM_CONTEXT);
79
- const platformIds = got.servers.filter(isPlatformServer).map((s) => s.id);
80
- expect(platformIds.sort()).toEqual([s1.id, s2.id].sort());
81
- expect(got.defaultServerId).toBe(s1.id);
82
- });
83
-
84
- it('savePlatformServers replaces the previous platform set', async () => {
85
- const store = await createStore();
86
- await store.savePlatformServers(
87
- SYSTEM_CONTEXT,
88
- [platformServer({ label: 'Old' })],
89
- undefined
90
- );
91
-
92
- const newS = platformServer({ label: 'New' });
93
- await store.savePlatformServers(SYSTEM_CONTEXT, [newS], newS.id);
94
-
95
- const got = await store.getConfig(SYSTEM_CONTEXT);
96
- const platformIds = got.servers.filter(isPlatformServer).map((s) => s.id);
97
- expect(platformIds).toEqual([newS.id]);
98
- });
99
-
100
- it('getConfig on empty returns an empty servers list', async () => {
101
- const store = await createStore();
102
- const got = await store.getConfig(SYSTEM_CONTEXT);
103
- expect(Array.isArray(got.servers)).toBe(true);
104
- });
105
-
106
- // ============================================================================
107
- // Org-private servers + orgDefaults (Architecture spec §4.8)
108
- // ============================================================================
109
-
110
- it('saveOrgServers stores rows owned by the org', async () => {
111
- const store = await createStore();
112
- const orgId = await seed();
113
- const s = orgServer(orgId, { label: "Org's BYO" });
114
- await store.saveOrgServers(SYSTEM_CONTEXT, orgId, [s], s.id);
115
-
116
- const got = await store.getConfig(SYSTEM_CONTEXT);
117
- const ownRows = got.servers.filter(
118
- (row): row is OrgComputeServer => isOrgServer(row) && row.ownerOrgId === orgId
119
- );
120
- expect(ownRows.map((r) => r.id)).toEqual([s.id]);
121
- expect(got.orgDefaults?.[orgId]).toBe(s.id);
122
- });
123
-
124
- it('saveOrgServers does not clobber platform rows (and vice versa)', async () => {
125
- const store = await createStore();
126
- const orgId = await seed();
127
- const platS = platformServer({ label: 'Platform' });
128
- const orgS = orgServer(orgId, { label: 'Org' });
129
-
130
- await store.savePlatformServers(SYSTEM_CONTEXT, [platS], platS.id);
131
- await store.saveOrgServers(SYSTEM_CONTEXT, orgId, [orgS], orgS.id);
132
-
133
- // Replacing the platform set should leave org rows untouched.
134
- const replacement = platformServer({ label: 'Replacement' });
135
- await store.savePlatformServers(SYSTEM_CONTEXT, [replacement], replacement.id);
136
-
137
- const got = await store.getConfig(SYSTEM_CONTEXT);
138
- const platformIds = got.servers.filter(isPlatformServer).map((s) => s.id);
139
- expect(platformIds).toEqual([replacement.id]);
140
- const orgIds = got.servers
141
- .filter((s): s is OrgComputeServer => isOrgServer(s) && s.ownerOrgId === orgId)
142
- .map((s) => s.id);
143
- expect(orgIds).toEqual([orgS.id]);
144
- expect(got.defaultServerId).toBe(replacement.id);
145
- expect(got.orgDefaults?.[orgId]).toBe(orgS.id);
146
- });
147
-
148
- it('setOrgDefault updates and clears the per-org default', async () => {
149
- const store = await createStore();
150
- const orgId = await seed();
151
- const s = orgServer(orgId);
152
- await store.saveOrgServers(SYSTEM_CONTEXT, orgId, [s]);
153
-
154
- await store.setOrgDefault(SYSTEM_CONTEXT, orgId, s.id);
155
- let got = await store.getConfig(SYSTEM_CONTEXT);
156
- expect(got.orgDefaults?.[orgId]).toBe(s.id);
157
-
158
- await store.setOrgDefault(SYSTEM_CONTEXT, orgId, null);
159
- got = await store.getConfig(SYSTEM_CONTEXT);
160
- expect(got.orgDefaults?.[orgId]).toBeUndefined();
161
- });
162
-
163
- it('deleteByOrg removes org-private rows and the orgDefaults entry', async () => {
164
- const store = await createStore();
165
- const orgId = await seed();
166
- const orgS = orgServer(orgId);
167
- await store.saveOrgServers(SYSTEM_CONTEXT, orgId, [orgS], orgS.id);
168
-
169
- await store.deleteByOrg(SYSTEM_CONTEXT, orgId);
170
-
171
- const got = await store.getConfig(SYSTEM_CONTEXT);
172
- const remaining = got.servers.filter(
173
- (s): s is ComputeServerConfig => isOrgServer(s) && s.ownerOrgId === orgId
174
- );
175
- expect(remaining).toEqual([]);
176
- expect(got.orgDefaults?.[orgId]).toBeUndefined();
177
- });
178
-
179
- it('deleteByOrg strips this org from platform sharedWith allowlists', async () => {
180
- const store = await createStore();
181
- const orgId = await seed();
182
- const otherOrgId = await seed();
183
- const platS = platformServer({ sharedWith: [orgId, otherOrgId] });
184
- await store.savePlatformServers(SYSTEM_CONTEXT, [platS], platS.id);
185
-
186
- await store.deleteByOrg(SYSTEM_CONTEXT, orgId);
187
-
188
- const got = await store.getConfig(SYSTEM_CONTEXT);
189
- const found = got.servers.find(
190
- (s): s is PlatformComputeServer => isPlatformServer(s) && s.id === platS.id
191
- );
192
- expect(found?.sharedWith).toEqual([otherOrgId]);
193
- });
194
- });
195
- }