From ce5b25dd67d31ab26c90f2a0274b2e6279f9845e Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Thu, 4 Jun 2026 19:05:42 +0000 Subject: [PATCH] =?UTF-8?q?[SEA-NodeJS]=20Rename=20SEA=20=E2=86=92=20kerne?= =?UTF-8?q?l=20across=20the=20driver=20(useSEA=20=E2=86=92=20useKernel)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-applied on top of main (which now includes #416's consolidated kernel backend: connection/statement options, TLS & configurable sync/async). The original rename branch was based on the older sea-kernel-rev-pin stack; a literal rebase would have hit modify/delete conflicts on every backend file, so the rename was re-derived against current main. Scope: - Directories: lib/sea→lib/kernel, native/sea→native/kernel, tests/unit/sea→tests/unit/kernel, tests/e2e/sea→tests/e2e/kernel. - Files/classes: Sea*→Kernel* (SeaBackend→KernelBackend, SeaSessionBackend, SeaOperationBackend, SeaNativeLoader, SeaAuth, …). - Public option: useSEA→useKernel (hard rename; no back-compat alias). - COMPLETE this time: the internal helpers the prior pass left sea-named are renamed too — getSeaNative→getKernelNative, buildSea*→buildKernel*, seaCancel/Close/Finished→kernel*, seaServerInfoValue→kernelServerInfoValue, SEA_DBMS_*/SEA_NATIVE_EXPECTED→KERNEL_*. Intentionally preserved: 'SEA' where it denotes the Statement Execution API wire protocol (comments/JSDoc), Python's use_sea reference, and the telemetry backend union member 'sea' (dashboard back-compat). Verified: tsc clean, lint clean, full unit suite 1166 passing, live end-to-end (2 executions via useKernel) green. Co-authored-by: Isaac Signed-off-by: Madhavendra Rathore --- .gitattributes | 4 +- .gitignore | 8 +- .npmignore | 4 +- .prettierignore | 4 +- lib/DBSQLClient.ts | 8 +- lib/contracts/IBackend.ts | 4 +- lib/contracts/IDBSQLSession.ts | 2 +- lib/contracts/InternalConnectionOptions.ts | 4 +- .../KernelArrowIpc.ts} | 12 +- .../KernelArrowIpcDurationFix.ts} | 2 +- lib/{sea/SeaAuth.ts => kernel/KernelAuth.ts} | 30 ++--- .../SeaBackend.ts => kernel/KernelBackend.ts} | 52 ++++----- .../KernelErrorMapping.ts} | 4 +- .../KernelInputValidation.ts} | 0 .../KernelNativeLoader.ts} | 64 +++++------ .../KernelOperationBackend.ts} | 108 +++++++++--------- .../KernelOperationLifecycle.ts} | 36 +++--- .../KernelPositionalParams.ts} | 14 +-- .../KernelResultsProvider.ts} | 14 +-- .../KernelServerInfo.ts} | 14 +-- .../KernelSessionBackend.ts} | 50 ++++---- lib/result/ArrowResultConverter.ts | 12 +- lib/thrift-backend/ThriftSessionBackend.ts | 4 +- native/{sea => kernel}/README.md | 8 +- native/{sea => kernel}/index.d.ts | 0 native/{sea => kernel}/index.js | 0 package.json | 4 +- .../e2e/{sea => kernel}/auth-m2m-e2e.test.ts | 10 +- .../e2e/{sea => kernel}/auth-pat-e2e.test.ts | 8 +- .../e2e/{sea => kernel}/auth-u2m-e2e.test.ts | 6 +- tests/e2e/{sea => kernel}/e2e-smoke.test.ts | 10 +- .../e2e/{sea => kernel}/execution-e2e.test.ts | 14 +-- .../interval-duration-e2e.test.ts | 8 +- .../{sea => kernel}/interval-edge-e2e.test.ts | 12 +- .../operation-lifecycle-e2e.test.ts | 38 +++--- tests/e2e/{sea => kernel}/results-e2e.test.ts | 8 +- tests/unit/DBSQLClient.test.ts | 14 +-- .../KernelArrowIpc.test.ts} | 12 +- .../KernelIntervalParity.test.ts} | 32 +++--- .../KernelOperationBackend.test.ts} | 14 +-- .../{sea => kernel}/_helpers/fakeBinding.ts | 14 +-- .../{sea => kernel}/auth-edge-cases.test.ts | 76 ++++++------ tests/unit/{sea => kernel}/auth-m2m.test.ts | 38 +++--- tests/unit/{sea => kernel}/auth-pat.test.ts | 33 +++--- tests/unit/{sea => kernel}/auth-u2m.test.ts | 32 +++--- .../{sea => kernel}/connectionOptions.test.ts | 49 ++++---- .../{sea => kernel}/error-mapping.test.ts | 4 +- tests/unit/{sea => kernel}/execution.test.ts | 91 ++++++++------- .../{sea => kernel}/inputValidation.test.ts | 4 +- tests/unit/{sea => kernel}/loader.test.ts | 32 +++--- .../{sea => kernel}/native-packaging.test.ts | 12 +- .../operation-lifecycle.test.ts | 92 +++++++-------- .../{sea => kernel}/positionalParams.test.ts | 30 ++--- tests/unit/{sea => kernel}/serverInfo.test.ts | 33 +++--- tests/unit/{sea => kernel}/version.test.ts | 8 +- 55 files changed, 617 insertions(+), 583 deletions(-) rename lib/{sea/SeaArrowIpc.ts => kernel/KernelArrowIpc.ts} (96%) rename lib/{sea/SeaArrowIpcDurationFix.ts => kernel/KernelArrowIpcDurationFix.ts} (99%) rename lib/{sea/SeaAuth.ts => kernel/KernelAuth.ts} (95%) rename lib/{sea/SeaBackend.ts => kernel/KernelBackend.ts} (75%) rename lib/{sea/SeaErrorMapping.ts => kernel/KernelErrorMapping.ts} (98%) rename lib/{sea/SeaInputValidation.ts => kernel/KernelInputValidation.ts} (100%) rename lib/{sea/SeaNativeLoader.ts => kernel/KernelNativeLoader.ts} (82%) rename lib/{sea/SeaOperationBackend.ts => kernel/KernelOperationBackend.ts} (88%) rename lib/{sea/SeaOperationLifecycle.ts => kernel/KernelOperationLifecycle.ts} (90%) rename lib/{sea/SeaPositionalParams.ts => kernel/KernelPositionalParams.ts} (93%) rename lib/{sea/SeaResultsProvider.ts => kernel/KernelResultsProvider.ts} (92%) rename lib/{sea/SeaServerInfo.ts => kernel/KernelServerInfo.ts} (87%) rename lib/{sea/SeaSessionBackend.ts => kernel/KernelSessionBackend.ts} (91%) rename native/{sea => kernel}/README.md (93%) rename native/{sea => kernel}/index.d.ts (100%) rename native/{sea => kernel}/index.js (100%) rename tests/e2e/{sea => kernel}/auth-m2m-e2e.test.ts (94%) rename tests/e2e/{sea => kernel}/auth-pat-e2e.test.ts (91%) rename tests/e2e/{sea => kernel}/auth-u2m-e2e.test.ts (93%) rename tests/e2e/{sea => kernel}/e2e-smoke.test.ts (89%) rename tests/e2e/{sea => kernel}/execution-e2e.test.ts (91%) rename tests/e2e/{sea => kernel}/interval-duration-e2e.test.ts (93%) rename tests/e2e/{sea => kernel}/interval-edge-e2e.test.ts (85%) rename tests/e2e/{sea => kernel}/operation-lifecycle-e2e.test.ts (88%) rename tests/e2e/{sea => kernel}/results-e2e.test.ts (94%) rename tests/unit/{sea/SeaArrowIpc.test.ts => kernel/KernelArrowIpc.test.ts} (90%) rename tests/unit/{sea/SeaIntervalParity.test.ts => kernel/KernelIntervalParity.test.ts} (92%) rename tests/unit/{sea/SeaOperationBackend.test.ts => kernel/KernelOperationBackend.test.ts} (95%) rename tests/unit/{sea => kernel}/_helpers/fakeBinding.ts (84%) rename tests/unit/{sea => kernel}/auth-edge-cases.test.ts (87%) rename tests/unit/{sea => kernel}/auth-m2m.test.ts (82%) rename tests/unit/{sea => kernel}/auth-pat.test.ts (77%) rename tests/unit/{sea => kernel}/auth-u2m.test.ts (81%) rename tests/unit/{sea => kernel}/connectionOptions.test.ts (64%) rename tests/unit/{sea => kernel}/error-mapping.test.ts (98%) rename tests/unit/{sea => kernel}/execution.test.ts (93%) rename tests/unit/{sea => kernel}/inputValidation.test.ts (92%) rename tests/unit/{sea => kernel}/loader.test.ts (83%) rename tests/unit/{sea => kernel}/native-packaging.test.ts (83%) rename tests/unit/{sea => kernel}/operation-lifecycle.test.ts (82%) rename tests/unit/{sea => kernel}/positionalParams.test.ts (82%) rename tests/unit/{sea => kernel}/serverInfo.test.ts (50%) rename tests/unit/{sea => kernel}/version.test.ts (90%) diff --git a/.gitattributes b/.gitattributes index 0a49a4b9..7f9c4ad2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -36,5 +36,5 @@ Dockerfile* text # crate; regenerated by `npm run build:native`. Tell git/GitHub they're # machine-generated so they collapse in diffs and are excluded from # blame and language stats. -native/sea/index.d.ts linguist-generated=true -native/sea/index.js linguist-generated=true +native/kernel/index.d.ts linguist-generated=true +native/kernel/index.js linguist-generated=true diff --git a/.gitignore b/.gitignore index c3801f4b..445c58aa 100644 --- a/.gitignore +++ b/.gitignore @@ -12,10 +12,10 @@ dist lib/version.ts # SEA native binding — copied/generated from kernel workspace by `npm run build:native`. -# The committed contract is `native/sea/index.d.ts` (TypeScript declarations) and -# `native/sea/index.js` (the napi-rs platform router — small, stable, and required in +# The committed contract is `native/kernel/index.d.ts` (TypeScript declarations) and +# `native/kernel/index.js` (the napi-rs platform router — small, stable, and required in # the publish tarball so a missing build step can't ship a tarball that can't load). # The `.node` binaries are large per-platform artifacts and must NOT be committed; # in production they arrive via the `@databricks/sql-kernel-` optional deps. -native/sea/index.node -native/sea/index.*.node +native/kernel/index.node +native/kernel/index.*.node diff --git a/.npmignore b/.npmignore index 448289a7..7b2f225c 100644 --- a/.npmignore +++ b/.npmignore @@ -7,8 +7,8 @@ # selects the per-platform `.node` artifact from `@databricks/sql-kernel-*` # optionalDependencies (populated when the kernel CI publishes them); # the .d.ts is the consumer-facing type contract. -!native/sea/index.js -!native/sea/index.d.ts +!native/kernel/index.js +!native/kernel/index.d.ts !LICENSE !NOTICE diff --git a/.prettierignore b/.prettierignore index 4a764095..afcb3d3f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,5 +15,5 @@ package-lock.json # Generated by napi-rs from the kernel's `napi-binding/napi/` crate; # regenerated by `npm run build:native`. Format follows napi-rs's # defaults (no semicolons), not this repo's prettier config. -native/sea/index.d.ts -native/sea/index.js +native/kernel/index.d.ts +native/kernel/index.js diff --git a/lib/DBSQLClient.ts b/lib/DBSQLClient.ts index c3506680..624b5446 100644 --- a/lib/DBSQLClient.ts +++ b/lib/DBSQLClient.ts @@ -18,7 +18,7 @@ import { buildUserAgentString } from './utils'; import IBackend from './contracts/IBackend'; import { InternalConnectionOptions } from './contracts/InternalConnectionOptions'; import ThriftBackend from './thrift-backend/ThriftBackend'; -import SeaBackend from './sea/SeaBackend'; +import KernelBackend from './kernel/KernelBackend'; import PlainHttpAuthentication from './connection/auth/PlainHttpAuthentication'; import DatabricksOAuth, { OAuthFlow } from './connection/auth/DatabricksOAuth'; import { @@ -627,12 +627,12 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient, I this.connectionProvider = this.createConnectionProvider(options); - // M0: `useSEA` is consumed via a non-exported internal-options cast so it + // M0: `useKernel` is consumed via a non-exported internal-options cast so it // doesn't ship in the public `.d.ts`. Mirrors Python's `kwargs.get("use_sea")` // pattern (see databricks-sql-python/src/databricks/sql/session.py). const internalOptions = options as ConnectionOptions & InternalConnectionOptions; - const backend = internalOptions.useSEA - ? new SeaBackend({ context: this }) + const backend = internalOptions.useKernel + ? new KernelBackend({ context: this }) : new ThriftBackend({ context: this, onConnectionEvent: (event, payload) => this.forwardConnectionEvent(event, payload), diff --git a/lib/contracts/IBackend.ts b/lib/contracts/IBackend.ts index 2e5edd16..c2a1ddb8 100644 --- a/lib/contracts/IBackend.ts +++ b/lib/contracts/IBackend.ts @@ -3,14 +3,14 @@ import ISessionBackend from './ISessionBackend'; /** * Top-level backend dispatch handle. One instance per `DBSQLClient`, - * chosen at `connect()` time based on the `useSEA` flag and never + * chosen at `connect()` time based on the `useKernel` flag and never * re-selected per-call. */ export default interface IBackend { /** * Establish backend-level state before any session is opened. Implementations * consume `options` to build backend-specific connection parameters (e.g. the - * SEA backend derives napi-binding `SeaNativeConnectionOptions` from the auth + * SEA backend derives napi-binding `KernelNativeConnectionOptions` from the auth * + host fields here). Transport-layer connection providers are owned by * `DBSQLClient` (via `IClientContext`) and exposed to backends through * constructor injection. diff --git a/lib/contracts/IDBSQLSession.ts b/lib/contracts/IDBSQLSession.ts index d17cfa5a..3b6be9d5 100644 --- a/lib/contracts/IDBSQLSession.ts +++ b/lib/contracts/IDBSQLSession.ts @@ -19,7 +19,7 @@ export type ExecuteStatementOptions = { * - **Thrift backend:** no-op. The Thrift path always submits asynchronously * (`runAsync: true` on the wire) and polls during fetch; this option is not * read. - * - **Kernel backend (`useSEA`):** selects the kernel execution path — + * - **Kernel backend (`useKernel`):** selects the kernel execution path — * `false`/unset (default) runs the blocking direct-results path (faster, * cancellable mid-compute); `true` submits and polls (returns a pending * handle before completion). Default is sync, matching the python diff --git a/lib/contracts/InternalConnectionOptions.ts b/lib/contracts/InternalConnectionOptions.ts index 24575984..fc6c6295 100644 --- a/lib/contracts/InternalConnectionOptions.ts +++ b/lib/contracts/InternalConnectionOptions.ts @@ -7,7 +7,7 @@ * signature (see `databricks-sql-python/src/databricks/sql/session.py`). * * Callers cast `ConnectionOptions` to this type *only* at the read site - * inside the driver; user code that wants to set `useSEA` may still do so + * inside the driver; user code that wants to set `useKernel` may still do so * via an untyped object literal — the option is not part of the public * contract and may be removed without notice. */ @@ -17,7 +17,7 @@ export interface InternalConnectionOptions { * backend instead of the default Thrift backend. Defaults to `false`. * @internal Not stable; M0 stub only. */ - useSEA?: boolean; + useKernel?: boolean; /** * SEA-only: kernel connection-pool size (`ConnectionOptions.max_connections`). diff --git a/lib/sea/SeaArrowIpc.ts b/lib/kernel/KernelArrowIpc.ts similarity index 96% rename from lib/sea/SeaArrowIpc.ts rename to lib/kernel/KernelArrowIpc.ts index 95071895..1d15bee5 100644 --- a/lib/sea/SeaArrowIpc.ts +++ b/lib/kernel/KernelArrowIpc.ts @@ -14,7 +14,7 @@ import { RecordBatchReader, MessageReader, MessageHeader, Schema, Field, DataType, TypeMap } from 'apache-arrow'; import { TTableSchema, TTypeId, TPrimitiveTypeEntry } from '../../thrift/TCLIService_types'; -import { rewriteDurationToInt64, DURATION_UNIT_METADATA_KEY } from './SeaArrowIpcDurationFix'; +import { rewriteDurationToInt64, DURATION_UNIT_METADATA_KEY } from './KernelArrowIpcDurationFix'; import HiveDriverError from '../errors/HiveDriverError'; /** @@ -29,7 +29,7 @@ const DATABRICKS_TYPE_NAME = 'databricks.type_name'; * * Why this exists: `ArrowResultConverter` consumes `ArrowBatch` objects * that carry an explicit `rowCount`, but the kernel's IPC payload only - * carries per-RecordBatch `length` (no separate total). `SeaResultsProvider` + * carries per-RecordBatch `length` (no separate total). `KernelResultsProvider` * needs that count to build the `ArrowBatch` it hands to the converter — * which then re-decodes the same bytes for the actual values. * @@ -89,12 +89,12 @@ export function decodeIpcSchema(ipcBytes: Buffer): Schema { * Pre-process raw IPC bytes from the kernel so they're consumable by * `apache-arrow@13`. The current transformation is `Duration → Int64` * with the original duration unit preserved in field metadata (see - * `SeaArrowIpcDurationFix.ts`). Returned bytes are byte-identical to + * `KernelArrowIpcDurationFix.ts`). Returned bytes are byte-identical to * the input when no transformation is needed. * * Exposed so callers can pre-patch the buffer **once** and pass the * result through both `decodeIpcBatch` (for row-count extraction in - * `SeaResultsProvider`) and `ArrowResultConverter.fetchNext` (which + * `KernelResultsProvider`) and `ArrowResultConverter.fetchNext` (which * re-decodes the same bytes via `RecordBatchReader.from`). Without * this, the converter would re-throw on `Duration` because it never * sees the patched bytes. @@ -198,7 +198,7 @@ function arrowTypeToTTypeId(field: Field): TTypeId { if (DataType.isInt(arrowType)) { // Duration columns are rewritten to Int64 with a // `databricks.arrow.duration_unit` metadata marker (see - // `SeaArrowIpcDurationFix.ts`). Surface them as INTERVAL_DAY_TIME + // `KernelArrowIpcDurationFix.ts`). Surface them as INTERVAL_DAY_TIME // so the converter formats them back into the thrift string form. if (arrowType.bitWidth === 64 && field.metadata.has(DURATION_UNIT_METADATA_KEY)) { return TTypeId.INTERVAL_DAY_TIME_TYPE; @@ -246,7 +246,7 @@ function arrowTypeToTTypeId(field: Field): TTypeId { /** * Synthesize a Thrift `TTableSchema` from an Arrow schema decoded out - * of the kernel's IPC stream. Used by `SeaOperationBackend.getResultMetadata` + * of the kernel's IPC stream. Used by `KernelOperationBackend.getResultMetadata` * to drive `ArrowResultConverter.convertThriftTypes` (Phase 2) without * changing that code. */ diff --git a/lib/sea/SeaArrowIpcDurationFix.ts b/lib/kernel/KernelArrowIpcDurationFix.ts similarity index 99% rename from lib/sea/SeaArrowIpcDurationFix.ts rename to lib/kernel/KernelArrowIpcDurationFix.ts index 84349faa..02d7ac40 100644 --- a/lib/sea/SeaArrowIpcDurationFix.ts +++ b/lib/kernel/KernelArrowIpcDurationFix.ts @@ -45,7 +45,7 @@ * * Why this lives in its own file: the rewriter is the only place in the * codebase that needs to construct FlatBuffers by hand using the - * `flatbuffers` library; isolating it keeps `SeaArrowIpc.ts` focused on + * `flatbuffers` library; isolating it keeps `KernelArrowIpc.ts` focused on * the high-level Arrow-decoded views. * * @see lib/result/ArrowResultConverter.ts — the Phase-1 INTERVAL formatter diff --git a/lib/sea/SeaAuth.ts b/lib/kernel/KernelAuth.ts similarity index 95% rename from lib/sea/SeaAuth.ts rename to lib/kernel/KernelAuth.ts index a9d9d116..31fe086a 100644 --- a/lib/sea/SeaAuth.ts +++ b/lib/kernel/KernelAuth.ts @@ -29,7 +29,7 @@ const U2M_DEFAULT_REDIRECT_PORT = 8030; /** * Shape consumed by the napi-binding's `openSession()` (see - * `native/sea/index.d.ts`). Mirrors `ConnectionOptions` in the binding's + * `native/kernel/index.d.ts`). Mirrors `ConnectionOptions` in the binding's * `.d.ts`; declared locally to avoid coupling the JS-side adapter to the * auto-generated TS file. * @@ -45,10 +45,10 @@ const U2M_DEFAULT_REDIRECT_PORT = 8030; * variant names verbatim (`'Pat'`, `'OAuthM2m'`, `'OAuthU2m'` — napi-rs's * `#[napi(string_enum)]` without an explicit case option emits the * Rust variant identifier as-is). We duplicate the values here instead - * of importing `AuthMode` from `native/sea/index.d.ts` because that + * of importing `AuthMode` from `native/kernel/index.d.ts` because that * file declares `AuthMode` as `export const enum`, which is * incompatible with `isolatedModules` and a runtime-coupling hazard. - * The Rust source of truth lives at `native/sea/src/database.rs`. + * The Rust source of truth lives at `native/kernel/src/database.rs`. */ /** * Session-level defaults shared across all auth-mode variants. @@ -63,7 +63,7 @@ const U2M_DEFAULT_REDIRECT_PORT = 8030; * creation. Mirror that here so the adapter doesn't promise a * capability the binding can't honour. */ -export interface SeaSessionDefaults { +export interface KernelSessionDefaults { catalog?: string; schema?: string; sessionConf?: Record; @@ -88,7 +88,7 @@ export interface SeaSessionDefaults { /** * Per-session kernel connection-pool size * (kernel `ConnectionOptions.max_connections`). Validated as a positive - * integer within the napi `u32` range by `buildSeaConnectionOptions`. + * integer within the napi `u32` range by `buildKernelConnectionOptions`. */ maxConnections?: number; } @@ -100,10 +100,10 @@ export interface SeaSessionDefaults { * * The napi shape takes `customCaCert` as a `Buffer` only; the public * `ConnectionOptions` additionally accepts a PEM string, which - * `buildSeaConnectionOptions` normalises to a `Buffer` before crossing + * `buildKernelConnectionOptions` normalises to a `Buffer` before crossing * the FFI boundary. */ -export interface SeaTlsOptions { +export interface KernelTlsOptions { /** * Verify the server's TLS certificate. The SEA backend is * **secure-by-default**: omitting this leaves the kernel default of @@ -117,8 +117,8 @@ export interface SeaTlsOptions { customCaCert?: Buffer; } -export type SeaNativeConnectionOptions = SeaSessionDefaults & - SeaTlsOptions & +export type KernelNativeConnectionOptions = KernelSessionDefaults & + KernelTlsOptions & ( | { hostName: string; @@ -181,13 +181,13 @@ const MAX_U32 = 0xffffffff; * Throws `HiveDriverError` when `customCaCert` is supplied but empty or * (for strings) lacks a PEM certificate header. */ -export function buildSeaTlsOptions(options: ConnectionOptions): SeaTlsOptions { +export function buildKernelTlsOptions(options: ConnectionOptions): KernelTlsOptions { // Read the SEA-only fields through the purpose-built internal options type // rather than an ad-hoc inline cast, so the shape can't silently drift from // its declaration and a typo'd key fails to compile. const { checkServerCertificate, customCaCert } = options as ConnectionOptions & InternalConnectionOptions; - const tls: SeaTlsOptions = {}; + const tls: KernelTlsOptions = {}; if (checkServerCertificate !== undefined) { tls.checkServerCertificate = checkServerCertificate; @@ -274,7 +274,7 @@ export function buildSeaTlsOptions(options: ConnectionOptions): SeaTlsOptions { * - `HiveDriverError` for unsupported auth modes / Azure-direct / * custom persistence / ambiguous combinations. */ -export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNativeConnectionOptions { +export function buildKernelConnectionOptions(options: ConnectionOptions): KernelNativeConnectionOptions { const { authType } = options as { authType?: string }; const base: { @@ -282,7 +282,7 @@ export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNative httpPath: string; intervalsAsString: boolean; maxConnections?: number; - } & SeaTlsOptions = { + } & KernelTlsOptions = { hostName: options.host, httpPath: prependSlash(options.path), // Match the NodeJS Thrift driver, which surfaces INTERVAL columns as @@ -294,7 +294,7 @@ export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNative intervalsAsString: true, // TLS knobs (server-cert verification toggle + custom CA). Validated and // normalised (string PEM → Buffer) here so the napi shape only sees a Buffer. - ...buildSeaTlsOptions(options), + ...buildKernelTlsOptions(options), }; // SEA-only pool sizing; read via cast to match how this function reads the @@ -363,7 +363,7 @@ export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNative // "secret is required" error rather than being silently routed to U2M // (which would hide their intent). Cost: `id + no secret` throws here // where thrift would run U2M, and SEA U2M has no custom-client-id support - // (see buildSeaConnectionOptions header). The U2M arm still defends against an id + // (see buildKernelConnectionOptions header). The U2M arm still defends against an id // sneaking through: fires only when `oauthClientId` is provided as // a blank-reserved literal (e.g., whitespace, `"null"`, `"undefined"`) // alongside an absent/blank secret — both `idIsBlank` and diff --git a/lib/sea/SeaBackend.ts b/lib/kernel/KernelBackend.ts similarity index 75% rename from lib/sea/SeaBackend.ts rename to lib/kernel/KernelBackend.ts index d609cac9..eff7c11d 100644 --- a/lib/sea/SeaBackend.ts +++ b/lib/kernel/KernelBackend.ts @@ -19,26 +19,26 @@ import { ConnectionOptions, OpenSessionRequest } from '../contracts/IDBSQLClient import { InternalConnectionOptions } from '../contracts/InternalConnectionOptions'; import { LogLevel } from '../contracts/IDBSQLLogger'; import HiveDriverError from '../errors/HiveDriverError'; -import { getSeaNative, SeaNativeBinding, SeaConnection } from './SeaNativeLoader'; -import { decodeNapiKernelError } from './SeaErrorMapping'; -import { buildSeaConnectionOptions, SeaNativeConnectionOptions } from './SeaAuth'; -import SeaSessionBackend from './SeaSessionBackend'; +import { getKernelNative, KernelNativeBinding, KernelConnection } from './KernelNativeLoader'; +import { decodeNapiKernelError } from './KernelErrorMapping'; +import { buildKernelConnectionOptions, KernelNativeConnectionOptions } from './KernelAuth'; +import KernelSessionBackend from './KernelSessionBackend'; -export interface SeaBackendOptions { +export interface KernelBackendOptions { /** * Required. Provides the logger + config the SEA session/operation chain * logs through. `DBSQLClient` supplies it via the SEA seam - * (`new SeaBackend({ context: this })`); unit tests pass a stub. Kept + * (`new KernelBackend({ context: this })`); unit tests pass a stub. Kept * mandatory (rather than an `as IClientContext` downcast of `undefined`) * so a missing context is a compile error, not a latent runtime NPE. */ context: IClientContext; /** * Optional injection seam for unit tests. When provided, replaces the - * default `getSeaNative()` call so tests can swap in a mock napi + * default `getKernelNative()` call so tests can swap in a mock napi * binding without loading the `.node` artifact. */ - nativeBinding?: SeaNativeBinding; + nativeBinding?: KernelNativeBinding; } /** @@ -50,12 +50,12 @@ export interface SeaBackendOptions { * captures the `ConnectionOptions` and validates that PAT auth is in * use. The actual session open happens inside `openSession()`. * - * **Auth validation:** delegates to `buildSeaConnectionOptions` from - * `SeaAuth`, which mirrors the existing DBSQLClient validation pattern + * **Auth validation:** delegates to `buildKernelConnectionOptions` from + * `KernelAuth`, which mirrors the existing DBSQLClient validation pattern * (slash-prepended httpPath, AuthenticationError on missing token or * blank OAuth credentials, HiveDriverError on unsupported authType / * Azure-direct / ambiguous credential combinations). M2M and U2M - * routing key off `oauthClientId` presence; see SeaAuth.ts. + * routing key off `oauthClientId` presence; see KernelAuth.ts. * * **Why we don't use IClientContext's connectionProvider here:** that * provider is the Thrift HTTP transport. The kernel owns its own @@ -63,23 +63,23 @@ export interface SeaBackendOptions { * NodeJS-level connection state to manage on the SEA path. The * `IClientContext` is still useful for logger + config access. */ -export default class SeaBackend implements IBackend { +export default class KernelBackend implements IBackend { private readonly context: IClientContext; - private readonly binding: SeaNativeBinding; + private readonly binding: KernelNativeBinding; - private nativeOptions?: SeaNativeConnectionOptions; + private nativeOptions?: KernelNativeConnectionOptions; - constructor(options: SeaBackendOptions) { + constructor(options: KernelBackendOptions) { this.context = options.context; - this.binding = options.nativeBinding ?? getSeaNative(); + this.binding = options.nativeBinding ?? getKernelNative(); } public async connect(options: ConnectionOptions): Promise { // Validate PAT auth + capture the napi-binding option shape. // Any non-PAT mode (or a missing/empty token) throws here, before // we ever touch the native binding. - this.nativeOptions = buildSeaConnectionOptions(options); + this.nativeOptions = buildKernelConnectionOptions(options); // Warn on the insecure combo: a `customCaCert` paired with // `checkServerCertificate: false` is almost always a mistake — verification @@ -100,7 +100,7 @@ export default class SeaBackend implements IBackend { public async openSession(request: OpenSessionRequest): Promise { if (!this.nativeOptions) { - throw new HiveDriverError('SeaBackend: not connected. Call connect() first.'); + throw new HiveDriverError('KernelBackend: not connected. Call connect() first.'); } // Fold session-level defaults from the OpenSessionRequest into the @@ -112,7 +112,7 @@ export default class SeaBackend implements IBackend { // Only set the optional keys when present so the napi call shape // stays minimal — keeps wire snapshots / test assertions stable // for callers who pass no defaults. - const sessionOptions: SeaNativeConnectionOptions = { ...this.nativeOptions }; + const sessionOptions: KernelNativeConnectionOptions = { ...this.nativeOptions }; if (request.initialCatalog !== undefined) { sessionOptions.catalog = request.initialCatalog; } @@ -123,22 +123,22 @@ export default class SeaBackend implements IBackend { sessionOptions.sessionConf = { ...request.configuration }; } - let nativeConnection: SeaConnection; + let nativeConnection: KernelConnection; try { - // `SeaNativeConnectionOptions.authMode` is a string-literal union + // `KernelNativeConnectionOptions.authMode` is a string-literal union // ('Pat' | 'OAuthM2m' | 'OAuthU2m') — deliberately not the binding's - // `const enum AuthMode` (see SeaAuth's note on why a const-enum import + // `const enum AuthMode` (see KernelAuth's note on why a const-enum import // is avoided). The literal values are byte-identical to the enum's, so // the only divergence is TS's const-enum strictness; cast to the // binding's parameter type at this single boundary. nativeConnection = (await this.binding.openSession( - sessionOptions as unknown as Parameters[0], - )) as SeaConnection; + sessionOptions as unknown as Parameters[0], + )) as KernelConnection; } catch (err) { throw decodeNapiKernelError(err); } - return new SeaSessionBackend({ + return new KernelSessionBackend({ connection: nativeConnection!, context: this.context, id: nativeConnection!.sessionId, @@ -146,7 +146,7 @@ export default class SeaBackend implements IBackend { } public async close(): Promise { - // No backend-level resources to release — each `SeaSessionBackend` + // No backend-level resources to release — each `KernelSessionBackend` // owns its own napi `Connection` lifecycle. this.nativeOptions = undefined; } diff --git a/lib/sea/SeaErrorMapping.ts b/lib/kernel/KernelErrorMapping.ts similarity index 98% rename from lib/sea/SeaErrorMapping.ts rename to lib/kernel/KernelErrorMapping.ts index b17d594a..eba69761 100644 --- a/lib/sea/SeaErrorMapping.ts +++ b/lib/kernel/KernelErrorMapping.ts @@ -7,7 +7,7 @@ import ParameterError from '../errors/ParameterError'; * Sentinel prefix the napi binding's `napi_err_from_kernel` puts on * `Error.message` when the underlying failure was a structured kernel * `Error` rather than a plain napi `InvalidArg` from binding-side - * validation. Defined here (and in `native/sea/src/error.rs:44`) — the + * validation. Defined here (and in `native/kernel/src/error.rs:44`) — the * two MUST stay in lockstep. */ const ERROR_SENTINEL = '__databricks_error__:'; @@ -53,7 +53,7 @@ export type KernelErrorCode = /** * Optional metadata fields the kernel may attach via the - * `__databricks_error__:` envelope (per `native/sea/src/error.rs:50-89`). + * `__databricks_error__:` envelope (per `native/kernel/src/error.rs:50-89`). * * `errorCode` is namespaced under `kernelMetadata` rather than placed at * the top level because `OperationStateError` already declares a top-level diff --git a/lib/sea/SeaInputValidation.ts b/lib/kernel/KernelInputValidation.ts similarity index 100% rename from lib/sea/SeaInputValidation.ts rename to lib/kernel/KernelInputValidation.ts diff --git a/lib/sea/SeaNativeLoader.ts b/lib/kernel/KernelNativeLoader.ts similarity index 82% rename from lib/sea/SeaNativeLoader.ts rename to lib/kernel/KernelNativeLoader.ts index 80352be6..6c762108 100644 --- a/lib/sea/SeaNativeLoader.ts +++ b/lib/kernel/KernelNativeLoader.ts @@ -19,12 +19,12 @@ * `.node` artifact ships via per-platform optional dependencies * (`@databricks/sql-kernel-`), so its absence must not crash * a Thrift-only consumer of the driver. Callers that actually need - * SEA construct a {@link SeaNativeLoader} (or use the process-global - * {@link getSeaNative}) which throws a structured error if the binding + * SEA construct a {@link KernelNativeLoader} (or use the process-global + * {@link getKernelNative}) which throws a structured error if the binding * could not be loaded. * * M0 publishes a single triple (`linux-x64-gnu`); see - * `native/sea/README.md` for the supported-platform policy. + * `native/kernel/README.md` for the supported-platform policy. */ import type { @@ -39,18 +39,18 @@ import type { AsyncStatement as NativeAsyncStatement, AsyncResultHandle as NativeAsyncResultHandle, CancellableExecution as NativeCancellableExecution, -} from '../../native/sea'; +} from '../../native/kernel'; // SEA-prefixed re-exports. The kernel-generated `.d.ts` keeps the // napi-rs default names (`ConnectionOptions`, `ArrowBatch`, …); we // disambiguate on the TS-wrapper side so these never collide with the // Thrift-side `ConnectionOptions` (lib/contracts/IDBSQLClient.ts) or // `ArrowBatch` (lib/result/utils.ts) when imported elsewhere. -export type SeaConnectionOptions = NativeConnectionOptions; -export type SeaArrowBatch = NativeArrowBatch; -export type SeaArrowSchema = NativeArrowSchema; -export type SeaConnection = NativeConnection; -export type SeaStatement = NativeStatement; +export type KernelConnectionOptions = NativeConnectionOptions; +export type KernelArrowBatch = NativeArrowBatch; +export type KernelArrowSchema = NativeArrowSchema; +export type KernelConnection = NativeConnection; +export type KernelStatement = NativeStatement; // Per-statement execution options and bound-parameter inputs are kernel // concerns: the napi binding generates the canonical shapes (`positionalParams` @@ -58,17 +58,17 @@ export type SeaStatement = NativeStatement; // `rowLimit`, `queryTimeoutSecs`, `statementConf`, `queryTags`). We re-export // rather than re-declare so the driver-side param codec can never drift from // the kernel contract. -export type SeaNativeExecuteOptions = NativeExecuteOptions; -export type SeaNativeTypedValueInput = NativeTypedValueInput; -export type SeaNativeNamedTypedValueInput = NativeNamedTypedValueInput; +export type KernelNativeExecuteOptions = NativeExecuteOptions; +export type KernelNativeTypedValueInput = NativeTypedValueInput; +export type KernelNativeNamedTypedValueInput = NativeNamedTypedValueInput; // Async-submit surface: `Connection.submitStatement` returns an // `AsyncStatement` (status / awaitResult / cancel / close); `awaitResult` // yields an `AsyncResultHandle` whose `fetchNextBatch` / `schema` match the // blocking `Statement`'s fetch surface, so the results pipeline consumes // either interchangeably. -export type SeaNativeAsyncStatement = NativeAsyncStatement; -export type SeaNativeAsyncResultHandle = NativeAsyncResultHandle; +export type KernelNativeAsyncStatement = NativeAsyncStatement; +export type KernelNativeAsyncResultHandle = NativeAsyncResultHandle; // Cancellable sync-execute surface: `Connection.executeStatementCancellable` // returns a `CancellableExecution` that captures a detached StatementCanceller @@ -76,7 +76,7 @@ export type SeaNativeAsyncResultHandle = NativeAsyncResultHandle; // interrupts a still-running query mid-compute. `result()` drives the blocking // execute and resolves to the same terminal `Statement` `executeStatement` // returns. -export type SeaNativeCancellableExecution = NativeCancellableExecution; +export type KernelNativeCancellableExecution = NativeCancellableExecution; /** * The full native binding surface, derived from the generated module @@ -84,7 +84,7 @@ export type SeaNativeCancellableExecution = NativeCancellableExecution; * adds or renames a free function / class, this type follows * automatically and `defaultRequire`'s cast stays correct. */ -export type SeaNativeBinding = typeof import('../../native/sea'); +export type KernelNativeBinding = typeof import('../../native/kernel'); const MIN_NODE_MAJOR = 18; @@ -105,7 +105,7 @@ function loadFailureHint(err: NodeJS.ErrnoException): string { // 404. Point at the README's supported-triple list instead. const installHint = 'Install the matching @databricks/sql-kernel-* optional dependency for your platform ' + - '(see native/sea/README.md for the supported triples; M0 ships linux-x64-gnu only).'; + '(see native/kernel/README.md for the supported triples; M0 ships linux-x64-gnu only).'; if (err.code === 'MODULE_NOT_FOUND') { return `SEA native binding not installed for platform ${platform} on Node ${process.version}. ${installHint}`; } @@ -123,13 +123,13 @@ function loadFailureHint(err: NodeJS.ErrnoException): string { } /** - * Default loader: resolves `native/sea/index.js` (the napi-rs router), + * Default loader: resolves `native/kernel/index.js` (the napi-rs router), * which selects the per-platform `.node`. `.js` is omitted so eslint's * `import/extensions` rule accepts the call. */ -function defaultRequire(): SeaNativeBinding { +function defaultRequire(): KernelNativeBinding { // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require - return require('../../native/sea') as SeaNativeBinding; + return require('../../native/kernel') as KernelNativeBinding; } /** @@ -137,7 +137,7 @@ function defaultRequire(): SeaNativeBinding { * Catches kernel-side renames at load time rather than letting them * surface as `undefined is not a function` deep in a call path. */ -function assertBindingShape(binding: SeaNativeBinding): void { +function assertBindingShape(binding: KernelNativeBinding): void { const missing: string[] = []; if (typeof binding.version !== 'function') missing.push('version'); if (typeof binding.openSession !== 'function') missing.push('openSession'); @@ -160,13 +160,13 @@ function assertBindingShape(binding: SeaNativeBinding): void { /** * Loads and caches the SEA native binding. Exposed as a class with an - * injectable `load` seam so consumers (e.g. `SeaBackend`) can be unit + * injectable `load` seam so consumers (e.g. `KernelBackend`) can be unit * tested with a stub binding instead of requiring a real `.node` on the * test machine. Most production code uses the process-global default - * via {@link getSeaNative} / {@link tryGetSeaNative}. + * via {@link getKernelNative} / {@link tryGetKernelNative}. */ -export class SeaNativeLoader { - private cached: SeaNativeBinding | null | undefined; +export class KernelNativeLoader { + private cached: KernelNativeBinding | null | undefined; private cachedError: Error | undefined; @@ -178,11 +178,11 @@ export class SeaNativeLoader { * runner's actual Node version (the matrix spans 14–20). */ constructor( - private readonly load: () => SeaNativeBinding = defaultRequire, + private readonly load: () => KernelNativeBinding = defaultRequire, private readonly nodeMajor: () => number = detectNodeMajor, ) {} - private tryLoad(): SeaNativeBinding | undefined { + private tryLoad(): KernelNativeBinding | undefined { const nodeMajor = this.nodeMajor(); // Fail closed: if we cannot determine the Node major (NaN) or it is // below the floor, refuse the load and fall back to Thrift. @@ -215,7 +215,7 @@ export class SeaNativeLoader { * Returns the loaded native binding. Throws a structured error if the * binding is unavailable on this platform / Node version. */ - get(): SeaNativeBinding { + get(): KernelNativeBinding { if (this.cached === undefined) { this.cached = this.tryLoad() ?? null; } @@ -230,7 +230,7 @@ export class SeaNativeLoader { * loaded. Use this for capability-detection at startup; use * {@link get} at the point where SEA is actually required. */ - tryGet(): SeaNativeBinding | undefined { + tryGet(): KernelNativeBinding | undefined { if (this.cached === undefined) { this.cached = this.tryLoad() ?? null; } @@ -239,13 +239,13 @@ export class SeaNativeLoader { } // Process-global default instance + thin convenience wrappers. -const defaultLoader = new SeaNativeLoader(); +const defaultLoader = new KernelNativeLoader(); /** * Returns the loaded native binding from the process-global loader. * Throws a structured error if the binding is unavailable. */ -export function getSeaNative(): SeaNativeBinding { +export function getKernelNative(): KernelNativeBinding { return defaultLoader.get(); } @@ -253,6 +253,6 @@ export function getSeaNative(): SeaNativeBinding { * Returns the loaded binding from the process-global loader, or * `undefined` if it could not be loaded. */ -export function tryGetSeaNative(): SeaNativeBinding | undefined { +export function tryGetKernelNative(): KernelNativeBinding | undefined { return defaultLoader.tryGet(); } diff --git a/lib/sea/SeaOperationBackend.ts b/lib/kernel/KernelOperationBackend.ts similarity index 88% rename from lib/sea/SeaOperationBackend.ts rename to lib/kernel/KernelOperationBackend.ts index 2a4c8136..eebd36e3 100644 --- a/lib/sea/SeaOperationBackend.ts +++ b/lib/kernel/KernelOperationBackend.ts @@ -17,15 +17,15 @@ * * Combines: * - **Fetch pipeline (from sea-results):** - * `napi.Statement.fetchNextBatch()` → `SeaResultsProvider` → + * `napi.Statement.fetchNextBatch()` → `KernelResultsProvider` → * `ArrowResultConverter` (Phase 1 + Phase 2; reused unchanged) → * `ResultSlicer` (chunk-size normalisation; reused unchanged). The M0 * row shape is byte-identical to the thrift path for every M0 - * datatype (parity gate exercised by `tests/e2e/sea/results-e2e.test.ts`). + * datatype (parity gate exercised by `tests/e2e/kernel/results-e2e.test.ts`). * * - **Lifecycle (from sea-operation):** `cancel()` / `close()` / * `finished()` (alias of `waitUntilReady`) delegate to the helpers - * in `SeaOperationLifecycle.ts`. The helpers handle idempotency, + * in `KernelOperationLifecycle.ts`. The helpers handle idempotency, * flag-set-before-await ordering (so cancel-mid-fetch propagates), * logging via `IClientContext`, and kernel-error mapping. * @@ -48,24 +48,24 @@ import HiveDriverError from '../errors/HiveDriverError'; import OperationStateError, { OperationStateErrorCode } from '../errors/OperationStateError'; import ArrowResultConverter from '../result/ArrowResultConverter'; import ResultSlicer from '../result/ResultSlicer'; -import SeaResultsProvider from './SeaResultsProvider'; -import { arrowSchemaToThriftSchema, decodeIpcSchema, patchIpcBytes } from './SeaArrowIpc'; -import { decodeNapiKernelError } from './SeaErrorMapping'; +import KernelResultsProvider from './KernelResultsProvider'; +import { arrowSchemaToThriftSchema, decodeIpcSchema, patchIpcBytes } from './KernelArrowIpc'; +import { decodeNapiKernelError } from './KernelErrorMapping'; import { - SeaStatement, - SeaNativeAsyncStatement, - SeaNativeAsyncResultHandle, - SeaNativeCancellableExecution, -} from './SeaNativeLoader'; + KernelStatement, + KernelNativeAsyncStatement, + KernelNativeAsyncResultHandle, + KernelNativeCancellableExecution, +} from './KernelNativeLoader'; import { - SeaStatementHandle, - SeaOperationLifecycleState, + KernelStatementHandle, + KernelOperationLifecycleState, createLifecycleState, - seaCancel, - seaClose, - seaFinished, + kernelCancel, + kernelClose, + kernelFinished, failIfNotActive, -} from './SeaOperationLifecycle'; +} from './KernelOperationLifecycle'; /** * Structural union of the lifecycle surface (cancel/close) and the @@ -74,16 +74,16 @@ import { * cancel/close half — fetch methods are accessed lazily and the * lifecycle tests never reach that path. */ -export type SeaOperationStatement = SeaStatementHandle & Partial; +export type KernelOperationStatement = KernelStatementHandle & Partial; /** * The fetch surface shared by the blocking metadata `Statement` and the async * query path's `AsyncResultHandle` (from `awaitResult()`): both expose * `fetchNextBatch()` + a synchronous `schema()`, so the results pipeline - * (`SeaResultsProvider` → `ArrowResultConverter` → `ResultSlicer`) consumes + * (`KernelResultsProvider` → `ArrowResultConverter` → `ResultSlicer`) consumes * either interchangeably. */ -type SeaFetchHandle = Pick; +type KernelFetchHandle = Pick; /** Poll cadence for the async `status()` loop — matches the Thrift backend's 100ms. */ const STATUS_POLL_INTERVAL_MS = 100; @@ -116,15 +116,15 @@ function statusStringToOperationState(state: string): OperationState { } /** - * Constructor options for `SeaOperationBackend`. Exactly one of + * Constructor options for `KernelOperationBackend`. Exactly one of * `asyncStatement` (query path — `Connection.submitStatement`) or `statement` * (metadata path — `Connection.list*` / `get*`, already terminal) must be set. */ -export interface SeaOperationBackendOptions { +export interface KernelOperationBackendOptions { /** The pending napi `AsyncStatement` from `Connection.submitStatement(...)`. */ - asyncStatement?: SeaNativeAsyncStatement; + asyncStatement?: KernelNativeAsyncStatement; /** The terminal napi `Statement` from a metadata call. */ - statement?: SeaOperationStatement; + statement?: KernelOperationStatement; /** * The pending napi `CancellableExecution` from * `Connection.executeStatementCancellable(...)` — the sync (`runAsync: false`) @@ -133,7 +133,7 @@ export interface SeaOperationBackendOptions { * interrupts a still-running `result()` mid-COMPUTE. Exactly one of * `asyncStatement`, `statement`, or `cancellableExecution` must be set. */ - cancellableExecution?: SeaNativeCancellableExecution; + cancellableExecution?: KernelNativeCancellableExecution; context: IClientContext; /** * Optional override for `id`. Defaults to the napi statement-id when the @@ -151,35 +151,35 @@ export interface SeaOperationBackendOptions { queryTimeoutSecs?: number; } -export default class SeaOperationBackend implements IOperationBackend { +export default class KernelOperationBackend implements IOperationBackend { // Async query path: pending async statement we poll to terminal. Undefined on // the metadata / sync-execute paths. - private readonly asyncStatement?: SeaNativeAsyncStatement; + private readonly asyncStatement?: KernelNativeAsyncStatement; // Sync query path (`runAsync: false`): pending cancellable execution whose // `result()` drives the blocking `execute()` to a terminal `Statement`. // Undefined on the async / metadata paths. - private readonly cancellableExecution?: SeaNativeCancellableExecution; + private readonly cancellableExecution?: KernelNativeCancellableExecution; // Metadata path: terminal statement. Also the resolved fetch handle on the // sync-execute path once `cancellableExecution.result()` settles. - private blockingStatement?: SeaOperationStatement; + private blockingStatement?: KernelOperationStatement; // The cancel/close surface — whichever handle backs this operation. Both // `AsyncStatement` and `Statement` expose `cancel()` / `close()`; the // sync-execute path uses a composite that routes `cancel()` to the // cancellable execution (mid-compute) and `close()` to the resolved statement. - private readonly lifecycleHandle: SeaStatementHandle; + private readonly lifecycleHandle: KernelStatementHandle; private readonly context: IClientContext; private readonly _id: string; - private readonly lifecycle: SeaOperationLifecycleState = createLifecycleState(); + private readonly lifecycle: KernelOperationLifecycleState = createLifecycleState(); private resultSlicer?: ResultSlicer; - private resultsProvider?: SeaResultsProvider; + private resultsProvider?: KernelResultsProvider; private metadata?: ResultMetadata; @@ -188,7 +188,7 @@ export default class SeaOperationBackend implements IOperationBackend { // Memoised fetch handle: on the async path it is `awaitResult()`'s result // (resolved once the statement is terminal); on the metadata path it is the // already-terminal statement. Drives both fetch and result-metadata. - private fetchHandlePromise?: Promise; + private fetchHandlePromise?: Promise; // Client-side query-timeout deadline in ms (the public `queryTimeout`), // undefined when unset. Enforced in the async poll loop. @@ -201,7 +201,7 @@ export default class SeaOperationBackend implements IOperationBackend { context, id, queryTimeoutSecs, - }: SeaOperationBackendOptions) { + }: KernelOperationBackendOptions) { // Exactly one of the three handle kinds must be supplied. const providedCount = (asyncStatement !== undefined ? 1 : 0) + @@ -209,7 +209,7 @@ export default class SeaOperationBackend implements IOperationBackend { (cancellableExecution !== undefined ? 1 : 0); if (providedCount !== 1) { throw new HiveDriverError( - 'SeaOperationBackend: exactly one of `asyncStatement`, `statement`, or `cancellableExecution` must be provided', + 'KernelOperationBackend: exactly one of `asyncStatement`, `statement`, or `cancellableExecution` must be provided', ); } this.asyncStatement = asyncStatement; @@ -228,7 +228,7 @@ export default class SeaOperationBackend implements IOperationBackend { cancel: () => cancellableExecution.cancel(), close: () => (this.blockingStatement ? this.blockingStatement.close() : cancellableExecution.cancel()), } - : ((asyncStatement ?? statement) as SeaStatementHandle); + : ((asyncStatement ?? statement) as KernelStatementHandle); this.context = context; this._id = id ?? asyncStatement?.statementId ?? statement?.statementId ?? cancellableExecution?.statementId ?? uuidv4(); @@ -248,7 +248,7 @@ export default class SeaOperationBackend implements IOperationBackend { } public hasResultSet(): boolean { - // M0 only routes through SeaOperationBackend for executeStatement + // M0 only routes through KernelOperationBackend for executeStatement // calls. DDL/DML without a result set is not exercised through SEA // for M0; the napi Statement still produces a schema (empty) in // that case, which the converter renders as zero rows. Reporting @@ -291,12 +291,12 @@ export default class SeaOperationBackend implements IOperationBackend { // reclaims it promptly — best-effort, so a close failure never masks the // original fetch error — then surface a typed kernel error. // - // If close() ALSO fails, seaClose has reset isClosed back to false and + // If close() ALSO fails, kernelClose has reset isClosed back to false and // the kernel-side statement handle is now leaked (the stream is already // wedged, so nothing downstream forces another close). We still don't // mask the original fetch error, but log the close failure at warn so // the leak is diagnosable rather than completely invisible. - await seaClose(this.lifecycle, this.lifecycleHandle, this.context, this._id).catch((closeErr) => { + await kernelClose(this.lifecycle, this.lifecycleHandle, this.context, this._id).catch((closeErr) => { const cause = closeErr instanceof Error ? closeErr.message : String(closeErr); this.context .getLogger() @@ -330,7 +330,7 @@ export default class SeaOperationBackend implements IOperationBackend { // `getFetchHandle()` once the statement is terminal). const handle = await this.getFetchHandle(); if (!handle.schema) { - throw new HiveDriverError('SeaOperationBackend: schema() is not available on this handle'); + throw new HiveDriverError('KernelOperationBackend: schema() is not available on this handle'); } // `schema()` is a synchronous napi getter (returns `ArrowSchema`, not a // Promise) — no `await` needed. @@ -401,17 +401,17 @@ export default class SeaOperationBackend implements IOperationBackend { return this.waitUntilReadyCancellable(options); } // Metadata path: the kernel statement has already resolved, so there is - // nothing to poll. seaFinished fires the progress callback once with a + // nothing to poll. kernelFinished fires the progress callback once with a // synthesised completion tick, matching the Thrift path's final tick. - return seaFinished(this.lifecycle, options); + return kernelFinished(this.lifecycle, options); } public async cancel(): Promise { - return seaCancel(this.lifecycle, this.lifecycleHandle, this.context, this._id); + return kernelCancel(this.lifecycle, this.lifecycleHandle, this.context, this._id); } public async close(): Promise { - return seaClose(this.lifecycle, this.lifecycleHandle, this.context, this._id); + return kernelClose(this.lifecycle, this.lifecycleHandle, this.context, this._id); } // --------------------------------------------------------------------------- @@ -575,7 +575,7 @@ export default class SeaOperationBackend implements IOperationBackend { * original error; warn-logs a close failure so the leak is diagnosable. */ private async bestEffortClose(): Promise { - await seaClose(this.lifecycle, this.lifecycleHandle, this.context, this._id).catch((closeErr) => { + await kernelClose(this.lifecycle, this.lifecycleHandle, this.context, this._id).catch((closeErr) => { const cause = closeErr instanceof Error ? closeErr.message : String(closeErr); this.context .getLogger() @@ -591,12 +591,12 @@ export default class SeaOperationBackend implements IOperationBackend { * Resolve (and memoise) the fetch handle: `awaitResult()`'s `AsyncResultHandle` * on the query path, or the already-terminal `Statement` on the metadata path. */ - private getFetchHandle(): Promise { + private getFetchHandle(): Promise { if (!this.fetchHandlePromise) { if (this.asyncStatement) { this.fetchHandlePromise = this.asyncStatement.awaitResult().catch((err) => { throw decodeNapiKernelError(err); - }) as Promise; + }) as Promise; } else if (this.cancellableExecution) { // Sync (`runAsync: false`) path: drive the blocking `result()` to the // terminal `Statement`. Store it on `blockingStatement` so `close()` can @@ -604,7 +604,7 @@ export default class SeaOperationBackend implements IOperationBackend { this.fetchHandlePromise = this.cancellableExecution .result() .then((stmt) => { - this.blockingStatement = stmt as unknown as SeaOperationStatement; + this.blockingStatement = stmt as unknown as KernelOperationStatement; // Log the now-known server statement id (NOT surfaced via `id`, // which must stay stable for telemetry correlation) so a sync op is // correlatable to server/kernel logs by its client operation id. @@ -614,7 +614,7 @@ export default class SeaOperationBackend implements IOperationBackend { .getLogger() .log(LogLevel.debug, `SEA operation ${this._id} resolved to server statement_id ${serverId}`); } - return stmt as unknown as SeaFetchHandle; + return stmt as unknown as KernelFetchHandle; }) .catch((err) => { const mapped = decodeNapiKernelError(err); @@ -632,9 +632,11 @@ export default class SeaOperationBackend implements IOperationBackend { } else { const stmt = this.blockingStatement!; if (!stmt.fetchNextBatch) { - throw new HiveDriverError('SeaOperationBackend: statement.fetchNextBatch() is not available on this handle'); + throw new HiveDriverError( + 'KernelOperationBackend: statement.fetchNextBatch() is not available on this handle', + ); } - this.fetchHandlePromise = Promise.resolve(stmt as unknown as SeaFetchHandle); + this.fetchHandlePromise = Promise.resolve(stmt as unknown as KernelFetchHandle); } } return this.fetchHandlePromise; @@ -646,9 +648,9 @@ export default class SeaOperationBackend implements IOperationBackend { } const metadata = await this.getResultMetadata(); const handle = await this.getFetchHandle(); - // SeaResultsProvider consumes only `fetchNextBatch`; both the async result + // KernelResultsProvider consumes only `fetchNextBatch`; both the async result // handle and the blocking statement satisfy that surface. - this.resultsProvider = new SeaResultsProvider(handle as unknown as SeaStatement); + this.resultsProvider = new KernelResultsProvider(handle as unknown as KernelStatement); const converter = new ArrowResultConverter(this.context, this.resultsProvider, metadata); this.resultSlicer = new ResultSlicer(this.context, converter); return this.resultSlicer; diff --git a/lib/sea/SeaOperationLifecycle.ts b/lib/kernel/KernelOperationLifecycle.ts similarity index 90% rename from lib/sea/SeaOperationLifecycle.ts rename to lib/kernel/KernelOperationLifecycle.ts index d8b6b2c9..450e7290 100644 --- a/lib/sea/SeaOperationLifecycle.ts +++ b/lib/kernel/KernelOperationLifecycle.ts @@ -16,10 +16,10 @@ * SEA operation lifecycle helpers (M0). * * The three methods exposed here (`cancel`, `close`, `finished`) are - * standalone functions that the `SeaOperationBackend` implementation + * standalone functions that the `KernelOperationBackend` implementation * delegates to. Keeping them in this dedicated file lets the parallel * impl-results work (which owns the fetch-* methods on - * `SeaOperationBackend`) land independently — at merge time it can + * `KernelOperationBackend`) land independently — at merge time it can * either import these helpers from here or inline them, with no * conflicts on the call sites. * @@ -47,7 +47,7 @@ import Status from '../dto/Status'; import { OperationStatus, OperationState } from '../contracts/OperationStatus'; import { LogLevel } from '../contracts/IDBSQLLogger'; import IClientContext from '../contracts/IClientContext'; -import { decodeNapiKernelError } from './SeaErrorMapping'; +import { decodeNapiKernelError } from './KernelErrorMapping'; import OperationStateError, { OperationStateErrorCode } from '../errors/OperationStateError'; /** @@ -55,24 +55,24 @@ import OperationStateError, { OperationStateErrorCode } from '../errors/Operatio * depend on. Declared structurally so unit tests can hand in a mock * without pulling the real native binding into the test process. * - * The real binding's `Statement` (see `native/sea/index.d.ts`) has + * The real binding's `Statement` (see `native/kernel/index.d.ts`) has * additional methods (`fetchNextBatch`, `schema`) which the lifecycle * helpers deliberately don't touch — those belong to the results * feature's surface. */ -export interface SeaStatementHandle { +export interface KernelStatementHandle { cancel(): Promise; close(): Promise; } /** * Internal lifecycle state shared between the operation backend and - * these helpers. `SeaOperationBackend` keeps an instance of this and + * these helpers. `KernelOperationBackend` keeps an instance of this and * passes it to each helper call. Centralising the flags here means * the helpers stay pure (no `this`) and the backend stays * straightforward. */ -export interface SeaOperationLifecycleState { +export interface KernelOperationLifecycleState { /** True once `cancel()` has succeeded — subsequent fetch* must throw. */ isCancelled: boolean; /** True once `close()` has been called (idempotent). */ @@ -83,7 +83,7 @@ export interface SeaOperationLifecycleState { * Factory for a fresh lifecycle-state record. Helps keep test setup * tidy. */ -export function createLifecycleState(): SeaOperationLifecycleState { +export function createLifecycleState(): KernelOperationLifecycleState { return { isCancelled: false, isClosed: false }; } @@ -119,9 +119,9 @@ function rethrowKernelError(err: unknown): never { * - returns a `Status.success()` on success (no rich Thrift status * payload is available from the kernel side). */ -export async function seaCancel( - state: SeaOperationLifecycleState, - statement: SeaStatementHandle, +export async function kernelCancel( + state: KernelOperationLifecycleState, + statement: KernelStatementHandle, context: IClientContext, operationId: string, ): Promise { @@ -160,9 +160,9 @@ export async function seaCancel( * - sets the closed flag _before_ awaiting so a concurrent fetch * sees the closed state as soon as the await yields. */ -export async function seaClose( - state: SeaOperationLifecycleState, - statement: SeaStatementHandle, +export async function kernelClose( + state: KernelOperationLifecycleState, + statement: KernelStatementHandle, context: IClientContext, operationId: string, ): Promise { @@ -221,8 +221,8 @@ function synthesizeFinishedStatus(): OperationStatus { * without throwing; throwing is the responsibility of subsequent * fetch calls). */ -export async function seaFinished( - state: SeaOperationLifecycleState, +export async function kernelFinished( + state: KernelOperationLifecycleState, options?: { progress?: boolean; callback?: (status: OperationStatus) => unknown; @@ -241,7 +241,7 @@ export async function seaFinished( } /** - * Pre-flight check used by fetch* methods on `SeaOperationBackend`. + * Pre-flight check used by fetch* methods on `KernelOperationBackend`. * If the operation has been cancelled or closed, throw the same * `OperationStateError` classes the facade uses. Keeping these typed lets * callers branch on `OperationStateErrorCode` consistently for Thrift and SEA. @@ -249,7 +249,7 @@ export async function seaFinished( * Exported so impl-results can call it at the top of every fetch * call without duplicating the if/throw logic. */ -export function failIfNotActive(state: SeaOperationLifecycleState): void { +export function failIfNotActive(state: KernelOperationLifecycleState): void { if (state.isCancelled) { // Use the canonical `OperationStateError(Canceled)` (message "The operation // was canceled by a client") rather than a custom string, so the message diff --git a/lib/sea/SeaPositionalParams.ts b/lib/kernel/KernelPositionalParams.ts similarity index 93% rename from lib/sea/SeaPositionalParams.ts rename to lib/kernel/KernelPositionalParams.ts index 0089d018..ef5ca25f 100644 --- a/lib/sea/SeaPositionalParams.ts +++ b/lib/kernel/KernelPositionalParams.ts @@ -14,8 +14,8 @@ import { DBSQLParameter, DBSQLParameterValue } from '../DBSQLParameter'; import ParameterError from '../errors/ParameterError'; -import { SeaNativeTypedValueInput, SeaNativeNamedTypedValueInput } from './SeaNativeLoader'; -import assertBindableValue from './SeaInputValidation'; +import { KernelNativeTypedValueInput, KernelNativeNamedTypedValueInput } from './KernelNativeLoader'; +import assertBindableValue from './KernelInputValidation'; /** * Derive `(precision,scale)` from a decimal value string for the SEA @@ -66,7 +66,7 @@ function decimalPrecisionScale(v: string): string { * - INTERVAL * → `INTERVAL` (the codec's single interval type name) * - a missing value ⇒ SQL NULL (`parse_typed_value` maps `value: None` to NULL). */ -function toTypedValueInput(value: DBSQLParameter | DBSQLParameterValue): SeaNativeTypedValueInput { +function toTypedValueInput(value: DBSQLParameter | DBSQLParameterValue): KernelNativeTypedValueInput { const param = value instanceof DBSQLParameter ? value : new DBSQLParameter({ value }); const spark = param.toSparkParameter(); const stringValue = spark.value?.stringValue ?? undefined; @@ -93,9 +93,9 @@ function toTypedValueInput(value: DBSQLParameter | DBSQLParameterValue): SeaNati * when none were supplied, so the caller can keep the minimal no-options * call shape. */ -export function buildSeaPositionalParams( +export function buildKernelPositionalParams( ordinalParameters?: Array, -): Array | undefined { +): Array | undefined { if (ordinalParameters === undefined || ordinalParameters.length === 0) { return undefined; } @@ -111,9 +111,9 @@ export function buildSeaPositionalParams( * same `toTypedValueInput` mapping (DECIMAL → DECIMAL(p,s), NULL → VOID, …), * then carries its name. Returns `undefined` when none were supplied. */ -export function buildSeaNamedParams( +export function buildKernelNamedParams( namedParameters?: Record, -): Array | undefined { +): Array | undefined { if (namedParameters === undefined || Object.keys(namedParameters).length === 0) { return undefined; } diff --git a/lib/sea/SeaResultsProvider.ts b/lib/kernel/KernelResultsProvider.ts similarity index 92% rename from lib/sea/SeaResultsProvider.ts rename to lib/kernel/KernelResultsProvider.ts index ce34641c..7262be9b 100644 --- a/lib/sea/SeaResultsProvider.ts +++ b/lib/kernel/KernelResultsProvider.ts @@ -14,7 +14,7 @@ import IResultsProvider, { ResultsProviderFetchNextOptions } from '../result/IResultsProvider'; import { ArrowBatch } from '../result/utils'; -import { countRowsInIpc, patchIpcBytes } from './SeaArrowIpc'; +import { countRowsInIpc, patchIpcBytes } from './KernelArrowIpc'; /** * The minimal slice of the napi-binding `Statement` class that we @@ -22,7 +22,7 @@ import { countRowsInIpc, patchIpcBytes } from './SeaArrowIpc'; * d.ts) so the loader layer's loose `unknown` typing doesn't force * unsafe casts at every call site, and so unit tests can pass a stub. */ -export interface SeaFetchHandle { +export interface KernelFetchHandle { fetchNextBatch(): Promise<{ ipcBytes: Buffer } | null>; } @@ -45,10 +45,10 @@ export interface SeaFetchHandle { * sum of `RecordBatch.numRows` across messages in the stream) because * the converter consumes that as an explicit field rather than * deriving it from the batch contents. See the comment in - * `SeaArrowIpc.ts:decodeIpcBatch` for the cost rationale. + * `KernelArrowIpc.ts:decodeIpcBatch` for the cost rationale. */ -export default class SeaResultsProvider implements IResultsProvider { - private readonly statement: SeaFetchHandle; +export default class KernelResultsProvider implements IResultsProvider { + private readonly statement: KernelFetchHandle; // Prefetched next batch so `hasMore()` can be answered without an // extra round-trip. Set by `prime()` (lazy) and by `fetchNext`. @@ -57,7 +57,7 @@ export default class SeaResultsProvider implements IResultsProvider // Set once the kernel returns `null` from `fetchNextBatch()`. private exhausted = false; - constructor(statement: SeaFetchHandle) { + constructor(statement: KernelFetchHandle) { this.statement = statement; } @@ -104,7 +104,7 @@ export default class SeaResultsProvider implements IResultsProvider // apache-arrow@13 (which predates Duration support) can decode the // stream. The downstream `RecordBatchReader.from` inside // `ArrowResultConverter` sees the same patched buffer. See - // `SeaArrowIpcDurationFix.ts`. + // `KernelArrowIpcDurationFix.ts`. const ipcBytes = patchIpcBytes(next.ipcBytes); // Row count only — `countRowsInIpc` reads the RecordBatch metadata // headers without materializing vectors (the converter re-decodes diff --git a/lib/sea/SeaServerInfo.ts b/lib/kernel/KernelServerInfo.ts similarity index 87% rename from lib/sea/SeaServerInfo.ts rename to lib/kernel/KernelServerInfo.ts index 955e006a..abae5b31 100644 --- a/lib/sea/SeaServerInfo.ts +++ b/lib/kernel/KernelServerInfo.ts @@ -41,10 +41,10 @@ import { TGetInfoType, TGetInfoValue } from '../../thrift/TCLIService_types'; */ /** Canonical DBMS product name — identical to the Thrift server's value. */ -export const SEA_DBMS_NAME = 'Spark SQL'; +export const KERNEL_DBMS_NAME = 'Spark SQL'; /** Server-name answer — identical to the Thrift server's value. */ -export const SEA_SERVER_NAME = 'Spark SQL'; +export const KERNEL_SERVER_NAME = 'Spark SQL'; /** * DBMS version string. Mirrors the constant the Databricks Thrift server @@ -52,7 +52,7 @@ export const SEA_SERVER_NAME = 'Spark SQL'; * the DBR release). Kept in lock-step with Thrift for parity; if the server * ever changes it the comparator's GET_INFO suite flags the drift. */ -export const SEA_DBMS_VERSION = '3.1.1'; +export const KERNEL_DBMS_VERSION = '3.1.1'; /** * Synthesize the `TGetInfoValue` for a `getInfo` request on the SEA path. @@ -60,14 +60,14 @@ export const SEA_DBMS_VERSION = '3.1.1'; * answer — the caller surfaces that as an error, matching Thrift's * reject-unsupported-info-type behaviour. */ -export function seaServerInfoValue(infoType: number): TGetInfoValue | undefined { +export function kernelServerInfoValue(infoType: number): TGetInfoValue | undefined { switch (infoType) { case TGetInfoType.CLI_SERVER_NAME: - return new TGetInfoValue({ stringValue: SEA_SERVER_NAME }); + return new TGetInfoValue({ stringValue: KERNEL_SERVER_NAME }); case TGetInfoType.CLI_DBMS_NAME: - return new TGetInfoValue({ stringValue: SEA_DBMS_NAME }); + return new TGetInfoValue({ stringValue: KERNEL_DBMS_NAME }); case TGetInfoType.CLI_DBMS_VER: - return new TGetInfoValue({ stringValue: SEA_DBMS_VERSION }); + return new TGetInfoValue({ stringValue: KERNEL_DBMS_VERSION }); default: return undefined; } diff --git a/lib/sea/SeaSessionBackend.ts b/lib/kernel/KernelSessionBackend.ts similarity index 91% rename from lib/sea/SeaSessionBackend.ts rename to lib/kernel/KernelSessionBackend.ts index d593f87e..bf9e87e1 100644 --- a/lib/sea/SeaSessionBackend.ts +++ b/lib/kernel/KernelSessionBackend.ts @@ -33,17 +33,17 @@ import InfoValue from '../dto/InfoValue'; import HiveDriverError from '../errors/HiveDriverError'; import ParameterError from '../errors/ParameterError'; import { LogLevel } from '../contracts/IDBSQLLogger'; -import { SeaConnection, SeaNativeExecuteOptions, SeaStatement } from './SeaNativeLoader'; -import { decodeNapiKernelError } from './SeaErrorMapping'; +import { KernelConnection, KernelNativeExecuteOptions, KernelStatement } from './KernelNativeLoader'; +import { decodeNapiKernelError } from './KernelErrorMapping'; import { numberToInt64 } from '../thrift-backend/ThriftSessionBackend'; -import SeaOperationBackend from './SeaOperationBackend'; -import { buildSeaPositionalParams, buildSeaNamedParams } from './SeaPositionalParams'; -import { seaServerInfoValue } from './SeaServerInfo'; +import KernelOperationBackend from './KernelOperationBackend'; +import { buildKernelPositionalParams, buildKernelNamedParams } from './KernelPositionalParams'; +import { kernelServerInfoValue } from './KernelServerInfo'; import { serializeQueryTags } from '../utils'; -export interface SeaSessionBackendOptions { +export interface KernelSessionBackendOptions { /** The opaque napi `Connection` handle returned by `openSession`. */ - connection: SeaConnection; + connection: KernelConnection; context: IClientContext; /** Optional override for `id`. Defaults to a fresh UUIDv4. */ id?: string; @@ -57,7 +57,7 @@ export interface SeaSessionBackendOptions { * `getColumns`, `getFunctions`, `getTableTypes`, `getTypeInfo`, * `getPrimaryKeys`, `getCrossReference`) — each forwards to the kernel's napi * metadata calls (see `runMetadata`). The Thrift backend remains the default; - * callers opt into the kernel path via `ConnectionOptions.useSEA`. + * callers opt into the kernel path via `ConnectionOptions.useKernel`. * * **Session config flow:** catalog / schema / sessionConf are applied * once at session creation (kernel `Session::builder().defaults()` + @@ -67,8 +67,8 @@ export interface SeaSessionBackendOptions { * needed — that pattern was removed when the napi binding moved these * onto `openSession` to match pyo3. */ -export default class SeaSessionBackend implements ISessionBackend { - private readonly connection: SeaConnection; +export default class KernelSessionBackend implements ISessionBackend { + private readonly connection: KernelConnection; private readonly context: IClientContext; @@ -76,7 +76,7 @@ export default class SeaSessionBackend implements ISessionBackend { private closed = false; - constructor({ connection, context, id }: SeaSessionBackendOptions) { + constructor({ connection, context, id }: KernelSessionBackendOptions) { this.connection = connection; this.context = context; this._id = id ?? uuidv4(); @@ -99,11 +99,11 @@ export default class SeaSessionBackend implements ISessionBackend { * synthesize (`"Spark SQL"` / `"Spark SQL"` / `"3.1.1"`, re-verified live). * So rejecting an unsupported type matches Thrift's effective behaviour — we * just surface a clearer, typed error than the server's opaque one. See - * {@link seaServerInfoValue}. + * {@link kernelServerInfoValue}. */ public async getInfo(infoType: number): Promise { this.failIfClosed(); - const value = seaServerInfoValue(infoType); + const value = kernelServerInfoValue(infoType); if (value === undefined) { throw new HiveDriverError( `SEA getInfo: unsupported TGetInfoType ${infoType}. Only the info types the Databricks ` + @@ -195,7 +195,7 @@ export default class SeaSessionBackend implements ISessionBackend { } catch (err) { throw this.logAndMapError('executeStatement', err); } - return new SeaOperationBackend({ + return new KernelOperationBackend({ cancellableExecution: cancellableExecution!, context: this.context, // The kernel honours `queryTimeoutSecs` on the sync `execute` path, so @@ -221,7 +221,7 @@ export default class SeaSessionBackend implements ISessionBackend { // loop: the kernel ignores `queryTimeoutSecs` on the async submit path // (`submitStatement` always sends `wait_timeout=0s`), so we do NOT forward // it to the napi options — passing it there would be a silent no-op. - return new SeaOperationBackend({ + return new KernelOperationBackend({ asyncStatement: asyncStatement!, context: this.context, // `queryTimeout` is typed `number | bigint | Int64`; `numberToInt64(...).toNumber()` @@ -238,18 +238,18 @@ export default class SeaSessionBackend implements ISessionBackend { private buildExecuteOptions( options: ExecuteStatementOptions, queryTimeoutSecs?: number, - ): SeaNativeExecuteOptions | undefined { + ): KernelNativeExecuteOptions | undefined { // Positional (`?`) and named (`:name`) parameters are mutually exclusive — // the kernel binds one placeholder style per statement. Use the SAME error // type and message as the Thrift backend (`ThriftSessionBackend`) so a // caller catching `ParameterError` behaves identically across backends. - const positionalParams = buildSeaPositionalParams(options.ordinalParameters); - const namedParams = buildSeaNamedParams(options.namedParameters); + const positionalParams = buildKernelPositionalParams(options.ordinalParameters); + const namedParams = buildKernelNamedParams(options.namedParameters); if (positionalParams !== undefined && namedParams !== undefined) { throw new ParameterError('Driver does not support both ordinal and named parameters.'); } - const execOptions: SeaNativeExecuteOptions = {}; + const execOptions: KernelNativeExecuteOptions = {}; if (positionalParams !== undefined) { execOptions.positionalParams = positionalParams; } @@ -288,8 +288,8 @@ export default class SeaSessionBackend implements ISessionBackend { } /** Wrap a napi metadata `Statement` (already terminal) as an operation backend. */ - private wrapStatement(nativeStatement: SeaStatement): IOperationBackend { - return new SeaOperationBackend({ + private wrapStatement(nativeStatement: KernelStatement): IOperationBackend { + return new KernelOperationBackend({ statement: nativeStatement, context: this.context, id: nativeStatement.statementId, @@ -382,8 +382,8 @@ export default class SeaSessionBackend implements ISessionBackend { } /** Run a napi metadata call, mapping kernel errors and wrapping the result handle. */ - private async runMetadata(call: () => Promise): Promise { - let nativeStatement: SeaStatement; + private async runMetadata(call: () => Promise): Promise { + let nativeStatement: KernelStatement; try { nativeStatement = await call(); } catch (err) { @@ -395,7 +395,7 @@ export default class SeaSessionBackend implements ISessionBackend { /** * Map a napi/kernel error to a typed driver error and emit a debug breadcrumb * first, matching the rest of the SEA backend's logging convention - * (`SeaOperationLifecycle` / `SeaOperationBackend`). Metadata and bound-param + * (`KernelOperationLifecycle` / `KernelOperationBackend`). Metadata and bound-param * execute failures otherwise threw with no on-call signal. */ private logAndMapError(op: string, err: unknown): Error { @@ -419,7 +419,7 @@ export default class SeaSessionBackend implements ISessionBackend { private failIfClosed(): void { if (this.closed) { - throw new HiveDriverError('SeaSessionBackend: session is closed'); + throw new HiveDriverError('KernelSessionBackend: session is closed'); } } } diff --git a/lib/result/ArrowResultConverter.ts b/lib/result/ArrowResultConverter.ts index 3902ac25..5b3f3a98 100644 --- a/lib/result/ArrowResultConverter.ts +++ b/lib/result/ArrowResultConverter.ts @@ -29,16 +29,16 @@ type ArrowSchemaField = Field>; /** * Metadata key carrying the original Arrow `Duration` time unit on fields * rewritten to `Int64` by the SEA IPC pre-processor - * (`lib/sea/SeaArrowIpcDurationFix.ts`). Re-declared here (rather than + * (`lib/kernel/KernelArrowIpcDurationFix.ts`). Re-declared here (rather than * imported) to keep this generic `lib/result` converter free of a - * compile-time dependency on `lib/sea`. + * compile-time dependency on `lib/kernel`. * * **SEA-gated by construction — NOT shared with Thrift.** This key (and the * `DataType.isInterval` / duration branches below) only ever execute on the * SEA path. The Thrift backend sets `intervalTypesAsArrow: false` and maps * both INTERVAL `TTypeId`s to `ArrowString` (`lib/result/utils.ts`), so the * server pre-formats intervals to strings and this logic is never reached. - * `export`ed so `SeaIntervalParity.test` can pin it equal to the SEA-side + * `export`ed so `KernelIntervalParity.test` can pin it equal to the SEA-side * declaration and catch a rename/typo that would silently no-op the consumer. */ export const DURATION_UNIT_METADATA_KEY = 'databricks.arrow.duration_unit'; @@ -101,7 +101,7 @@ function formatYearMonth(years: number, months: number): string { * millis/seconds depending on `unit`) * @param unit one of `SECOND` / `MILLISECOND` / `MICROSECOND` / * `NANOSECOND` (the original Arrow time unit, captured - * by `SeaArrowIpcDurationFix.ts`) + * by `KernelArrowIpcDurationFix.ts`) */ function formatDurationToIntervalDayTime(value: bigint | number, unit: string): string { const bi = typeof value === 'bigint' ? value : BigInt(value); @@ -119,7 +119,7 @@ function formatDurationToIntervalDayTime(value: bigint | number, unit: string): * * Throws on any other unit rather than silently treating it as * NANOSECOND. The four units above are exactly what - * `SeaArrowIpcDurationFix` enumerates when it stamps the + * `KernelArrowIpcDurationFix` enumerates when it stamps the * `databricks.arrow.duration_unit` metadata, so an unrecognized unit * here means the two sides have drifted — fail loud (matching * `formatArrowInterval`'s stance) instead of emitting a confidently @@ -360,7 +360,7 @@ export default class ArrowResultConverter implements IResultsProvider // INTERVAL — Spark/Databricks SEA emits two flavours: native Arrow // `Interval[YearMonth]` / `Interval[DayTime]` (handled here) and // `Duration` (transparently rewritten to `Int64` upstream by - // `SeaArrowIpcDurationFix.ts`; handled in the bigint/Int64 branch + // `KernelArrowIpcDurationFix.ts`; handled in the bigint/Int64 branch // below). In every case we coerce to the canonical thrift string // form so the SEA path is byte-identical with the thrift path: // YEAR-MONTH → `"Y-M"` diff --git a/lib/thrift-backend/ThriftSessionBackend.ts b/lib/thrift-backend/ThriftSessionBackend.ts index d8db377e..98a2e355 100644 --- a/lib/thrift-backend/ThriftSessionBackend.ts +++ b/lib/thrift-backend/ThriftSessionBackend.ts @@ -178,9 +178,9 @@ export default class ThriftSessionBackend implements ISessionBackend { .getLogger() .log( LogLevel.warn, - 'ThriftSessionBackend.executeStatement: rowLimit / statementConf are kernel-backend (useSEA) ' + + 'ThriftSessionBackend.executeStatement: rowLimit / statementConf are kernel-backend (useKernel) ' + 'options with no Thrift wire equivalent — they are IGNORED on the Thrift path (e.g. rowLimit ' + - 'will not cap the result set). Use the kernel backend (useSEA) to honour them.', + 'will not cap the result set). Use the kernel backend (useKernel) to honour them.', ); } diff --git a/native/sea/README.md b/native/kernel/README.md similarity index 93% rename from native/sea/README.md rename to native/kernel/README.md index c5b57b05..d29f1618 100644 --- a/native/sea/README.md +++ b/native/kernel/README.md @@ -1,4 +1,4 @@ -# `native/sea/` — consumer-side directory for the Rust napi binding +# `native/kernel/` — consumer-side directory for the Rust napi binding **The Rust binding source lives in the kernel repo** at `databricks-sql-kernel/napi/`. Building it requires a local checkout @@ -27,7 +27,7 @@ and reintroduce the same clash. Standalone-workspace is the fix. ## What lives in this directory -- `index.d.ts` — TypeScript declarations consumed by `lib/sea/`. +- `index.d.ts` — TypeScript declarations consumed by `lib/kernel/`. Generated by napi-rs from the Rust source; checked in as the consumer-facing type contract. - `index.js` — napi-rs's per-platform router shim. Gitignored; @@ -57,7 +57,7 @@ nodejs repo. At release time the kernel's CI publishes `@databricks/sql-kernel-` npm packages — one per supported -platform — each containing a single `.node` binary. `native/sea/index.js` +platform — each containing a single `.node` binary. `native/kernel/index.js` (the napi-rs router) `require()`s the package matching the consumer's `process.platform` / `process.arch` at load time. @@ -77,7 +77,7 @@ M0 targets a **single** triple: **`linux-x64-gnu`** (package `@databricks/sql-kernel-linux-x64-gnu`, once published). On every other platform (macOS, Windows, linux-arm64, linux-x64-musl -/ Alpine, …) the SEA binding is simply absent: `SeaNativeLoader` +/ Alpine, …) the SEA binding is simply absent: `KernelNativeLoader` returns `undefined` from `tryGet()` / throws a structured `MODULE_NOT_FOUND` hint from `get()`, and the driver continues to use the Thrift backend exclusively. This is expected, not a regression — diff --git a/native/sea/index.d.ts b/native/kernel/index.d.ts similarity index 100% rename from native/sea/index.d.ts rename to native/kernel/index.d.ts diff --git a/native/sea/index.js b/native/kernel/index.js similarity index 100% rename from native/sea/index.js rename to native/kernel/index.js diff --git a/package.json b/package.json index 02f8eeca..11389894 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ "test": "nyc --report-dir=${NYC_REPORT_DIR:-coverage_unit} mocha --config tests/unit/.mocharc.js", "update-version": "node bin/update-version.js && prettier --write ./lib/version.ts", "build": "npm run update-version && tsc --project tsconfig.build.json", - "build:native": "bash -c 'cd ${DATABRICKS_SQL_KERNEL_REPO:-../../databricks-sql-kernel}/napi && npx --yes @napi-rs/cli@2.18.4 build --platform ${BUILD_PROFILE:---release} && cp index.* $OLDPWD/native/sea/'", - "prepack": "test -f native/sea/index.js || { echo 'ERROR: native/sea/index.js (napi-rs router) is missing — the published tarball would fail to load SEA. It is committed to git; run `npm run build:native` if you removed it.' >&2; exit 1; }", + "build:native": "bash -c 'cd ${DATABRICKS_SQL_KERNEL_REPO:-../../databricks-sql-kernel}/napi && npx --yes @napi-rs/cli@2.18.4 build --platform ${BUILD_PROFILE:---release} && cp index.* $OLDPWD/native/kernel/'", + "prepack": "test -f native/kernel/index.js || { echo 'ERROR: native/kernel/index.js (napi-rs router) is missing — the published tarball would fail to load SEA. It is committed to git; run `npm run build:native` if you removed it.' >&2; exit 1; }", "watch": "tsc --project tsconfig.build.json --watch", "type-check": "tsc --noEmit", "prettier": "prettier . --check", diff --git a/tests/e2e/sea/auth-m2m-e2e.test.ts b/tests/e2e/kernel/auth-m2m-e2e.test.ts similarity index 94% rename from tests/e2e/sea/auth-m2m-e2e.test.ts rename to tests/e2e/kernel/auth-m2m-e2e.test.ts index debd74a7..4fc22ba1 100644 --- a/tests/e2e/sea/auth-m2m-e2e.test.ts +++ b/tests/e2e/kernel/auth-m2m-e2e.test.ts @@ -15,14 +15,14 @@ import { expect } from 'chai'; import { DBSQLClient } from '../../../lib'; import AuthenticationError from '../../../lib/errors/AuthenticationError'; -import { isBlankOrReserved } from '../../../lib/sea/SeaAuth'; +import { isBlankOrReserved } from '../../../lib/kernel/KernelAuth'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnectionOptions'; /** * sea-auth M1 OAuth M2M end-to-end: * 1. Construct a DBSQLClient. - * 2. `connect({ useSEA: true, authType: 'databricks-oauth', oauthClientId, + * 2. `connect({ useKernel: true, authType: 'databricks-oauth', oauthClientId, * oauthClientSecret })` against pecotesting. * 3. `openSession()` — kernel runs OIDC discovery + client_credentials * exchange. Successful openSession is the proof that the kernel-side @@ -44,7 +44,7 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec * Skipped (not failed) when any of the four env vars is missing, so CI * machines without OAuth credentials don't fail-flap. */ -describe('sea-auth e2e — OAuth M2M through DBSQLClient ↔ SeaBackend ↔ napi binding', function suite() { +describe('sea-auth e2e — OAuth M2M through DBSQLClient ↔ KernelBackend ↔ napi binding', function suite() { const host = process.env.DATABRICKS_PECOTESTING_SERVER_HOSTNAME; const path = process.env.DATABRICKS_PECOTESTING_HTTP_PATH2; const oauthClientId = process.env.DATABRICKS_PECOTESTING_AAD_CLIENT_ID; @@ -76,7 +76,7 @@ describe('sea-auth e2e — OAuth M2M through DBSQLClient ↔ SeaBackend ↔ napi authType: 'databricks-oauth', oauthClientId: oauthClientId as string, oauthClientSecret: oauthClientSecret as string, - useSEA: true, + useKernel: true, } as ConnectionOptions & InternalConnectionOptions); expect(connected).to.equal(client); @@ -103,7 +103,7 @@ describe('sea-auth e2e — OAuth M2M through DBSQLClient ↔ SeaBackend ↔ napi authType: 'databricks-oauth', oauthClientId: oauthClientId as string, oauthClientSecret: 'definitely-not-the-real-secret-deadbeef', - useSEA: true, + useKernel: true, } as ConnectionOptions & InternalConnectionOptions); let caught: unknown; diff --git a/tests/e2e/sea/auth-pat-e2e.test.ts b/tests/e2e/kernel/auth-pat-e2e.test.ts similarity index 91% rename from tests/e2e/sea/auth-pat-e2e.test.ts rename to tests/e2e/kernel/auth-pat-e2e.test.ts index d061d5b2..78ab2747 100644 --- a/tests/e2e/sea/auth-pat-e2e.test.ts +++ b/tests/e2e/kernel/auth-pat-e2e.test.ts @@ -20,7 +20,7 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec /** * sea-auth M0 end-to-end: * 1. Construct a DBSQLClient. - * 2. `connect({ useSEA: true, token })` against pecotesting. + * 2. `connect({ useKernel: true, token })` against pecotesting. * 3. `openSession()` — round-trips through the napi binding. * 4. Close the session, then the client. * @@ -38,7 +38,7 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec * If any of the three required env vars is missing, the suite is skipped * so CI machines without secrets don't fail-flap. */ -describe('sea-auth e2e — PAT through DBSQLClient ↔ SeaBackend ↔ napi binding', function suite() { +describe('sea-auth e2e — PAT through DBSQLClient ↔ KernelBackend ↔ napi binding', function suite() { const host = process.env.DATABRICKS_PECOTESTING_SERVER_HOSTNAME; const path = process.env.DATABRICKS_PECOTESTING_HTTP_PATH; const token = process.env.DATABRICKS_PECOTESTING_TOKEN_PERSONAL || process.env.DATABRICKS_PECOTESTING_TOKEN; @@ -59,10 +59,10 @@ describe('sea-auth e2e — PAT through DBSQLClient ↔ SeaBackend ↔ napi bindi host: host as string, path: path as string, token: token as string, - // `useSEA` is an internal opt-in (InternalConnectionOptions), not a + // `useKernel` is an internal opt-in (InternalConnectionOptions), not a // public ConnectionOptions field — cast exactly as DBSQLClient.connect // does internally so the literal passes excess-property checking. - useSEA: true, + useKernel: true, } as ConnectionOptions & InternalConnectionOptions); expect(connected).to.equal(client); diff --git a/tests/e2e/sea/auth-u2m-e2e.test.ts b/tests/e2e/kernel/auth-u2m-e2e.test.ts similarity index 93% rename from tests/e2e/sea/auth-u2m-e2e.test.ts rename to tests/e2e/kernel/auth-u2m-e2e.test.ts index 923d5f0e..dd0a4bfc 100644 --- a/tests/e2e/sea/auth-u2m-e2e.test.ts +++ b/tests/e2e/kernel/auth-u2m-e2e.test.ts @@ -32,7 +32,7 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec * suite carries a slot for whoever lands the harness work. * * The intended assertion sequence (mirrors `auth-m2m-e2e.test.ts`): - * 1. `client.connect({ useSEA: true, authType: 'databricks-oauth' })` + * 1. `client.connect({ useKernel: true, authType: 'databricks-oauth' })` * — NO `oauthClientSecret` → kernel picks the U2M flow. * 2. `openSession()` — kernel opens browser, waits for callback on * localhost:8030, exchanges the auth code, returns Bearer token, @@ -45,7 +45,7 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec * - DATABRICKS_PECOTESTING_HTTP_PATH * - (no client_id/secret — U2M uses kernel default `databricks-cli`) */ -describe('sea-auth e2e — OAuth U2M through DBSQLClient ↔ SeaBackend ↔ napi binding', function suite() { +describe('sea-auth e2e — OAuth U2M through DBSQLClient ↔ KernelBackend ↔ napi binding', function suite() { this.timeout(300_000); it.skip('[pending TBD-oauth_u2m_test_harness] interactive U2M round-trip', async () => { @@ -58,7 +58,7 @@ describe('sea-auth e2e — OAuth U2M through DBSQLClient ↔ SeaBackend ↔ napi host, path, authType: 'databricks-oauth', - useSEA: true, + useKernel: true, } as ConnectionOptions & InternalConnectionOptions); expect(connected).to.equal(client); diff --git a/tests/e2e/sea/e2e-smoke.test.ts b/tests/e2e/kernel/e2e-smoke.test.ts similarity index 89% rename from tests/e2e/sea/e2e-smoke.test.ts rename to tests/e2e/kernel/e2e-smoke.test.ts index e96efe34..4345fc14 100644 --- a/tests/e2e/sea/e2e-smoke.test.ts +++ b/tests/e2e/kernel/e2e-smoke.test.ts @@ -14,7 +14,7 @@ import { expect } from 'chai'; import { tableFromIPC } from 'apache-arrow'; -import { tryGetSeaNative, SeaConnection, SeaStatement } from '../../../lib/sea/SeaNativeLoader'; +import { tryGetKernelNative, KernelConnection, KernelStatement } from '../../../lib/kernel/KernelNativeLoader'; import config from '../utils/config'; // End-to-end smoke test against a live warehouse: @@ -32,7 +32,7 @@ describe('SEA native binding — end-to-end smoke', function smoke() { // Live-warehouse tests can take >2s through warm-up. this.timeout(60_000); - const binding = tryGetSeaNative(); + const binding = tryGetKernelNative(); if (binding === undefined) { // Optional dependency absent on this platform — never reach the live path. it.skip('SEA native binding not available on this platform'); @@ -42,10 +42,10 @@ describe('SEA native binding — end-to-end smoke', function smoke() { const { host: hostName, path: httpPath, token } = config; it('opens a session, runs SELECT 1, decodes the IPC payload to 1', async () => { - const connection: SeaConnection = await binding.openSession({ hostName, httpPath, token }); + const connection: KernelConnection = await binding.openSession({ hostName, httpPath, token }); expect(connection).to.be.an('object'); - let statement: SeaStatement | null = null; + let statement: KernelStatement | null = null; try { statement = await connection.executeStatement('SELECT 1'); expect(statement).to.be.an('object'); @@ -76,7 +76,7 @@ describe('SEA native binding — end-to-end smoke', function smoke() { }); it('returns a schema IPC payload before any batch is fetched', async () => { - const connection: SeaConnection = await binding.openSession({ hostName, httpPath, token }); + const connection: KernelConnection = await binding.openSession({ hostName, httpPath, token }); try { const statement = await connection.executeStatement('SELECT 1'); try { diff --git a/tests/e2e/sea/execution-e2e.test.ts b/tests/e2e/kernel/execution-e2e.test.ts similarity index 91% rename from tests/e2e/sea/execution-e2e.test.ts rename to tests/e2e/kernel/execution-e2e.test.ts index 28dd1035..ef48cd5d 100644 --- a/tests/e2e/sea/execution-e2e.test.ts +++ b/tests/e2e/kernel/execution-e2e.test.ts @@ -20,14 +20,14 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec /** * sea-execution end-to-end test. * - * Walks the full `DBSQLClient` → `SeaBackend` → napi binding → kernel + * Walks the full `DBSQLClient` → `KernelBackend` → napi binding → kernel * pipeline against a live warehouse over PAT: * - * 1. `connect({ useSEA: true })` selects the SEA backend. + * 1. `connect({ useKernel: true })` selects the SEA backend. * 2. `openSession({ initialCatalog: 'main' })` opens a kernel session * and threads `initialCatalog` through to the napi `ExecuteOptions`. * 3. `executeStatement('SELECT 1')` returns an `IOperation` backed by - * `SeaOperationBackend` (wraps a napi `Statement`). + * `KernelOperationBackend` (wraps a napi `Statement`). * 4. `operation.id` is observable (via `IOperation.id` on the public * surface). * 5. `operation.cancel()` and `operation.close()` succeed without @@ -39,7 +39,7 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec * provisioned secrets don't flap. * * **Proxy-validation note (per execution plan §17.4):** M0 verifies - * "no thrift fallback" indirectly — by selecting `useSEA: true` and + * "no thrift fallback" indirectly — by selecting `useKernel: true` and * exercising the executeStatement path. A proxy that captures * `executeStatement` + `GetStatement` wire counts lands in the * sea-integration round; for now we assert that the SEA pipeline @@ -67,7 +67,7 @@ describe('SEA execution end-to-end', function e2eSuite() { host: hostName as string, path: httpPath as string, token: token as string, - useSEA: true, + useKernel: true, } as ConnectionOptions & InternalConnectionOptions); const session = await client.openSession({ @@ -79,7 +79,7 @@ describe('SEA execution end-to-end', function e2eSuite() { const operation = await session.executeStatement('SELECT 1', {}); expect(operation).to.be.an('object'); // `IOperation.id` is the public-API observable identity for the - // returned operation. SeaOperationBackend generates a UUIDv4 for + // returned operation. KernelOperationBackend generates a UUIDv4 for // M0 until the napi binding surfaces the server statement id. expect(operation.id).to.be.a('string').and.have.length.greaterThan(0); @@ -100,7 +100,7 @@ describe('SEA execution end-to-end', function e2eSuite() { host: hostName as string, path: httpPath as string, token: token as string, - useSEA: true, + useKernel: true, } as ConnectionOptions & InternalConnectionOptions); // Sanity-check that supplying session-level Spark conf does not diff --git a/tests/e2e/sea/interval-duration-e2e.test.ts b/tests/e2e/kernel/interval-duration-e2e.test.ts similarity index 93% rename from tests/e2e/sea/interval-duration-e2e.test.ts rename to tests/e2e/kernel/interval-duration-e2e.test.ts index 877b7b9a..0c48227c 100644 --- a/tests/e2e/sea/interval-duration-e2e.test.ts +++ b/tests/e2e/kernel/interval-duration-e2e.test.ts @@ -19,7 +19,7 @@ import { DBSQLClient } from '../../../lib'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnectionOptions'; -// Exercises the SeaArrowIpcDurationFix rewriter against a REAL kernel-produced +// Exercises the KernelArrowIpcDurationFix rewriter against a REAL kernel-produced // Arrow Duration buffer (Spark INTERVAL DAY-TIME surfaces as Arrow Duration, // type id 18, which apache-arrow@13 can't decode). Without the rewriter this // query throws `Unrecognized type: "Duration" (18)`, so a passing fetch proves @@ -28,7 +28,7 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec // On THIS layer (SEA execution + results, PR 2/3) the converter does not yet // consume the `duration_unit` marker, so the value surfaces as a raw Int64 — // asserted here. The Phase-1 formatter that turns it into the thrift string -// "1 02:03:04.000000000" lands in PR 3/3 (#411), where SeaIntervalParity covers +// "1 02:03:04.000000000" lands in PR 3/3 (#411), where KernelIntervalParity covers // the formatted output. Requires the pecotesting secrets; skips otherwise. const DURATION_QUERY = "SELECT INTERVAL '1 02:03:04' DAY TO SECOND AS dt"; @@ -47,13 +47,13 @@ function readSecrets(): PecoSecrets | null { return { host, path, token }; } -async function fetchOneRow(useSEA: boolean, secrets: PecoSecrets): Promise> { +async function fetchOneRow(useKernel: boolean, secrets: PecoSecrets): Promise> { const client = new DBSQLClient(); await client.connect({ host: secrets.host, path: secrets.path, token: secrets.token, - useSEA, + useKernel, } as ConnectionOptions & InternalConnectionOptions); try { const session = await client.openSession(); diff --git a/tests/e2e/sea/interval-edge-e2e.test.ts b/tests/e2e/kernel/interval-edge-e2e.test.ts similarity index 85% rename from tests/e2e/sea/interval-edge-e2e.test.ts rename to tests/e2e/kernel/interval-edge-e2e.test.ts index 00ab0ae1..7c4ee3b3 100644 --- a/tests/e2e/sea/interval-edge-e2e.test.ts +++ b/tests/e2e/kernel/interval-edge-e2e.test.ts @@ -18,7 +18,7 @@ import { expect } from 'chai'; import { DBSQLClient } from '../../../lib'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnectionOptions'; -import { getSeaNative } from '../../../lib/sea/SeaNativeLoader'; +import { getKernelNative } from '../../../lib/kernel/KernelNativeLoader'; // INTERVAL edge cases the unit suite can't easily build (null, multi-row). // Verified byte-identical to the Thrift backend against a live warehouse. @@ -38,9 +38,9 @@ function readSecrets(): PecoSecrets | null { return { host, path, token }; } -async function seaValues(sql: string, secrets: PecoSecrets): Promise { +async function kernelValues(sql: string, secrets: PecoSecrets): Promise { const client = new DBSQLClient(); - await client.connect({ ...secrets, useSEA: true } as ConnectionOptions & InternalConnectionOptions); + await client.connect({ ...secrets, useKernel: true } as ConnectionOptions & InternalConnectionOptions); try { const session = await client.openSession(); const op = await session.executeStatement(sql); @@ -67,19 +67,19 @@ describe('SEA INTERVAL edge cases end-to-end', function suite() { } // Skip (not error) when the native binding isn't built/installed. try { - getSeaNative(); + getKernelNative(); } catch { self.skip(); } }); it('NULL INTERVAL DAY-TIME → null', async () => { - const values = await seaValues('SELECT CAST(NULL AS INTERVAL DAY TO SECOND) AS v', secrets as PecoSecrets); + const values = await kernelValues('SELECT CAST(NULL AS INTERVAL DAY TO SECOND) AS v', secrets as PecoSecrets); expect(values).to.deep.equal([null]); }); it('multi-row INTERVAL DAY-TIME batch formats every row', async () => { - const values = await seaValues( + const values = await kernelValues( "SELECT * FROM VALUES (INTERVAL '1' DAY), (INTERVAL '2 03:00:00' DAY TO SECOND), (INTERVAL '0' DAY) AS t(v)", secrets as PecoSecrets, ); diff --git a/tests/e2e/sea/operation-lifecycle-e2e.test.ts b/tests/e2e/kernel/operation-lifecycle-e2e.test.ts similarity index 88% rename from tests/e2e/sea/operation-lifecycle-e2e.test.ts rename to tests/e2e/kernel/operation-lifecycle-e2e.test.ts index 5d529aca..e1a7d201 100644 --- a/tests/e2e/sea/operation-lifecycle-e2e.test.ts +++ b/tests/e2e/kernel/operation-lifecycle-e2e.test.ts @@ -14,34 +14,34 @@ /** * End-to-end tests for the SEA operation lifecycle (cancel / close / - * finished) wired through `SeaOperationBackend`. + * finished) wired through `KernelOperationBackend`. * * The impl-execution feature has not yet wired - * `DBSQLClient.connect({ useSEA: true })` to dispatch into - * `SeaBackend`, so this test drives the lifecycle by: + * `DBSQLClient.connect({ useKernel: true })` to dispatch into + * `KernelBackend`, so this test drives the lifecycle by: * 1. Calling the napi `openSession(...)` free function directly to * get a kernel `Connection`. * 2. Calling `connection.executeStatement(...)` to get a napi * `Statement` handle. - * 3. Wrapping that handle in a `SeaOperationBackend` and exercising + * 3. Wrapping that handle in a `KernelOperationBackend` and exercising * its `cancel()` / `close()` / `waitUntilReady()` methods. * - * This mirrors how the eventual `SeaSessionBackend.executeStatement` + * This mirrors how the eventual `KernelSessionBackend.executeStatement` * call path will assemble the operation — we just inline the kernel * call here since the session backend is being built in parallel. * * Path note: the original task spec referenced - * `tests/integration/sea/operation-lifecycle-e2e.test.ts`. The + * `tests/integration/kernel/operation-lifecycle-e2e.test.ts`. The * existing project structure uses `tests/e2e/**` (with its own - * `.mocharc.js`), so this file lives under `tests/e2e/sea/` to be + * `.mocharc.js`), so this file lives under `tests/e2e/kernel/` to be * picked up by `npm run e2e` automatically. */ import { expect } from 'chai'; import IClientContext from '../../../lib/contracts/IClientContext'; import IDBSQLLogger, { LogLevel } from '../../../lib/contracts/IDBSQLLogger'; -import { getSeaNative } from '../../../lib/sea/SeaNativeLoader'; -import SeaOperationBackend from '../../../lib/sea/SeaOperationBackend'; +import { getKernelNative } from '../../../lib/kernel/KernelNativeLoader'; +import KernelOperationBackend from '../../../lib/kernel/KernelOperationBackend'; import OperationStateError, { OperationStateErrorCode } from '../../../lib/errors/OperationStateError'; // Minimal binding type shapes (mirrors the napi `index.d.ts`). @@ -108,17 +108,17 @@ describe('SEA operation lifecycle — end-to-end', function suite() { return; } // Creds present but the native binding not built/installed (e.g. a CI - // runner without the .node) must SKIP, not error: probe getSeaNative() + // runner without the .node) must SKIP, not error: probe getKernelNative() // here so every test isn't an uncaught-throw at its first call. try { - getSeaNative(); + getKernelNative(); } catch { self.skip(); } }); it('cancel() succeeds against a live SEA statement', async () => { - const binding = getSeaNative() as unknown as NativeBinding; + const binding = getKernelNative() as unknown as NativeBinding; const connection = await binding.openSession({ hostName: hostName as string, @@ -135,7 +135,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { statement = await connection.executeStatement('SELECT * FROM range(0, 100000000)', {}); expect(statement).to.be.an('object'); - const op = new SeaOperationBackend({ + const op = new KernelOperationBackend({ statement: statement as unknown as NativeStatement, context: makeContext(), }); @@ -160,7 +160,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { }); it('cancel mid-fetch — subsequent fetchChunk throws OperationStateError', async () => { - const binding = getSeaNative() as unknown as NativeBinding; + const binding = getKernelNative() as unknown as NativeBinding; const connection = await binding.openSession({ hostName: hostName as string, @@ -172,7 +172,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { try { statement = await connection.executeStatement('SELECT * FROM range(0, 100000000)', {}); - const op = new SeaOperationBackend({ + const op = new KernelOperationBackend({ statement: statement as unknown as NativeStatement, context: makeContext(), }); @@ -203,7 +203,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { }); it('close() succeeds against a SEA statement and is idempotent', async () => { - const binding = getSeaNative() as unknown as NativeBinding; + const binding = getKernelNative() as unknown as NativeBinding; const connection = await binding.openSession({ hostName: hostName as string, @@ -214,7 +214,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { try { const statement = await connection.executeStatement('SELECT 1', {}); - const op = new SeaOperationBackend({ + const op = new KernelOperationBackend({ statement: statement as unknown as NativeStatement, context: makeContext(), }); @@ -233,7 +233,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { }); it('finished() resolves immediately and fires the progress callback', async () => { - const binding = getSeaNative() as unknown as NativeBinding; + const binding = getKernelNative() as unknown as NativeBinding; const connection = await binding.openSession({ hostName: hostName as string, @@ -245,7 +245,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { try { statement = await connection.executeStatement('SELECT 1', {}); - const op = new SeaOperationBackend({ + const op = new KernelOperationBackend({ statement: statement as unknown as NativeStatement, context: makeContext(), }); diff --git a/tests/e2e/sea/results-e2e.test.ts b/tests/e2e/kernel/results-e2e.test.ts similarity index 94% rename from tests/e2e/sea/results-e2e.test.ts rename to tests/e2e/kernel/results-e2e.test.ts index 497889c5..72bbc15b 100644 --- a/tests/e2e/sea/results-e2e.test.ts +++ b/tests/e2e/kernel/results-e2e.test.ts @@ -44,13 +44,13 @@ function readSecrets(): PecoSecrets | null { return { host, path, token }; } -async function fetchProbeRows(useSEA: boolean, secrets: PecoSecrets): Promise>> { +async function fetchProbeRows(useKernel: boolean, secrets: PecoSecrets): Promise>> { const client = new DBSQLClient(); await client.connect({ host: secrets.host, path: secrets.path, token: secrets.token, - useSEA, + useKernel, } as ConnectionOptions & InternalConnectionOptions); try { const session = await client.openSession(); @@ -121,8 +121,8 @@ describe('SEA results end-to-end (pecotesting parity gate)', function suite() { }); it('Thrift and SEA produce byte-identical rows for the probe query (parity gate)', async () => { - const seaRows = await fetchProbeRows(true, secrets as PecoSecrets); + const kernelRows = await fetchProbeRows(true, secrets as PecoSecrets); const thriftRows = await fetchProbeRows(false, secrets as PecoSecrets); - expect(seaRows.map(canonical)).to.deep.equal(thriftRows.map(canonical)); + expect(kernelRows.map(canonical)).to.deep.equal(thriftRows.map(canonical)); }); }); diff --git a/tests/unit/DBSQLClient.test.ts b/tests/unit/DBSQLClient.test.ts index 81d41f2e..ab45251b 100644 --- a/tests/unit/DBSQLClient.test.ts +++ b/tests/unit/DBSQLClient.test.ts @@ -118,19 +118,19 @@ describe('DBSQLClient.connect', () => { logSpy.restore(); }); - it('useSEA: true routes to SeaBackend and leaves `backend` unset when connect() throws', async () => { + it('useKernel: true routes to KernelBackend and leaves `backend` unset when connect() throws', async () => { const client = new DBSQLClient(); - // `useSEA` is on a non-exported InternalConnectionOptions; cast through any. - // An empty token makes the real SeaBackend reject during connect() (auth + // `useKernel` is on a non-exported InternalConnectionOptions; cast through any. + // An empty token makes the real KernelBackend reject during connect() (auth // validation); where the native binding is absent (e.g. CI, which does not // build it) construction throws even earlier. Either way connect() must // reject, so we can assert the partial-init guard leaves `backend` unset. - const seaOptions = { ...connectOptions, token: '', useSEA: true } as any; + const kernelOptions = { ...connectOptions, token: '', useKernel: true } as any; try { - await client.connect(seaOptions); - expect.fail('SeaBackend connect() should reject (empty PAT / absent native binding)'); + await client.connect(kernelOptions); + expect.fail('KernelBackend connect() should reject (empty PAT / absent native binding)'); } catch (error) { if (error instanceof AssertionError || !(error instanceof Error)) { throw error; @@ -141,7 +141,7 @@ describe('DBSQLClient.connect', () => { // The partial-init guard (L2 fix) means backend stays undefined after a // failed connect, so the next openSession surfaces "not connected" rather - // than the SeaBackend's own connect/auth error. + // than the KernelBackend's own connect/auth error. expect((client as any).backend).to.equal(undefined); try { diff --git a/tests/unit/sea/SeaArrowIpc.test.ts b/tests/unit/kernel/KernelArrowIpc.test.ts similarity index 90% rename from tests/unit/sea/SeaArrowIpc.test.ts rename to tests/unit/kernel/KernelArrowIpc.test.ts index e93fa8c1..dc7440e8 100644 --- a/tests/unit/sea/SeaArrowIpc.test.ts +++ b/tests/unit/kernel/KernelArrowIpc.test.ts @@ -14,13 +14,13 @@ import { expect } from 'chai'; import { tableFromArrays, RecordBatchStreamWriter } from 'apache-arrow'; -import { countRowsInIpc, decodeIpcSchema, patchIpcBytes } from '../../../lib/sea/SeaArrowIpc'; -import { rewriteDurationToInt64 } from '../../../lib/sea/SeaArrowIpcDurationFix'; +import { countRowsInIpc, decodeIpcSchema, patchIpcBytes } from '../../../lib/kernel/KernelArrowIpc'; +import { rewriteDurationToInt64 } from '../../../lib/kernel/KernelArrowIpcDurationFix'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; // Hermetic coverage for the SEA Arrow-IPC layer. apache-arrow@13 cannot // construct a `Duration` column, so the Duration-positive rewrite path is -// covered by the live e2e (tests/e2e/sea/interval-duration-e2e.test.ts). +// covered by the live e2e (tests/e2e/kernel/interval-duration-e2e.test.ts). // These tests pin everything reachable without a warehouse: the IPC // framing walk, the no-op / malformed-input handling, the cheap // row-count path, and the schema-decode guard. @@ -46,7 +46,7 @@ function makeSchemaOnlyStream(): Buffer { return Buffer.from(writer.toUint8Array(true)); } -describe('SeaArrowIpc.countRowsInIpc', () => { +describe('KernelArrowIpc.countRowsInIpc', () => { it('sums RecordBatch row counts across messages', () => { const ipc = makeIpcStream([ [1, 2, 3], @@ -64,7 +64,7 @@ describe('SeaArrowIpc.countRowsInIpc', () => { }); }); -describe('SeaArrowIpc.rewriteDurationToInt64 (no-Duration / malformed paths)', () => { +describe('KernelArrowIpc.rewriteDurationToInt64 (no-Duration / malformed paths)', () => { it('is a no-op for a stream with no Duration field (returns input unchanged)', () => { const ipc = makeIpcStream([[1, 2, 3]]); const out = rewriteDurationToInt64(ipc); @@ -90,7 +90,7 @@ describe('SeaArrowIpc.rewriteDurationToInt64 (no-Duration / malformed paths)', ( }); }); -describe('SeaArrowIpc.decodeIpcSchema', () => { +describe('KernelArrowIpc.decodeIpcSchema', () => { it('decodes the schema of a normal stream', () => { const schema = decodeIpcSchema(makeIpcStream([[1]])); expect(schema.fields.map((f) => f.name)).to.deep.equal(['a']); diff --git a/tests/unit/sea/SeaIntervalParity.test.ts b/tests/unit/kernel/KernelIntervalParity.test.ts similarity index 92% rename from tests/unit/sea/SeaIntervalParity.test.ts rename to tests/unit/kernel/KernelIntervalParity.test.ts index bee5a5b9..4911838d 100644 --- a/tests/unit/sea/SeaIntervalParity.test.ts +++ b/tests/unit/kernel/KernelIntervalParity.test.ts @@ -13,7 +13,7 @@ * INTERVAL YEAR-MONTH and INTERVAL DAY-TIME columns, regardless of * whether the kernel emits the value as native Arrow `Interval` or * native Arrow `Duration` (the latter is transparently rewritten to - * `Int64` by `lib/sea/SeaArrowIpcDurationFix.ts` because `apache-arrow@13` + * `Int64` by `lib/kernel/KernelArrowIpcDurationFix.ts` because `apache-arrow@13` * predates the `Duration` type id). * * Reference failure modes (round 5 testing): @@ -59,11 +59,11 @@ import { Duration as FbDuration } from 'apache-arrow/fb/duration'; // eslint-disable-next-line import/no-internal-modules import { TimeUnit as FbTimeUnit } from 'apache-arrow/fb/time-unit'; -import SeaOperationBackend from '../../../lib/sea/SeaOperationBackend'; +import KernelOperationBackend from '../../../lib/kernel/KernelOperationBackend'; import ClientContextStub from '../.stubs/ClientContextStub'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; import { DURATION_UNIT_METADATA_KEY as CONVERTER_DURATION_KEY } from '../../../lib/result/ArrowResultConverter'; -import { DURATION_UNIT_METADATA_KEY as REWRITER_DURATION_KEY } from '../../../lib/sea/SeaArrowIpcDurationFix'; +import { DURATION_UNIT_METADATA_KEY as REWRITER_DURATION_KEY } from '../../../lib/kernel/KernelArrowIpcDurationFix'; // --------------------------------------------------------------------------- // Test helpers. @@ -245,7 +245,7 @@ function buildDurationIpc( // Tests. // --------------------------------------------------------------------------- -describe('SeaOperationBackend — INTERVAL parity with thrift', () => { +describe('KernelOperationBackend — INTERVAL parity with thrift', () => { it('YEAR-MONTH via native Arrow Interval[YearMonth] → "Y-M"', async () => { // Arrow `Interval[YearMonth]` carries a single int32 total-months // value. apache-arrow surfaces it as Int32Array(2) via the @@ -260,7 +260,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const dataIpc = ipcFromColumns(schema, { iv: Int32Array.from([14]) }); const stub = new StatementStub(schemaIpc, [dataIpc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('1-2'); @@ -275,7 +275,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const dataIpc = ipcFromColumns(schema, { iv: Int32Array.from([-14]) }); const stub = new StatementStub(schemaIpc, [dataIpc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('-1-2'); @@ -288,7 +288,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.MICROSECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('1 02:03:04.000000000'); @@ -301,7 +301,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.NANOSECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('1 02:03:04.123456789'); @@ -314,7 +314,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.MILLISECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('1 02:03:04.000000000'); @@ -327,7 +327,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.SECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('1 02:03:04.000000000'); @@ -345,7 +345,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const dataIpc = ipcFromColumns(schema, { iv: [Int32Array.from([1, 1000])] }); const stub = new StatementStub(schemaIpc, [dataIpc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); let thrown: unknown; try { await backend.fetchChunk({ limit: 100 }); @@ -361,7 +361,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.MICROSECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('0 00:00:00.000000000'); @@ -374,7 +374,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.MICROSECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('-1 02:03:04.000000000'); @@ -400,7 +400,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.MICROSECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); // Round-trip the metadata to confirm we synthesise the right TTypeId. const metadata = await backend.getResultMetadata(); @@ -426,7 +426,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const dataIpc = ipcFromColumns(schema, { iv: Int32Array.from([-1]) }); const stub = new StatementStub(schemaIpc, [dataIpc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('-0-1'); @@ -434,7 +434,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { it('the duration_unit metadata key is identical in the rewriter and the converter', () => { // Both modules declare the key independently (the converter avoids a - // compile-time dependency on lib/sea). Pin them equal so a rename/typo in + // compile-time dependency on lib/kernel). Pin them equal so a rename/typo in // one doesn't silently turn the converter's duration branch into a no-op. expect(CONVERTER_DURATION_KEY).to.equal(REWRITER_DURATION_KEY); expect(CONVERTER_DURATION_KEY).to.equal('databricks.arrow.duration_unit'); diff --git a/tests/unit/sea/SeaOperationBackend.test.ts b/tests/unit/kernel/KernelOperationBackend.test.ts similarity index 95% rename from tests/unit/sea/SeaOperationBackend.test.ts rename to tests/unit/kernel/KernelOperationBackend.test.ts index ada6616d..15818f03 100644 --- a/tests/unit/sea/SeaOperationBackend.test.ts +++ b/tests/unit/kernel/KernelOperationBackend.test.ts @@ -36,12 +36,12 @@ import { vectorFromArray, } from 'apache-arrow'; -import SeaOperationBackend from '../../../lib/sea/SeaOperationBackend'; +import KernelOperationBackend from '../../../lib/kernel/KernelOperationBackend'; import ClientContextStub from '../.stubs/ClientContextStub'; // Minimal stub of the napi `Statement` surface that emits a precomputed // Arrow IPC payload per `fetchNextBatch()` call. Used to feed -// `SeaOperationBackend` synthetic batches that mirror the kernel's +// `KernelOperationBackend` synthetic batches that mirror the kernel's // per-batch IPC stream contract (`schema header + 1 record-batch // message`) without loading the native binding. class StatementStub { @@ -140,7 +140,7 @@ function ipcSchemaOnly(schema: Schema): Buffer { return Buffer.from(tableToIPC(table, 'stream')); } -describe('SeaOperationBackend — M0 datatype round-trip via napi → ArrowResultConverter', () => { +describe('KernelOperationBackend — M0 datatype round-trip via napi → ArrowResultConverter', () => { it('passes M0 primitive datatypes through the same converter the thrift path uses', async () => { // One row per M0 primitive type with a kernel-style metadata tag on // each field. Decimal carries a real scale (2) so the converter's @@ -189,7 +189,7 @@ describe('SeaOperationBackend — M0 datatype round-trip via napi → ArrowResul }); const stub = new StatementStub(schemaIpc, [dataIpc]); - const backend = new SeaOperationBackend({ + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub(), }); @@ -245,7 +245,7 @@ describe('SeaOperationBackend — M0 datatype round-trip via napi → ArrowResul }); const stub = new StatementStub(strSchemaIpc, [strDataIpc]); - const backend = new SeaOperationBackend({ + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub(), }); @@ -264,7 +264,7 @@ describe('SeaOperationBackend — M0 datatype round-trip via napi → ArrowResul const batch2 = ipcFromColumns(schema, { x: [3] }); const stub = new StatementStub(schemaIpc, [batch1, batch2]); - const backend = new SeaOperationBackend({ + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub(), }); @@ -278,7 +278,7 @@ describe('SeaOperationBackend — M0 datatype round-trip via napi → ArrowResul const schema = new Schema([withTypeName(new Field('x', new Int32(), true), 'INT')]); const schemaIpc = ipcSchemaOnly(schema); const stub = new StatementStub(schemaIpc, []); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); await backend.cancel(); expect(stub.cancelled).to.equal(true); await backend.close(); diff --git a/tests/unit/sea/_helpers/fakeBinding.ts b/tests/unit/kernel/_helpers/fakeBinding.ts similarity index 84% rename from tests/unit/sea/_helpers/fakeBinding.ts rename to tests/unit/kernel/_helpers/fakeBinding.ts index 16c101de..0d73aafd 100644 --- a/tests/unit/sea/_helpers/fakeBinding.ts +++ b/tests/unit/kernel/_helpers/fakeBinding.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { SeaNativeBinding, SeaConnection } from '../../../../lib/sea/SeaNativeLoader'; +import { KernelNativeBinding, KernelConnection } from '../../../../lib/kernel/KernelNativeLoader'; import IClientContext from '../../../../lib/contracts/IClientContext'; export interface RecordedCall { @@ -21,7 +21,7 @@ export interface RecordedCall { } /** - * Minimal `IClientContext` for SEA backend tests. `SeaBackend` requires a + * Minimal `IClientContext` for SEA backend tests. `KernelBackend` requires a * context (it threads logger/config into the session → operation chain), so * even auth-routing tests that never log must supply one. Only `getLogger` * is wired (to a no-op); the rest throw if unexpectedly reached. @@ -40,12 +40,12 @@ export function makeFakeContext(): IClientContext { } export interface FakeBinding { - binding: SeaNativeBinding; + binding: KernelNativeBinding; calls: RecordedCall[]; } /** - * Build a fake `SeaNativeBinding` that records every `openSession` call + * Build a fake `KernelNativeBinding` that records every `openSession` call * and returns a `Connection` whose `close()` is also recorded. Shared * across the SEA auth unit-test files (PAT / M2M / U2M / future flows) * so the closure shape lives in exactly one place. @@ -71,13 +71,13 @@ export function makeFakeBinding(): FakeBinding { version() { return 'fake-binding'; }, - async openSession(opts: Parameters[0]) { + async openSession(opts: Parameters[0]) { calls.push({ method: 'openSession', args: [opts] }); - return fakeConnection as unknown as SeaConnection; + return fakeConnection as unknown as KernelConnection; }, Connection: function FakeConnection() {}, Statement: function FakeStatement() {}, - } as unknown as SeaNativeBinding; + } as unknown as KernelNativeBinding; return { binding, calls }; } diff --git a/tests/unit/sea/auth-edge-cases.test.ts b/tests/unit/kernel/auth-edge-cases.test.ts similarity index 87% rename from tests/unit/sea/auth-edge-cases.test.ts rename to tests/unit/kernel/auth-edge-cases.test.ts index 9f0928bb..371ac22f 100644 --- a/tests/unit/sea/auth-edge-cases.test.ts +++ b/tests/unit/kernel/auth-edge-cases.test.ts @@ -13,14 +13,14 @@ // limitations under the License. import { expect } from 'chai'; -import SeaBackend from '../../../lib/sea/SeaBackend'; -import { buildSeaConnectionOptions } from '../../../lib/sea/SeaAuth'; +import KernelBackend from '../../../lib/kernel/KernelBackend'; +import { buildKernelConnectionOptions } from '../../../lib/kernel/KernelAuth'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import AuthenticationError from '../../../lib/errors/AuthenticationError'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; import { makeFakeBinding, makeFakeContext } from './_helpers/fakeBinding'; -describe('SeaAuth — edge cases (input validation + ambiguity)', () => { +describe('KernelAuth — edge cases (input validation + ambiguity)', () => { describe('whitespace-only and reserved-literal credentials are rejected', () => { it('rejects whitespace-only PAT', () => { const opts: ConnectionOptions = { @@ -29,7 +29,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { token: ' \t ', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); }); it('rejects literal "undefined" as PAT (buggy shell-export hazard)', () => { @@ -39,7 +39,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { token: 'undefined', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); }); it('rejects literal "null" as PAT', () => { @@ -49,7 +49,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { token: 'null', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); }); it('rejects mixed-case "UNDEFINED" / "Null" / "NULL" as PAT (case-insensitive)', () => { @@ -60,7 +60,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { token: reserved, }; - expect(() => buildSeaConnectionOptions(opts), `for token=${reserved}`).to.throw( + expect(() => buildKernelConnectionOptions(opts), `for token=${reserved}`).to.throw( AuthenticationError, /non-empty PAT/, ); @@ -82,7 +82,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: 'NULL', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( AuthenticationError, /oauthClientSecret.*non-empty.*OAuth M2M/, ); @@ -97,7 +97,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: 'dose-fake-secret', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); }); it('rejects whitespace-only oauthClientSecret with AuthenticationError when oauthClientId is set (M2M intent)', () => { @@ -109,7 +109,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: '\n\t', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( AuthenticationError, /oauthClientSecret.*non-empty.*OAuth M2M/, ); @@ -124,7 +124,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: 'dose-fake-secret', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); }); it('rejects literal "undefined" as oauthClientSecret with AuthenticationError when id is set (M2M intent)', () => { @@ -136,7 +136,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: 'undefined', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( AuthenticationError, /oauthClientSecret.*non-empty.*OAuth M2M/, ); @@ -162,7 +162,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: '', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( AuthenticationError, /oauthClientSecret.*non-empty.*OAuth M2M/, ); @@ -183,7 +183,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientId: ' ', } as unknown as ConnectionOptions; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( HiveDriverError, /oauthClientId.*not supported on the OAuth U2M flow/, ); @@ -201,7 +201,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientId: 'client-uuid', } as any; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( HiveDriverError, /cannot supply both `token` and `oauthClientId/, ); @@ -217,7 +217,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: 'dose-fake-secret', } as any; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( HiveDriverError, /cannot supply both `token` and `oauthClientId/, ); @@ -234,7 +234,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { token: 'dapi-fake-pat', } as any; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( HiveDriverError, /cannot supply `token` alongside `authType: 'databricks-oauth'`/, ); @@ -250,7 +250,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { token: 'dapi-fake-pat', } as any; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( HiveDriverError, /cannot supply `token` alongside `authType: 'databricks-oauth'`/, ); @@ -268,7 +268,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: '', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthU2m'); }); @@ -280,7 +280,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: ' \t ', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthU2m'); }); @@ -292,7 +292,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: 'undefined', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthU2m'); }); }); @@ -308,7 +308,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { azureTenantId: undefined, }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthM2m'); }); @@ -322,7 +322,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { useDatabricksOAuthInAzure: false, }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthM2m'); }); @@ -334,13 +334,13 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { azureTenantId: undefined, }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthU2m'); }); }); }); -describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { +describe('KernelBackend — kernel error envelope decoding (DA-F1)', () => { /** * Build a fake binding whose `openSession` rejects with the verbatim * `__databricks_error__:{...}` envelope shape the napi binding's @@ -366,7 +366,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { const binding = bindingRejectingWith( '{"code":"Unauthenticated","message":"OAuth M2M token exchange failed: invalid_client"}', ); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -383,7 +383,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { const binding = bindingRejectingWith( '{"code":"NetworkError","message":"OIDC discovery failed: connection refused"}', ); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -398,7 +398,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { it('preserves SQLSTATE on the decoded error when present', async () => { const binding = bindingRejectingWith('{"code":"Unauthenticated","message":"forbidden","sqlState":"28000"}'); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -416,7 +416,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { binding.openSession = (async () => { throw new Error('openSession: `token` is required for the requested auth mode'); }) as typeof binding.openSession; - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -431,7 +431,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { it('falls back to original Error for a corrupted envelope, stripping the internal sentinel', async () => { const binding = bindingRejectingWith('not valid json'); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -462,7 +462,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { '"sqlState":"08006","errorCode":"UPSTREAM_TIMEOUT","vendorCode":1234,' + '"httpStatus":503,"retryable":true,"queryId":"query-abc-123"}', ); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -485,7 +485,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { it('keeps sqlState and kernelMetadata non-enumerable (matches Node `.code` pattern)', async () => { const binding = bindingRejectingWith('{"code":"NetworkError","message":"x","sqlState":"08000","httpStatus":502}'); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -513,7 +513,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { const binding = bindingRejectingWith( '{"code":"Cancelled","message":"user-cancel","errorCode":"USER_REQUESTED_CANCEL"}', ); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -543,7 +543,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { const binding = bindingRejectingWith( '{"code":"NetworkError","message":"x","errorCode":42,"vendorCode":"not-a-number","httpStatus":502,"retryable":"true","queryId":null}', ); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -564,7 +564,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { // namespace because that's pure noise. The sqlState top-level // field is unaffected. const binding = bindingRejectingWith('{"code":"Internal","message":"x","sqlState":"08001"}'); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -579,8 +579,8 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { expect(err.kernelMetadata).to.equal(undefined); }); - // NF-1: SeaSessionBackend.close() must wrap the napi call too. - it('SeaSessionBackend.close() decodes kernel-error envelopes from native.close()', async () => { + // NF-1: KernelSessionBackend.close() must wrap the napi call too. + it('KernelSessionBackend.close() decodes kernel-error envelopes from native.close()', async () => { const { binding } = makeFakeBinding(); // Make openSession return a fake Connection whose close() throws // a kernel-shaped envelope. @@ -594,7 +594,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { }; binding.openSession = (async () => failingClose as unknown) as typeof binding.openSession; - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); const session = await backend.openSession({}); diff --git a/tests/unit/sea/auth-m2m.test.ts b/tests/unit/kernel/auth-m2m.test.ts similarity index 82% rename from tests/unit/sea/auth-m2m.test.ts rename to tests/unit/kernel/auth-m2m.test.ts index 159afe1d..10249150 100644 --- a/tests/unit/sea/auth-m2m.test.ts +++ b/tests/unit/kernel/auth-m2m.test.ts @@ -13,15 +13,15 @@ // limitations under the License. import { expect } from 'chai'; -import SeaBackend from '../../../lib/sea/SeaBackend'; -import { buildSeaConnectionOptions } from '../../../lib/sea/SeaAuth'; +import KernelBackend from '../../../lib/kernel/KernelBackend'; +import { buildKernelConnectionOptions } from '../../../lib/kernel/KernelAuth'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import AuthenticationError from '../../../lib/errors/AuthenticationError'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; import { makeFakeBinding, makeFakeContext } from './_helpers/fakeBinding'; -describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { - describe('buildSeaConnectionOptions', () => { +describe('KernelAuth + KernelBackend — OAuth M2M auth flow', () => { + describe('buildKernelConnectionOptions', () => { it('accepts databricks-oauth + oauthClientId + oauthClientSecret', () => { const opts: ConnectionOptions = { host: 'example.cloud.databricks.com', @@ -31,7 +31,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { oauthClientSecret: 'dose-fake-secret', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native).to.deep.equal({ hostName: 'example.cloud.databricks.com', httpPath: '/sql/1.0/warehouses/abc', @@ -51,7 +51,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { oauthClientSecret: 'dose-fake-secret', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.httpPath).to.equal('/sql/1.0/warehouses/abc'); }); @@ -63,7 +63,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { oauthClientSecret: 'dose-fake-secret', } as unknown as ConnectionOptions; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); }); it('rejects empty oauthClientId with AuthenticationError', () => { @@ -75,7 +75,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { oauthClientSecret: 'dose-fake-secret', } as unknown as ConnectionOptions; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); }); it('rejects empty oauthClientSecret with AuthenticationError when oauthClientId is set (M2M intent)', () => { @@ -91,7 +91,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { // is a typo/missing-env, not a request to fall back to U2M. // Surface the M2M "secret required" error so the user knows the // real problem instead of getting routed to a different flow. - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( AuthenticationError, /oauthClientSecret.*non-empty.*OAuth M2M/, ); @@ -107,7 +107,10 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { azureTenantId: 'tenant-uuid', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /Azure-direct OAuth.*is not supported/); + expect(() => buildKernelConnectionOptions(opts)).to.throw( + HiveDriverError, + /Azure-direct OAuth.*is not supported/, + ); }); it('rejects useDatabricksOAuthInAzure with the same Entra-direct error', () => { @@ -120,7 +123,10 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { useDatabricksOAuthInAzure: true, }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /Azure-direct OAuth.*is not supported/); + expect(() => buildKernelConnectionOptions(opts)).to.throw( + HiveDriverError, + /Azure-direct OAuth.*is not supported/, + ); }); it('rejects a `persistence` hook on M2M (no cache needed)', () => { @@ -136,17 +142,17 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { }, }; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( HiveDriverError, /`persistence` is not supported on OAuth M2M/, ); }); }); - describe('SeaBackend.connect + openSession (M2M)', () => { + describe('KernelBackend.connect + openSession (M2M)', () => { it('round-trips M2M options through to the napi binding', async () => { const { binding, calls } = makeFakeBinding(); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect({ host: 'example.cloud.databricks.com', @@ -157,7 +163,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { }); const session = await backend.openSession({}); - // Post-integration: SeaSessionBackend generates UUIDv4 ids; the + // Post-integration: KernelSessionBackend generates UUIDv4 ids; the // earlier auth-only counter-id scheme was superseded. expect(session.id).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i); @@ -178,7 +184,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { it('rejects connect() for missing oauthClientId before touching the binding', async () => { const { binding, calls } = makeFakeBinding(); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); let caught: unknown; try { diff --git a/tests/unit/sea/auth-pat.test.ts b/tests/unit/kernel/auth-pat.test.ts similarity index 77% rename from tests/unit/sea/auth-pat.test.ts rename to tests/unit/kernel/auth-pat.test.ts index bd82eb87..76291857 100644 --- a/tests/unit/sea/auth-pat.test.ts +++ b/tests/unit/kernel/auth-pat.test.ts @@ -13,13 +13,13 @@ // limitations under the License. import { expect } from 'chai'; -import { buildSeaConnectionOptions } from '../../../lib/sea/SeaAuth'; +import { buildKernelConnectionOptions } from '../../../lib/kernel/KernelAuth'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import AuthenticationError from '../../../lib/errors/AuthenticationError'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; -describe('SeaAuth — PAT auth options builder', () => { - describe('buildSeaConnectionOptions', () => { +describe('KernelAuth — PAT auth options builder', () => { + describe('buildKernelConnectionOptions', () => { it('accepts a bare access-token PAT (undefined authType)', () => { const opts: ConnectionOptions = { host: 'example.cloud.databricks.com', @@ -27,7 +27,7 @@ describe('SeaAuth — PAT auth options builder', () => { token: 'dapi-fake-pat', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native).to.deep.equal({ hostName: 'example.cloud.databricks.com', httpPath: '/sql/1.0/warehouses/abc', @@ -45,7 +45,7 @@ describe('SeaAuth — PAT auth options builder', () => { token: 'dapi-fake-pat', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('Pat'); if (native.authMode === 'Pat') { expect(native.token).to.equal('dapi-fake-pat'); @@ -59,7 +59,7 @@ describe('SeaAuth — PAT auth options builder', () => { token: 'dapi-fake-pat', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.httpPath).to.equal('/sql/1.0/warehouses/abc'); }); @@ -71,7 +71,7 @@ describe('SeaAuth — PAT auth options builder', () => { // no token } as unknown as ConnectionOptions; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); }); it('throws AuthenticationError when token is an empty string', () => { @@ -81,7 +81,7 @@ describe('SeaAuth — PAT auth options builder', () => { token: '', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); }); it('accepts databricks-oauth without oauthClientSecret as the U2M happy path', () => { @@ -91,7 +91,7 @@ describe('SeaAuth — PAT auth options builder', () => { authType: 'databricks-oauth', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthU2m'); }); @@ -104,7 +104,10 @@ describe('SeaAuth — PAT auth options builder', () => { tokenProvider: { getToken: async () => 'tok' } as any, }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /unsupported auth mode 'token-provider'/); + expect(() => buildKernelConnectionOptions(opts)).to.throw( + HiveDriverError, + /unsupported auth mode 'token-provider'/, + ); }); it('rejects external-token, static-token, and custom auth modes', () => { @@ -116,16 +119,16 @@ describe('SeaAuth — PAT auth options builder', () => { path: '/p', authType, } as any; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /unsupported auth mode/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(HiveDriverError, /unsupported auth mode/); } }); }); - // Note: SeaBackend.connect/openSession round-trip + error-path coverage - // moved to tests/unit/sea/execution.test.ts during the sea-integration - // merge (the execution branch's SeaBackend constructor signature + // Note: KernelBackend.connect/openSession round-trip + error-path coverage + // moved to tests/unit/kernel/execution.test.ts during the sea-integration + // merge (the execution branch's KernelBackend constructor signature // {context, nativeBinding} supersedes the auth-only (binding) shape). // OAuth-specific flow-dispatch tests live in auth-m2m.test.ts and // auth-u2m.test.ts; M2M end-to-end against a live workspace lives in - // tests/integration/sea/auth-m2m-e2e.test.ts. + // tests/integration/kernel/auth-m2m-e2e.test.ts. }); diff --git a/tests/unit/sea/auth-u2m.test.ts b/tests/unit/kernel/auth-u2m.test.ts similarity index 81% rename from tests/unit/sea/auth-u2m.test.ts rename to tests/unit/kernel/auth-u2m.test.ts index 828ca961..f4dff3ab 100644 --- a/tests/unit/sea/auth-u2m.test.ts +++ b/tests/unit/kernel/auth-u2m.test.ts @@ -13,15 +13,15 @@ // limitations under the License. import { expect } from 'chai'; -import SeaBackend from '../../../lib/sea/SeaBackend'; -import { buildSeaConnectionOptions } from '../../../lib/sea/SeaAuth'; +import KernelBackend from '../../../lib/kernel/KernelBackend'; +import { buildKernelConnectionOptions } from '../../../lib/kernel/KernelAuth'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import AuthenticationError from '../../../lib/errors/AuthenticationError'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; import { makeFakeBinding, makeFakeContext } from './_helpers/fakeBinding'; -describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { - describe('buildSeaConnectionOptions', () => { +describe('KernelAuth + KernelBackend — OAuth U2M auth flow', () => { + describe('buildKernelConnectionOptions', () => { it('accepts databricks-oauth with no clientSecret as the U2M happy path (hardcoded port 8030)', () => { const opts: ConnectionOptions = { host: 'example.cloud.databricks.com', @@ -29,7 +29,7 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { authType: 'databricks-oauth', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native).to.deep.equal({ hostName: 'example.cloud.databricks.com', httpPath: '/sql/1.0/warehouses/abc', @@ -58,7 +58,7 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { oauthClientId: 'custom-client', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( AuthenticationError, /oauthClientSecret.*non-empty.*OAuth M2M/, ); @@ -71,7 +71,7 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { authType: 'databricks-oauth', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.httpPath).to.equal('/sql/1.0/warehouses/abc'); }); @@ -83,7 +83,10 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { azureTenantId: 'tenant-uuid', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /Azure-direct OAuth.*is not supported/); + expect(() => buildKernelConnectionOptions(opts)).to.throw( + HiveDriverError, + /Azure-direct OAuth.*is not supported/, + ); }); it('rejects useDatabricksOAuthInAzure on the U2M path', () => { @@ -94,7 +97,10 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { useDatabricksOAuthInAzure: true, }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /Azure-direct OAuth.*is not supported/); + expect(() => buildKernelConnectionOptions(opts)).to.throw( + HiveDriverError, + /Azure-direct OAuth.*is not supported/, + ); }); it('rejects a `persistence` hook on U2M citing the AuthConfig::External kernel-plumbing gap', () => { @@ -108,14 +114,14 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { }, }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /AuthConfig::External.*plumbing/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(HiveDriverError, /AuthConfig::External.*plumbing/); }); }); - describe('SeaBackend.connect + openSession (U2M)', () => { + describe('KernelBackend.connect + openSession (U2M)', () => { it('round-trips U2M options through to the napi binding', async () => { const { binding, calls } = makeFakeBinding(); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect({ host: 'example.cloud.databricks.com', @@ -124,7 +130,7 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { }); const session = await backend.openSession({}); - // Post-integration: SeaSessionBackend generates UUIDv4 ids; the + // Post-integration: KernelSessionBackend generates UUIDv4 ids; the // earlier auth-only counter-id scheme was superseded. expect(session.id).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i); diff --git a/tests/unit/sea/connectionOptions.test.ts b/tests/unit/kernel/connectionOptions.test.ts similarity index 64% rename from tests/unit/sea/connectionOptions.test.ts rename to tests/unit/kernel/connectionOptions.test.ts index 4869bd16..b989c10c 100644 --- a/tests/unit/sea/connectionOptions.test.ts +++ b/tests/unit/kernel/connectionOptions.test.ts @@ -13,7 +13,7 @@ // limitations under the License. import { expect } from 'chai'; -import { buildSeaConnectionOptions, buildSeaTlsOptions } from '../../../lib/sea/SeaAuth'; +import { buildKernelConnectionOptions, buildKernelTlsOptions } from '../../../lib/kernel/KernelAuth'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; @@ -23,32 +23,32 @@ const PAT = { host: 'h.databricks.com', path: '/sql/1.0/warehouses/abc', token: // surface, so tests build untyped option literals. const opts = (extra: Record) => ({ ...PAT, ...extra } as unknown as ConnectionOptions); -describe('SeaAuth connection options — intervalsAsString default', () => { +describe('KernelAuth connection options — intervalsAsString default', () => { it('always sets intervalsAsString:true (thrift-compatible interval rendering)', () => { - const native = buildSeaConnectionOptions(opts({})) as { intervalsAsString?: boolean }; + const native = buildKernelConnectionOptions(opts({})) as { intervalsAsString?: boolean }; expect(native.intervalsAsString).to.equal(true); }); it('does NOT force complexTypesAsJson (native Arrow nested types match Thrift)', () => { - const native = buildSeaConnectionOptions(opts({})) as { complexTypesAsJson?: boolean }; + const native = buildKernelConnectionOptions(opts({})) as { complexTypesAsJson?: boolean }; expect(native.complexTypesAsJson).to.equal(undefined); }); }); -describe('SeaAuth connection options — maxConnections', () => { +describe('KernelAuth connection options — maxConnections', () => { it('forwards a valid positive integer', () => { - const native = buildSeaConnectionOptions(opts({ maxConnections: 10 })) as { maxConnections?: number }; + const native = buildKernelConnectionOptions(opts({ maxConnections: 10 })) as { maxConnections?: number }; expect(native.maxConnections).to.equal(10); }); it('omits maxConnections when unset', () => { - const native = buildSeaConnectionOptions(opts({})) as { maxConnections?: number }; + const native = buildKernelConnectionOptions(opts({})) as { maxConnections?: number }; expect(native.maxConnections).to.equal(undefined); }); for (const bad of [0, -1, 1.5]) { it(`rejects non-positive-integer maxConnections (${bad})`, () => { - expect(() => buildSeaConnectionOptions(opts({ maxConnections: bad }))).to.throw( + expect(() => buildKernelConnectionOptions(opts({ maxConnections: bad }))).to.throw( HiveDriverError, /positive integer/, ); @@ -56,41 +56,44 @@ describe('SeaAuth connection options — maxConnections', () => { } it('rejects maxConnections beyond the u32 limit', () => { - expect(() => buildSeaConnectionOptions(opts({ maxConnections: 0x1_0000_0000 }))).to.throw( + expect(() => buildKernelConnectionOptions(opts({ maxConnections: 0x1_0000_0000 }))).to.throw( HiveDriverError, /u32 limit/, ); }); }); -describe('SeaAuth TLS options (buildSeaTlsOptions)', () => { +describe('KernelAuth TLS options (buildKernelTlsOptions)', () => { it('is empty by default (secure-by-default — kernel default verify-on)', () => { - expect(buildSeaTlsOptions(opts({}))).to.deep.equal({}); + expect(buildKernelTlsOptions(opts({}))).to.deep.equal({}); }); it('passes checkServerCertificate through verbatim (including false)', () => { - expect(buildSeaTlsOptions(opts({ checkServerCertificate: false }))).to.deep.equal({ + expect(buildKernelTlsOptions(opts({ checkServerCertificate: false }))).to.deep.equal({ checkServerCertificate: false, }); - expect(buildSeaTlsOptions(opts({ checkServerCertificate: true }))).to.deep.equal({ + expect(buildKernelTlsOptions(opts({ checkServerCertificate: true }))).to.deep.equal({ checkServerCertificate: true, }); }); it('normalises a PEM string to a Buffer', () => { const pem = '-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n'; - const tls = buildSeaTlsOptions(opts({ customCaCert: pem })); + const tls = buildKernelTlsOptions(opts({ customCaCert: pem })); expect(Buffer.isBuffer(tls.customCaCert)).to.equal(true); expect(tls.customCaCert?.toString('utf8')).to.equal(pem); }); it('passes a Buffer customCaCert through unchanged', () => { const buf = Buffer.from('-----BEGIN CERTIFICATE-----\nx\n-----END CERTIFICATE-----'); - expect(buildSeaTlsOptions(opts({ customCaCert: buf })).customCaCert).to.equal(buf); + expect(buildKernelTlsOptions(opts({ customCaCert: buf })).customCaCert).to.equal(buf); }); it('rejects a non-PEM string', () => { - expect(() => buildSeaTlsOptions(opts({ customCaCert: 'not-a-pem' }))).to.throw(HiveDriverError, /PEM certificate/); + expect(() => buildKernelTlsOptions(opts({ customCaCert: 'not-a-pem' }))).to.throw( + HiveDriverError, + /PEM certificate/, + ); }); it('rejects out-of-order / partial PEM markers (ordered match, not two substrings)', () => { @@ -100,20 +103,26 @@ describe('SeaAuth TLS options (buildSeaTlsOptions)', () => { const beginOnly = '-----BEGIN CERTIFICATE-----\nMIIB...\n'; const endOnly = 'MIIB...\n-----END CERTIFICATE-----'; for (const bad of [reversed, beginOnly, endOnly]) { - expect(() => buildSeaTlsOptions(opts({ customCaCert: bad })), bad).to.throw(HiveDriverError, /PEM certificate/); + expect(() => buildKernelTlsOptions(opts({ customCaCert: bad })), bad).to.throw( + HiveDriverError, + /PEM certificate/, + ); } }); it('rejects an empty Buffer', () => { - expect(() => buildSeaTlsOptions(opts({ customCaCert: Buffer.alloc(0) }))).to.throw(HiveDriverError, /empty/); + expect(() => buildKernelTlsOptions(opts({ customCaCert: Buffer.alloc(0) }))).to.throw(HiveDriverError, /empty/); }); it('rejects a non-string, non-Buffer customCaCert', () => { - expect(() => buildSeaTlsOptions(opts({ customCaCert: 123 }))).to.throw(HiveDriverError, /PEM string or a Buffer/); + expect(() => buildKernelTlsOptions(opts({ customCaCert: 123 }))).to.throw( + HiveDriverError, + /PEM string or a Buffer/, + ); }); it('folds TLS options into the full connection options', () => { - const native = buildSeaConnectionOptions(opts({ checkServerCertificate: false })) as { + const native = buildKernelConnectionOptions(opts({ checkServerCertificate: false })) as { checkServerCertificate?: boolean; }; expect(native.checkServerCertificate).to.equal(false); diff --git a/tests/unit/sea/error-mapping.test.ts b/tests/unit/kernel/error-mapping.test.ts similarity index 98% rename from tests/unit/sea/error-mapping.test.ts rename to tests/unit/kernel/error-mapping.test.ts index 8b5bdf70..d00c550a 100644 --- a/tests/unit/sea/error-mapping.test.ts +++ b/tests/unit/kernel/error-mapping.test.ts @@ -1,11 +1,11 @@ import { expect } from 'chai'; -import { mapKernelErrorToJsError, KernelErrorCode, KernelErrorShape } from '../../../lib/sea/SeaErrorMapping'; +import { mapKernelErrorToJsError, KernelErrorCode, KernelErrorShape } from '../../../lib/kernel/KernelErrorMapping'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; import AuthenticationError from '../../../lib/errors/AuthenticationError'; import OperationStateError, { OperationStateErrorCode } from '../../../lib/errors/OperationStateError'; import ParameterError from '../../../lib/errors/ParameterError'; -describe('SeaErrorMapping.mapKernelErrorToJsError', () => { +describe('KernelErrorMapping.mapKernelErrorToJsError', () => { // The 13 kernel ErrorCode variants — kept in sync with src/kernel_error.rs:66-134. // Tabular driver: each row is (kernel code, expected class, optional extra assertion). type Case = { diff --git a/tests/unit/sea/execution.test.ts b/tests/unit/kernel/execution.test.ts similarity index 93% rename from tests/unit/sea/execution.test.ts rename to tests/unit/kernel/execution.test.ts index 81cdfadd..fc93bee7 100644 --- a/tests/unit/sea/execution.test.ts +++ b/tests/unit/kernel/execution.test.ts @@ -15,10 +15,10 @@ import { expect } from 'chai'; import sinon from 'sinon'; import Int64 from 'node-int64'; -import SeaBackend from '../../../lib/sea/SeaBackend'; -import SeaSessionBackend from '../../../lib/sea/SeaSessionBackend'; -import SeaOperationBackend from '../../../lib/sea/SeaOperationBackend'; -import { SeaNativeBinding, SeaConnection, SeaStatement } from '../../../lib/sea/SeaNativeLoader'; +import KernelBackend from '../../../lib/kernel/KernelBackend'; +import KernelSessionBackend from '../../../lib/kernel/KernelSessionBackend'; +import KernelOperationBackend from '../../../lib/kernel/KernelOperationBackend'; +import { KernelNativeBinding, KernelConnection, KernelStatement } from '../../../lib/kernel/KernelNativeLoader'; import IClientContext, { ClientConfig } from '../../../lib/contracts/IClientContext'; import IDBSQLLogger, { LogLevel } from '../../../lib/contracts/IDBSQLLogger'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; @@ -33,7 +33,7 @@ import { OperationState } from '../../../lib/contracts/OperationStatus'; // pulling in test-only fixtures from outside the sea/ namespace. // ----------------------------------------------------------------------------- -class FakeNativeStatement implements SeaStatement { +class FakeNativeStatement implements KernelStatement { public closed = false; public cancelled = false; @@ -180,7 +180,7 @@ class FakeCancellableExecution { } } -class FakeNativeConnection implements SeaConnection { +class FakeNativeConnection implements KernelConnection { public closed = false; public lastSql?: string; @@ -213,7 +213,7 @@ class FakeNativeConnection implements SeaConnection { // The bare blocking executeStatement path: the SEA backend's sync default // routes through executeStatementCancellable (below), but the binding still // exposes this for completeness. - public async executeStatement(sql: string, options?: unknown): Promise { + public async executeStatement(sql: string, options?: unknown): Promise { if (this.throwOnExecute) { throw this.throwOnExecute; } @@ -246,7 +246,7 @@ class FakeNativeConnection implements SeaConnection { return this.lastAsyncStatement; } - private recordMetadata(method: string, args: unknown[]): Promise { + private recordMetadata(method: string, args: unknown[]): Promise { this.metadataCalls.push([method, ...args]); return Promise.resolve(this.statementToReturn); } @@ -310,7 +310,7 @@ class FakeNativeConnection implements SeaConnection { } } -function makeBinding(connection: SeaConnection): SeaNativeBinding & { +function makeBinding(connection: KernelConnection): KernelNativeBinding & { openSessionStub: sinon.SinonStub; } { const openSessionStub = sinon.stub().resolves(connection); @@ -322,7 +322,7 @@ function makeBinding(connection: SeaConnection): SeaNativeBinding & { openSession: openSessionStub, Connection: function Connection() {}, Statement: function Statement() {}, - } as unknown as SeaNativeBinding; + } as unknown as KernelNativeBinding; return Object.assign(binding, { openSessionStub }); } @@ -352,11 +352,11 @@ function makeContext(logger?: IDBSQLLogger): IClientContext { // Tests // ----------------------------------------------------------------------------- -describe('SeaBackend', () => { +describe('KernelBackend', () => { it('connect() captures the connection options and validates PAT auth', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); await backend.connect({ host: 'example.databricks.com', @@ -377,7 +377,7 @@ describe('SeaBackend', () => { it('connect() rejects unsupported auth modes (non-PAT, non-OAuth)', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); let thrown: unknown; try { @@ -397,7 +397,7 @@ describe('SeaBackend', () => { it('connect() rejects missing token', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); let thrown: unknown; try { @@ -411,7 +411,7 @@ describe('SeaBackend', () => { } expect(thrown).to.be.instanceOf(HiveDriverError); // After sea-integration merge, missing-token validation goes through - // SeaAuth.buildSeaConnectionOptions which throws AuthenticationError + // KernelAuth.buildKernelConnectionOptions which throws AuthenticationError // (extends HiveDriverError) with the "non-empty PAT" message. expect((thrown as Error).message).to.match(/non-empty PAT/); }); @@ -419,7 +419,7 @@ describe('SeaBackend', () => { it('openSession() throws if connect() was not called', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); let thrown: unknown; try { @@ -434,7 +434,7 @@ describe('SeaBackend', () => { it('openSession() forwards hostName / httpPath / token to napi binding', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); await backend.connect({ host: 'workspace.example', @@ -446,7 +446,7 @@ describe('SeaBackend', () => { expect(binding.openSessionStub.calledOnce).to.equal(true); const args = binding.openSessionStub.firstCall.args[0]; - // sea-auth-u2m introduced the discriminated SeaNativeConnectionOptions + // sea-auth-u2m introduced the discriminated KernelNativeConnectionOptions // shape with a leading `authMode` tag — `'Pat'` for the PAT branch. // `intervalsAsString: true` is always set so the SEA result shape is a // byte-compatible drop-in for the Thrift backend (interval-as-string). @@ -459,10 +459,10 @@ describe('SeaBackend', () => { }); }); - it('openSession() returns a SeaSessionBackend wrapping the napi Connection', async () => { + it('openSession() returns a KernelSessionBackend wrapping the napi Connection', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); await backend.connect({ host: 'h', @@ -471,14 +471,14 @@ describe('SeaBackend', () => { } as ConnectionOptions); const sessionBackend = await backend.openSession({}); - expect(sessionBackend).to.be.instanceOf(SeaSessionBackend); + expect(sessionBackend).to.be.instanceOf(KernelSessionBackend); expect(sessionBackend.id).to.be.a('string').and.have.length.greaterThan(0); }); it('openSession() forwards initialCatalog / initialSchema / configuration to the napi openSession call (not per-statement)', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); await backend.connect({ host: 'h', @@ -513,7 +513,7 @@ describe('SeaBackend', () => { it('close() clears connection state without throwing', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); await backend.connect({ host: 'h', path: '/p', token: 't' } as ConnectionOptions); await backend.close(); @@ -527,9 +527,9 @@ describe('SeaBackend', () => { }); }); -describe('SeaSessionBackend', () => { - function makeSession(connection: SeaConnection) { - return new SeaSessionBackend({ connection, context: makeContext() }); +describe('KernelSessionBackend', () => { + function makeSession(connection: KernelConnection) { + return new KernelSessionBackend({ connection, context: makeContext() }); } it('executeStatement passes sql through verbatim', async () => { @@ -539,11 +539,11 @@ describe('SeaSessionBackend', () => { expect(connection.lastSql).to.equal('SELECT * FROM foo'); }); - it('executeStatement returns a SeaOperationBackend with an id', async () => { + it('executeStatement returns a KernelOperationBackend with an id', async () => { const connection = new FakeNativeConnection(); const session = makeSession(connection); const op = await session.executeStatement('SELECT 1', {}); - expect(op).to.be.instanceOf(SeaOperationBackend); + expect(op).to.be.instanceOf(KernelOperationBackend); expect(op.id).to.be.a('string').and.have.length.greaterThan(0); }); @@ -713,14 +713,14 @@ describe('SeaSessionBackend', () => { } // Metadata calls forward to the kernel's metadata surface and wrap the - // returned napi `Statement` as a `SeaOperationBackend`. Each case asserts + // returned napi `Statement` as a `KernelOperationBackend`. Each case asserts // the request → napi-argument mapping (the only logic the driver owns). it('metadata calls forward to the napi binding with mapped arguments', async () => { const connection = new FakeNativeConnection(); const session = makeSession(connection); const op = await session.getCatalogs({}); - expect(op).to.be.instanceOf(SeaOperationBackend); + expect(op).to.be.instanceOf(KernelOperationBackend); await session.getSchemas({ catalogName: 'main', schemaName: 'def%' }); await session.getTables({ catalogName: 'main', schemaName: 'def', tableName: 't%', tableTypes: ['TABLE', 'VIEW'] }); @@ -822,9 +822,9 @@ describe('SeaSessionBackend', () => { }); }); -describe('SeaOperationBackend', () => { - function makeOperation(statement: SeaStatement = new FakeNativeStatement()) { - return new SeaOperationBackend({ statement, context: makeContext() }); +describe('KernelOperationBackend', () => { + function makeOperation(statement: KernelStatement = new FakeNativeStatement()) { + return new KernelOperationBackend({ statement, context: makeContext() }); } it('id is a stable string', () => { @@ -867,26 +867,29 @@ describe('SeaOperationBackend', () => { }); // Note: after sea-integration merge, fetchChunk is no longer a stub — - // the sea-results SeaResultsProvider + ArrowResultConverter pipeline + // the sea-results KernelResultsProvider + ArrowResultConverter pipeline // implements the real fetch path. Full coverage lives in - // tests/unit/sea/SeaOperationBackend.test.ts and the parity-gate e2e - // at tests/e2e/sea/results-e2e.test.ts. + // tests/unit/kernel/KernelOperationBackend.test.ts and the parity-gate e2e + // at tests/e2e/kernel/results-e2e.test.ts. }); -describe('SeaOperationBackend — async (submitStatement) path', () => { +describe('KernelOperationBackend — async (submitStatement) path', () => { const makeAsyncOp = (asyncStatement: FakeAsyncStatement, queryTimeoutSecs?: number) => // eslint-disable-next-line @typescript-eslint/no-explicit-any - new SeaOperationBackend({ asyncStatement: asyncStatement as any, context: makeContext(), queryTimeoutSecs }); + new KernelOperationBackend({ asyncStatement: asyncStatement as any, context: makeContext(), queryTimeoutSecs }); it('rejects when neither asyncStatement nor statement is provided', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(() => new SeaOperationBackend({ context: makeContext() } as any)).to.throw(HiveDriverError, /exactly one/); + expect(() => new KernelOperationBackend({ context: makeContext() } as any)).to.throw( + HiveDriverError, + /exactly one/, + ); }); it('rejects when BOTH asyncStatement and statement are provided', () => { expect( () => - new SeaOperationBackend({ + new KernelOperationBackend({ // eslint-disable-next-line @typescript-eslint/no-explicit-any asyncStatement: new FakeAsyncStatement() as any, statement: new FakeNativeStatement(), @@ -1016,9 +1019,9 @@ describe('SeaOperationBackend — async (submitStatement) path', () => { }); }); -describe('SeaOperationBackend — sync (executeStatementCancellable) path', () => { +describe('KernelOperationBackend — sync (executeStatementCancellable) path', () => { const makeSyncOp = (cancellableExecution: FakeCancellableExecution, queryTimeoutSecs?: number) => - new SeaOperationBackend({ + new KernelOperationBackend({ // eslint-disable-next-line @typescript-eslint/no-explicit-any cancellableExecution: cancellableExecution as any, context: makeContext(), @@ -1028,7 +1031,7 @@ describe('SeaOperationBackend — sync (executeStatementCancellable) path', () = it('rejects when more than one handle kind is provided', () => { expect( () => - new SeaOperationBackend({ + new KernelOperationBackend({ // eslint-disable-next-line @typescript-eslint/no-explicit-any cancellableExecution: new FakeCancellableExecution() as any, statement: new FakeNativeStatement(), @@ -1043,7 +1046,7 @@ describe('SeaOperationBackend — sync (executeStatementCancellable) path', () = // summary). The server statement_id is surfaced via a debug log instead. const logs: Array<{ level: LogLevel; message: string }> = []; const logger: IDBSQLLogger = { log: (level, message) => logs.push({ level, message }) }; - const op = new SeaOperationBackend({ + const op = new KernelOperationBackend({ // eslint-disable-next-line @typescript-eslint/no-explicit-any cancellableExecution: new FakeCancellableExecution() as any, context: makeContext(logger), diff --git a/tests/unit/sea/inputValidation.test.ts b/tests/unit/kernel/inputValidation.test.ts similarity index 92% rename from tests/unit/sea/inputValidation.test.ts rename to tests/unit/kernel/inputValidation.test.ts index 6564b694..a5e11af1 100644 --- a/tests/unit/sea/inputValidation.test.ts +++ b/tests/unit/kernel/inputValidation.test.ts @@ -14,11 +14,11 @@ import { expect } from 'chai'; import Int64 from 'node-int64'; -import assertBindableValue from '../../../lib/sea/SeaInputValidation'; +import assertBindableValue from '../../../lib/kernel/KernelInputValidation'; import { DBSQLParameter, DBSQLParameterType } from '../../../lib/DBSQLParameter'; import ParameterError from '../../../lib/errors/ParameterError'; -describe('SeaInputValidation.assertBindableValue', () => { +describe('KernelInputValidation.assertBindableValue', () => { it('accepts scalars, Date, Int64, bigint, null, and DBSQLParameter', () => { expect(() => assertBindableValue(42, 'p')).to.not.throw(); expect(() => assertBindableValue('x', 'p')).to.not.throw(); diff --git a/tests/unit/sea/loader.test.ts b/tests/unit/kernel/loader.test.ts similarity index 83% rename from tests/unit/sea/loader.test.ts rename to tests/unit/kernel/loader.test.ts index 3eb642ac..a6105918 100644 --- a/tests/unit/sea/loader.test.ts +++ b/tests/unit/kernel/loader.test.ts @@ -13,9 +13,9 @@ // limitations under the License. import { expect } from 'chai'; -import { SeaNativeLoader, SeaNativeBinding } from '../../../lib/sea/SeaNativeLoader'; +import { KernelNativeLoader, KernelNativeBinding } from '../../../lib/kernel/KernelNativeLoader'; -// Pure-logic tests for SeaNativeLoader. These exercise the load-failure +// Pure-logic tests for KernelNativeLoader. These exercise the load-failure // hint branches, the Node-version gate, the shape check, and caching via // the injectable `load` and `nodeMajor` seams — so they run everywhere // regardless of whether a real `.node` is installed on the test machine @@ -25,7 +25,7 @@ import { SeaNativeLoader, SeaNativeBinding } from '../../../lib/sea/SeaNativeLoa // circuits them; the gate's own tests inject the version under test. const SUPPORTED_NODE_MAJOR = () => 18; -function stubBinding(overrides: Partial> = {}): SeaNativeBinding { +function stubBinding(overrides: Partial> = {}): KernelNativeBinding { return { version: () => '1.2.3', openSession: async () => ({}), @@ -35,7 +35,7 @@ function stubBinding(overrides: Partial> AsyncResultHandle: function AsyncResultHandle() {}, CancellableExecution: function CancellableExecution() {}, ...overrides, - } as unknown as SeaNativeBinding; + } as unknown as KernelNativeBinding; } function errWithCode(code: string, message: string): NodeJS.ErrnoException { @@ -56,11 +56,11 @@ function thrownMessage(fn: () => unknown): string { return expect.fail('expected the call to throw, but it did not') as never; } -describe('SeaNativeLoader', () => { +describe('KernelNativeLoader', () => { describe('successful load', () => { it('get() returns the binding from the injected loader', () => { const binding = stubBinding(); - const loader = new SeaNativeLoader(() => binding, SUPPORTED_NODE_MAJOR); + const loader = new KernelNativeLoader(() => binding, SUPPORTED_NODE_MAJOR); expect(loader.get()).to.equal(binding); expect(loader.tryGet()).to.equal(binding); }); @@ -68,7 +68,7 @@ describe('SeaNativeLoader', () => { it('caches the result — the load function runs at most once', () => { let calls = 0; const binding = stubBinding(); - const loader = new SeaNativeLoader(() => { + const loader = new KernelNativeLoader(() => { calls += 1; return binding; }, SUPPORTED_NODE_MAJOR); @@ -81,8 +81,8 @@ describe('SeaNativeLoader', () => { describe('load-failure hints', () => { it('MODULE_NOT_FOUND → "not installed" hint pointing at the README', () => { - const loader = new SeaNativeLoader(() => { - throw errWithCode('MODULE_NOT_FOUND', "Cannot find module '../../native/sea'"); + const loader = new KernelNativeLoader(() => { + throw errWithCode('MODULE_NOT_FOUND', "Cannot find module '../../native/kernel'"); }, SUPPORTED_NODE_MAJOR); expect(loader.tryGet()).to.equal(undefined); const msg = thrownMessage(() => loader.get()); @@ -91,7 +91,7 @@ describe('SeaNativeLoader', () => { }); it('ERR_DLOPEN_FAILED → includes the underlying dlerror string and remediation', () => { - const loader = new SeaNativeLoader(() => { + const loader = new KernelNativeLoader(() => { throw errWithCode('ERR_DLOPEN_FAILED', 'GLIBC_2.32 not found'); }, SUPPORTED_NODE_MAJOR); const msg = thrownMessage(() => loader.get()); @@ -101,14 +101,14 @@ describe('SeaNativeLoader', () => { }); it('a generic Error (no code) preserves its message', () => { - const loader = new SeaNativeLoader(() => { + const loader = new KernelNativeLoader(() => { throw new Error('totally unexpected'); }, SUPPORTED_NODE_MAJOR); expect(() => loader.get()).to.throw(/totally unexpected/); }); it('a non-Error throw is wrapped', () => { - const loader = new SeaNativeLoader(() => { + const loader = new KernelNativeLoader(() => { // eslint-disable-next-line no-throw-literal throw 'a string'; }, SUPPORTED_NODE_MAJOR); @@ -118,7 +118,7 @@ describe('SeaNativeLoader', () => { describe('shape check', () => { it('rejects a binding missing an expected export', () => { - const loader = new SeaNativeLoader(() => stubBinding({ openSession: undefined }), SUPPORTED_NODE_MAJOR); + const loader = new KernelNativeLoader(() => stubBinding({ openSession: undefined }), SUPPORTED_NODE_MAJOR); expect(loader.tryGet()).to.equal(undefined); const msg = thrownMessage(() => loader.get()); expect(msg).to.match(/missing expected export/); @@ -129,7 +129,7 @@ describe('SeaNativeLoader', () => { // An older cached .node would load but crash mid-query at e.g. // `submitStatement` / `executeStatementCancellable`; fail fast at load. for (const cls of ['AsyncStatement', 'AsyncResultHandle', 'CancellableExecution'] as const) { - const loader = new SeaNativeLoader(() => stubBinding({ [cls]: undefined }), SUPPORTED_NODE_MAJOR); + const loader = new KernelNativeLoader(() => stubBinding({ [cls]: undefined }), SUPPORTED_NODE_MAJOR); const msg = thrownMessage(() => loader.get()); expect(msg, cls).to.match(/missing expected export/); expect(msg, cls).to.contain(cls); @@ -140,7 +140,7 @@ describe('SeaNativeLoader', () => { describe('Node-version gate', () => { it('fails closed on a Node version below the floor', () => { let loadCalled = false; - const loader = new SeaNativeLoader( + const loader = new KernelNativeLoader( () => { loadCalled = true; return stubBinding(); @@ -152,7 +152,7 @@ describe('SeaNativeLoader', () => { }); it('fails closed when the Node version is unparseable (NaN)', () => { - const loader = new SeaNativeLoader( + const loader = new KernelNativeLoader( () => stubBinding(), () => NaN, ); diff --git a/tests/unit/sea/native-packaging.test.ts b/tests/unit/kernel/native-packaging.test.ts similarity index 83% rename from tests/unit/sea/native-packaging.test.ts rename to tests/unit/kernel/native-packaging.test.ts index b2732673..93cc4e69 100644 --- a/tests/unit/sea/native-packaging.test.ts +++ b/tests/unit/kernel/native-packaging.test.ts @@ -18,15 +18,15 @@ import { join } from 'path'; // Guards the napi-rs router's per-platform npm package names. A misconfigured // `npmName` once baked the M0 triple into the prefix for *every* platform -// (e.g. `@databricks/sea-native-linux-x64-gnu-darwin-arm64`, and the doubled -// `@databricks/sea-native-linux-x64-gnu-linux-x64-gnu`), so a published +// (e.g. `@databricks/kernel-native-linux-x64-gnu-darwin-arm64`, and the doubled +// `@databricks/kernel-native-linux-x64-gnu-linux-x64-gnu`), so a published // install would never resolve a `.node`. The canonical name is -// `@databricks/sql-kernel-` (see native/sea/README.md and the -// SeaNativeLoader load-failure hint). -describe('SEA native binding — packaging (native/sea/index.js)', () => { +// `@databricks/sql-kernel-` (see native/kernel/README.md and the +// KernelNativeLoader load-failure hint). +describe('SEA native binding — packaging (native/kernel/index.js)', () => { // Resolved from the repo root (the cwd for `npm test`) so the test does not // depend on the module system's `__dirname`. - const indexJs = readFileSync(join(process.cwd(), 'native/sea/index.js'), 'utf8'); + const indexJs = readFileSync(join(process.cwd(), 'native/kernel/index.js'), 'utf8'); // Every `require('@databricks/...')` fallback in the generated router. const required = Array.from(indexJs.matchAll(/require\('(@databricks\/[^']+)'\)/g)).map((m) => m[1]); diff --git a/tests/unit/sea/operation-lifecycle.test.ts b/tests/unit/kernel/operation-lifecycle.test.ts similarity index 82% rename from tests/unit/sea/operation-lifecycle.test.ts rename to tests/unit/kernel/operation-lifecycle.test.ts index 1148cd47..a17ff021 100644 --- a/tests/unit/sea/operation-lifecycle.test.ts +++ b/tests/unit/kernel/operation-lifecycle.test.ts @@ -14,12 +14,12 @@ /** * Unit tests for the SEA operation lifecycle (`cancel`, `close`, - * `finished`) — both via the `SeaOperationLifecycle` helpers and - * via `SeaOperationBackend` which composes them. + * `finished`) — both via the `KernelOperationLifecycle` helpers and + * via `KernelOperationBackend` which composes them. * * We mock the napi binding's `Statement` handle so the test process * doesn't touch any native code; the helpers and the backend are - * structurally typed against `SeaStatementHandle` exactly so this + * structurally typed against `KernelStatementHandle` exactly so this * works. */ @@ -29,14 +29,14 @@ import { OperationStatus, OperationState } from '../../../lib/contracts/Operatio import IClientContext from '../../../lib/contracts/IClientContext'; import IDBSQLLogger, { LogLevel } from '../../../lib/contracts/IDBSQLLogger'; import { - SeaStatementHandle, + KernelStatementHandle, createLifecycleState, - seaCancel, - seaClose, - seaFinished, + kernelCancel, + kernelClose, + kernelFinished, failIfNotActive, -} from '../../../lib/sea/SeaOperationLifecycle'; -import SeaOperationBackend from '../../../lib/sea/SeaOperationBackend'; +} from '../../../lib/kernel/KernelOperationLifecycle'; +import KernelOperationBackend from '../../../lib/kernel/KernelOperationBackend'; import OperationStateError, { OperationStateErrorCode } from '../../../lib/errors/OperationStateError'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; @@ -65,8 +65,8 @@ function makeContext(): IClientContext { } as unknown as IClientContext; } -function makeStatement(overrides: Partial = {}): { - handle: SeaStatementHandle; +function makeStatement(overrides: Partial = {}): { + handle: KernelStatementHandle; cancel: sinon.SinonStub; close: sinon.SinonStub; } { @@ -79,14 +79,14 @@ function makeStatement(overrides: Partial = {}): { }; } -describe('SeaOperationLifecycle (helpers)', () => { - describe('seaCancel', () => { +describe('KernelOperationLifecycle (helpers)', () => { + describe('kernelCancel', () => { it('calls statement.cancel() and resolves with a success Status', async () => { const ctx = makeContext(); const { handle, cancel } = makeStatement(); const state = createLifecycleState(); - const status = await seaCancel(state, handle, ctx, 'op-id-1'); + const status = await kernelCancel(state, handle, ctx, 'op-id-1'); expect(cancel.calledOnce).to.equal(true); expect(status.isSuccess).to.equal(true); @@ -98,8 +98,8 @@ describe('SeaOperationLifecycle (helpers)', () => { const { handle, cancel } = makeStatement(); const state = createLifecycleState(); - await seaCancel(state, handle, ctx, 'op-id-2'); - await seaCancel(state, handle, ctx, 'op-id-2'); + await kernelCancel(state, handle, ctx, 'op-id-2'); + await kernelCancel(state, handle, ctx, 'op-id-2'); expect(cancel.calledOnce).to.equal(true); }); @@ -110,7 +110,7 @@ describe('SeaOperationLifecycle (helpers)', () => { const state = createLifecycleState(); state.isClosed = true; - const status = await seaCancel(state, handle, ctx, 'op-id-3'); + const status = await kernelCancel(state, handle, ctx, 'op-id-3'); expect(cancel.called).to.equal(false); expect(status.isSuccess).to.equal(true); @@ -125,14 +125,14 @@ describe('SeaOperationLifecycle (helpers)', () => { const cancelPromise = new Promise((resolve) => { release = resolve; }); - const handle: SeaStatementHandle = { + const handle: KernelStatementHandle = { cancel: () => cancelPromise, close: async () => undefined, }; - const inflight = seaCancel(state, handle, ctx, 'op-id-4'); + const inflight = kernelCancel(state, handle, ctx, 'op-id-4'); - // Yield once so the synchronous prelude of seaCancel runs. + // Yield once so the synchronous prelude of kernelCancel runs. await Promise.resolve(); expect(state.isCancelled).to.equal(true); // Before the await resolves, failIfNotActive must already throw. @@ -146,7 +146,7 @@ describe('SeaOperationLifecycle (helpers)', () => { it('propagates binding errors via the kernel error mapping', async () => { const ctx = makeContext(); const state = createLifecycleState(); - const handle: SeaStatementHandle = { + const handle: KernelStatementHandle = { cancel: async () => { // Simulate the binding's JSON-envelope error format. const payload = JSON.stringify({ @@ -160,7 +160,7 @@ describe('SeaOperationLifecycle (helpers)', () => { let thrown: unknown; try { - await seaCancel(state, handle, ctx, 'op-err-1'); + await kernelCancel(state, handle, ctx, 'op-err-1'); } catch (err) { thrown = err; } @@ -178,19 +178,19 @@ describe('SeaOperationLifecycle (helpers)', () => { const { handle } = makeStatement(); const state = createLifecycleState(); - await seaCancel(state, handle, ctx, 'op-id-log'); + await kernelCancel(state, handle, ctx, 'op-id-log'); expect(logger.entries.some((e) => e.level === LogLevel.debug && e.message.includes('op-id-log'))).to.equal(true); }); }); - describe('seaClose', () => { + describe('kernelClose', () => { it('calls statement.close() and resolves with a success Status', async () => { const ctx = makeContext(); const { handle, close } = makeStatement(); const state = createLifecycleState(); - const status = await seaClose(state, handle, ctx, 'op-close-1'); + const status = await kernelClose(state, handle, ctx, 'op-close-1'); expect(close.calledOnce).to.equal(true); expect(status.isSuccess).to.equal(true); @@ -202,8 +202,8 @@ describe('SeaOperationLifecycle (helpers)', () => { const { handle, close } = makeStatement(); const state = createLifecycleState(); - await seaClose(state, handle, ctx, 'op-close-2'); - await seaClose(state, handle, ctx, 'op-close-2'); + await kernelClose(state, handle, ctx, 'op-close-2'); + await kernelClose(state, handle, ctx, 'op-close-2'); expect(close.calledOnce).to.equal(true); }); @@ -211,7 +211,7 @@ describe('SeaOperationLifecycle (helpers)', () => { it('propagates binding errors via the kernel error mapping', async () => { const ctx = makeContext(); const state = createLifecycleState(); - const handle: SeaStatementHandle = { + const handle: KernelStatementHandle = { cancel: async () => undefined, close: async () => { const payload = JSON.stringify({ @@ -224,7 +224,7 @@ describe('SeaOperationLifecycle (helpers)', () => { let thrown: unknown; try { - await seaClose(state, handle, ctx, 'op-err-close'); + await kernelClose(state, handle, ctx, 'op-err-close'); } catch (err) { thrown = err; } @@ -233,11 +233,11 @@ describe('SeaOperationLifecycle (helpers)', () => { }); }); - describe('seaFinished', () => { + describe('kernelFinished', () => { it('resolves immediately when no callback is provided (M0 no-op)', async () => { const state = createLifecycleState(); const start = Date.now(); - await seaFinished(state); + await kernelFinished(state); // Should be near-instantaneous — no 100ms poll. expect(Date.now() - start).to.be.lessThan(50); }); @@ -246,7 +246,7 @@ describe('SeaOperationLifecycle (helpers)', () => { const state = createLifecycleState(); const callback = sinon.stub(); - await seaFinished(state, { callback }); + await kernelFinished(state, { callback }); expect(callback.calledOnce).to.equal(true); const arg = callback.firstCall.args[0] as OperationStatus; @@ -262,7 +262,7 @@ describe('SeaOperationLifecycle (helpers)', () => { resolvedInsideCallback = true; }; - await seaFinished(state, { callback }); + await kernelFinished(state, { callback }); expect(resolvedInsideCallback).to.equal(true); }); @@ -272,7 +272,7 @@ describe('SeaOperationLifecycle (helpers)', () => { state.isCancelled = true; const callback = sinon.stub(); - await seaFinished(state, { callback }); + await kernelFinished(state, { callback }); expect(callback.called).to.equal(false); }); @@ -314,11 +314,11 @@ describe('SeaOperationLifecycle (helpers)', () => { }); }); -describe('SeaOperationBackend (lifecycle integration)', () => { +describe('KernelOperationBackend (lifecycle integration)', () => { it('cancel() forwards to statement.cancel()', async () => { const ctx = makeContext(); const { handle, cancel } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); const status = await op.cancel(); @@ -329,7 +329,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('close() forwards to statement.close()', async () => { const ctx = makeContext(); const { handle, close } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); const status = await op.close(); @@ -340,7 +340,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('finished() resolves immediately and fires the callback once', async () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); const responses: OperationStatus[] = []; const start = Date.now(); @@ -354,7 +354,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('fetchChunk after cancel throws the cancellation error', async () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); await op.cancel(); @@ -371,7 +371,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('cancel() is idempotent across the backend surface', async () => { const ctx = makeContext(); const { handle, cancel } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); await op.cancel(); await op.cancel(); @@ -383,7 +383,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('close() is idempotent across the backend surface', async () => { const ctx = makeContext(); const { handle, close } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); await op.close(); await op.close(); @@ -394,7 +394,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('status() reports FINISHED_STATE when active', async () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); const status = await op.status(false); expect(status.state).to.equal(OperationState.Succeeded); @@ -403,7 +403,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('status() reports CANCELED_STATE after cancel', async () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); await op.cancel(); const status = await op.status(false); @@ -413,7 +413,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('id getter is stable', () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx, id: 'fixed-id' }); + const op = new KernelOperationBackend({ statement: handle, context: ctx, id: 'fixed-id' }); expect(op.id).to.equal('fixed-id'); expect(op.id).to.equal('fixed-id'); @@ -422,7 +422,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('id getter defaults to a uuid when none is supplied', () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); // RFC4122 v4 — 36 chars with hyphens at positions 8/13/18/23. expect(op.id).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/); @@ -431,7 +431,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('hasResultSet is true by default (kernel always streams)', () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); expect(op.hasResultSet()).to.equal(true); }); diff --git a/tests/unit/sea/positionalParams.test.ts b/tests/unit/kernel/positionalParams.test.ts similarity index 82% rename from tests/unit/sea/positionalParams.test.ts rename to tests/unit/kernel/positionalParams.test.ts index d9902303..f6070147 100644 --- a/tests/unit/sea/positionalParams.test.ts +++ b/tests/unit/kernel/positionalParams.test.ts @@ -13,18 +13,18 @@ // limitations under the License. import { expect } from 'chai'; -import { buildSeaPositionalParams, buildSeaNamedParams } from '../../../lib/sea/SeaPositionalParams'; +import { buildKernelPositionalParams, buildKernelNamedParams } from '../../../lib/kernel/KernelPositionalParams'; import { DBSQLParameter, DBSQLParameterType } from '../../../lib/DBSQLParameter'; import ParameterError from '../../../lib/errors/ParameterError'; -describe('SeaPositionalParams.buildSeaPositionalParams', () => { +describe('KernelPositionalParams.buildKernelPositionalParams', () => { it('returns undefined for no params (keeps the no-options fast path)', () => { - expect(buildSeaPositionalParams(undefined)).to.equal(undefined); - expect(buildSeaPositionalParams([])).to.equal(undefined); + expect(buildKernelPositionalParams(undefined)).to.equal(undefined); + expect(buildKernelPositionalParams([])).to.equal(undefined); }); it('infers types from raw values, matching DBSQLParameter rules', () => { - expect(buildSeaPositionalParams([42, 'hello', true])).to.deep.equal([ + expect(buildKernelPositionalParams([42, 'hello', true])).to.deep.equal([ { sqlType: 'INTEGER', value: '42' }, { sqlType: 'STRING', value: 'hello' }, { sqlType: 'BOOLEAN', value: 'TRUE' }, @@ -32,7 +32,7 @@ describe('SeaPositionalParams.buildSeaPositionalParams', () => { }); const decimal = (value: string) => () => - buildSeaPositionalParams([new DBSQLParameter({ type: DBSQLParameterType.DECIMAL, value })]); + buildKernelPositionalParams([new DBSQLParameter({ type: DBSQLParameterType.DECIMAL, value })]); it('emits DECIMAL in the parenthesised DECIMAL(p,s) form the kernel codec requires', () => { expect(decimal('99.99')()).to.deep.equal([{ sqlType: 'DECIMAL(4,2)', value: '99.99' }]); @@ -65,7 +65,7 @@ describe('SeaPositionalParams.buildSeaPositionalParams', () => { it('collapses every INTERVAL subtype to the kernel codec\'s single "INTERVAL" type name', () => { expect( - buildSeaPositionalParams([ + buildKernelPositionalParams([ new DBSQLParameter({ type: DBSQLParameterType.INTERVALMONTH, value: '13' }), new DBSQLParameter({ type: DBSQLParameterType.INTERVALDAY, value: '1 02:03:04' }), ]), @@ -76,12 +76,12 @@ describe('SeaPositionalParams.buildSeaPositionalParams', () => { }); it('maps NULL to a value-less VOID input', () => { - expect(buildSeaPositionalParams([null])).to.deep.equal([{ sqlType: 'VOID' }]); + expect(buildKernelPositionalParams([null])).to.deep.equal([{ sqlType: 'VOID' }]); }); it('honours explicit DATE / TIMESTAMP types', () => { expect( - buildSeaPositionalParams([ + buildKernelPositionalParams([ new DBSQLParameter({ type: DBSQLParameterType.DATE, value: '2024-01-15' }), new DBSQLParameter({ type: DBSQLParameterType.TIMESTAMP, value: '2024-01-15 10:30:00' }), ]), @@ -93,7 +93,7 @@ describe('SeaPositionalParams.buildSeaPositionalParams', () => { it('binds TIMESTAMP_NTZ natively and TIMESTAMP_LTZ as TIMESTAMP (Spark has no distinct LTZ type)', () => { expect( - buildSeaPositionalParams([ + buildKernelPositionalParams([ new DBSQLParameter({ type: DBSQLParameterType.TIMESTAMP_NTZ, value: '2024-01-15 10:30:00' }), new DBSQLParameter({ type: DBSQLParameterType.TIMESTAMP_LTZ, value: '2024-01-15 10:30:00' }), ]), @@ -104,15 +104,15 @@ describe('SeaPositionalParams.buildSeaPositionalParams', () => { }); }); -describe('SeaPositionalParams.buildSeaNamedParams', () => { +describe('KernelPositionalParams.buildKernelNamedParams', () => { it('returns undefined for no named params', () => { - expect(buildSeaNamedParams(undefined)).to.equal(undefined); - expect(buildSeaNamedParams({})).to.equal(undefined); + expect(buildKernelNamedParams(undefined)).to.equal(undefined); + expect(buildKernelNamedParams({})).to.equal(undefined); }); it('emits {name, sqlType, value} triples, reusing the same type mapping', () => { expect( - buildSeaNamedParams({ + buildKernelNamedParams({ n: 42, s: 'hello', d: new DBSQLParameter({ type: DBSQLParameterType.DECIMAL, value: '99.99' }), @@ -125,6 +125,6 @@ describe('SeaPositionalParams.buildSeaNamedParams', () => { }); it('maps a named NULL to a value-less VOID input (with the name)', () => { - expect(buildSeaNamedParams({ x: null })).to.deep.equal([{ name: 'x', sqlType: 'VOID' }]); + expect(buildKernelNamedParams({ x: null })).to.deep.equal([{ name: 'x', sqlType: 'VOID' }]); }); }); diff --git a/tests/unit/sea/serverInfo.test.ts b/tests/unit/kernel/serverInfo.test.ts similarity index 50% rename from tests/unit/sea/serverInfo.test.ts rename to tests/unit/kernel/serverInfo.test.ts index 4a027864..a8568de6 100644 --- a/tests/unit/sea/serverInfo.test.ts +++ b/tests/unit/kernel/serverInfo.test.ts @@ -13,35 +13,40 @@ // limitations under the License. import { expect } from 'chai'; -import { seaServerInfoValue, SEA_DBMS_NAME, SEA_SERVER_NAME, SEA_DBMS_VERSION } from '../../../lib/sea/SeaServerInfo'; +import { + kernelServerInfoValue, + KERNEL_DBMS_NAME, + KERNEL_SERVER_NAME, + KERNEL_DBMS_VERSION, +} from '../../../lib/kernel/KernelServerInfo'; import { TGetInfoType } from '../../../thrift/TCLIService_types'; -describe('SeaServerInfo.seaServerInfoValue', () => { +describe('KernelServerInfo.kernelServerInfoValue', () => { it('CLI_DBMS_NAME matches Thrift exactly ("Spark SQL")', () => { - expect(seaServerInfoValue(TGetInfoType.CLI_DBMS_NAME)?.stringValue).to.equal('Spark SQL'); - expect(SEA_DBMS_NAME).to.equal('Spark SQL'); + expect(kernelServerInfoValue(TGetInfoType.CLI_DBMS_NAME)?.stringValue).to.equal('Spark SQL'); + expect(KERNEL_DBMS_NAME).to.equal('Spark SQL'); }); it('CLI_DBMS_VER matches the Thrift server version constant', () => { - expect(seaServerInfoValue(TGetInfoType.CLI_DBMS_VER)?.stringValue).to.equal('3.1.1'); - expect(SEA_DBMS_VERSION).to.equal('3.1.1'); + expect(kernelServerInfoValue(TGetInfoType.CLI_DBMS_VER)?.stringValue).to.equal('3.1.1'); + expect(KERNEL_DBMS_VERSION).to.equal('3.1.1'); }); it('CLI_SERVER_NAME matches Thrift exactly ("Spark SQL")', () => { - const v = seaServerInfoValue(TGetInfoType.CLI_SERVER_NAME)?.stringValue; - expect(v).to.equal(SEA_SERVER_NAME); + const v = kernelServerInfoValue(TGetInfoType.CLI_SERVER_NAME)?.stringValue; + expect(v).to.equal(KERNEL_SERVER_NAME); expect(v).to.equal('Spark SQL'); }); it('all three answered info types are byte-identical to Thrift', () => { - expect(seaServerInfoValue(TGetInfoType.CLI_SERVER_NAME)?.stringValue).to.equal('Spark SQL'); - expect(seaServerInfoValue(TGetInfoType.CLI_DBMS_NAME)?.stringValue).to.equal('Spark SQL'); - expect(seaServerInfoValue(TGetInfoType.CLI_DBMS_VER)?.stringValue).to.equal('3.1.1'); + expect(kernelServerInfoValue(TGetInfoType.CLI_SERVER_NAME)?.stringValue).to.equal('Spark SQL'); + expect(kernelServerInfoValue(TGetInfoType.CLI_DBMS_NAME)?.stringValue).to.equal('Spark SQL'); + expect(kernelServerInfoValue(TGetInfoType.CLI_DBMS_VER)?.stringValue).to.equal('3.1.1'); }); it('returns undefined for info types the Thrift server rejects (e.g. CLI_MAX_DRIVER_CONNECTIONS)', () => { - expect(seaServerInfoValue(TGetInfoType.CLI_MAX_DRIVER_CONNECTIONS)).to.equal(undefined); - expect(seaServerInfoValue(TGetInfoType.CLI_USER_NAME)).to.equal(undefined); - expect(seaServerInfoValue(99999)).to.equal(undefined); + expect(kernelServerInfoValue(TGetInfoType.CLI_MAX_DRIVER_CONNECTIONS)).to.equal(undefined); + expect(kernelServerInfoValue(TGetInfoType.CLI_USER_NAME)).to.equal(undefined); + expect(kernelServerInfoValue(99999)).to.equal(undefined); }); }); diff --git a/tests/unit/sea/version.test.ts b/tests/unit/kernel/version.test.ts similarity index 90% rename from tests/unit/sea/version.test.ts rename to tests/unit/kernel/version.test.ts index 24a05d7a..36ef9b98 100644 --- a/tests/unit/sea/version.test.ts +++ b/tests/unit/kernel/version.test.ts @@ -13,12 +13,12 @@ // limitations under the License. import { expect } from 'chai'; -import { tryGetSeaNative } from '../../../lib/sea/SeaNativeLoader'; +import { tryGetKernelNative } from '../../../lib/kernel/KernelNativeLoader'; // Fail loudly only when the binding is actually expected to be present — // i.e. a CI step has provisioned it (a published `@databricks/sql-kernel-*` // optional dep installed, or `npm run build:native` was run) and opts in via -// `SEA_NATIVE_EXPECTED=1`. A missing binding there is a real packaging / build +// `KERNEL_NATIVE_EXPECTED=1`. A missing binding there is a real packaging / build // regression that a silent skip would mask. // // Until those binding packages are published, the standard CI cannot install @@ -26,11 +26,11 @@ import { tryGetSeaNative } from '../../../lib/sea/SeaNativeLoader'; // legitimately absent — default to a skip rather than a spurious hard failure. // (`npm ci` already skips the unpublished optional dep.) function bindingIsExpected(): boolean { - return process.env.SEA_NATIVE_EXPECTED === '1'; + return process.env.KERNEL_NATIVE_EXPECTED === '1'; } describe('SEA native binding — smoke test', function smoke() { - const binding = tryGetSeaNative(); + const binding = tryGetKernelNative(); if (binding === undefined) { if (bindingIsExpected()) {