@fabricorg/databricks-testkit 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +375 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -5
- package/dist/index.d.ts +57 -5
- package/dist/index.js +364 -20
- package/dist/index.js.map +1 -1
- package/package.json +12 -14
- package/LICENSE +0 -21
package/dist/index.d.cts
CHANGED
|
@@ -170,7 +170,7 @@ declare function compareToGolden(goldenPath: string, actual: readonly Record<str
|
|
|
170
170
|
* vitest without vi.fn so it is equally usable from cucumber-js worlds.
|
|
171
171
|
*/
|
|
172
172
|
interface RecordedCall {
|
|
173
|
-
method: 'GET' | 'POST';
|
|
173
|
+
method: 'GET' | 'POST' | 'DELETE';
|
|
174
174
|
path: string;
|
|
175
175
|
body?: unknown;
|
|
176
176
|
query?: Record<string, string | number | boolean | undefined>;
|
|
@@ -178,12 +178,13 @@ interface RecordedCall {
|
|
|
178
178
|
interface MockDatabricksClient {
|
|
179
179
|
get<T>(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
|
|
180
180
|
post<T>(path: string, body?: unknown, query?: Record<string, string | number | boolean | undefined>, options?: unknown): Promise<T>;
|
|
181
|
+
request<T>(method: 'GET' | 'POST' | 'DELETE', path: string, body?: unknown, query?: Record<string, string | number | boolean | undefined>, options?: unknown): Promise<T>;
|
|
181
182
|
/** Queue the next response for calls whose path starts with `pathPrefix`. FIFO per prefix. */
|
|
182
|
-
queue(method: 'GET' | 'POST', pathPrefix: string, response: unknown): MockDatabricksClient;
|
|
183
|
+
queue(method: 'GET' | 'POST' | 'DELETE', pathPrefix: string, response: unknown): MockDatabricksClient;
|
|
183
184
|
/** Queue an error for the next matching call. */
|
|
184
|
-
queueError(method: 'GET' | 'POST', pathPrefix: string, error: Error): MockDatabricksClient;
|
|
185
|
+
queueError(method: 'GET' | 'POST' | 'DELETE', pathPrefix: string, error: Error): MockDatabricksClient;
|
|
185
186
|
readonly calls: readonly RecordedCall[];
|
|
186
|
-
callsTo(method: 'GET' | 'POST', pathPrefix: string): RecordedCall[];
|
|
187
|
+
callsTo(method: 'GET' | 'POST' | 'DELETE', pathPrefix: string): RecordedCall[];
|
|
187
188
|
}
|
|
188
189
|
declare function createMockDatabricksClient(): MockDatabricksClient;
|
|
189
190
|
|
|
@@ -239,6 +240,8 @@ declare function runLiveSuite(spec: LiveSuiteSpec, runtime?: LiveSuiteRuntime):
|
|
|
239
240
|
declare function renderJUnit(evidence: LiveEvidence): string;
|
|
240
241
|
|
|
241
242
|
declare function sqlRoundTripCheck(): LiveCheck;
|
|
243
|
+
/** Fail closed if governance checks accidentally run with bootstrap or administrator credentials. */
|
|
244
|
+
declare function restrictedPrincipalCheck(): LiveCheck;
|
|
242
245
|
interface DeltaColumnContract {
|
|
243
246
|
name: string;
|
|
244
247
|
type?: string;
|
|
@@ -264,6 +267,37 @@ declare function lakebaseCredentialCheck(options?: LakebaseCheckOptions): LiveCh
|
|
|
264
267
|
/** Exercise the production provider before and after an explicit credential/pool refresh. */
|
|
265
268
|
declare function lakebaseRoundTripCheck(options?: LakebaseCheckOptions): LiveCheck;
|
|
266
269
|
|
|
270
|
+
interface LakebaseControlClient {
|
|
271
|
+
get<T>(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
|
|
272
|
+
post<T>(path: string, body?: unknown, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
|
|
273
|
+
request<T>(method: 'DELETE', path: string, body?: unknown, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
|
|
274
|
+
}
|
|
275
|
+
interface EphemeralLakebaseBranch {
|
|
276
|
+
project: string;
|
|
277
|
+
branch: string;
|
|
278
|
+
branchId: string;
|
|
279
|
+
endpoint: string;
|
|
280
|
+
host: string;
|
|
281
|
+
databaseResource: string;
|
|
282
|
+
database: string;
|
|
283
|
+
createdAt: string;
|
|
284
|
+
expiresAt: string;
|
|
285
|
+
}
|
|
286
|
+
interface LakebaseBranchOptions {
|
|
287
|
+
env?: Record<string, string | undefined>;
|
|
288
|
+
client?: LakebaseControlClient;
|
|
289
|
+
project?: string;
|
|
290
|
+
branchId: string;
|
|
291
|
+
ttlMs: number;
|
|
292
|
+
pollIntervalMs?: number;
|
|
293
|
+
timeoutMs?: number;
|
|
294
|
+
now?: () => Date;
|
|
295
|
+
}
|
|
296
|
+
declare function resolveLakebaseProject(env: Record<string, string | undefined>, explicit?: string): string;
|
|
297
|
+
declare function toLakebaseBranchId(environmentName: string): string;
|
|
298
|
+
declare function createEphemeralLakebaseBranch(options: LakebaseBranchOptions): Promise<EphemeralLakebaseBranch>;
|
|
299
|
+
declare function deleteEphemeralLakebaseBranch(branch: Pick<EphemeralLakebaseBranch, 'branch'>, options?: Omit<LakebaseBranchOptions, 'branchId' | 'ttlMs' | 'project'>): Promise<void>;
|
|
300
|
+
|
|
267
301
|
/** Assert secret key metadata only; values are never requested. */
|
|
268
302
|
declare function secretScopeCheck(): LiveCheck;
|
|
269
303
|
interface AppHealthCheckOptions {
|
|
@@ -298,4 +332,22 @@ interface PollOptions {
|
|
|
298
332
|
}
|
|
299
333
|
declare function waitForPipelineUpdate(client: WorkspaceClientLike, pipelineId: string, updateId: string, options?: PollOptions): Promise<Record<string, unknown>>;
|
|
300
334
|
|
|
301
|
-
|
|
335
|
+
/** Measure the real Statement Execution path and fail when its p95 exceeds the declared budget. */
|
|
336
|
+
declare function performanceBudgetCheck(): LiveCheck;
|
|
337
|
+
/** Prove a failed statement does not poison the session or the next workload. */
|
|
338
|
+
declare function failureRecoveryCheck(): LiveCheck;
|
|
339
|
+
/** Rotate a disposable secret key and remove it. Values are never returned or written to evidence. */
|
|
340
|
+
interface SecretRotationCheckOptions {
|
|
341
|
+
randomId?: () => string;
|
|
342
|
+
}
|
|
343
|
+
declare function secretRotationCheck(options?: SecretRotationCheckOptions): LiveCheck;
|
|
344
|
+
/** Exercise a complete Delta backup, destructive loss, and restore cycle in the scratch schema. */
|
|
345
|
+
declare function backupRestoreCheck(): LiveCheck;
|
|
346
|
+
/** Prove Delta time-travel rollback returns a corrupted scratch table to its known-good version. */
|
|
347
|
+
declare function rollbackCheck(): LiveCheck;
|
|
348
|
+
/** Certify that the configured SQL warehouse is serverless and usable by the test identity. */
|
|
349
|
+
declare function serverlessComputeCheck(): LiveCheck;
|
|
350
|
+
/** Launch a disposable classic job cluster and require the shared notebook probe to succeed. */
|
|
351
|
+
declare function classicComputeCheck(): LiveCheck;
|
|
352
|
+
|
|
353
|
+
export { type AppHealthCheckOptions, type AutoLoaderCheckOptions, type CreateContextOptions, type DefinedLiveSuite, type DeltaColumnContract, type DuckDbConnectionLike, type DuckDbContextOptions, type EphemeralLakebaseBranch, type ExecutionContext, type LakebaseBranchOptions, type LakebaseCheckOptions, type LakebaseControlClient, type LiveCheck, type LiveCheckContext, type LiveCheckResult, type LiveCheckStatus, type LiveEvidence, type LiveSuiteRuntime, type LiveSuiteSpec, type MockDatabricksClient, type PollOptions, type RecordedCall, type SecretRotationCheckOptions, TableFixture, type TestProfile, type VolumeIOCheckOptions, type WorkspaceClientLike, type WorkspaceContextOptions, appHealthCheck, assertDeltaContract, autoLoaderIngestionCheck, backupRestoreCheck, classicComputeCheck, compareToGolden, createClientFromEnv, createDuckDbContext, createEphemeralLakebaseBranch, createExecutionContext, createLakebaseTestProvider, createMockDatabricksClient, createWorkspaceContext, customLiveCheck, dbtBuildCheck, defineLiveSuite, deleteEphemeralLakebaseBranch, deleteVolumeFile, deltaContractCheck, ensureVolumeDirectory, expectQueryToMatchGolden, expectTable, failureRecoveryCheck, jobRunCheck, lakebaseCredentialCheck, lakebaseRoundTripCheck, normalizeRow, normalizeVolumeRoot, notebookSubmitCheck, parseStatementRows, performanceBudgetCheck, pipelineRefreshCheck, renderJUnit, resolveLakebaseProject, resolveProfile, resolveTokenProvider, restrictedPrincipalCheck, rollbackCheck, runLiveSuite, secretRotationCheck, secretScopeCheck, serverlessComputeCheck, sqlRoundTripCheck, toLakebaseBranchId, toNamedParameters, unityCatalogAccessCheck, uploadVolumeFile, volumeIOCheck, waitForPipelineUpdate };
|
package/dist/index.d.ts
CHANGED
|
@@ -170,7 +170,7 @@ declare function compareToGolden(goldenPath: string, actual: readonly Record<str
|
|
|
170
170
|
* vitest without vi.fn so it is equally usable from cucumber-js worlds.
|
|
171
171
|
*/
|
|
172
172
|
interface RecordedCall {
|
|
173
|
-
method: 'GET' | 'POST';
|
|
173
|
+
method: 'GET' | 'POST' | 'DELETE';
|
|
174
174
|
path: string;
|
|
175
175
|
body?: unknown;
|
|
176
176
|
query?: Record<string, string | number | boolean | undefined>;
|
|
@@ -178,12 +178,13 @@ interface RecordedCall {
|
|
|
178
178
|
interface MockDatabricksClient {
|
|
179
179
|
get<T>(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
|
|
180
180
|
post<T>(path: string, body?: unknown, query?: Record<string, string | number | boolean | undefined>, options?: unknown): Promise<T>;
|
|
181
|
+
request<T>(method: 'GET' | 'POST' | 'DELETE', path: string, body?: unknown, query?: Record<string, string | number | boolean | undefined>, options?: unknown): Promise<T>;
|
|
181
182
|
/** Queue the next response for calls whose path starts with `pathPrefix`. FIFO per prefix. */
|
|
182
|
-
queue(method: 'GET' | 'POST', pathPrefix: string, response: unknown): MockDatabricksClient;
|
|
183
|
+
queue(method: 'GET' | 'POST' | 'DELETE', pathPrefix: string, response: unknown): MockDatabricksClient;
|
|
183
184
|
/** Queue an error for the next matching call. */
|
|
184
|
-
queueError(method: 'GET' | 'POST', pathPrefix: string, error: Error): MockDatabricksClient;
|
|
185
|
+
queueError(method: 'GET' | 'POST' | 'DELETE', pathPrefix: string, error: Error): MockDatabricksClient;
|
|
185
186
|
readonly calls: readonly RecordedCall[];
|
|
186
|
-
callsTo(method: 'GET' | 'POST', pathPrefix: string): RecordedCall[];
|
|
187
|
+
callsTo(method: 'GET' | 'POST' | 'DELETE', pathPrefix: string): RecordedCall[];
|
|
187
188
|
}
|
|
188
189
|
declare function createMockDatabricksClient(): MockDatabricksClient;
|
|
189
190
|
|
|
@@ -239,6 +240,8 @@ declare function runLiveSuite(spec: LiveSuiteSpec, runtime?: LiveSuiteRuntime):
|
|
|
239
240
|
declare function renderJUnit(evidence: LiveEvidence): string;
|
|
240
241
|
|
|
241
242
|
declare function sqlRoundTripCheck(): LiveCheck;
|
|
243
|
+
/** Fail closed if governance checks accidentally run with bootstrap or administrator credentials. */
|
|
244
|
+
declare function restrictedPrincipalCheck(): LiveCheck;
|
|
242
245
|
interface DeltaColumnContract {
|
|
243
246
|
name: string;
|
|
244
247
|
type?: string;
|
|
@@ -264,6 +267,37 @@ declare function lakebaseCredentialCheck(options?: LakebaseCheckOptions): LiveCh
|
|
|
264
267
|
/** Exercise the production provider before and after an explicit credential/pool refresh. */
|
|
265
268
|
declare function lakebaseRoundTripCheck(options?: LakebaseCheckOptions): LiveCheck;
|
|
266
269
|
|
|
270
|
+
interface LakebaseControlClient {
|
|
271
|
+
get<T>(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
|
|
272
|
+
post<T>(path: string, body?: unknown, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
|
|
273
|
+
request<T>(method: 'DELETE', path: string, body?: unknown, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
|
|
274
|
+
}
|
|
275
|
+
interface EphemeralLakebaseBranch {
|
|
276
|
+
project: string;
|
|
277
|
+
branch: string;
|
|
278
|
+
branchId: string;
|
|
279
|
+
endpoint: string;
|
|
280
|
+
host: string;
|
|
281
|
+
databaseResource: string;
|
|
282
|
+
database: string;
|
|
283
|
+
createdAt: string;
|
|
284
|
+
expiresAt: string;
|
|
285
|
+
}
|
|
286
|
+
interface LakebaseBranchOptions {
|
|
287
|
+
env?: Record<string, string | undefined>;
|
|
288
|
+
client?: LakebaseControlClient;
|
|
289
|
+
project?: string;
|
|
290
|
+
branchId: string;
|
|
291
|
+
ttlMs: number;
|
|
292
|
+
pollIntervalMs?: number;
|
|
293
|
+
timeoutMs?: number;
|
|
294
|
+
now?: () => Date;
|
|
295
|
+
}
|
|
296
|
+
declare function resolveLakebaseProject(env: Record<string, string | undefined>, explicit?: string): string;
|
|
297
|
+
declare function toLakebaseBranchId(environmentName: string): string;
|
|
298
|
+
declare function createEphemeralLakebaseBranch(options: LakebaseBranchOptions): Promise<EphemeralLakebaseBranch>;
|
|
299
|
+
declare function deleteEphemeralLakebaseBranch(branch: Pick<EphemeralLakebaseBranch, 'branch'>, options?: Omit<LakebaseBranchOptions, 'branchId' | 'ttlMs' | 'project'>): Promise<void>;
|
|
300
|
+
|
|
267
301
|
/** Assert secret key metadata only; values are never requested. */
|
|
268
302
|
declare function secretScopeCheck(): LiveCheck;
|
|
269
303
|
interface AppHealthCheckOptions {
|
|
@@ -298,4 +332,22 @@ interface PollOptions {
|
|
|
298
332
|
}
|
|
299
333
|
declare function waitForPipelineUpdate(client: WorkspaceClientLike, pipelineId: string, updateId: string, options?: PollOptions): Promise<Record<string, unknown>>;
|
|
300
334
|
|
|
301
|
-
|
|
335
|
+
/** Measure the real Statement Execution path and fail when its p95 exceeds the declared budget. */
|
|
336
|
+
declare function performanceBudgetCheck(): LiveCheck;
|
|
337
|
+
/** Prove a failed statement does not poison the session or the next workload. */
|
|
338
|
+
declare function failureRecoveryCheck(): LiveCheck;
|
|
339
|
+
/** Rotate a disposable secret key and remove it. Values are never returned or written to evidence. */
|
|
340
|
+
interface SecretRotationCheckOptions {
|
|
341
|
+
randomId?: () => string;
|
|
342
|
+
}
|
|
343
|
+
declare function secretRotationCheck(options?: SecretRotationCheckOptions): LiveCheck;
|
|
344
|
+
/** Exercise a complete Delta backup, destructive loss, and restore cycle in the scratch schema. */
|
|
345
|
+
declare function backupRestoreCheck(): LiveCheck;
|
|
346
|
+
/** Prove Delta time-travel rollback returns a corrupted scratch table to its known-good version. */
|
|
347
|
+
declare function rollbackCheck(): LiveCheck;
|
|
348
|
+
/** Certify that the configured SQL warehouse is serverless and usable by the test identity. */
|
|
349
|
+
declare function serverlessComputeCheck(): LiveCheck;
|
|
350
|
+
/** Launch a disposable classic job cluster and require the shared notebook probe to succeed. */
|
|
351
|
+
declare function classicComputeCheck(): LiveCheck;
|
|
352
|
+
|
|
353
|
+
export { type AppHealthCheckOptions, type AutoLoaderCheckOptions, type CreateContextOptions, type DefinedLiveSuite, type DeltaColumnContract, type DuckDbConnectionLike, type DuckDbContextOptions, type EphemeralLakebaseBranch, type ExecutionContext, type LakebaseBranchOptions, type LakebaseCheckOptions, type LakebaseControlClient, type LiveCheck, type LiveCheckContext, type LiveCheckResult, type LiveCheckStatus, type LiveEvidence, type LiveSuiteRuntime, type LiveSuiteSpec, type MockDatabricksClient, type PollOptions, type RecordedCall, type SecretRotationCheckOptions, TableFixture, type TestProfile, type VolumeIOCheckOptions, type WorkspaceClientLike, type WorkspaceContextOptions, appHealthCheck, assertDeltaContract, autoLoaderIngestionCheck, backupRestoreCheck, classicComputeCheck, compareToGolden, createClientFromEnv, createDuckDbContext, createEphemeralLakebaseBranch, createExecutionContext, createLakebaseTestProvider, createMockDatabricksClient, createWorkspaceContext, customLiveCheck, dbtBuildCheck, defineLiveSuite, deleteEphemeralLakebaseBranch, deleteVolumeFile, deltaContractCheck, ensureVolumeDirectory, expectQueryToMatchGolden, expectTable, failureRecoveryCheck, jobRunCheck, lakebaseCredentialCheck, lakebaseRoundTripCheck, normalizeRow, normalizeVolumeRoot, notebookSubmitCheck, parseStatementRows, performanceBudgetCheck, pipelineRefreshCheck, renderJUnit, resolveLakebaseProject, resolveProfile, resolveTokenProvider, restrictedPrincipalCheck, rollbackCheck, runLiveSuite, secretRotationCheck, secretScopeCheck, serverlessComputeCheck, sqlRoundTripCheck, toLakebaseBranchId, toNamedParameters, unityCatalogAccessCheck, uploadVolumeFile, volumeIOCheck, waitForPipelineUpdate };
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { dirname, resolve, basename } from 'path';
|
|
|
9
9
|
import { waitForDatabricksRun } from '@fabric-harness/databricks';
|
|
10
10
|
export { waitForDatabricksRun, waitForDatabricksStatement } from '@fabric-harness/databricks';
|
|
11
11
|
import { LakebaseDatabaseProvider, exchangeLakebaseCredential } from '@fabricorg/experiments-db-pool';
|
|
12
|
-
import { randomUUID } from 'crypto';
|
|
12
|
+
import { randomUUID, randomBytes } from 'crypto';
|
|
13
13
|
|
|
14
14
|
// src/context.ts
|
|
15
15
|
function resolveProfile(env = process.env) {
|
|
@@ -91,7 +91,7 @@ function looselyEqual(a, b) {
|
|
|
91
91
|
|
|
92
92
|
// src/mock-client.ts
|
|
93
93
|
function createMockDatabricksClient() {
|
|
94
|
-
const queues = { GET: [], POST: [] };
|
|
94
|
+
const queues = { GET: [], POST: [], DELETE: [] };
|
|
95
95
|
const calls = [];
|
|
96
96
|
function take(method, path) {
|
|
97
97
|
const index = queues[method].findIndex((q) => path.startsWith(q.pathPrefix));
|
|
@@ -115,6 +115,12 @@ function createMockDatabricksClient() {
|
|
|
115
115
|
if (queued.error) throw queued.error;
|
|
116
116
|
return queued.response;
|
|
117
117
|
},
|
|
118
|
+
async request(method, path, body, query) {
|
|
119
|
+
calls.push({ method, path, body, query });
|
|
120
|
+
const queued = take(method, path);
|
|
121
|
+
if (queued.error) throw queued.error;
|
|
122
|
+
return queued.response;
|
|
123
|
+
},
|
|
118
124
|
queue(method, pathPrefix, response) {
|
|
119
125
|
queues[method].push({ pathPrefix, response });
|
|
120
126
|
return client;
|
|
@@ -280,6 +286,26 @@ function sqlRoundTripCheck() {
|
|
|
280
286
|
}
|
|
281
287
|
};
|
|
282
288
|
}
|
|
289
|
+
function restrictedPrincipalCheck() {
|
|
290
|
+
return {
|
|
291
|
+
id: "restricted-principal",
|
|
292
|
+
description: "Live checks use the designated least-privilege service principal",
|
|
293
|
+
configured: (env) => Boolean(env.DBX_TEST_RESTRICTED_PRINCIPAL_ID),
|
|
294
|
+
async run({ env }) {
|
|
295
|
+
const expected = env.DBX_TEST_RESTRICTED_PRINCIPAL_ID?.toLowerCase();
|
|
296
|
+
const actual = env.DATABRICKS_CLIENT_ID?.toLowerCase();
|
|
297
|
+
if (!expected || !actual) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
"Restricted-principal check requires DBX_TEST_RESTRICTED_PRINCIPAL_ID and DATABRICKS_CLIENT_ID"
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
if (actual !== expected) {
|
|
303
|
+
throw new Error(`Live suite is using principal '${actual}', expected '${expected}'`);
|
|
304
|
+
}
|
|
305
|
+
return { principalId: actual };
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
283
309
|
function assertDeltaContract(table, rows, columns) {
|
|
284
310
|
for (const expected of columns) {
|
|
285
311
|
const actual = rows.find((row) => String(row.col_name ?? row.column_name) === expected.name);
|
|
@@ -371,17 +397,21 @@ function notebookSubmitCheck() {
|
|
|
371
397
|
return {
|
|
372
398
|
id: "notebook",
|
|
373
399
|
description: "Databricks notebook submit run reaches SUCCESS",
|
|
374
|
-
configured: (env) => Boolean(
|
|
400
|
+
configured: (env) => Boolean(
|
|
401
|
+
env.DBX_TEST_NOTEBOOK_PATH && (env.DBX_TEST_CLUSTER_ID || env.DBX_TEST_NOTEBOOK_SERVERLESS === "1")
|
|
402
|
+
),
|
|
375
403
|
async run({ env, client }) {
|
|
376
|
-
if (!env.DBX_TEST_NOTEBOOK_PATH || !env.DBX_TEST_CLUSTER_ID) {
|
|
377
|
-
throw new Error(
|
|
404
|
+
if (!env.DBX_TEST_NOTEBOOK_PATH || !env.DBX_TEST_CLUSTER_ID && env.DBX_TEST_NOTEBOOK_SERVERLESS !== "1") {
|
|
405
|
+
throw new Error(
|
|
406
|
+
"Notebook submit requires DBX_TEST_NOTEBOOK_PATH and either DBX_TEST_CLUSTER_ID or DBX_TEST_NOTEBOOK_SERVERLESS=1"
|
|
407
|
+
);
|
|
378
408
|
}
|
|
379
409
|
const submitted = await client.post("/api/2.1/jobs/runs/submit", {
|
|
380
410
|
run_name: `fabric-experiments-notebook-${Date.now()}`,
|
|
381
411
|
tasks: [
|
|
382
412
|
{
|
|
383
413
|
task_key: "notebook",
|
|
384
|
-
existing_cluster_id: env.DBX_TEST_CLUSTER_ID,
|
|
414
|
+
...env.DBX_TEST_CLUSTER_ID ? { existing_cluster_id: env.DBX_TEST_CLUSTER_ID } : {},
|
|
385
415
|
notebook_task: { notebook_path: env.DBX_TEST_NOTEBOOK_PATH }
|
|
386
416
|
}
|
|
387
417
|
]
|
|
@@ -401,29 +431,59 @@ function dbtBuildCheck() {
|
|
|
401
431
|
return {
|
|
402
432
|
id: "dbt",
|
|
403
433
|
description: "Databricks dbt task reaches SUCCESS",
|
|
404
|
-
configured: (env) => Boolean(
|
|
434
|
+
configured: (env) => Boolean(
|
|
435
|
+
(env.DBX_TEST_DBT_GIT_URL || env.DBX_TEST_DBT_PROJECT_DIR) && env.DATABRICKS_WAREHOUSE_ID
|
|
436
|
+
),
|
|
405
437
|
async run({ env, client }) {
|
|
406
|
-
if (!env.DBX_TEST_DBT_GIT_URL || !env.DATABRICKS_WAREHOUSE_ID) {
|
|
407
|
-
throw new Error(
|
|
438
|
+
if (!env.DBX_TEST_DBT_GIT_URL && !env.DBX_TEST_DBT_PROJECT_DIR || !env.DATABRICKS_WAREHOUSE_ID) {
|
|
439
|
+
throw new Error(
|
|
440
|
+
"dbt build requires DBX_TEST_DBT_GIT_URL or DBX_TEST_DBT_PROJECT_DIR, plus DATABRICKS_WAREHOUSE_ID"
|
|
441
|
+
);
|
|
408
442
|
}
|
|
409
|
-
const
|
|
443
|
+
const sourceVars = JSON.stringify({
|
|
444
|
+
source_catalog: env.DBX_TEST_DBT_SOURCE_CATALOG ?? env.DBX_TEST_CATALOG,
|
|
445
|
+
source_schema: env.DBX_TEST_DBT_SOURCE_SCHEMA
|
|
446
|
+
});
|
|
447
|
+
const serverless = env.DBX_TEST_DBT_SERVERLESS === "1";
|
|
448
|
+
const request = {
|
|
410
449
|
run_name: `fabric-experiments-dbt-${Date.now()}`,
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
450
|
+
...env.DBX_TEST_DBT_GIT_URL ? {
|
|
451
|
+
git_source: {
|
|
452
|
+
git_url: env.DBX_TEST_DBT_GIT_URL,
|
|
453
|
+
git_provider: env.DBX_TEST_DBT_GIT_PROVIDER ?? "gitHub",
|
|
454
|
+
git_branch: env.DBX_TEST_DBT_GIT_BRANCH ?? "main"
|
|
455
|
+
}
|
|
456
|
+
} : {},
|
|
416
457
|
tasks: [
|
|
417
458
|
{
|
|
418
459
|
task_key: "dbt_build",
|
|
460
|
+
...serverless ? { environment_key: "dbt" } : {},
|
|
419
461
|
dbt_task: {
|
|
420
|
-
commands: ["dbt deps",
|
|
462
|
+
commands: ["dbt deps", `dbt build --vars '${sourceVars}'`],
|
|
421
463
|
warehouse_id: env.DATABRICKS_WAREHOUSE_ID,
|
|
464
|
+
...env.DBX_TEST_CATALOG ? { catalog: env.DBX_TEST_CATALOG } : {},
|
|
465
|
+
...env.DBX_TEST_SCHEMA ? { schema: env.DBX_TEST_SCHEMA } : {},
|
|
466
|
+
...env.DBX_TEST_DBT_GIT_URL ? {} : { source: "WORKSPACE" },
|
|
422
467
|
...env.DBX_TEST_DBT_PROJECT_DIR ? { project_directory: env.DBX_TEST_DBT_PROJECT_DIR } : {}
|
|
423
468
|
}
|
|
424
469
|
}
|
|
425
|
-
]
|
|
426
|
-
|
|
470
|
+
],
|
|
471
|
+
...serverless ? {
|
|
472
|
+
environments: [
|
|
473
|
+
{
|
|
474
|
+
environment_key: "dbt",
|
|
475
|
+
spec: {
|
|
476
|
+
environment_version: "2",
|
|
477
|
+
dependencies: ["dbt-databricks>=1.8,<2"]
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
]
|
|
481
|
+
} : {}
|
|
482
|
+
};
|
|
483
|
+
const submitted = await client.post(
|
|
484
|
+
"/api/2.1/jobs/runs/submit",
|
|
485
|
+
request
|
|
486
|
+
);
|
|
427
487
|
if (!submitted.run_id) throw new Error("dbt submit returned no run_id");
|
|
428
488
|
const run = await waitForDatabricksRun(client, submitted.run_id, {
|
|
429
489
|
timeoutMs: Number(env.DBX_TEST_TIMEOUT_MS ?? 9e5),
|
|
@@ -431,7 +491,10 @@ function dbtBuildCheck() {
|
|
|
431
491
|
});
|
|
432
492
|
const result = run.state?.result_state;
|
|
433
493
|
if (result !== "SUCCESS") throw new Error(`dbt run ended in ${result ?? "UNKNOWN"}`);
|
|
434
|
-
return {
|
|
494
|
+
return {
|
|
495
|
+
runId: submitted.run_id,
|
|
496
|
+
source: env.DBX_TEST_DBT_GIT_URL ?? env.DBX_TEST_DBT_PROJECT_DIR
|
|
497
|
+
};
|
|
435
498
|
}
|
|
436
499
|
};
|
|
437
500
|
}
|
|
@@ -507,6 +570,98 @@ function lakebaseRoundTripCheck(options = {}) {
|
|
|
507
570
|
};
|
|
508
571
|
}
|
|
509
572
|
|
|
573
|
+
// src/lakebase-branch.ts
|
|
574
|
+
var DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
575
|
+
var DEFAULT_TIMEOUT_MS = 5 * 6e4;
|
|
576
|
+
function resolveLakebaseProject(env, explicit) {
|
|
577
|
+
const project = explicit ?? env.DBX_TEST_LAKEBASE_PROJECT;
|
|
578
|
+
if (!project || !/^projects\/[a-z0-9][a-z0-9-]{0,62}$/.test(project)) {
|
|
579
|
+
throw new Error(
|
|
580
|
+
`Lakebase project must look like projects/fabric-experiments; got '${project ?? ""}'`
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
return project;
|
|
584
|
+
}
|
|
585
|
+
function toLakebaseBranchId(environmentName) {
|
|
586
|
+
const normalized = environmentName.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
587
|
+
const prefixed = normalized.startsWith("fx-") ? normalized : `fx-${normalized}`;
|
|
588
|
+
const trimmed = prefixed.slice(0, 63).replace(/-+$/g, "");
|
|
589
|
+
if (!/^fx-[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(trimmed)) {
|
|
590
|
+
throw new Error(`Cannot derive a safe Lakebase branch ID from '${environmentName}'`);
|
|
591
|
+
}
|
|
592
|
+
return trimmed;
|
|
593
|
+
}
|
|
594
|
+
async function createEphemeralLakebaseBranch(options) {
|
|
595
|
+
const env = options.env ?? process.env;
|
|
596
|
+
const client = options.client ?? createClientFromEnv(env);
|
|
597
|
+
const project = resolveLakebaseProject(env, options.project);
|
|
598
|
+
const branchId = toLakebaseBranchId(options.branchId);
|
|
599
|
+
const ttlSeconds = Math.max(60, Math.ceil(options.ttlMs / 1e3));
|
|
600
|
+
const operation = await client.post(
|
|
601
|
+
`/api/2.0/postgres/${project}/branches`,
|
|
602
|
+
{ spec: { ttl: `${ttlSeconds}s` } },
|
|
603
|
+
{ branch_id: branchId, replace_existing: false }
|
|
604
|
+
);
|
|
605
|
+
const branch = await waitForOperation(client, operation, options);
|
|
606
|
+
const branchName = branch?.name ?? `${project}/branches/${branchId}`;
|
|
607
|
+
const [endpointResult, databaseResult] = await Promise.all([
|
|
608
|
+
client.get(`/api/2.0/postgres/${branchName}/endpoints`),
|
|
609
|
+
client.get(`/api/2.0/postgres/${branchName}/databases`)
|
|
610
|
+
]);
|
|
611
|
+
const endpoint = endpointResult.endpoints?.find((item) => item.endpoint_id === "primary") ?? endpointResult.endpoints?.[0];
|
|
612
|
+
const database = databaseResult.databases?.[0];
|
|
613
|
+
const host = endpoint?.status?.hosts?.host;
|
|
614
|
+
const postgresDatabase = database?.status?.postgres_database;
|
|
615
|
+
if (!endpoint?.name || !host || !database?.name || !postgresDatabase) {
|
|
616
|
+
throw new Error(`Lakebase branch '${branchName}' did not expose an endpoint and database`);
|
|
617
|
+
}
|
|
618
|
+
const createdAt = branch?.create_time ?? (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
619
|
+
return {
|
|
620
|
+
project,
|
|
621
|
+
branch: branchName,
|
|
622
|
+
branchId,
|
|
623
|
+
endpoint: endpoint.name,
|
|
624
|
+
host,
|
|
625
|
+
databaseResource: database.name,
|
|
626
|
+
database: postgresDatabase,
|
|
627
|
+
createdAt,
|
|
628
|
+
expiresAt: new Date(Date.parse(createdAt) + ttlSeconds * 1e3).toISOString()
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
async function deleteEphemeralLakebaseBranch(branch, options = {}) {
|
|
632
|
+
if (!/^projects\/[a-z0-9-]+\/branches\/fx-[a-z0-9-]+$/.test(branch.branch)) {
|
|
633
|
+
throw new Error(`Refusing to delete non-ephemeral Lakebase branch '${branch.branch}'`);
|
|
634
|
+
}
|
|
635
|
+
const env = options.env ?? process.env;
|
|
636
|
+
const client = options.client ?? createClientFromEnv(env);
|
|
637
|
+
const operation = await client.request(
|
|
638
|
+
"DELETE",
|
|
639
|
+
`/api/2.0/postgres/${branch.branch}`,
|
|
640
|
+
void 0,
|
|
641
|
+
{ purge: true }
|
|
642
|
+
);
|
|
643
|
+
await waitForOperation(client, operation, options);
|
|
644
|
+
}
|
|
645
|
+
async function waitForOperation(client, initial, options) {
|
|
646
|
+
let operation = initial;
|
|
647
|
+
const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
648
|
+
while (!operation.done) {
|
|
649
|
+
if (!operation.name) throw new Error("Lakebase operation did not return an operation name");
|
|
650
|
+
if (Date.now() >= deadline) throw new Error(`Lakebase operation '${operation.name}' timed out`);
|
|
651
|
+
await delay(options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
|
|
652
|
+
operation = await client.get(`/api/2.0/postgres/${operation.name}`);
|
|
653
|
+
}
|
|
654
|
+
if (operation.error) {
|
|
655
|
+
throw new Error(
|
|
656
|
+
`Lakebase operation failed${operation.error.code ? ` (${operation.error.code})` : ""}: ${operation.error.message ?? "unknown error"}`
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
return operation.response;
|
|
660
|
+
}
|
|
661
|
+
function delay(ms) {
|
|
662
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
663
|
+
}
|
|
664
|
+
|
|
510
665
|
// src/databricks-http.ts
|
|
511
666
|
async function databricksFetch(env, pathOrUrl, init = {}, fetchImpl = fetch) {
|
|
512
667
|
const host = env.DATABRICKS_HOST?.replace(/\/$/, "");
|
|
@@ -684,7 +839,196 @@ function autoLoaderIngestionCheck(options = {}) {
|
|
|
684
839
|
}
|
|
685
840
|
};
|
|
686
841
|
}
|
|
842
|
+
function safeName(prefix) {
|
|
843
|
+
return `${prefix}_${randomUUID().replaceAll("-", "_")}`;
|
|
844
|
+
}
|
|
845
|
+
function performanceBudgetCheck() {
|
|
846
|
+
return {
|
|
847
|
+
id: "performance",
|
|
848
|
+
description: "SQL Statement Execution p95 stays within the configured latency budget",
|
|
849
|
+
configured: (env) => Boolean(env.DBX_TEST_PERFORMANCE_P95_MS),
|
|
850
|
+
async run({ env, warehouse }) {
|
|
851
|
+
const budgetMs = Number(env.DBX_TEST_PERFORMANCE_P95_MS);
|
|
852
|
+
const runs = Number(env.DBX_TEST_PERFORMANCE_RUNS ?? 7);
|
|
853
|
+
if (!Number.isFinite(budgetMs) || budgetMs <= 0) {
|
|
854
|
+
throw new Error("DBX_TEST_PERFORMANCE_P95_MS must be a positive number");
|
|
855
|
+
}
|
|
856
|
+
if (!Number.isInteger(runs) || runs < 3 || runs > 100) {
|
|
857
|
+
throw new Error("DBX_TEST_PERFORMANCE_RUNS must be an integer from 3 to 100");
|
|
858
|
+
}
|
|
859
|
+
await warehouse.query("SELECT 1 AS warmup");
|
|
860
|
+
const durations = [];
|
|
861
|
+
for (let index = 0; index < runs; index += 1) {
|
|
862
|
+
const started = performance.now();
|
|
863
|
+
await warehouse.query("SELECT 1 AS performance_probe");
|
|
864
|
+
durations.push(performance.now() - started);
|
|
865
|
+
}
|
|
866
|
+
durations.sort((left, right) => left - right);
|
|
867
|
+
const p95 = durations[Math.ceil(durations.length * 0.95) - 1] ?? 0;
|
|
868
|
+
if (p95 > budgetMs) {
|
|
869
|
+
throw new Error(`SQL p95 ${Math.round(p95)}ms exceeded ${budgetMs}ms budget`);
|
|
870
|
+
}
|
|
871
|
+
return {
|
|
872
|
+
runs,
|
|
873
|
+
p50Ms: Math.round(durations[Math.floor(durations.length * 0.5)] ?? 0),
|
|
874
|
+
p95Ms: Math.round(p95),
|
|
875
|
+
budgetMs
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
function failureRecoveryCheck() {
|
|
881
|
+
return {
|
|
882
|
+
id: "failure-recovery",
|
|
883
|
+
description: "A failed statement is isolated and a subsequent statement recovers",
|
|
884
|
+
async run({ warehouse }) {
|
|
885
|
+
let failedAsExpected = false;
|
|
886
|
+
try {
|
|
887
|
+
await warehouse.query("SELECT * FROM __fabric_experiments_expected_missing_table__");
|
|
888
|
+
} catch {
|
|
889
|
+
failedAsExpected = true;
|
|
890
|
+
}
|
|
891
|
+
if (!failedAsExpected) throw new Error("Injected failure unexpectedly succeeded");
|
|
892
|
+
const rows = await warehouse.query("SELECT 1 AS recovered");
|
|
893
|
+
if (Number(rows[0]?.recovered) !== 1) {
|
|
894
|
+
throw new Error("Statement execution did not recover after the injected failure");
|
|
895
|
+
}
|
|
896
|
+
return { injectedFailure: true, recovered: true };
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
function secretRotationCheck(options = {}) {
|
|
901
|
+
return {
|
|
902
|
+
id: "secret-rotation",
|
|
903
|
+
description: "Databricks secret put, overwrite, metadata verification, and cleanup",
|
|
904
|
+
configured: (env) => Boolean(env.DBX_TEST_ROTATION_SECRET_SCOPE),
|
|
905
|
+
async run({ env, client }) {
|
|
906
|
+
const scope = env.DBX_TEST_ROTATION_SECRET_SCOPE;
|
|
907
|
+
const key = options.randomId?.() ?? safeName("fx_rotation_probe");
|
|
908
|
+
const put = async (value) => client.post("/api/2.0/secrets/put", { scope, key, string_value: value });
|
|
909
|
+
try {
|
|
910
|
+
await put(randomBytes(32).toString("base64url"));
|
|
911
|
+
await put(randomBytes(32).toString("base64url"));
|
|
912
|
+
const listed = await client.get("/api/2.0/secrets/list", {
|
|
913
|
+
scope
|
|
914
|
+
});
|
|
915
|
+
if (!(listed.secrets ?? []).some((secret) => secret.key === key)) {
|
|
916
|
+
throw new Error(`Rotated secret metadata was not visible in scope ${scope}`);
|
|
917
|
+
}
|
|
918
|
+
return { scope, rotated: true, cleanedUp: true };
|
|
919
|
+
} finally {
|
|
920
|
+
await client.post("/api/2.0/secrets/delete", { scope, key }).catch(() => void 0);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
function backupRestoreCheck() {
|
|
926
|
+
return {
|
|
927
|
+
id: "backup-restore",
|
|
928
|
+
description: "Delta DEEP CLONE backup restores a dropped scratch table",
|
|
929
|
+
async run({ warehouse }) {
|
|
930
|
+
const source = warehouse.qual(safeName("backup_source"));
|
|
931
|
+
const backup = warehouse.qual(safeName("backup_clone"));
|
|
932
|
+
try {
|
|
933
|
+
await warehouse.exec(`CREATE TABLE ${source} USING DELTA AS SELECT 7 AS value`);
|
|
934
|
+
await warehouse.exec(`CREATE TABLE ${backup} DEEP CLONE ${source}`);
|
|
935
|
+
await warehouse.exec(`DROP TABLE ${source}`);
|
|
936
|
+
await warehouse.exec(`CREATE TABLE ${source} DEEP CLONE ${backup}`);
|
|
937
|
+
const rows = await warehouse.query(`SELECT value FROM ${source}`);
|
|
938
|
+
if (Number(rows[0]?.value) !== 7) throw new Error("Restored Delta table lost its data");
|
|
939
|
+
return { restoredRows: rows.length, deepClone: true };
|
|
940
|
+
} finally {
|
|
941
|
+
await warehouse.exec(`DROP TABLE IF EXISTS ${source}`).catch(() => void 0);
|
|
942
|
+
await warehouse.exec(`DROP TABLE IF EXISTS ${backup}`).catch(() => void 0);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function rollbackCheck() {
|
|
948
|
+
return {
|
|
949
|
+
id: "rollback",
|
|
950
|
+
description: "Delta time-travel rollback restores a known-good table version",
|
|
951
|
+
async run({ warehouse }) {
|
|
952
|
+
const table = warehouse.qual(safeName("rollback_probe"));
|
|
953
|
+
try {
|
|
954
|
+
await warehouse.exec(`CREATE TABLE ${table} (value INT) USING DELTA`);
|
|
955
|
+
await warehouse.exec(`INSERT INTO ${table} VALUES (1)`);
|
|
956
|
+
const history = await warehouse.query(`DESCRIBE HISTORY ${table} LIMIT 1`);
|
|
957
|
+
const knownGoodVersion = Number(history[0]?.version);
|
|
958
|
+
if (!Number.isInteger(knownGoodVersion)) {
|
|
959
|
+
throw new Error("Delta history did not return a known-good version");
|
|
960
|
+
}
|
|
961
|
+
await warehouse.exec(`INSERT INTO ${table} VALUES (999)`);
|
|
962
|
+
await warehouse.exec(`RESTORE TABLE ${table} TO VERSION AS OF ${knownGoodVersion}`);
|
|
963
|
+
const rows = await warehouse.query(`SELECT value FROM ${table} ORDER BY value`);
|
|
964
|
+
if (rows.length !== 1 || Number(rows[0]?.value) !== 1) {
|
|
965
|
+
throw new Error("Delta rollback did not restore the known-good contents");
|
|
966
|
+
}
|
|
967
|
+
return { knownGoodVersion, restoredRows: rows.length };
|
|
968
|
+
} finally {
|
|
969
|
+
await warehouse.exec(`DROP TABLE IF EXISTS ${table}`).catch(() => void 0);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
function serverlessComputeCheck() {
|
|
975
|
+
return {
|
|
976
|
+
id: "azure-serverless",
|
|
977
|
+
description: "Azure Databricks serverless SQL warehouse is enabled and executes SQL",
|
|
978
|
+
configured: (env) => Boolean(env.DATABRICKS_WAREHOUSE_ID),
|
|
979
|
+
async run({ env, client, warehouse }) {
|
|
980
|
+
const id = env.DATABRICKS_WAREHOUSE_ID;
|
|
981
|
+
const details = await client.get(`/api/2.0/sql/warehouses/${encodeURIComponent(id)}`);
|
|
982
|
+
if (details.enable_serverless_compute !== true) {
|
|
983
|
+
throw new Error(`SQL warehouse ${id} is not configured for serverless compute`);
|
|
984
|
+
}
|
|
985
|
+
const rows = await warehouse.query("SELECT current_catalog() AS catalog");
|
|
986
|
+
return { warehouseId: id, name: details.name, state: details.state, rows: rows.length };
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
function classicComputeCheck() {
|
|
991
|
+
return {
|
|
992
|
+
id: "azure-classic",
|
|
993
|
+
description: "Azure Databricks classic job cluster starts and completes a notebook workload",
|
|
994
|
+
configured: (env) => Boolean(
|
|
995
|
+
env.DBX_TEST_CLASSIC_SPARK_VERSION && env.DBX_TEST_CLASSIC_NODE_TYPE && env.DBX_TEST_NOTEBOOK_PATH
|
|
996
|
+
),
|
|
997
|
+
async run({ env, client }) {
|
|
998
|
+
const submitted = await client.post("/api/2.1/jobs/runs/submit", {
|
|
999
|
+
run_name: `fabric-experiments-classic-certification-${Date.now()}`,
|
|
1000
|
+
tasks: [
|
|
1001
|
+
{
|
|
1002
|
+
task_key: "classic_notebook",
|
|
1003
|
+
new_cluster: {
|
|
1004
|
+
spark_version: env.DBX_TEST_CLASSIC_SPARK_VERSION,
|
|
1005
|
+
node_type_id: env.DBX_TEST_CLASSIC_NODE_TYPE,
|
|
1006
|
+
num_workers: Number(env.DBX_TEST_CLASSIC_WORKERS ?? 1),
|
|
1007
|
+
autotermination_minutes: 10,
|
|
1008
|
+
data_security_mode: env.DBX_TEST_CLASSIC_SECURITY_MODE ?? "SINGLE_USER"
|
|
1009
|
+
},
|
|
1010
|
+
notebook_task: { notebook_path: env.DBX_TEST_NOTEBOOK_PATH }
|
|
1011
|
+
}
|
|
1012
|
+
]
|
|
1013
|
+
});
|
|
1014
|
+
if (!submitted.run_id) throw new Error("Classic compute submit returned no run_id");
|
|
1015
|
+
const run = await waitForDatabricksRun(client, submitted.run_id, {
|
|
1016
|
+
timeoutMs: Number(env.DBX_TEST_CLASSIC_TIMEOUT_MS ?? 18e5),
|
|
1017
|
+
pollIntervalMs: Number(env.DBX_TEST_POLL_INTERVAL_MS ?? 15e3)
|
|
1018
|
+
});
|
|
1019
|
+
const result = run.state?.result_state;
|
|
1020
|
+
if (result !== "SUCCESS") {
|
|
1021
|
+
throw new Error(`Classic compute run ended in ${result ?? "UNKNOWN"}`);
|
|
1022
|
+
}
|
|
1023
|
+
return {
|
|
1024
|
+
runId: submitted.run_id,
|
|
1025
|
+
sparkVersion: env.DBX_TEST_CLASSIC_SPARK_VERSION,
|
|
1026
|
+
nodeType: env.DBX_TEST_CLASSIC_NODE_TYPE
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
687
1031
|
|
|
688
|
-
export { appHealthCheck, assertDeltaContract, autoLoaderIngestionCheck, createExecutionContext, createLakebaseTestProvider, createMockDatabricksClient, customLiveCheck, dbtBuildCheck, defineLiveSuite, deleteVolumeFile, deltaContractCheck, ensureVolumeDirectory, expectQueryToMatchGolden, expectTable, jobRunCheck, lakebaseCredentialCheck, lakebaseRoundTripCheck, normalizeVolumeRoot, notebookSubmitCheck, pipelineRefreshCheck, renderJUnit, resolveProfile, runLiveSuite, secretScopeCheck, sqlRoundTripCheck, unityCatalogAccessCheck, uploadVolumeFile, volumeIOCheck, waitForPipelineUpdate };
|
|
1032
|
+
export { appHealthCheck, assertDeltaContract, autoLoaderIngestionCheck, backupRestoreCheck, classicComputeCheck, createEphemeralLakebaseBranch, createExecutionContext, createLakebaseTestProvider, createMockDatabricksClient, customLiveCheck, dbtBuildCheck, defineLiveSuite, deleteEphemeralLakebaseBranch, deleteVolumeFile, deltaContractCheck, ensureVolumeDirectory, expectQueryToMatchGolden, expectTable, failureRecoveryCheck, jobRunCheck, lakebaseCredentialCheck, lakebaseRoundTripCheck, normalizeVolumeRoot, notebookSubmitCheck, performanceBudgetCheck, pipelineRefreshCheck, renderJUnit, resolveLakebaseProject, resolveProfile, restrictedPrincipalCheck, rollbackCheck, runLiveSuite, secretRotationCheck, secretScopeCheck, serverlessComputeCheck, sqlRoundTripCheck, toLakebaseBranchId, unityCatalogAccessCheck, uploadVolumeFile, volumeIOCheck, waitForPipelineUpdate };
|
|
689
1033
|
//# sourceMappingURL=index.js.map
|
|
690
1034
|
//# sourceMappingURL=index.js.map
|