@malloy-publisher/server 0.0.195 → 0.0.196
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/app/api-doc.yaml +213 -214
- package/dist/app/assets/EnvironmentPage-1j6QDWAy.js +1 -0
- package/dist/app/assets/HomePage-DMop21VG.js +1 -0
- package/dist/app/assets/MainPage-BbE8ETz1.js +2 -0
- package/dist/app/assets/ModelPage-D2jvfe3t.js +1 -0
- package/dist/app/assets/PackagePage-BbnhGoD3.js +1 -0
- package/dist/app/assets/{RouteError-DefbDO7F.js → RouteError-D3LGEZ3i.js} +1 -1
- package/dist/app/assets/WorkbookPage-DttVIj4u.js +1 -0
- package/dist/app/assets/{core-BrfQApxh.es-DnvCX4oH.js → core-w79IMXAG.es-Bd0UlzOL.js} +1 -1
- package/dist/app/assets/{index-Bu0ub036.js → index-5K9YjIxF.js} +117 -117
- package/dist/app/assets/{index-CkzK3JIl.js → index-C513UodQ.js} +1 -1
- package/dist/app/assets/{index-CoA6HIGS.js → index-DIgzgp69.js} +1 -1
- package/dist/app/assets/{index.umd-B6Ms2PpL.js → index.umd-BMeMPq_9.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/server.mjs +1976 -1322
- package/package.json +2 -2
- package/publisher.config.json +2 -2
- package/src/config.spec.ts +181 -66
- package/src/config.ts +68 -47
- package/src/controller/compile.controller.ts +10 -7
- package/src/controller/connection.controller.ts +79 -58
- package/src/controller/database.controller.ts +10 -7
- package/src/controller/manifest.controller.ts +23 -14
- package/src/controller/materialization.controller.ts +14 -14
- package/src/controller/model.controller.ts +35 -20
- package/src/controller/package.controller.ts +83 -49
- package/src/controller/query.controller.ts +11 -8
- package/src/controller/watch-mode.controller.ts +35 -29
- package/src/errors.ts +2 -2
- package/src/mcp/error_messages.ts +2 -2
- package/src/mcp/handler_utils.ts +23 -20
- package/src/mcp/mcp_constants.ts +1 -1
- package/src/mcp/prompts/handlers.ts +3 -3
- package/src/mcp/prompts/prompt_service.ts +5 -5
- package/src/mcp/prompts/utils.ts +12 -12
- package/src/mcp/resource_metadata.ts +3 -3
- package/src/mcp/resources/environment_resource.ts +187 -0
- package/src/mcp/resources/model_resource.ts +19 -17
- package/src/mcp/resources/notebook_resource.ts +13 -13
- package/src/mcp/resources/package_resource.ts +30 -27
- package/src/mcp/resources/query_resource.ts +15 -10
- package/src/mcp/resources/source_resource.ts +10 -10
- package/src/mcp/resources/view_resource.ts +11 -11
- package/src/mcp/server.ts +16 -14
- package/src/mcp/tools/discovery_tools.ts +67 -49
- package/src/mcp/tools/execute_query_tool.ts +14 -14
- package/src/server-old.ts +1119 -0
- package/src/server.ts +191 -159
- package/src/service/connection.spec.ts +158 -133
- package/src/service/connection.ts +42 -39
- package/src/service/connection_config.spec.ts +13 -11
- package/src/service/connection_config.ts +28 -19
- package/src/service/connection_service.spec.ts +63 -43
- package/src/service/connection_service.ts +106 -89
- package/src/service/{project.ts → environment.ts} +92 -77
- package/src/service/{project_compile.spec.ts → environment_compile.spec.ts} +1 -1
- package/src/service/{project_store.spec.ts → environment_store.spec.ts} +99 -83
- package/src/service/{project_store.ts → environment_store.ts} +373 -327
- package/src/service/manifest_service.spec.ts +15 -15
- package/src/service/manifest_service.ts +26 -21
- package/src/service/materialization_service.spec.ts +93 -59
- package/src/service/materialization_service.ts +71 -62
- package/src/service/materialized_table_gc.spec.ts +15 -15
- package/src/service/materialized_table_gc.ts +3 -3
- package/src/service/model.ts +4 -4
- package/src/service/package.spec.ts +2 -2
- package/src/service/package.ts +23 -21
- package/src/service/resolve_environment.ts +15 -0
- package/src/storage/DatabaseInterface.ts +34 -25
- package/src/storage/StorageManager.mock.ts +3 -3
- package/src/storage/StorageManager.ts +64 -28
- package/src/storage/duckdb/ConnectionRepository.ts +13 -11
- package/src/storage/duckdb/DuckDBConnection.ts +1 -1
- package/src/storage/duckdb/DuckDBManifestStore.ts +6 -6
- package/src/storage/duckdb/DuckDBRepository.ts +47 -47
- package/src/storage/duckdb/{ProjectRepository.ts → EnvironmentRepository.ts} +35 -35
- package/src/storage/duckdb/ManifestRepository.ts +21 -20
- package/src/storage/duckdb/MaterializationRepository.ts +31 -28
- package/src/storage/duckdb/PackageRepository.ts +11 -11
- package/src/storage/duckdb/manifest_store.spec.ts +2 -2
- package/src/storage/duckdb/schema.ts +61 -20
- package/src/storage/ducklake/DuckLakeManifestStore.ts +20 -11
- package/tests/fixtures/publisher.config.json +1 -1
- package/tests/harness/e2e.ts +1 -1
- package/tests/harness/mcp_test_setup.ts +12 -24
- package/tests/harness/mocks.ts +10 -8
- package/tests/harness/rest_e2e.ts +2 -2
- package/tests/integration/legacy_routes/legacy_routes.integration.spec.ts +259 -0
- package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +4 -4
- package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +28 -49
- package/tests/integration/mcp/mcp_resource.integration.spec.ts +39 -47
- package/tests/integration/mcp/mcp_transport.integration.spec.ts +1 -1
- package/tests/unit/duckdb/attached_databases.test.ts +51 -33
- package/tests/unit/duckdb/legacy_schema_migration.test.ts +194 -0
- package/tests/unit/ducklake/ducklake.test.ts +24 -22
- package/tests/unit/mcp/prompt_happy.test.ts +8 -8
- package/dist/app/assets/HomePage-DbZS0N7G.js +0 -1
- package/dist/app/assets/MainPage-CBuWkbmr.js +0 -2
- package/dist/app/assets/ModelPage-Bt37smot.js +0 -1
- package/dist/app/assets/PackagePage-DLZe50WG.js +0 -1
- package/dist/app/assets/ProjectPage-FQTEPXP4.js +0 -1
- package/dist/app/assets/WorkbookPage-CkAo16ar.js +0 -1
- package/src/mcp/resources/project_resource.ts +0 -184
- package/src/service/resolve_project.ts +0 -13
|
@@ -5,13 +5,14 @@ import * as sinon from "sinon";
|
|
|
5
5
|
import { components } from "../api";
|
|
6
6
|
import { isPublisherConfigFrozen } from "../config";
|
|
7
7
|
import { TEMP_DIR_PATH } from "../constants";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { Environment } from "./environment";
|
|
9
|
+
import { EnvironmentStore } from "./environment_store";
|
|
10
10
|
|
|
11
11
|
type MockData = Record<string, unknown>;
|
|
12
12
|
|
|
13
13
|
const initializeDuckLakeCalls: Array<{
|
|
14
|
-
|
|
14
|
+
environmentId: string;
|
|
15
|
+
environmentName: string;
|
|
15
16
|
config: { catalogUrl: string; dataPath: string };
|
|
16
17
|
}> = [];
|
|
17
18
|
|
|
@@ -22,19 +23,24 @@ mock.module("../storage/StorageManager", () => {
|
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
async
|
|
26
|
-
|
|
26
|
+
async initializeDuckLakeForEnvironment(
|
|
27
|
+
environmentId: string,
|
|
28
|
+
environmentName: string,
|
|
27
29
|
config: { catalogUrl: string; dataPath: string },
|
|
28
30
|
): Promise<void> {
|
|
29
|
-
initializeDuckLakeCalls.push({
|
|
31
|
+
initializeDuckLakeCalls.push({
|
|
32
|
+
environmentId,
|
|
33
|
+
environmentName,
|
|
34
|
+
config,
|
|
35
|
+
});
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
getRepository() {
|
|
33
39
|
return {
|
|
34
40
|
// ===== PROJECT METHODS =====
|
|
35
|
-
|
|
41
|
+
listEnvironments: async (): Promise<unknown[]> => [],
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
getEnvironmentById: async (
|
|
38
44
|
id: string,
|
|
39
45
|
): Promise<MockData | null> => ({
|
|
40
46
|
id,
|
|
@@ -46,14 +52,16 @@ mock.module("../storage/StorageManager", () => {
|
|
|
46
52
|
updatedAt: new Date(),
|
|
47
53
|
}),
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
getEnvironmentByName: async (
|
|
50
56
|
_name: string,
|
|
51
57
|
): Promise<MockData | null> => {
|
|
52
58
|
// Return null to simulate "project doesn't exist yet"
|
|
53
59
|
return null;
|
|
54
60
|
},
|
|
55
61
|
|
|
56
|
-
|
|
62
|
+
createEnvironment: async (
|
|
63
|
+
data: MockData,
|
|
64
|
+
): Promise<MockData> => ({
|
|
57
65
|
id: "test-project-id",
|
|
58
66
|
name: data.name,
|
|
59
67
|
path: data.path,
|
|
@@ -63,7 +71,7 @@ mock.module("../storage/StorageManager", () => {
|
|
|
63
71
|
updatedAt: new Date(),
|
|
64
72
|
}),
|
|
65
73
|
|
|
66
|
-
|
|
74
|
+
updateEnvironment: async (
|
|
67
75
|
id: string,
|
|
68
76
|
data: MockData,
|
|
69
77
|
): Promise<MockData> => ({
|
|
@@ -79,18 +87,18 @@ mock.module("../storage/StorageManager", () => {
|
|
|
79
87
|
updatedAt: new Date(),
|
|
80
88
|
}),
|
|
81
89
|
|
|
82
|
-
|
|
90
|
+
deleteEnvironment: async (_id: string): Promise<void> => {},
|
|
83
91
|
|
|
84
92
|
// ===== PACKAGE METHODS =====
|
|
85
93
|
listPackages: async (
|
|
86
|
-
|
|
94
|
+
_environmentId: string,
|
|
87
95
|
): Promise<unknown[]> => [],
|
|
88
96
|
|
|
89
97
|
getPackageById: async (
|
|
90
98
|
id: string,
|
|
91
99
|
): Promise<MockData | null> => ({
|
|
92
100
|
id,
|
|
93
|
-
|
|
101
|
+
environmentId: "test-project-id",
|
|
94
102
|
name: "test-package",
|
|
95
103
|
description: "Test package",
|
|
96
104
|
manifestPath: "/test/manifest.json",
|
|
@@ -100,13 +108,13 @@ mock.module("../storage/StorageManager", () => {
|
|
|
100
108
|
}),
|
|
101
109
|
|
|
102
110
|
getPackageByName: async (
|
|
103
|
-
|
|
111
|
+
_environmentId: string,
|
|
104
112
|
_name: string,
|
|
105
113
|
): Promise<MockData | null> => null,
|
|
106
114
|
|
|
107
115
|
createPackage: async (data: MockData): Promise<MockData> => ({
|
|
108
116
|
id: "test-package-id",
|
|
109
|
-
|
|
117
|
+
environmentId: data.environmentId,
|
|
110
118
|
name: data.name,
|
|
111
119
|
description: data.description,
|
|
112
120
|
manifestPath: data.manifestPath,
|
|
@@ -120,7 +128,7 @@ mock.module("../storage/StorageManager", () => {
|
|
|
120
128
|
data: MockData,
|
|
121
129
|
): Promise<MockData> => ({
|
|
122
130
|
id,
|
|
123
|
-
|
|
131
|
+
environmentId: "test-project-id",
|
|
124
132
|
name: "test-package",
|
|
125
133
|
description: data.description,
|
|
126
134
|
manifestPath: "/test/manifest.json",
|
|
@@ -133,14 +141,14 @@ mock.module("../storage/StorageManager", () => {
|
|
|
133
141
|
|
|
134
142
|
// ===== CONNECTION METHODS =====
|
|
135
143
|
listConnections: async (
|
|
136
|
-
|
|
144
|
+
_environmentId: string,
|
|
137
145
|
): Promise<unknown[]> => [],
|
|
138
146
|
|
|
139
147
|
getConnectionById: async (
|
|
140
148
|
id: string,
|
|
141
149
|
): Promise<MockData | null> => ({
|
|
142
150
|
id,
|
|
143
|
-
|
|
151
|
+
environmentId: "test-project-id",
|
|
144
152
|
name: "test-connection",
|
|
145
153
|
type: "postgres",
|
|
146
154
|
config: {},
|
|
@@ -149,13 +157,13 @@ mock.module("../storage/StorageManager", () => {
|
|
|
149
157
|
}),
|
|
150
158
|
|
|
151
159
|
getConnectionByName: async (
|
|
152
|
-
|
|
160
|
+
_environmentId: string,
|
|
153
161
|
_name: string,
|
|
154
162
|
): Promise<MockData | null> => null,
|
|
155
163
|
|
|
156
164
|
createConnection: async (data: MockData): Promise<MockData> => ({
|
|
157
165
|
id: "test-connection-id",
|
|
158
|
-
|
|
166
|
+
environmentId: data.environmentId,
|
|
159
167
|
name: data.name,
|
|
160
168
|
type: data.type,
|
|
161
169
|
config: data.config,
|
|
@@ -168,7 +176,7 @@ mock.module("../storage/StorageManager", () => {
|
|
|
168
176
|
data: MockData,
|
|
169
177
|
): Promise<MockData> => ({
|
|
170
178
|
id,
|
|
171
|
-
|
|
179
|
+
environmentId: "test-project-id",
|
|
172
180
|
name: "test-connection",
|
|
173
181
|
type: "postgres",
|
|
174
182
|
config: data.config || {},
|
|
@@ -194,8 +202,8 @@ const projectName = "organizationName-projectName";
|
|
|
194
202
|
|
|
195
203
|
let sandbox: sinon.SinonSandbox;
|
|
196
204
|
|
|
197
|
-
describe("
|
|
198
|
-
let
|
|
205
|
+
describe("EnvironmentStore Service", () => {
|
|
206
|
+
let environmentStore: EnvironmentStore;
|
|
199
207
|
|
|
200
208
|
beforeEach(async () => {
|
|
201
209
|
// Clean up any existing test directory
|
|
@@ -212,7 +220,7 @@ describe("ProjectStore Service", () => {
|
|
|
212
220
|
}));
|
|
213
221
|
|
|
214
222
|
// Create project store after mocking
|
|
215
|
-
|
|
223
|
+
environmentStore = new EnvironmentStore(serverRootPath);
|
|
216
224
|
});
|
|
217
225
|
|
|
218
226
|
afterEach(async () => {
|
|
@@ -226,7 +234,7 @@ describe("ProjectStore Service", () => {
|
|
|
226
234
|
|
|
227
235
|
it("should not load a package if the project does not exist", async () => {
|
|
228
236
|
await expect(
|
|
229
|
-
|
|
237
|
+
environmentStore.getEnvironment("non-existent-project"),
|
|
230
238
|
).rejects.toThrow();
|
|
231
239
|
});
|
|
232
240
|
|
|
@@ -254,7 +262,7 @@ describe("ProjectStore Service", () => {
|
|
|
254
262
|
publisherConfigPath,
|
|
255
263
|
JSON.stringify({
|
|
256
264
|
frozenConfig: false,
|
|
257
|
-
|
|
265
|
+
environments: [
|
|
258
266
|
{
|
|
259
267
|
name: projectName,
|
|
260
268
|
packages: [
|
|
@@ -275,8 +283,8 @@ describe("ProjectStore Service", () => {
|
|
|
275
283
|
);
|
|
276
284
|
|
|
277
285
|
// Test that the project can be retrieved
|
|
278
|
-
const project = await
|
|
279
|
-
expect(project).toBeInstanceOf(
|
|
286
|
+
const project = await environmentStore.getEnvironment(projectName);
|
|
287
|
+
expect(project).toBeInstanceOf(Environment);
|
|
280
288
|
expect(project.metadata.name).toBe(projectName);
|
|
281
289
|
},
|
|
282
290
|
{ timeout: 30000 },
|
|
@@ -301,7 +309,7 @@ describe("ProjectStore Service", () => {
|
|
|
301
309
|
publisherConfigPath,
|
|
302
310
|
JSON.stringify({
|
|
303
311
|
frozenConfig: false,
|
|
304
|
-
|
|
312
|
+
environments: [
|
|
305
313
|
{
|
|
306
314
|
name: projectName1,
|
|
307
315
|
packages: [
|
|
@@ -338,11 +346,11 @@ describe("ProjectStore Service", () => {
|
|
|
338
346
|
);
|
|
339
347
|
|
|
340
348
|
// Create a new project store that will read the configuration
|
|
341
|
-
const
|
|
342
|
-
await
|
|
349
|
+
const newEnvironmentStore = new EnvironmentStore(serverRootPath);
|
|
350
|
+
await newEnvironmentStore.finishedInitialization;
|
|
343
351
|
|
|
344
352
|
// Test that both projects can be listed
|
|
345
|
-
const projects = await
|
|
353
|
+
const projects = await newEnvironmentStore.listEnvironments();
|
|
346
354
|
expect(projects).toBeInstanceOf(Array);
|
|
347
355
|
expect(projects.length).toBe(2);
|
|
348
356
|
expect(projects.map((p) => p.name)).toContain(projectName1);
|
|
@@ -380,7 +388,7 @@ describe("ProjectStore Service", () => {
|
|
|
380
388
|
publisherConfigPath,
|
|
381
389
|
JSON.stringify({
|
|
382
390
|
frozenConfig: false,
|
|
383
|
-
|
|
391
|
+
environments: [
|
|
384
392
|
{
|
|
385
393
|
name: invalidProjectName,
|
|
386
394
|
packages: [
|
|
@@ -411,13 +419,13 @@ describe("ProjectStore Service", () => {
|
|
|
411
419
|
}),
|
|
412
420
|
);
|
|
413
421
|
|
|
414
|
-
const
|
|
415
|
-
await
|
|
422
|
+
const newEnvironmentStore = new EnvironmentStore(serverRootPath);
|
|
423
|
+
await newEnvironmentStore.finishedInitialization;
|
|
416
424
|
|
|
417
|
-
const projects = await
|
|
425
|
+
const projects = await newEnvironmentStore.listEnvironments();
|
|
418
426
|
expect(projects.map((p) => p.name)).toEqual([validProjectName]);
|
|
419
427
|
await expect(
|
|
420
|
-
|
|
428
|
+
newEnvironmentStore.getEnvironment(invalidProjectName),
|
|
421
429
|
).rejects.toThrow();
|
|
422
430
|
});
|
|
423
431
|
|
|
@@ -442,7 +450,7 @@ describe("ProjectStore Service", () => {
|
|
|
442
450
|
publisherConfigPath,
|
|
443
451
|
JSON.stringify({
|
|
444
452
|
frozenConfig: false,
|
|
445
|
-
|
|
453
|
+
environments: [
|
|
446
454
|
{
|
|
447
455
|
name: projectName,
|
|
448
456
|
packages: [
|
|
@@ -456,10 +464,10 @@ describe("ProjectStore Service", () => {
|
|
|
456
464
|
}),
|
|
457
465
|
);
|
|
458
466
|
|
|
459
|
-
await
|
|
467
|
+
await environmentStore.finishedInitialization;
|
|
460
468
|
|
|
461
469
|
// Get the project
|
|
462
|
-
const project = await
|
|
470
|
+
const project = await environmentStore.getEnvironment(projectName);
|
|
463
471
|
|
|
464
472
|
// Update the project
|
|
465
473
|
await project.update({
|
|
@@ -479,13 +487,13 @@ describe("ProjectStore Service", () => {
|
|
|
479
487
|
expect(readmeContent).toBe("Updated README content");
|
|
480
488
|
});
|
|
481
489
|
|
|
482
|
-
it("should propagate materializationStorage on
|
|
490
|
+
it("should propagate materializationStorage on addEnvironment for new environment", async () => {
|
|
483
491
|
writeFileSync(
|
|
484
492
|
path.join(serverRootPath, "publisher.config.json"),
|
|
485
|
-
JSON.stringify({ frozenConfig: false,
|
|
493
|
+
JSON.stringify({ frozenConfig: false, environments: [] }),
|
|
486
494
|
);
|
|
487
495
|
|
|
488
|
-
await
|
|
496
|
+
await environmentStore.finishedInitialization;
|
|
489
497
|
|
|
490
498
|
const materializationStorage = {
|
|
491
499
|
catalogUrl:
|
|
@@ -494,7 +502,7 @@ describe("ProjectStore Service", () => {
|
|
|
494
502
|
};
|
|
495
503
|
|
|
496
504
|
initializeDuckLakeCalls.length = 0;
|
|
497
|
-
const project = await
|
|
505
|
+
const project = await environmentStore.addEnvironment({
|
|
498
506
|
name: projectName,
|
|
499
507
|
materializationStorage,
|
|
500
508
|
});
|
|
@@ -517,7 +525,7 @@ describe("ProjectStore Service", () => {
|
|
|
517
525
|
path.join(serverRootPath, "publisher.config.json"),
|
|
518
526
|
JSON.stringify({
|
|
519
527
|
frozenConfig: false,
|
|
520
|
-
|
|
528
|
+
environments: [
|
|
521
529
|
{
|
|
522
530
|
name: projectName,
|
|
523
531
|
packages: [{ name: projectName, location: projectPath }],
|
|
@@ -526,8 +534,8 @@ describe("ProjectStore Service", () => {
|
|
|
526
534
|
}),
|
|
527
535
|
);
|
|
528
536
|
|
|
529
|
-
await
|
|
530
|
-
const project = await
|
|
537
|
+
await environmentStore.finishedInitialization;
|
|
538
|
+
const project = await environmentStore.getEnvironment(projectName);
|
|
531
539
|
|
|
532
540
|
const materializationStorage = {
|
|
533
541
|
catalogUrl:
|
|
@@ -565,7 +573,7 @@ describe("ProjectStore Service", () => {
|
|
|
565
573
|
writeFileSync(
|
|
566
574
|
publisherConfigPath,
|
|
567
575
|
JSON.stringify({
|
|
568
|
-
|
|
576
|
+
environments: [
|
|
569
577
|
{
|
|
570
578
|
name: projectName,
|
|
571
579
|
packages: [
|
|
@@ -580,13 +588,16 @@ describe("ProjectStore Service", () => {
|
|
|
580
588
|
);
|
|
581
589
|
|
|
582
590
|
// Get the project
|
|
583
|
-
const project1 = await
|
|
591
|
+
const project1 = await environmentStore.getEnvironment(projectName);
|
|
584
592
|
|
|
585
593
|
// Get the project again with reload=true
|
|
586
|
-
const project2 = await
|
|
594
|
+
const project2 = await environmentStore.getEnvironment(
|
|
595
|
+
projectName,
|
|
596
|
+
true,
|
|
597
|
+
);
|
|
587
598
|
|
|
588
|
-
expect(project1).toBeInstanceOf(
|
|
589
|
-
expect(project2).toBeInstanceOf(
|
|
599
|
+
expect(project1).toBeInstanceOf(Environment);
|
|
600
|
+
expect(project2).toBeInstanceOf(Environment);
|
|
590
601
|
expect(project1.metadata.name).toBe(project2.metadata.name as string);
|
|
591
602
|
},
|
|
592
603
|
{ timeout: 30000 },
|
|
@@ -601,7 +612,7 @@ describe("ProjectStore Service", () => {
|
|
|
601
612
|
writeFileSync(
|
|
602
613
|
publisherConfigPath,
|
|
603
614
|
JSON.stringify({
|
|
604
|
-
|
|
615
|
+
environments: [
|
|
605
616
|
{
|
|
606
617
|
name: projectName,
|
|
607
618
|
packages: [
|
|
@@ -616,7 +627,9 @@ describe("ProjectStore Service", () => {
|
|
|
616
627
|
);
|
|
617
628
|
|
|
618
629
|
// Test that getting the project throws an error
|
|
619
|
-
await expect(
|
|
630
|
+
await expect(
|
|
631
|
+
environmentStore.getEnvironment(projectName),
|
|
632
|
+
).rejects.toThrow();
|
|
620
633
|
});
|
|
621
634
|
|
|
622
635
|
it("should handle invalid publisher config", async () => {
|
|
@@ -628,11 +641,11 @@ describe("ProjectStore Service", () => {
|
|
|
628
641
|
writeFileSync(publisherConfigPath, "invalid json");
|
|
629
642
|
|
|
630
643
|
// Create a new project store that will read the invalid config
|
|
631
|
-
const
|
|
644
|
+
const newEnvironmentStore = new EnvironmentStore(serverRootPath);
|
|
632
645
|
|
|
633
646
|
// Test that the project store handles invalid JSON gracefully by falling back to empty config
|
|
634
|
-
await
|
|
635
|
-
const projects = await
|
|
647
|
+
await newEnvironmentStore.finishedInitialization;
|
|
648
|
+
const projects = await newEnvironmentStore.listEnvironments();
|
|
636
649
|
expect(projects).toEqual([]);
|
|
637
650
|
});
|
|
638
651
|
|
|
@@ -646,7 +659,7 @@ describe("ProjectStore Service", () => {
|
|
|
646
659
|
publisherConfigPath,
|
|
647
660
|
JSON.stringify({
|
|
648
661
|
frozenConfig: false,
|
|
649
|
-
|
|
662
|
+
environments: [
|
|
650
663
|
{
|
|
651
664
|
invalidKey1: "malloy-samples", // Invalid: should be "name"
|
|
652
665
|
invalidKey2: [
|
|
@@ -669,11 +682,11 @@ describe("ProjectStore Service", () => {
|
|
|
669
682
|
);
|
|
670
683
|
|
|
671
684
|
// Create a new project store that will read the invalid config
|
|
672
|
-
const
|
|
685
|
+
const newEnvironmentStore = new EnvironmentStore(serverRootPath);
|
|
673
686
|
|
|
674
687
|
// Test that the project store handles invalid fields gracefully without crashing
|
|
675
|
-
await
|
|
676
|
-
const projects = await
|
|
688
|
+
await newEnvironmentStore.finishedInitialization;
|
|
689
|
+
const projects = await newEnvironmentStore.listEnvironments();
|
|
677
690
|
|
|
678
691
|
// Should not crash and should return empty array since invalid projects are filtered out
|
|
679
692
|
expect(projects).toEqual([]);
|
|
@@ -699,7 +712,7 @@ describe("ProjectStore Service", () => {
|
|
|
699
712
|
publisherConfigPath,
|
|
700
713
|
JSON.stringify({
|
|
701
714
|
frozenConfig: false,
|
|
702
|
-
|
|
715
|
+
environments: [
|
|
703
716
|
{
|
|
704
717
|
// Invalid project: missing "name" field
|
|
705
718
|
packages: [
|
|
@@ -744,11 +757,11 @@ describe("ProjectStore Service", () => {
|
|
|
744
757
|
);
|
|
745
758
|
|
|
746
759
|
// Create a new project store that will read the config
|
|
747
|
-
const
|
|
760
|
+
const newEnvironmentStore = new EnvironmentStore(serverRootPath);
|
|
748
761
|
|
|
749
762
|
// Test that invalid projects are filtered out
|
|
750
|
-
await
|
|
751
|
-
const projects = await
|
|
763
|
+
await newEnvironmentStore.finishedInitialization;
|
|
764
|
+
const projects = await newEnvironmentStore.listEnvironments();
|
|
752
765
|
|
|
753
766
|
// Should only have the valid project
|
|
754
767
|
expect(projects.length).toBe(1);
|
|
@@ -778,7 +791,7 @@ describe("ProjectStore Service", () => {
|
|
|
778
791
|
publisherConfigPath,
|
|
779
792
|
JSON.stringify({
|
|
780
793
|
frozenConfig: false,
|
|
781
|
-
|
|
794
|
+
environments: [
|
|
782
795
|
{
|
|
783
796
|
name: projectName,
|
|
784
797
|
packages: [
|
|
@@ -798,18 +811,18 @@ describe("ProjectStore Service", () => {
|
|
|
798
811
|
}),
|
|
799
812
|
);
|
|
800
813
|
|
|
801
|
-
await
|
|
814
|
+
await environmentStore.finishedInitialization;
|
|
802
815
|
|
|
803
816
|
// Test concurrent access to the same project
|
|
804
817
|
const promises = Array.from({ length: 5 }, () =>
|
|
805
|
-
|
|
818
|
+
environmentStore.getEnvironment(projectName),
|
|
806
819
|
);
|
|
807
820
|
|
|
808
821
|
const projects = await Promise.all(promises);
|
|
809
822
|
|
|
810
823
|
expect(projects).toHaveLength(5);
|
|
811
824
|
projects.forEach((project) => {
|
|
812
|
-
expect(project).toBeInstanceOf(
|
|
825
|
+
expect(project).toBeInstanceOf(Environment);
|
|
813
826
|
expect(project.metadata.name).toBe(projectName);
|
|
814
827
|
});
|
|
815
828
|
},
|
|
@@ -819,7 +832,7 @@ describe("ProjectStore Service", () => {
|
|
|
819
832
|
|
|
820
833
|
describe("Project Service Error Recovery", () => {
|
|
821
834
|
let sandbox: sinon.SinonSandbox;
|
|
822
|
-
let
|
|
835
|
+
let environmentStore: EnvironmentStore;
|
|
823
836
|
const serverRootPath = path.join(
|
|
824
837
|
TEMP_DIR_PATH,
|
|
825
838
|
"pathways-worker-publisher-error-recovery-test",
|
|
@@ -850,7 +863,7 @@ describe("Project Service Error Recovery", () => {
|
|
|
850
863
|
}));
|
|
851
864
|
|
|
852
865
|
// Create project store after mocking
|
|
853
|
-
|
|
866
|
+
environmentStore = new EnvironmentStore(serverRootPath);
|
|
854
867
|
});
|
|
855
868
|
|
|
856
869
|
afterEach(async () => {
|
|
@@ -870,7 +883,7 @@ describe("Project Service Error Recovery", () => {
|
|
|
870
883
|
writeFileSync(
|
|
871
884
|
publisherConfigPath,
|
|
872
885
|
JSON.stringify({
|
|
873
|
-
|
|
886
|
+
environments: [
|
|
874
887
|
{
|
|
875
888
|
name: projectName,
|
|
876
889
|
packages: [
|
|
@@ -888,7 +901,9 @@ describe("Project Service Error Recovery", () => {
|
|
|
888
901
|
);
|
|
889
902
|
|
|
890
903
|
// Test that the project store handles the missing directory
|
|
891
|
-
await expect(
|
|
904
|
+
await expect(
|
|
905
|
+
environmentStore.getEnvironment(projectName),
|
|
906
|
+
).rejects.toThrow();
|
|
892
907
|
});
|
|
893
908
|
|
|
894
909
|
it(
|
|
@@ -921,7 +936,7 @@ describe("Project Service Error Recovery", () => {
|
|
|
921
936
|
writeFileSync(
|
|
922
937
|
publisherConfigPath,
|
|
923
938
|
JSON.stringify({
|
|
924
|
-
|
|
939
|
+
environments: [
|
|
925
940
|
{
|
|
926
941
|
name: projectName,
|
|
927
942
|
packages: [
|
|
@@ -937,8 +952,8 @@ describe("Project Service Error Recovery", () => {
|
|
|
937
952
|
|
|
938
953
|
// Test that the project store handles corrupted connection files gracefully
|
|
939
954
|
// (The current implementation loads the project even with corrupted connection files)
|
|
940
|
-
const project = await
|
|
941
|
-
expect(project).toBeInstanceOf(
|
|
955
|
+
const project = await environmentStore.getEnvironment(projectName);
|
|
956
|
+
expect(project).toBeInstanceOf(Environment);
|
|
942
957
|
expect(project.metadata.name).toBe(projectName);
|
|
943
958
|
},
|
|
944
959
|
{ timeout: 30000 },
|
|
@@ -972,7 +987,7 @@ describe("Project Service Error Recovery", () => {
|
|
|
972
987
|
writeFileSync(
|
|
973
988
|
publisherConfigPath,
|
|
974
989
|
JSON.stringify({
|
|
975
|
-
|
|
990
|
+
environments: [
|
|
976
991
|
{
|
|
977
992
|
name: projectName,
|
|
978
993
|
packages: [
|
|
@@ -987,17 +1002,18 @@ describe("Project Service Error Recovery", () => {
|
|
|
987
1002
|
);
|
|
988
1003
|
|
|
989
1004
|
// Get the project successfully
|
|
990
|
-
const project = await
|
|
991
|
-
expect(project).toBeInstanceOf(
|
|
1005
|
+
const project = await environmentStore.getEnvironment(projectName);
|
|
1006
|
+
expect(project).toBeInstanceOf(Environment);
|
|
992
1007
|
|
|
993
1008
|
// Try to get a non-existent project
|
|
994
1009
|
await expect(
|
|
995
|
-
|
|
1010
|
+
environmentStore.getEnvironment("non-existent"),
|
|
996
1011
|
).rejects.toThrow();
|
|
997
1012
|
|
|
998
1013
|
// Verify the original project is still accessible
|
|
999
|
-
const projectAgain =
|
|
1000
|
-
|
|
1014
|
+
const projectAgain =
|
|
1015
|
+
await environmentStore.getEnvironment(projectName);
|
|
1016
|
+
expect(projectAgain).toBeInstanceOf(Environment);
|
|
1001
1017
|
expect(projectAgain.metadata.name).toBe(projectName);
|
|
1002
1018
|
},
|
|
1003
1019
|
{ timeout: 30000 },
|