@malloy-publisher/server 0.0.235 → 0.0.237
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 +83 -27
- package/dist/app/assets/{EnvironmentPage-BsAnavYN.js → EnvironmentPage-DaP6CpgK.js} +1 -1
- package/dist/app/assets/{HomePage-CADE138j.js → HomePage-C9yNw3iu.js} +1 -1
- package/dist/app/assets/{LightMode-Cfh7KzN8.js → LightMode-Be4-G-dX.js} +1 -1
- package/dist/app/assets/{MainPage-CO3pRlnV.js → MainPage-J_rU2SgI.js} +2 -2
- package/dist/app/assets/{MaterializationsPage-p9YjkRXZ.js → MaterializationsPage-DHgPI4MO.js} +1 -1
- package/dist/app/assets/ModelPage-BzXfjTu5.js +1 -0
- package/dist/app/assets/{PackagePage-e4kN75YR.js → PackagePage-ByoSTLJ4.js} +1 -1
- package/dist/app/assets/RouteError-CrSeKN2r.js +1 -0
- package/dist/app/assets/{ThemeEditorPage-CciagFTq.js → ThemeEditorPage-Btxnmhtk.js} +1 -1
- package/dist/app/assets/{WorkbookPage-DNWmkCXa.js → WorkbookPage-D6JLPnWO.js} +1 -1
- package/dist/app/assets/{core-Rj_4rRnA.es-BZyvITuO.js → core-BSRgt1mG.es-Bc_D2Lq4.js} +1 -1
- package/dist/app/assets/{index-CH2AcDzc.js → index-BRijWj6K.js} +15 -15
- package/dist/app/assets/{index-DQa463gC.js → index-BX4D94xw.js} +1 -1
- package/dist/app/assets/{index-VBbcc8s6.js → index-Cvy0xtb7.js} +1 -1
- package/dist/app/assets/{index-DQpV7MyA.js → index-DMAhIJNC.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/package_load_worker.mjs +59 -20
- package/dist/server.mjs +1342 -616
- package/package.json +8 -2
- package/.eslintrc.json +0 -44
- package/.prettierignore +0 -1
- package/.prettierrc +0 -4
- package/build.ts +0 -97
- package/dist/app/assets/ModelPage-C1OSTv-x.js +0 -1
- package/dist/app/assets/RouteError-CzbfOkng.js +0 -1
- package/eslint.config.mjs +0 -76
- package/k6-tests/README.md +0 -144
- package/k6-tests/bun.lock +0 -797
- package/k6-tests/load-test/load-test-crud-connections.ts +0 -263
- package/k6-tests/load-test/load-test-crud-packages.ts +0 -461
- package/k6-tests/load-test/load-test-crud-projects.ts +0 -160
- package/k6-tests/load-test/load-test-crud.ts +0 -142
- package/k6-tests/load-test/load-test.ts +0 -784
- package/k6-tests/package.json +0 -27
- package/k6-tests/scripts/run-with-otel.sh +0 -50
- package/k6-tests/smoke-test/smoke-test.ts +0 -158
- package/k6-tests/tsconfig.json +0 -29
- package/k6-tests/utils/client_factory.ts +0 -128
- package/k6-tests/utils/common.ts +0 -489
- package/k6-tests/utils/logger.ts +0 -62
- package/k6-tests/utils/types.d.ts +0 -35
- package/malloy_mcp.dxt +0 -0
- package/publisher.config.example.bigquery.json +0 -33
- package/publisher.config.example.duckdb.json +0 -23
- package/publisher.config.json +0 -23
- package/scripts/bake-duckdb-extensions.js +0 -110
- package/src/config.spec.ts +0 -1535
- package/src/config.theme.spec.ts +0 -178
- package/src/config.ts +0 -1142
- package/src/constants.ts +0 -105
- package/src/controller/compile.controller.ts +0 -40
- package/src/controller/connection.controller.spec.ts +0 -849
- package/src/controller/connection.controller.ts +0 -901
- package/src/controller/database.controller.ts +0 -24
- package/src/controller/materialization.controller.spec.ts +0 -293
- package/src/controller/materialization.controller.ts +0 -319
- package/src/controller/model.controller.ts +0 -164
- package/src/controller/package.controller.spec.ts +0 -273
- package/src/controller/package.controller.ts +0 -306
- package/src/controller/query.controller.ts +0 -152
- package/src/controller/theme.controller.ts +0 -83
- package/src/controller/watch-mode.controller.ts +0 -243
- package/src/data_styles.ts +0 -71
- package/src/default-publisher.config.json +0 -23
- package/src/ducklake_version.spec.ts +0 -163
- package/src/ducklake_version.ts +0 -153
- package/src/errors.spec.ts +0 -119
- package/src/errors.ts +0 -237
- package/src/filter_deprecation.spec.ts +0 -64
- package/src/filter_deprecation.ts +0 -42
- package/src/health.spec.ts +0 -123
- package/src/health.ts +0 -233
- package/src/heap_check.spec.ts +0 -144
- package/src/heap_check.ts +0 -144
- package/src/instrumentation.ts +0 -214
- package/src/json_utils.spec.ts +0 -51
- package/src/json_utils.ts +0 -33
- package/src/logger.spec.ts +0 -210
- package/src/logger.ts +0 -251
- package/src/malloy_pin_prereqs.spec.ts +0 -25
- package/src/materialization_metrics.spec.ts +0 -169
- package/src/materialization_metrics.ts +0 -346
- package/src/mcp/error_messages.spec.ts +0 -35
- package/src/mcp/error_messages.ts +0 -183
- package/src/mcp/handler_utils.spec.ts +0 -108
- package/src/mcp/handler_utils.ts +0 -251
- package/src/mcp/mcp_constants.ts +0 -6
- package/src/mcp/query_envelope.spec.ts +0 -229
- package/src/mcp/query_envelope.ts +0 -240
- package/src/mcp/server.protocol.spec.ts +0 -250
- package/src/mcp/server.ts +0 -87
- package/src/mcp/skills/build_skills_bundle.spec.ts +0 -51
- package/src/mcp/skills/build_skills_bundle.ts +0 -176
- package/src/mcp/skills/skills_bundle.json +0 -1
- package/src/mcp/skills/skills_bundle.spec.ts +0 -171
- package/src/mcp/tool_response.spec.ts +0 -108
- package/src/mcp/tool_response.ts +0 -138
- package/src/mcp/tools/compile_tool.spec.ts +0 -315
- package/src/mcp/tools/compile_tool.ts +0 -208
- package/src/mcp/tools/docs_search/build_docs_index.ts +0 -132
- package/src/mcp/tools/docs_search/malloy_docs_index.json +0 -1
- package/src/mcp/tools/docs_search_tool.spec.ts +0 -32
- package/src/mcp/tools/docs_search_tool.ts +0 -128
- package/src/mcp/tools/embedding_index.spec.ts +0 -1236
- package/src/mcp/tools/embedding_index.ts +0 -808
- package/src/mcp/tools/execute_query_tool.spec.ts +0 -294
- package/src/mcp/tools/execute_query_tool.ts +0 -316
- package/src/mcp/tools/get_context_eval.ts +0 -275
- package/src/mcp/tools/get_context_tool.spec.ts +0 -592
- package/src/mcp/tools/get_context_tool.ts +0 -585
- package/src/mcp/tools/reload_package_tool.spec.ts +0 -232
- package/src/mcp/tools/reload_package_tool.ts +0 -132
- package/src/mcp_config.spec.ts +0 -919
- package/src/mcp_config.ts +0 -425
- package/src/oom_guards.integration.spec.ts +0 -269
- package/src/package_load/package_load_pool.spec.ts +0 -252
- package/src/package_load/package_load_pool.ts +0 -931
- package/src/package_load/package_load_worker.ts +0 -1034
- package/src/package_load/protocol.ts +0 -364
- package/src/package_load/rpc_wait_accountant.spec.ts +0 -109
- package/src/package_load/rpc_wait_accountant.ts +0 -76
- package/src/package_load_metrics.spec.ts +0 -114
- package/src/package_load_metrics.ts +0 -127
- package/src/path_safety.spec.ts +0 -158
- package/src/path_safety.ts +0 -146
- package/src/pg_helpers.spec.ts +0 -223
- package/src/pg_helpers.ts +0 -52
- package/src/query_cap_metrics.spec.ts +0 -89
- package/src/query_cap_metrics.ts +0 -116
- package/src/query_concurrency.spec.ts +0 -247
- package/src/query_concurrency.ts +0 -237
- package/src/query_metadata_metrics.ts +0 -49
- package/src/query_param_utils.ts +0 -29
- package/src/query_timeout.spec.ts +0 -224
- package/src/query_timeout.ts +0 -179
- package/src/runtime/publisher.js +0 -328
- package/src/server-old.ts +0 -988
- package/src/server.ts +0 -2059
- package/src/service/annotations.spec.ts +0 -118
- package/src/service/annotations.ts +0 -91
- package/src/service/authorize.spec.ts +0 -154
- package/src/service/authorize.ts +0 -498
- package/src/service/authorize_integration.spec.ts +0 -2041
- package/src/service/build_plan.spec.ts +0 -685
- package/src/service/build_plan.ts +0 -797
- package/src/service/compile_authorize.spec.ts +0 -85
- package/src/service/compile_fragment_techniques.spec.ts +0 -156
- package/src/service/connection.spec.ts +0 -2360
- package/src/service/connection.ts +0 -2187
- package/src/service/connection_config.spec.ts +0 -734
- package/src/service/connection_config.ts +0 -899
- package/src/service/connection_federation.spec.ts +0 -184
- package/src/service/connection_fingerprint.spec.ts +0 -102
- package/src/service/connection_service.spec.ts +0 -711
- package/src/service/connection_service.ts +0 -291
- package/src/service/cron_evaluator.spec.ts +0 -79
- package/src/service/cron_evaluator.ts +0 -105
- package/src/service/db_utils.spec.ts +0 -1085
- package/src/service/db_utils.ts +0 -1502
- package/src/service/duckdb_instance_isolation.spec.ts +0 -137
- package/src/service/ducklake_lazy_attach.spec.ts +0 -110
- package/src/service/embedding_provider.spec.ts +0 -329
- package/src/service/embedding_provider.ts +0 -236
- package/src/service/environment.ts +0 -2191
- package/src/service/environment_admission.spec.ts +0 -344
- package/src/service/environment_compile.spec.ts +0 -197
- package/src/service/environment_store.spec.ts +0 -2274
- package/src/service/environment_store.ts +0 -2434
- package/src/service/environment_store_anchoring.spec.ts +0 -107
- package/src/service/environment_store_clone.spec.ts +0 -350
- package/src/service/explore_visibility.spec.ts +0 -434
- package/src/service/exports_probe.spec.ts +0 -107
- package/src/service/extension_fetch_policy.spec.ts +0 -256
- package/src/service/filter.spec.ts +0 -458
- package/src/service/filter.ts +0 -341
- package/src/service/filter_bypass.spec.ts +0 -418
- package/src/service/filter_integration.spec.ts +0 -1004
- package/src/service/freshness.spec.ts +0 -183
- package/src/service/freshness.ts +0 -112
- package/src/service/gcs_s3_utils.ts +0 -427
- package/src/service/given.ts +0 -105
- package/src/service/givens_integration.spec.ts +0 -219
- package/src/service/manifest_loader.spec.ts +0 -189
- package/src/service/manifest_loader.ts +0 -156
- package/src/service/materialization_build_session.spec.ts +0 -435
- package/src/service/materialization_build_session.ts +0 -681
- package/src/service/materialization_config_validation.spec.ts +0 -99
- package/src/service/materialization_config_validation.ts +0 -120
- package/src/service/materialization_eligibility.spec.ts +0 -158
- package/src/service/materialization_eligibility.ts +0 -305
- package/src/service/materialization_schedule_surface.spec.ts +0 -256
- package/src/service/materialization_scheduler.spec.ts +0 -289
- package/src/service/materialization_scheduler.ts +0 -304
- package/src/service/materialization_serve_transform.spec.ts +0 -1003
- package/src/service/materialization_serve_transform.ts +0 -779
- package/src/service/materialization_service.spec.ts +0 -2373
- package/src/service/materialization_service.ts +0 -2342
- package/src/service/materialization_test_fixtures.ts +0 -249
- package/src/service/model.spec.ts +0 -1157
- package/src/service/model.ts +0 -3370
- package/src/service/model_limits.spec.ts +0 -209
- package/src/service/model_limits.ts +0 -131
- package/src/service/model_storage_serve.spec.ts +0 -193
- package/src/service/model_storage_serve_joins.spec.ts +0 -193
- package/src/service/package.spec.ts +0 -612
- package/src/service/package.ts +0 -1799
- package/src/service/package_manifest.spec.ts +0 -230
- package/src/service/package_manifest.ts +0 -241
- package/src/service/package_memory_governor.spec.ts +0 -173
- package/src/service/package_memory_governor.ts +0 -233
- package/src/service/package_quote_bound_tables.spec.ts +0 -171
- package/src/service/package_race.spec.ts +0 -208
- package/src/service/package_reload_safety.spec.ts +0 -193
- package/src/service/package_rollback.spec.ts +0 -190
- package/src/service/package_worker_path.spec.ts +0 -645
- package/src/service/path_injection.spec.ts +0 -39
- package/src/service/persist_annotation_validation.spec.ts +0 -89
- package/src/service/persist_annotation_validation.ts +0 -59
- package/src/service/persistence_policy.spec.ts +0 -374
- package/src/service/proxy.spec.ts +0 -414
- package/src/service/proxy.ts +0 -248
- package/src/service/query_boundary.spec.ts +0 -470
- package/src/service/query_metadata.spec.ts +0 -408
- package/src/service/query_metadata.ts +0 -492
- package/src/service/query_metadata_identity.spec.ts +0 -149
- package/src/service/query_text.spec.ts +0 -79
- package/src/service/query_text.ts +0 -43
- package/src/service/quoting.spec.ts +0 -135
- package/src/service/quoting.ts +0 -78
- package/src/service/resolve_environment.ts +0 -15
- package/src/service/restricted_mode.spec.ts +0 -299
- package/src/service/source_extraction.ts +0 -225
- package/src/service/theme_store.ts +0 -199
- package/src/storage/DatabaseInterface.ts +0 -198
- package/src/storage/StorageManager.ts +0 -113
- package/src/storage/duckdb/ConnectionRepository.ts +0 -143
- package/src/storage/duckdb/DuckDBConnection.ts +0 -123
- package/src/storage/duckdb/DuckDBRepository.ts +0 -208
- package/src/storage/duckdb/EnvironmentRepository.ts +0 -148
- package/src/storage/duckdb/MaterializationRepository.spec.ts +0 -191
- package/src/storage/duckdb/MaterializationRepository.ts +0 -336
- package/src/storage/duckdb/PackageRepository.ts +0 -135
- package/src/storage/duckdb/schema.ts +0 -229
- package/src/stream_helpers.spec.ts +0 -280
- package/src/stream_helpers.ts +0 -162
- package/src/telemetry.ts +0 -20
- package/src/test_helpers/metrics_harness.ts +0 -166
- package/src/theme_key_parity.spec.ts +0 -57
- package/src/utils.ts +0 -29
- package/tests/fixtures/authorize-compile/model.malloy +0 -9
- package/tests/fixtures/authorize-compile/publisher.json +0 -4
- package/tests/fixtures/html-pages-nopublic/model.malloy +0 -1
- package/tests/fixtures/html-pages-nopublic/publisher.json +0 -5
- package/tests/fixtures/html-pages-test/data.csv +0 -3
- package/tests/fixtures/html-pages-test/public/assets/app.css +0 -3
- package/tests/fixtures/html-pages-test/public/barehtml.html +0 -4
- package/tests/fixtures/html-pages-test/public/bodymeta.html +0 -8
- package/tests/fixtures/html-pages-test/public/data.json +0 -1
- package/tests/fixtures/html-pages-test/public/fitcomment.html +0 -11
- package/tests/fixtures/html-pages-test/public/index.html +0 -9
- package/tests/fixtures/html-pages-test/public/nohead.html +0 -8
- package/tests/fixtures/html-pages-test/public/notfit.html +0 -13
- package/tests/fixtures/html-pages-test/public/slides.html +0 -12
- package/tests/fixtures/html-pages-test/public/sub/page2.html +0 -9
- package/tests/fixtures/html-pages-test/public/unterminated.html +0 -10
- package/tests/fixtures/html-pages-test/publisher.json +0 -5
- package/tests/fixtures/html-pages-test/report.malloy +0 -1
- package/tests/fixtures/persist-multi-level/data/orders.csv +0 -5
- package/tests/fixtures/persist-multi-level/multi_level.malloy +0 -18
- package/tests/fixtures/persist-multi-level/publisher.json +0 -5
- package/tests/fixtures/persist-schedule-test/data/orders.csv +0 -5
- package/tests/fixtures/persist-schedule-test/persist_schedule_test.malloy +0 -11
- package/tests/fixtures/persist-schedule-test/publisher.json +0 -7
- package/tests/fixtures/persist-schedule-warn-test/data/orders.csv +0 -5
- package/tests/fixtures/persist-schedule-warn-test/persist_warn_test.malloy +0 -11
- package/tests/fixtures/persist-schedule-warn-test/publisher.json +0 -6
- package/tests/fixtures/persist-test/data/orders.csv +0 -5
- package/tests/fixtures/persist-test/persist_test.malloy +0 -11
- package/tests/fixtures/persist-test/publisher.json +0 -5
- package/tests/fixtures/publisher.config.json +0 -15
- package/tests/fixtures/query-givens/data/orders.csv +0 -7
- package/tests/fixtures/query-givens/model.malloy +0 -34
- package/tests/fixtures/query-givens/publisher.json +0 -5
- package/tests/fixtures/xlsx/database.xlsx +0 -0
- package/tests/harness/mcp_test_setup.ts +0 -332
- package/tests/harness/rest_e2e.ts +0 -68
- package/tests/integration/authorize/compile_authorize_http.integration.spec.ts +0 -92
- package/tests/integration/concurrent_package/concurrent_package.integration.spec.ts +0 -280
- package/tests/integration/duckdb_storage/duckdb_storage.integration.spec.ts +0 -138
- package/tests/integration/first_boot/readiness_line.integration.spec.ts +0 -177
- package/tests/integration/html_pages/html_pages.integration.spec.ts +0 -440
- package/tests/integration/legacy_routes/legacy_routes.integration.spec.ts +0 -259
- package/tests/integration/materialization/environment_list.integration.spec.ts +0 -133
- package/tests/integration/materialization/freshness_gate.integration.spec.ts +0 -292
- package/tests/integration/materialization/manifest_binding.integration.spec.ts +0 -430
- package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +0 -441
- package/tests/integration/materialization/orchestrated_rebind.integration.spec.ts +0 -150
- package/tests/integration/materialization/reference_manifest.integration.spec.ts +0 -251
- package/tests/integration/materialization/schedule_edit.integration.spec.ts +0 -126
- package/tests/integration/materialization/scheduler.integration.spec.ts +0 -200
- package/tests/integration/materialization/scheduler_recovery.integration.spec.ts +0 -172
- package/tests/integration/materialization/scheduler_transitions.integration.spec.ts +0 -256
- package/tests/integration/materialization/scheduler_wiring.integration.spec.ts +0 -143
- package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +0 -438
- package/tests/integration/mcp/mcp_get_context_semantic.integration.spec.ts +0 -235
- package/tests/integration/mcp/mcp_transport.integration.spec.ts +0 -98
- package/tests/integration/query_givens/query_givens.integration.spec.ts +0 -146
- package/tests/integration/query_givens/query_givens_authorize.integration.spec.ts +0 -121
- package/tests/integration/sdk_givens/sdk_givens.integration.spec.ts +0 -110
- package/tests/integration/watch-mode/watch_mode.integration.spec.ts +0 -421
- package/tests/unit/controller/theme.controller.spec.ts +0 -141
- package/tests/unit/duckdb/attached_databases.test.ts +0 -1243
- package/tests/unit/duckdb/duckdb_connection.test.ts +0 -181
- package/tests/unit/duckdb/legacy_schema_migration.test.ts +0 -197
- package/tests/unit/duckdb/repositories.test.ts +0 -208
- package/tests/unit/ducklake/ducklake.test.ts +0 -952
- package/tests/unit/service/theme_store.spec.ts +0 -139
- package/tsconfig.json +0 -32
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
/// <reference types="bun-types" />
|
|
2
|
-
|
|
3
|
-
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
|
4
|
-
import path from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
6
|
-
import { RestE2EEnv, startRestE2E } from "../../harness/rest_e2e";
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
|
|
11
|
-
const PROJECT_NAME = "test-project-refmanifest";
|
|
12
|
-
const PACKAGE_NAME = "persist-multi-level";
|
|
13
|
-
const API = `/api/v0/environments/${PROJECT_NAME}/packages/${PACKAGE_NAME}`;
|
|
14
|
-
|
|
15
|
-
const TERMINAL_STATUSES = ["MANIFEST_FILE_READY", "FAILED", "CANCELLED"];
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* WI-1 (Phase D): the orchestrated build seeds its build Manifest from
|
|
19
|
-
* `referenceManifest` and honors `strictUpstreams`. Proven end-to-end over a
|
|
20
|
-
* two-level persist DAG (`orders_base` -> `orders_rollup`):
|
|
21
|
-
*
|
|
22
|
-
* - Build the downstream `orders_rollup` ALONE with a `referenceManifest`
|
|
23
|
-
* pointing at the already-materialized `orders_base` table and
|
|
24
|
-
* `strictUpstreams=true` -> the build SUCCEEDS, which under strict mode is
|
|
25
|
-
* only possible if the upstream reference resolved to the physical table
|
|
26
|
-
* (a strict miss would throw).
|
|
27
|
-
* - Build `orders_rollup` ALONE under `strictUpstreams=true` with NO
|
|
28
|
-
* `referenceManifest` -> the build FAILS loudly (runtime-manifest-strict-miss)
|
|
29
|
-
* instead of silently recomputing the upstream live.
|
|
30
|
-
*/
|
|
31
|
-
describe("Materialization reference manifest + strictUpstreams (E2E)", () => {
|
|
32
|
-
let env: (RestE2EEnv & { stop(): Promise<void> }) | null = null;
|
|
33
|
-
let baseUrl: string;
|
|
34
|
-
|
|
35
|
-
beforeAll(async () => {
|
|
36
|
-
env = await startRestE2E();
|
|
37
|
-
baseUrl = env.baseUrl;
|
|
38
|
-
|
|
39
|
-
const fixtureDir = path.resolve(
|
|
40
|
-
__dirname,
|
|
41
|
-
"../../fixtures/persist-multi-level",
|
|
42
|
-
);
|
|
43
|
-
const createRes = await fetch(`${baseUrl}/api/v0/environments`, {
|
|
44
|
-
method: "POST",
|
|
45
|
-
headers: { "Content-Type": "application/json" },
|
|
46
|
-
body: JSON.stringify({
|
|
47
|
-
name: PROJECT_NAME,
|
|
48
|
-
packages: [{ name: PACKAGE_NAME, location: fixtureDir }],
|
|
49
|
-
connections: [],
|
|
50
|
-
}),
|
|
51
|
-
});
|
|
52
|
-
if (!createRes.ok) {
|
|
53
|
-
const body = await createRes.text();
|
|
54
|
-
throw new Error(
|
|
55
|
-
`Failed to create test project (${createRes.status}): ${body}`,
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const deadline = Date.now() + 30_000;
|
|
60
|
-
let pkgReady = false;
|
|
61
|
-
while (!pkgReady && Date.now() < deadline) {
|
|
62
|
-
try {
|
|
63
|
-
const res = await fetch(`${baseUrl}${API}`);
|
|
64
|
-
if (res.ok) {
|
|
65
|
-
pkgReady = true;
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
} catch {
|
|
69
|
-
// not ready yet
|
|
70
|
-
}
|
|
71
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
72
|
-
}
|
|
73
|
-
if (!pkgReady) {
|
|
74
|
-
throw new Error("Test package did not become available in time");
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
afterAll(async () => {
|
|
79
|
-
if (baseUrl) {
|
|
80
|
-
try {
|
|
81
|
-
await fetch(`${baseUrl}/api/v0/environments/${PROJECT_NAME}`, {
|
|
82
|
-
method: "DELETE",
|
|
83
|
-
});
|
|
84
|
-
} catch {
|
|
85
|
-
// best-effort cleanup
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
await env?.stop();
|
|
89
|
-
env = null;
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
function url(p: string): string {
|
|
93
|
-
return `${baseUrl}${API}${p}`;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function createMaterialization(
|
|
97
|
-
body: Record<string, unknown> = {},
|
|
98
|
-
): Promise<Response> {
|
|
99
|
-
return fetch(url("/materializations"), {
|
|
100
|
-
method: "POST",
|
|
101
|
-
headers: { "Content-Type": "application/json" },
|
|
102
|
-
body: JSON.stringify(body),
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async function pollUntilTerminal(
|
|
107
|
-
id: string,
|
|
108
|
-
timeoutMs = 90_000,
|
|
109
|
-
): Promise<Record<string, unknown>> {
|
|
110
|
-
const deadline = Date.now() + timeoutMs;
|
|
111
|
-
while (Date.now() < deadline) {
|
|
112
|
-
const res = await fetch(url(`/materializations/${id}`));
|
|
113
|
-
expect(res.status).toBe(200);
|
|
114
|
-
const data = (await res.json()) as Record<string, unknown>;
|
|
115
|
-
if (TERMINAL_STATUSES.includes(data.status as string)) return data;
|
|
116
|
-
await new Promise((r) => setTimeout(r, 250));
|
|
117
|
-
}
|
|
118
|
-
throw new Error(`Materialization ${id} did not reach a terminal state`);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** Delete a terminal materialization record (optionally dropping its tables). */
|
|
122
|
-
async function deleteRun(id: string, dropTables = false): Promise<void> {
|
|
123
|
-
await fetch(
|
|
124
|
-
url(`/materializations/${id}${dropTables ? "?dropTables=true" : ""}`),
|
|
125
|
-
{ method: "DELETE" },
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** Build one source alone and drive it to a terminal state. */
|
|
130
|
-
async function buildOneAlone(
|
|
131
|
-
body: Record<string, unknown>,
|
|
132
|
-
): Promise<Record<string, unknown>> {
|
|
133
|
-
const createRes = await createMaterialization(body);
|
|
134
|
-
expect(createRes.status).toBe(201);
|
|
135
|
-
const { id } = (await createRes.json()) as { id: string };
|
|
136
|
-
const settled = await pollUntilTerminal(id);
|
|
137
|
-
return { id, ...settled };
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/** The planned sources keyed by name (off Package.buildPlan). */
|
|
141
|
-
async function planSourcesByName(): Promise<
|
|
142
|
-
Record<string, Record<string, unknown>>
|
|
143
|
-
> {
|
|
144
|
-
const res = await fetch(url(""));
|
|
145
|
-
expect(res.status).toBe(200);
|
|
146
|
-
const pkg = (await res.json()) as Record<string, unknown>;
|
|
147
|
-
const plan = pkg.buildPlan as Record<string, unknown>;
|
|
148
|
-
expect(plan).toBeDefined();
|
|
149
|
-
const sources = plan.sources as Record<string, Record<string, unknown>>;
|
|
150
|
-
const byName: Record<string, Record<string, unknown>> = {};
|
|
151
|
-
for (const s of Object.values(sources)) {
|
|
152
|
-
byName[s.name as string] = s;
|
|
153
|
-
}
|
|
154
|
-
return byName;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const UPSTREAM_TABLE = "orders_base_built";
|
|
158
|
-
|
|
159
|
-
it(
|
|
160
|
-
"builds a downstream source alone, referencing the upstream's physical table under strict",
|
|
161
|
-
async () => {
|
|
162
|
-
const sources = await planSourcesByName();
|
|
163
|
-
const base = sources["orders_base"];
|
|
164
|
-
const rollup = sources["orders_rollup"];
|
|
165
|
-
expect(base).toBeDefined();
|
|
166
|
-
expect(rollup).toBeDefined();
|
|
167
|
-
|
|
168
|
-
// 1) Materialize the upstream `orders_base` into a known physical table.
|
|
169
|
-
const baseRun = await buildOneAlone({
|
|
170
|
-
buildInstructions: {
|
|
171
|
-
sources: [
|
|
172
|
-
{
|
|
173
|
-
sourceEntityId: base.sourceEntityId,
|
|
174
|
-
sourceID: base.sourceID,
|
|
175
|
-
materializedTableId: "mt-base",
|
|
176
|
-
physicalTableName: UPSTREAM_TABLE,
|
|
177
|
-
realization: "COPY",
|
|
178
|
-
},
|
|
179
|
-
],
|
|
180
|
-
},
|
|
181
|
-
});
|
|
182
|
-
expect(baseRun.status).toBe("MANIFEST_FILE_READY");
|
|
183
|
-
await deleteRun(baseRun.id as string); // keep the physical table
|
|
184
|
-
|
|
185
|
-
// 2) Build the downstream `orders_rollup` ALONE, referencing the
|
|
186
|
-
// upstream's physical table under strictUpstreams. Success is only
|
|
187
|
-
// possible if the reference resolved (strict forbids the live fallback).
|
|
188
|
-
const rollupRun = await buildOneAlone({
|
|
189
|
-
buildInstructions: {
|
|
190
|
-
sources: [
|
|
191
|
-
{
|
|
192
|
-
sourceEntityId: rollup.sourceEntityId,
|
|
193
|
-
sourceID: rollup.sourceID,
|
|
194
|
-
materializedTableId: "mt-rollup",
|
|
195
|
-
physicalTableName: "orders_rollup_built",
|
|
196
|
-
realization: "COPY",
|
|
197
|
-
},
|
|
198
|
-
],
|
|
199
|
-
referenceManifest: [
|
|
200
|
-
{
|
|
201
|
-
sourceEntityId: base.sourceEntityId,
|
|
202
|
-
physicalTableName: UPSTREAM_TABLE,
|
|
203
|
-
},
|
|
204
|
-
],
|
|
205
|
-
strictUpstreams: true,
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
expect(rollupRun.status).toBe("MANIFEST_FILE_READY");
|
|
209
|
-
|
|
210
|
-
// Cleanup both physical tables and records.
|
|
211
|
-
await deleteRun(rollupRun.id as string, true);
|
|
212
|
-
// Drop the upstream table via a fresh no-op build+drop is overkill;
|
|
213
|
-
// the environment teardown removes the project. Best-effort direct drop
|
|
214
|
-
// is unnecessary for correctness here.
|
|
215
|
-
},
|
|
216
|
-
{ timeout: 120_000 },
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
it(
|
|
220
|
-
"fails loudly when a strict upstream is neither built nor referenced",
|
|
221
|
-
async () => {
|
|
222
|
-
const sources = await planSourcesByName();
|
|
223
|
-
const rollup = sources["orders_rollup"];
|
|
224
|
-
expect(rollup).toBeDefined();
|
|
225
|
-
|
|
226
|
-
// Build `orders_rollup` ALONE under strictUpstreams with NO
|
|
227
|
-
// referenceManifest for its upstream -> the compile throws
|
|
228
|
-
// runtime-manifest-strict-miss and the run FAILS.
|
|
229
|
-
const run = await buildOneAlone({
|
|
230
|
-
buildInstructions: {
|
|
231
|
-
sources: [
|
|
232
|
-
{
|
|
233
|
-
sourceEntityId: rollup.sourceEntityId,
|
|
234
|
-
sourceID: rollup.sourceID,
|
|
235
|
-
materializedTableId: "mt-rollup-strict",
|
|
236
|
-
physicalTableName: "orders_rollup_strict",
|
|
237
|
-
realization: "COPY",
|
|
238
|
-
},
|
|
239
|
-
],
|
|
240
|
-
strictUpstreams: true,
|
|
241
|
-
},
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
expect(run.status).toBe("FAILED");
|
|
245
|
-
expect(String(run.error ?? "")).toContain("manifest");
|
|
246
|
-
|
|
247
|
-
await deleteRun(run.id as string, true);
|
|
248
|
-
},
|
|
249
|
-
{ timeout: 120_000 },
|
|
250
|
-
);
|
|
251
|
-
});
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/// <reference types="bun-types" />
|
|
2
|
-
|
|
3
|
-
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
|
4
|
-
import path from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
6
|
-
import { RestE2EEnv, startRestE2E } from "../../harness/rest_e2e";
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
|
|
11
|
-
const PROJECT_NAME = "sched-edit-project";
|
|
12
|
-
const PACKAGE_NAME = "persist-schedule-test";
|
|
13
|
-
const API = `/api/v0/environments/${PROJECT_NAME}/packages/${PACKAGE_NAME}`;
|
|
14
|
-
|
|
15
|
-
// Exercises the editable-schedule PATCH path: materialization.schedule and
|
|
16
|
-
// scope are writable via updatePackage, validated with the same rules a publish
|
|
17
|
-
// enforces (schedule requires scope: version, valid 5-field cron), and
|
|
18
|
-
// persisted to publisher.json.
|
|
19
|
-
describe("Editable materialization schedule (PATCH)", () => {
|
|
20
|
-
let env: (RestE2EEnv & { stop(): Promise<void> }) | null = null;
|
|
21
|
-
let baseUrl: string;
|
|
22
|
-
|
|
23
|
-
beforeAll(async () => {
|
|
24
|
-
env = await startRestE2E();
|
|
25
|
-
baseUrl = env.baseUrl;
|
|
26
|
-
|
|
27
|
-
const fixtureDir = path.resolve(
|
|
28
|
-
__dirname,
|
|
29
|
-
"../../fixtures/persist-schedule-test",
|
|
30
|
-
);
|
|
31
|
-
const createRes = await fetch(`${baseUrl}/api/v0/environments`, {
|
|
32
|
-
method: "POST",
|
|
33
|
-
headers: { "Content-Type": "application/json" },
|
|
34
|
-
body: JSON.stringify({
|
|
35
|
-
name: PROJECT_NAME,
|
|
36
|
-
packages: [{ name: PACKAGE_NAME, location: fixtureDir }],
|
|
37
|
-
connections: [],
|
|
38
|
-
}),
|
|
39
|
-
});
|
|
40
|
-
if (!createRes.ok) {
|
|
41
|
-
throw new Error(
|
|
42
|
-
`Failed to create test project (${createRes.status}): ${await createRes.text()}`,
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const deadline = Date.now() + 30_000;
|
|
47
|
-
while (Date.now() < deadline) {
|
|
48
|
-
const res = await fetch(`${baseUrl}${API}`);
|
|
49
|
-
if (res.ok) break;
|
|
50
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
afterAll(async () => {
|
|
55
|
-
if (baseUrl) {
|
|
56
|
-
try {
|
|
57
|
-
await fetch(`${baseUrl}/api/v0/environments/${PROJECT_NAME}`, {
|
|
58
|
-
method: "DELETE",
|
|
59
|
-
});
|
|
60
|
-
} catch {
|
|
61
|
-
// best-effort
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
await env?.stop();
|
|
65
|
-
env = null;
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
async function getPackage(): Promise<Record<string, unknown>> {
|
|
69
|
-
const res = await fetch(`${baseUrl}${API}`);
|
|
70
|
-
expect(res.status).toBe(200);
|
|
71
|
-
return (await res.json()) as Record<string, unknown>;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function scheduleOf(
|
|
75
|
-
pkg: Record<string, unknown>,
|
|
76
|
-
): string | null | undefined {
|
|
77
|
-
return (pkg.materialization as { schedule?: string | null } | null)
|
|
78
|
-
?.schedule;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function patch(body: Record<string, unknown>): Promise<Response> {
|
|
82
|
-
return fetch(`${baseUrl}${API}`, {
|
|
83
|
-
method: "PATCH",
|
|
84
|
-
headers: { "Content-Type": "application/json" },
|
|
85
|
-
body: JSON.stringify({ name: PACKAGE_NAME, ...body }),
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
it("starts from the fixture's version-scoped schedule", async () => {
|
|
90
|
-
const pkg = await getPackage();
|
|
91
|
-
expect(pkg.scope).toBe("version");
|
|
92
|
-
expect(scheduleOf(pkg)).toBe("0 6 * * *");
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("edits and persists a new schedule", async () => {
|
|
96
|
-
const res = await patch({
|
|
97
|
-
materialization: { schedule: "*/30 * * * *" },
|
|
98
|
-
});
|
|
99
|
-
expect(res.ok).toBe(true);
|
|
100
|
-
expect(scheduleOf(await getPackage())).toBe("*/30 * * * *");
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("rejects an invalid cron (400) and leaves the schedule unchanged", async () => {
|
|
104
|
-
const res = await patch({ materialization: { schedule: "not a cron" } });
|
|
105
|
-
expect(res.status).toBe(400);
|
|
106
|
-
expect(scheduleOf(await getPackage())).toBe("*/30 * * * *");
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("rejects a schedule without version scope (400)", async () => {
|
|
110
|
-
const res = await patch({
|
|
111
|
-
scope: "package",
|
|
112
|
-
materialization: { schedule: "0 6 * * *" },
|
|
113
|
-
});
|
|
114
|
-
expect(res.status).toBe(400);
|
|
115
|
-
// Prior good state preserved (scope + schedule both unchanged).
|
|
116
|
-
const pkg = await getPackage();
|
|
117
|
-
expect(pkg.scope).toBe("version");
|
|
118
|
-
expect(scheduleOf(pkg)).toBe("*/30 * * * *");
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("clears the schedule with schedule: null", async () => {
|
|
122
|
-
const res = await patch({ materialization: { schedule: null } });
|
|
123
|
-
expect(res.ok).toBe(true);
|
|
124
|
-
expect(scheduleOf(await getPackage())).toBeNull();
|
|
125
|
-
});
|
|
126
|
-
});
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/// <reference types="bun-types" />
|
|
2
|
-
|
|
3
|
-
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
|
4
|
-
import fsp from "fs/promises";
|
|
5
|
-
import os from "os";
|
|
6
|
-
import path from "path";
|
|
7
|
-
import { fileURLToPath } from "url";
|
|
8
|
-
import { MaterializationScheduler } from "../../../src/service/materialization_scheduler";
|
|
9
|
-
import { MaterializationService } from "../../../src/service/materialization_service";
|
|
10
|
-
import { environmentStore } from "../../../src/server";
|
|
11
|
-
import { RestE2EEnv, startRestE2E } from "../../harness/rest_e2e";
|
|
12
|
-
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = path.dirname(__filename);
|
|
15
|
-
|
|
16
|
-
const PROJECT_NAME = "sched-project";
|
|
17
|
-
const PACKAGE_NAME = "persist-schedule-test";
|
|
18
|
-
const API = `/api/v0/environments/${PROJECT_NAME}/packages/${PACKAGE_NAME}`;
|
|
19
|
-
|
|
20
|
-
const TERMINAL = ["MANIFEST_FILE_READY", "FAILED", "CANCELLED"];
|
|
21
|
-
|
|
22
|
-
// Deterministic clock for the scheduler: we call tick(nowMs) explicitly, so the
|
|
23
|
-
// fixture's daily cron ("0 6 * * *") fires without waiting on a real minute.
|
|
24
|
-
const T0 = Date.parse("2026-01-01T00:00:00Z");
|
|
25
|
-
const THREE_DAYS_MS = 3 * 24 * 60 * 60 * 1000;
|
|
26
|
-
|
|
27
|
-
describe("MaterializationScheduler (E2E, real build)", () => {
|
|
28
|
-
let env: (RestE2EEnv & { stop(): Promise<void> }) | null = null;
|
|
29
|
-
let baseUrl: string;
|
|
30
|
-
let scheduler: MaterializationScheduler;
|
|
31
|
-
|
|
32
|
-
beforeAll(async () => {
|
|
33
|
-
env = await startRestE2E();
|
|
34
|
-
baseUrl = env.baseUrl;
|
|
35
|
-
|
|
36
|
-
const fixtureDir = path.resolve(
|
|
37
|
-
__dirname,
|
|
38
|
-
"../../fixtures/persist-schedule-test",
|
|
39
|
-
);
|
|
40
|
-
const createRes = await fetch(`${baseUrl}/api/v0/environments`, {
|
|
41
|
-
method: "POST",
|
|
42
|
-
headers: { "Content-Type": "application/json" },
|
|
43
|
-
body: JSON.stringify({
|
|
44
|
-
name: PROJECT_NAME,
|
|
45
|
-
packages: [{ name: PACKAGE_NAME, location: fixtureDir }],
|
|
46
|
-
connections: [],
|
|
47
|
-
}),
|
|
48
|
-
});
|
|
49
|
-
if (!createRes.ok) {
|
|
50
|
-
throw new Error(
|
|
51
|
-
`Failed to create test project (${createRes.status}): ${await createRes.text()}`,
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Wait for the package to load (so it's in the environmentStore the
|
|
56
|
-
// scheduler sweeps).
|
|
57
|
-
const deadline = Date.now() + 30_000;
|
|
58
|
-
while (Date.now() < deadline) {
|
|
59
|
-
const res = await fetch(`${baseUrl}${API}`);
|
|
60
|
-
if (res.ok) break;
|
|
61
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Drive the real scheduler against the server's real environmentStore and
|
|
65
|
-
// a real MaterializationService (shares the same repository). We call
|
|
66
|
-
// tick() by hand with a controlled clock rather than relying on the timer.
|
|
67
|
-
scheduler = new MaterializationScheduler(
|
|
68
|
-
environmentStore,
|
|
69
|
-
new MaterializationService(environmentStore),
|
|
70
|
-
{ tickIntervalMs: 60_000, maxFiresPerTick: 10 },
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
afterAll(async () => {
|
|
75
|
-
if (baseUrl) {
|
|
76
|
-
try {
|
|
77
|
-
await fetch(`${baseUrl}/api/v0/environments/${PROJECT_NAME}`, {
|
|
78
|
-
method: "DELETE",
|
|
79
|
-
});
|
|
80
|
-
} catch {
|
|
81
|
-
// best-effort
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
await env?.stop();
|
|
85
|
-
env = null;
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
async function listMaterializations(): Promise<
|
|
89
|
-
Array<Record<string, unknown>>
|
|
90
|
-
> {
|
|
91
|
-
const res = await fetch(`${baseUrl}${API}/materializations`);
|
|
92
|
-
expect(res.status).toBe(200);
|
|
93
|
-
return (await res.json()) as Array<Record<string, unknown>>;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function triggerOf(m: Record<string, unknown>): string | undefined {
|
|
97
|
-
return (m.metadata as { trigger?: string } | null)?.trigger;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async function pollTerminal(id: string): Promise<Record<string, unknown>> {
|
|
101
|
-
const deadline = Date.now() + 60_000;
|
|
102
|
-
while (Date.now() < deadline) {
|
|
103
|
-
const res = await fetch(`${baseUrl}${API}/materializations/${id}`);
|
|
104
|
-
expect(res.status).toBe(200);
|
|
105
|
-
const m = (await res.json()) as Record<string, unknown>;
|
|
106
|
-
if (TERMINAL.includes(m.status as string)) return m;
|
|
107
|
-
await new Promise((r) => setTimeout(r, 250));
|
|
108
|
-
}
|
|
109
|
-
throw new Error(`materialization ${id} did not reach a terminal state`);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
it(
|
|
113
|
-
"fires a due scheduled package as a SCHEDULER build that completes",
|
|
114
|
-
async () => {
|
|
115
|
-
// Arm on the first tick (nextFire is strictly future, so no fire yet).
|
|
116
|
-
await scheduler.tick(T0);
|
|
117
|
-
expect(
|
|
118
|
-
(await listMaterializations()).filter(
|
|
119
|
-
(m) => triggerOf(m) === "SCHEDULER",
|
|
120
|
-
).length,
|
|
121
|
-
).toBe(0);
|
|
122
|
-
|
|
123
|
-
// Advance well past the daily cron: the sweep fires one SCHEDULER run.
|
|
124
|
-
await scheduler.tick(T0 + THREE_DAYS_MS);
|
|
125
|
-
|
|
126
|
-
const scheduled = (await listMaterializations()).filter(
|
|
127
|
-
(m) => triggerOf(m) === "SCHEDULER",
|
|
128
|
-
);
|
|
129
|
-
expect(scheduled.length).toBe(1);
|
|
130
|
-
|
|
131
|
-
// The real build settles at MANIFEST_FILE_READY, producing the table.
|
|
132
|
-
const built = await pollTerminal(scheduled[0].id as string);
|
|
133
|
-
expect(built.status).toBe("MANIFEST_FILE_READY");
|
|
134
|
-
const entries = (
|
|
135
|
-
built.manifest as { entries: Record<string, unknown> }
|
|
136
|
-
).entries;
|
|
137
|
-
expect(Object.keys(entries).length).toBe(1);
|
|
138
|
-
// The SCHEDULER trigger must survive the build commit (not just exist
|
|
139
|
-
// while the run is in-flight) — otherwise a completed scheduled run is
|
|
140
|
-
// indistinguishable from a manual one.
|
|
141
|
-
expect(triggerOf(built)).toBe("SCHEDULER");
|
|
142
|
-
|
|
143
|
-
// Cleanup: drop the table + record so the negative case starts clean.
|
|
144
|
-
const del = await fetch(
|
|
145
|
-
`${baseUrl}${API}/materializations/${scheduled[0].id}?dropTables=true`,
|
|
146
|
-
{ method: "DELETE" },
|
|
147
|
-
);
|
|
148
|
-
expect(del.status).toBe(204);
|
|
149
|
-
},
|
|
150
|
-
{ timeout: 120_000 },
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
it(
|
|
154
|
-
"never fires a control-plane-driven package (manifestLocation set)",
|
|
155
|
-
async () => {
|
|
156
|
-
// Make the package orchestrated: point it at a (bind-clean) manifest.
|
|
157
|
-
const manifestFile = path.join(
|
|
158
|
-
os.tmpdir(),
|
|
159
|
-
`sched-empty-manifest-${T0}.json`,
|
|
160
|
-
);
|
|
161
|
-
await fsp.writeFile(
|
|
162
|
-
manifestFile,
|
|
163
|
-
JSON.stringify({
|
|
164
|
-
builtAt: new Date(T0).toISOString(),
|
|
165
|
-
strict: false,
|
|
166
|
-
entries: {},
|
|
167
|
-
}),
|
|
168
|
-
"utf8",
|
|
169
|
-
);
|
|
170
|
-
const patch = await fetch(`${baseUrl}${API}`, {
|
|
171
|
-
method: "PATCH",
|
|
172
|
-
headers: { "Content-Type": "application/json" },
|
|
173
|
-
body: JSON.stringify({
|
|
174
|
-
name: PACKAGE_NAME,
|
|
175
|
-
manifestLocation: manifestFile,
|
|
176
|
-
}),
|
|
177
|
-
});
|
|
178
|
-
expect(patch.ok).toBe(true);
|
|
179
|
-
|
|
180
|
-
const before = (await listMaterializations()).length;
|
|
181
|
-
|
|
182
|
-
// Even armed + well past the cron, an orchestrated package is skipped
|
|
183
|
-
// (Guard 2) — the control plane owns its refresh.
|
|
184
|
-
await scheduler.tick(T0);
|
|
185
|
-
await scheduler.tick(T0 + THREE_DAYS_MS);
|
|
186
|
-
|
|
187
|
-
expect((await listMaterializations()).length).toBe(before);
|
|
188
|
-
|
|
189
|
-
await fetch(`${baseUrl}${API}`, {
|
|
190
|
-
method: "PATCH",
|
|
191
|
-
headers: { "Content-Type": "application/json" },
|
|
192
|
-
body: JSON.stringify({
|
|
193
|
-
name: PACKAGE_NAME,
|
|
194
|
-
manifestLocation: null,
|
|
195
|
-
}),
|
|
196
|
-
});
|
|
197
|
-
},
|
|
198
|
-
{ timeout: 60_000 },
|
|
199
|
-
);
|
|
200
|
-
});
|