Local testing
Hermetic Databricks tests with no workspace — DuckDB execution contexts, client-boundary mocks, fixtures, and cross-engine parity.
Status: Available (Phase 1 shipped) —
@fabricorg/databricks-testkitprovides the DuckDB context, fixtures, golden files, the mock client, and cross-engine parity suites. The live half of parity runs whenDBX_TEST_LIVE=1is set (Phase 3 wires it into CI).
The local tier runs your Databricks-bound logic with no workspace, no credentials, and no network — fast enough for watch mode and every CI run.
DuckDB execution context
import { createDuckDbContext } from '@fabricorg/databricks-testkit'
const ctx = await createDuckDbContext()
await ctx.loadFixture({
table: 'exposures',
ndjsonFile: 'fixtures/exposures.ndjson',
})
const rows = await ctx.query(aggregateSql)DuckDB executes the portable SQL emitted for the local profile. The live profile
executes the corresponding Databricks SQL against a warehouse. Keep analytical SQL in a single emitter
(the repo's pattern is a shared SQL AST in packages/warehouse with
emitDatabricksAggregate / emitDuckDbAggregate) so both engines stay in sync
by construction.
Client-boundary mocks
Everything that talks to the Databricks REST API goes through a
DatabricksRestClient that accepts an injected fetch/client. Unit tests stub at
that boundary and drive multi-poll state machines with sequenced responses:
import { createMockDatabricksClient } from '@fabricorg/databricks-testkit'
const client = createMockDatabricksClient()
client.get
.mockResolvedValueOnce({ state: { life_cycle_state: 'RUNNING' } })
.mockResolvedValueOnce({
state: { life_cycle_state: 'TERMINATED', result_state: 'SUCCESS' },
})
const run = await jobs.wait(runId, { client })This tests job/pipeline orchestration logic — retries, timeouts, terminal-state
handling — in milliseconds. Both the REST client and OAuth exchange accept an
injectable fetchImpl; a record/replay fixture layer is planned for the live tier.
Fixtures
Fixtures are declarative and engine-agnostic — the same seed applies to a local DuckDB table or a scratch Unity Catalog schema in live runs:
await ctx.loadFixture({
table: 'conversions',
schema: 'subject_id STRING, event_name STRING, value DOUBLE, at TIMESTAMP',
rows: [
['u1', 'purchase', 49.0, '2026-07-01T10:00:00Z'],
['u2', 'purchase', 12.5, '2026-07-01T11:30:00Z'],
],
})Cross-engine parity
Golden-result suites prove DuckDB and Databricks agree:
import { defineParitySuite } from '@fabricorg/databricks-testkit'
defineParitySuite({
name: 'aggregate-attribution',
sql: emitDatabricksAggregate(query),
fixtures: ['fixtures/exposures.ndjson', 'fixtures/conversions.ndjson'],
golden: 'golden/aggregate-attribution.json',
})Locally the suite asserts DuckDB output against the golden file. When live
credentials are present (see Live artifact checks) it also
runs the statement on a real warehouse and asserts both engines match the same
golden rows. A change to the golden file is a reviewable, versioned event —
exactly like the frozen assignment vectors in packages/testkit.