@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
|
@@ -20,7 +20,8 @@ interface PackageContentEntry {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// --- Test Suite ---
|
|
23
|
-
|
|
23
|
+
// Serial: one shared MCP client; concurrent it() blocks can interleave StreamableHTTP requests.
|
|
24
|
+
describe.serial("MCP Resource Handlers (E2E Integration)", () => {
|
|
24
25
|
let env: McpE2ETestEnvironment | null = null;
|
|
25
26
|
let mcpClient: Client;
|
|
26
27
|
|
|
@@ -36,29 +37,29 @@ describe("MCP Resource Handlers (E2E Integration)", () => {
|
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
// --- Test Constants ---
|
|
39
|
-
const homeProjectUri = "malloy://
|
|
40
|
-
const faaPackageUri = "malloy://
|
|
40
|
+
const homeProjectUri = "malloy://environment/malloy-samples";
|
|
41
|
+
const faaPackageUri = "malloy://environment/malloy-samples/package/faa";
|
|
41
42
|
const flightsModelUri =
|
|
42
|
-
"malloy://
|
|
43
|
+
"malloy://environment/malloy-samples/package/faa/models/flights.malloy";
|
|
43
44
|
const FLIGHTS_SOURCE = "flights";
|
|
44
45
|
const FLIGHTS_CARRIER_QUERY = "flights_by_carrier";
|
|
45
46
|
const FLIGHTS_MONTH_VIEW = "flights_by_month";
|
|
46
47
|
const OVERVIEW_NOTEBOOK = "overview.malloynb";
|
|
47
48
|
const nonExistentPackageUri =
|
|
48
|
-
"malloy://
|
|
49
|
+
"malloy://environment/malloy-samples/package/nonexistent";
|
|
49
50
|
const nonExistentModelUri =
|
|
50
|
-
"malloy://
|
|
51
|
-
const nonExistentProjectUri = "malloy://
|
|
51
|
+
"malloy://environment/malloy-samples/package/faa/models/nonexistent.malloy";
|
|
52
|
+
const nonExistentProjectUri = "malloy://environment/invalid_project";
|
|
52
53
|
const invalidUri = "invalid://format";
|
|
53
54
|
|
|
54
|
-
const validSourceUri = `malloy://
|
|
55
|
-
const validQueryUri = `malloy://
|
|
56
|
-
const validViewUri = `malloy://
|
|
57
|
-
const validNotebookUri = `malloy://
|
|
58
|
-
const nonExistentSourceUri = `malloy://
|
|
59
|
-
const nonExistentQueryUri = `malloy://
|
|
60
|
-
const nonExistentViewUri = `malloy://
|
|
61
|
-
const nonExistentNotebookUri = `malloy://
|
|
55
|
+
const validSourceUri = `malloy://environment/malloy-samples/package/faa/models/flights.malloy/sources/${FLIGHTS_SOURCE}`;
|
|
56
|
+
const validQueryUri = `malloy://environment/malloy-samples/package/faa/models/flights.malloy/queries/${FLIGHTS_CARRIER_QUERY}`;
|
|
57
|
+
const validViewUri = `malloy://environment/malloy-samples/package/faa/models/flights.malloy/sources/${FLIGHTS_SOURCE}/views/${FLIGHTS_MONTH_VIEW}`;
|
|
58
|
+
const validNotebookUri = `malloy://environment/malloy-samples/package/faa/notebooks/${OVERVIEW_NOTEBOOK}`;
|
|
59
|
+
const nonExistentSourceUri = `malloy://environment/malloy-samples/package/faa/models/flights.malloy/sources/non_existent_source`;
|
|
60
|
+
const nonExistentQueryUri = `malloy://environment/malloy-samples/package/faa/models/flights.malloy/queries/non_existent_query`;
|
|
61
|
+
const nonExistentViewUri = `malloy://environment/malloy-samples/package/faa/models/flights.malloy/sources/${FLIGHTS_SOURCE}/views/non_existent_view`;
|
|
62
|
+
const nonExistentNotebookUri = `malloy://environment/malloy-samples/package/faa/notebooks/non_existent.malloynb`;
|
|
62
63
|
|
|
63
64
|
describe("client.listResources", () => {
|
|
64
65
|
it(
|
|
@@ -85,17 +86,17 @@ describe("MCP Resource Handlers (E2E Integration)", () => {
|
|
|
85
86
|
expect(faaPackageEntry).toBeDefined();
|
|
86
87
|
expect(faaPackageEntry?.name).toBe("faa");
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
expect(
|
|
91
|
-
expect(typeof
|
|
92
|
-
expect(
|
|
93
|
-
expect(
|
|
94
|
-
expect(typeof
|
|
95
|
-
expect(
|
|
96
|
-
expect(typeof
|
|
97
|
-
if (
|
|
98
|
-
expect(typeof
|
|
89
|
+
// Assert shape on a known row (order of listResources is not guaranteed).
|
|
90
|
+
const sample = faaPackageEntry!;
|
|
91
|
+
expect(sample.uri).toBeDefined();
|
|
92
|
+
expect(typeof sample.uri).toBe("string");
|
|
93
|
+
expect(sample.uri).toMatch(/^malloy:\/\//);
|
|
94
|
+
expect(sample.name).toBeDefined();
|
|
95
|
+
expect(typeof sample.name).toBe("string");
|
|
96
|
+
expect(sample.description).toBeDefined();
|
|
97
|
+
expect(typeof sample.description).toBe("string");
|
|
98
|
+
if (sample.mimeType) {
|
|
99
|
+
expect(typeof sample.mimeType).toBe("string");
|
|
99
100
|
}
|
|
100
101
|
},
|
|
101
102
|
{ timeout: 30000 },
|
|
@@ -216,7 +217,7 @@ describe("MCP Resource Handlers (E2E Integration)", () => {
|
|
|
216
217
|
);
|
|
217
218
|
if (flightsEntry) {
|
|
218
219
|
expect(flightsEntry.uri).toBe(
|
|
219
|
-
"malloy://
|
|
220
|
+
"malloy://environment/malloy-samples/package/faa/sources/flights.malloy",
|
|
220
221
|
);
|
|
221
222
|
expect(flightsEntry.metadata).toBeDefined();
|
|
222
223
|
expect(flightsEntry.metadata!.description).toContain(
|
|
@@ -231,7 +232,7 @@ describe("MCP Resource Handlers (E2E Integration)", () => {
|
|
|
231
232
|
);
|
|
232
233
|
if (notebookEntry) {
|
|
233
234
|
expect(notebookEntry.uri).toBe(
|
|
234
|
-
"malloy://
|
|
235
|
+
"malloy://environment/malloy-samples/package/faa/notebooks/aircraft_analysis.malloynb",
|
|
235
236
|
);
|
|
236
237
|
expect(notebookEntry.metadata).toBeDefined();
|
|
237
238
|
expect(notebookEntry.metadata!.description).toContain(
|
|
@@ -504,7 +505,7 @@ describe("MCP Resource Handlers (E2E Integration)", () => {
|
|
|
504
505
|
// Adjust test to expect the generic "Resource not found" error, as the specific
|
|
505
506
|
// "not a notebook" detail isn't easily surfaced in the standard error format.
|
|
506
507
|
expect(errorPayload.error).toMatch(/Notebook 'overview.malloynb'/);
|
|
507
|
-
expect(errorPayload.error).toMatch(/
|
|
508
|
+
expect(errorPayload.error).toMatch(/environment 'malloy-samples'/);
|
|
508
509
|
expect(errorPayload.suggestions).toBeDefined();
|
|
509
510
|
expect(Array.isArray(errorPayload.suggestions)).toBe(true);
|
|
510
511
|
expect(errorPayload.suggestions.length).toBeGreaterThan(0);
|
|
@@ -531,8 +532,7 @@ describe("MCP Resource Handlers (E2E Integration)", () => {
|
|
|
531
532
|
expect(errorPayload.error).toMatch(/Resource not found/i);
|
|
532
533
|
// Check for the specific source name in the message
|
|
533
534
|
expect(errorPayload.error).toMatch(/Source 'non_existent_source'/);
|
|
534
|
-
|
|
535
|
-
expect(errorPayload.error).toMatch(/project 'malloy-samples'/);
|
|
535
|
+
expect(errorPayload.error).toMatch(/environment 'malloy-samples'/);
|
|
536
536
|
expect(errorPayload.suggestions).toBeDefined();
|
|
537
537
|
expect(Array.isArray(errorPayload.suggestions)).toBe(true);
|
|
538
538
|
expect(errorPayload.suggestions.length).toBeGreaterThan(0);
|
|
@@ -557,8 +557,7 @@ describe("MCP Resource Handlers (E2E Integration)", () => {
|
|
|
557
557
|
expect(errorPayload.error).toMatch(/Resource not found/i);
|
|
558
558
|
// Check for the specific query name in the message
|
|
559
559
|
expect(errorPayload.error).toMatch(/Query 'non_existent_query'/);
|
|
560
|
-
|
|
561
|
-
expect(errorPayload.error).toMatch(/project 'malloy-samples'/);
|
|
560
|
+
expect(errorPayload.error).toMatch(/environment 'malloy-samples'/);
|
|
562
561
|
expect(errorPayload.suggestions).toBeDefined();
|
|
563
562
|
expect(Array.isArray(errorPayload.suggestions)).toBe(true);
|
|
564
563
|
expect(errorPayload.suggestions.length).toBeGreaterThan(0);
|
|
@@ -583,8 +582,7 @@ describe("MCP Resource Handlers (E2E Integration)", () => {
|
|
|
583
582
|
expect(errorPayload.error).toMatch(/Resource not found/i);
|
|
584
583
|
// Check for the specific view name in the message
|
|
585
584
|
expect(errorPayload.error).toMatch(/View 'non_existent_view'/);
|
|
586
|
-
|
|
587
|
-
expect(errorPayload.error).toMatch(/project 'malloy-samples'/);
|
|
585
|
+
expect(errorPayload.error).toMatch(/environment 'malloy-samples'/);
|
|
588
586
|
expect(errorPayload.suggestions).toBeDefined();
|
|
589
587
|
expect(Array.isArray(errorPayload.suggestions)).toBe(true);
|
|
590
588
|
expect(errorPayload.suggestions.length).toBeGreaterThan(0);
|
|
@@ -610,8 +608,7 @@ describe("MCP Resource Handlers (E2E Integration)", () => {
|
|
|
610
608
|
// Check for the specific notebook name and context in the message
|
|
611
609
|
// Adjust test to expect the generic "Resource not found" error
|
|
612
610
|
expect(errorPayload.error).toMatch(/Notebook 'non_existent.malloynb'/);
|
|
613
|
-
|
|
614
|
-
expect(errorPayload.error).toMatch(/project 'malloy-samples'/);
|
|
611
|
+
expect(errorPayload.error).toMatch(/environment 'malloy-samples'/);
|
|
615
612
|
expect(errorPayload.suggestions).toBeDefined();
|
|
616
613
|
expect(Array.isArray(errorPayload.suggestions)).toBe(true);
|
|
617
614
|
expect(errorPayload.suggestions.length).toBeGreaterThan(0);
|
|
@@ -622,7 +619,7 @@ describe("MCP Resource Handlers (E2E Integration)", () => {
|
|
|
622
619
|
|
|
623
620
|
it("should return structured app error when requesting view from wrong source", async () => {
|
|
624
621
|
if (!env) throw new Error("Test environment not initialized");
|
|
625
|
-
const wrongSourceUri = `malloy://
|
|
622
|
+
const wrongSourceUri = `malloy://environment/malloy-samples/package/faa/models/flights.malloy/sources/aircraft/views/${FLIGHTS_MONTH_VIEW}`;
|
|
626
623
|
const result = await mcpClient.readResource({ uri: wrongSourceUri });
|
|
627
624
|
expect(result.isError).toBe(true);
|
|
628
625
|
expect(result.contents).toBeDefined();
|
|
@@ -632,22 +629,17 @@ describe("MCP Resource Handlers (E2E Integration)", () => {
|
|
|
632
629
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
633
630
|
const errorPayload = JSON.parse((result.contents![0] as any).text);
|
|
634
631
|
expect(errorPayload.error).toBeDefined();
|
|
635
|
-
// Adjust expectation: The primary error should be the project not found
|
|
636
632
|
expect(errorPayload.error).toMatch(/Resource not found/i);
|
|
637
|
-
expect(errorPayload.error).toMatch(/
|
|
638
|
-
|
|
633
|
+
expect(errorPayload.error).toMatch(/View 'flights_by_month'/);
|
|
634
|
+
expect(errorPayload.error).toMatch(/source 'aircraft'/);
|
|
635
|
+
expect(errorPayload.error).toMatch(/environment 'malloy-samples'/);
|
|
639
636
|
expect(errorPayload.suggestions).toBeDefined();
|
|
640
637
|
expect(Array.isArray(errorPayload.suggestions)).toBe(true);
|
|
641
|
-
expect(errorPayload.suggestions.length).toBeGreaterThan(0);
|
|
642
|
-
|
|
643
|
-
// The suggestions come as full sentences, so we check for expected content
|
|
644
638
|
expect(errorPayload.suggestions.length).toBe(3);
|
|
645
639
|
expect(errorPayload.suggestions[0]).toContain(
|
|
646
640
|
"Verify the identifier or URI",
|
|
647
641
|
);
|
|
648
|
-
expect(errorPayload.suggestions[0]).toContain(
|
|
649
|
-
"project 'malloy-samples'",
|
|
650
|
-
);
|
|
642
|
+
expect(errorPayload.suggestions[0]).toContain("flights_by_month");
|
|
651
643
|
expect(errorPayload.suggestions[0]).toContain("is spelled correctly");
|
|
652
644
|
expect(errorPayload.suggestions[0]).toContain(
|
|
653
645
|
"Check capitalization and path separators",
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
// --- Test Suite ---
|
|
19
19
|
// Note: These tests assume interaction via a standard MCP client.
|
|
20
20
|
// Tests for raw HTTP edge cases (non-JSON, non-RPC) are omitted based on this assumption.
|
|
21
|
-
describe("MCP Transport Tests (E2E Integration)", () => {
|
|
21
|
+
describe.serial("MCP Transport Tests (E2E Integration)", () => {
|
|
22
22
|
let env: McpE2ETestEnvironment | null = null;
|
|
23
23
|
let mcpClient: Client<Request, Notification, Result>; // Convenience variable
|
|
24
24
|
|
|
@@ -12,7 +12,7 @@ import fs from "fs/promises";
|
|
|
12
12
|
import os from "os";
|
|
13
13
|
import path from "path";
|
|
14
14
|
import type { components } from "../../../src/api";
|
|
15
|
-
import {
|
|
15
|
+
import { createEnvironmentConnections } from "../../../src/service/connection";
|
|
16
16
|
|
|
17
17
|
type ApiConnection = components["schemas"]["Connection"];
|
|
18
18
|
|
|
@@ -355,7 +355,7 @@ describe("DuckDB Attached Databases", () => {
|
|
|
355
355
|
});
|
|
356
356
|
});
|
|
357
357
|
|
|
358
|
-
describe("
|
|
358
|
+
describe("createEnvironmentConnections - DuckDB", () => {
|
|
359
359
|
const PROJECT_TEST_DIR = path.join(os.tmpdir(), "duckdb-project-tests");
|
|
360
360
|
let createdConnections: Map<string, unknown> = new Map();
|
|
361
361
|
|
|
@@ -402,7 +402,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
402
402
|
];
|
|
403
403
|
|
|
404
404
|
const { malloyConnections, apiConnections } =
|
|
405
|
-
await
|
|
405
|
+
await createEnvironmentConnections(connections, PROJECT_TEST_DIR);
|
|
406
406
|
|
|
407
407
|
createdConnections = malloyConnections;
|
|
408
408
|
|
|
@@ -433,7 +433,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
433
433
|
];
|
|
434
434
|
|
|
435
435
|
const { malloyConnections, apiConnections } =
|
|
436
|
-
await
|
|
436
|
+
await createEnvironmentConnections(connections, PROJECT_TEST_DIR);
|
|
437
437
|
|
|
438
438
|
createdConnections = malloyConnections;
|
|
439
439
|
|
|
@@ -462,7 +462,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
462
462
|
];
|
|
463
463
|
|
|
464
464
|
const { malloyConnections, apiConnections } =
|
|
465
|
-
await
|
|
465
|
+
await createEnvironmentConnections(connections, PROJECT_TEST_DIR);
|
|
466
466
|
|
|
467
467
|
createdConnections = malloyConnections;
|
|
468
468
|
|
|
@@ -502,7 +502,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
502
502
|
},
|
|
503
503
|
];
|
|
504
504
|
|
|
505
|
-
const { malloyConnections } = await
|
|
505
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
506
506
|
connections,
|
|
507
507
|
PROJECT_TEST_DIR,
|
|
508
508
|
);
|
|
@@ -533,7 +533,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
533
533
|
},
|
|
534
534
|
];
|
|
535
535
|
|
|
536
|
-
const { malloyConnections } = await
|
|
536
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
537
537
|
connections,
|
|
538
538
|
PROJECT_TEST_DIR,
|
|
539
539
|
);
|
|
@@ -591,7 +591,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
591
591
|
},
|
|
592
592
|
];
|
|
593
593
|
|
|
594
|
-
const { malloyConnections } = await
|
|
594
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
595
595
|
connections,
|
|
596
596
|
PROJECT_TEST_DIR,
|
|
597
597
|
);
|
|
@@ -624,7 +624,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
624
624
|
];
|
|
625
625
|
|
|
626
626
|
await expect(
|
|
627
|
-
|
|
627
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
628
628
|
).rejects.toThrow();
|
|
629
629
|
});
|
|
630
630
|
|
|
@@ -637,7 +637,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
637
637
|
] as ApiConnection[];
|
|
638
638
|
|
|
639
639
|
await expect(
|
|
640
|
-
|
|
640
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
641
641
|
).rejects.toThrow("DuckDB connection configuration is missing");
|
|
642
642
|
});
|
|
643
643
|
|
|
@@ -658,7 +658,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
658
658
|
];
|
|
659
659
|
|
|
660
660
|
await expect(
|
|
661
|
-
|
|
661
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
662
662
|
).rejects.toThrow("Unsupported database type");
|
|
663
663
|
});
|
|
664
664
|
|
|
@@ -682,7 +682,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
682
682
|
];
|
|
683
683
|
|
|
684
684
|
await expect(
|
|
685
|
-
|
|
685
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
686
686
|
).rejects.toThrow("service account key required");
|
|
687
687
|
});
|
|
688
688
|
|
|
@@ -704,7 +704,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
704
704
|
] as ApiConnection[];
|
|
705
705
|
|
|
706
706
|
await expect(
|
|
707
|
-
|
|
707
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
708
708
|
).rejects.toThrow("keyId and secret are required");
|
|
709
709
|
});
|
|
710
710
|
|
|
@@ -726,7 +726,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
726
726
|
] as ApiConnection[];
|
|
727
727
|
|
|
728
728
|
await expect(
|
|
729
|
-
|
|
729
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
730
730
|
).rejects.toThrow("accessKeyId and secretAccessKey are required");
|
|
731
731
|
});
|
|
732
732
|
|
|
@@ -748,7 +748,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
748
748
|
] as ApiConnection[];
|
|
749
749
|
|
|
750
750
|
await expect(
|
|
751
|
-
|
|
751
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
752
752
|
).rejects.toThrow("PostgreSQL connection configuration is required");
|
|
753
753
|
});
|
|
754
754
|
|
|
@@ -761,7 +761,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
761
761
|
] as ApiConnection[];
|
|
762
762
|
|
|
763
763
|
await expect(
|
|
764
|
-
|
|
764
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
765
765
|
).rejects.toThrow("DuckLake connection configuration is missing");
|
|
766
766
|
});
|
|
767
767
|
|
|
@@ -785,7 +785,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
785
785
|
];
|
|
786
786
|
|
|
787
787
|
await expect(
|
|
788
|
-
|
|
788
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
789
789
|
).rejects.toThrow("username is required");
|
|
790
790
|
});
|
|
791
791
|
|
|
@@ -806,7 +806,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
806
806
|
];
|
|
807
807
|
|
|
808
808
|
await expect(
|
|
809
|
-
|
|
809
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
810
810
|
).rejects.toThrow("PostgreSQL connection configuration missing");
|
|
811
811
|
});
|
|
812
812
|
|
|
@@ -836,7 +836,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
836
836
|
];
|
|
837
837
|
|
|
838
838
|
await expect(
|
|
839
|
-
|
|
839
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
840
840
|
).rejects.toThrow(
|
|
841
841
|
"DuckDB attached database names cannot conflict with connection name",
|
|
842
842
|
);
|
|
@@ -854,7 +854,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
854
854
|
];
|
|
855
855
|
|
|
856
856
|
await expect(
|
|
857
|
-
|
|
857
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
858
858
|
).rejects.toThrow("DuckDB connection name cannot be 'duckdb'");
|
|
859
859
|
});
|
|
860
860
|
|
|
@@ -884,10 +884,28 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
884
884
|
];
|
|
885
885
|
|
|
886
886
|
await expect(
|
|
887
|
-
|
|
887
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
888
888
|
).rejects.toThrow("DuckDB connection name cannot be 'duckdb'");
|
|
889
889
|
});
|
|
890
890
|
|
|
891
|
+
it("should throw when DuckDB connection has no attached databases", async () => {
|
|
892
|
+
const connections: ApiConnection[] = [
|
|
893
|
+
{
|
|
894
|
+
name: "no_attached_db",
|
|
895
|
+
type: "duckdb",
|
|
896
|
+
duckdbConnection: {
|
|
897
|
+
attachedDatabases: [],
|
|
898
|
+
},
|
|
899
|
+
},
|
|
900
|
+
];
|
|
901
|
+
|
|
902
|
+
await expect(
|
|
903
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
904
|
+
).rejects.toThrow(
|
|
905
|
+
"DuckDB connection must have at least one attached database",
|
|
906
|
+
);
|
|
907
|
+
});
|
|
908
|
+
|
|
891
909
|
it("should throw on unsupported connection type", async () => {
|
|
892
910
|
const connections = [
|
|
893
911
|
{
|
|
@@ -897,7 +915,7 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
897
915
|
] as ApiConnection[];
|
|
898
916
|
|
|
899
917
|
await expect(
|
|
900
|
-
|
|
918
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
901
919
|
).rejects.toThrow("Unsupported connection type");
|
|
902
920
|
});
|
|
903
921
|
|
|
@@ -921,13 +939,13 @@ describe("createProjectConnections - DuckDB", () => {
|
|
|
921
939
|
] as ApiConnection[];
|
|
922
940
|
|
|
923
941
|
await expect(
|
|
924
|
-
|
|
942
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
925
943
|
).rejects.toThrow();
|
|
926
944
|
});
|
|
927
945
|
});
|
|
928
946
|
});
|
|
929
947
|
|
|
930
|
-
describe("
|
|
948
|
+
describe("createEnvironmentConnections - Other Connection Types", () => {
|
|
931
949
|
const PROJECT_TEST_DIR = path.join(
|
|
932
950
|
os.tmpdir(),
|
|
933
951
|
"connection-validation-tests",
|
|
@@ -970,7 +988,7 @@ describe("createProjectConnections - Other Connection Types", () => {
|
|
|
970
988
|
] as ApiConnection[];
|
|
971
989
|
|
|
972
990
|
await expect(
|
|
973
|
-
|
|
991
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
974
992
|
).rejects.toThrow("Snowflake connection configuration is missing");
|
|
975
993
|
});
|
|
976
994
|
|
|
@@ -988,7 +1006,7 @@ describe("createProjectConnections - Other Connection Types", () => {
|
|
|
988
1006
|
];
|
|
989
1007
|
|
|
990
1008
|
await expect(
|
|
991
|
-
|
|
1009
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
992
1010
|
).rejects.toThrow("Snowflake account is required");
|
|
993
1011
|
});
|
|
994
1012
|
|
|
@@ -1006,7 +1024,7 @@ describe("createProjectConnections - Other Connection Types", () => {
|
|
|
1006
1024
|
];
|
|
1007
1025
|
|
|
1008
1026
|
await expect(
|
|
1009
|
-
|
|
1027
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
1010
1028
|
).rejects.toThrow("Snowflake username is required");
|
|
1011
1029
|
});
|
|
1012
1030
|
|
|
@@ -1024,7 +1042,7 @@ describe("createProjectConnections - Other Connection Types", () => {
|
|
|
1024
1042
|
];
|
|
1025
1043
|
|
|
1026
1044
|
await expect(
|
|
1027
|
-
|
|
1045
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
1028
1046
|
).rejects.toThrow(
|
|
1029
1047
|
"Snowflake password or private key or private key path is required",
|
|
1030
1048
|
);
|
|
@@ -1044,7 +1062,7 @@ describe("createProjectConnections - Other Connection Types", () => {
|
|
|
1044
1062
|
];
|
|
1045
1063
|
|
|
1046
1064
|
await expect(
|
|
1047
|
-
|
|
1065
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
1048
1066
|
).rejects.toThrow("Snowflake warehouse is required");
|
|
1049
1067
|
});
|
|
1050
1068
|
});
|
|
@@ -1059,7 +1077,7 @@ describe("createProjectConnections - Other Connection Types", () => {
|
|
|
1059
1077
|
] as ApiConnection[];
|
|
1060
1078
|
|
|
1061
1079
|
await expect(
|
|
1062
|
-
|
|
1080
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
1063
1081
|
).rejects.toThrow("Trino connection configuration is missing");
|
|
1064
1082
|
});
|
|
1065
1083
|
|
|
@@ -1076,7 +1094,7 @@ describe("createProjectConnections - Other Connection Types", () => {
|
|
|
1076
1094
|
];
|
|
1077
1095
|
|
|
1078
1096
|
await expect(
|
|
1079
|
-
|
|
1097
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
1080
1098
|
).rejects.toThrow(
|
|
1081
1099
|
'Invalid Trino connection: expected "http://server:port" or "https://server:port"',
|
|
1082
1100
|
);
|
|
@@ -1093,7 +1111,7 @@ describe("createProjectConnections - Other Connection Types", () => {
|
|
|
1093
1111
|
] as ApiConnection[];
|
|
1094
1112
|
|
|
1095
1113
|
await expect(
|
|
1096
|
-
|
|
1114
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
1097
1115
|
).rejects.toThrow("MotherDuck connection configuration is missing");
|
|
1098
1116
|
});
|
|
1099
1117
|
|
|
@@ -1107,7 +1125,7 @@ describe("createProjectConnections - Other Connection Types", () => {
|
|
|
1107
1125
|
];
|
|
1108
1126
|
|
|
1109
1127
|
await expect(
|
|
1110
|
-
|
|
1128
|
+
createEnvironmentConnections(connections, PROJECT_TEST_DIR),
|
|
1111
1129
|
).rejects.toThrow("MotherDuck access token is required");
|
|
1112
1130
|
});
|
|
1113
1131
|
});
|