-
Notifications
You must be signed in to change notification settings - Fork 49
Expand file tree
/
Copy pathconnectionOptions.test.ts
More file actions
161 lines (138 loc) · 6.75 KB
/
Copy pathconnectionOptions.test.ts
File metadata and controls
161 lines (138 loc) · 6.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright (c) 2026 Databricks, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { expect } from 'chai';
import { buildSeaConnectionOptions, buildSeaTlsOptions, buildSeaRetryOptions } from '../../../lib/sea/SeaAuth';
import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient';
import HiveDriverError from '../../../lib/errors/HiveDriverError';
const PAT = { host: 'h.databricks.com', path: '/sql/1.0/warehouses/abc', token: 'dapi-x' };
// Cast helper: the SEA connection-tuning/TLS options live on the internal
// surface, so tests build untyped option literals.
const opts = (extra: Record<string, unknown>) => ({ ...PAT, ...extra }) as unknown as ConnectionOptions;
describe('SeaAuth connection options — intervalsAsString default', () => {
it('always sets intervalsAsString:true (thrift-compatible interval rendering)', () => {
const native = buildSeaConnectionOptions(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 };
expect(native.complexTypesAsJson).to.equal(undefined);
});
});
describe('SeaAuth connection options — maxConnections', () => {
it('forwards a valid positive integer', () => {
const native = buildSeaConnectionOptions(opts({ maxConnections: 10 })) as { maxConnections?: number };
expect(native.maxConnections).to.equal(10);
});
it('omits maxConnections when unset', () => {
const native = buildSeaConnectionOptions(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(
HiveDriverError,
/positive integer/,
);
});
}
it('rejects maxConnections beyond the u32 limit', () => {
expect(() => buildSeaConnectionOptions(opts({ maxConnections: 0x1_0000_0000 }))).to.throw(
HiveDriverError,
/u32 limit/,
);
});
});
describe('SeaAuth TLS options (buildSeaTlsOptions)', () => {
it('is empty by default (secure-by-default — kernel default verify-on)', () => {
expect(buildSeaTlsOptions(opts({}))).to.deep.equal({});
});
it('passes checkServerCertificate through verbatim (including false)', () => {
expect(buildSeaTlsOptions(opts({ checkServerCertificate: false }))).to.deep.equal({
checkServerCertificate: false,
});
expect(buildSeaTlsOptions(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 }));
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);
});
it('rejects a non-PEM string', () => {
expect(() => buildSeaTlsOptions(opts({ customCaCert: 'not-a-pem' }))).to.throw(HiveDriverError, /PEM certificate/);
});
it('rejects out-of-order / partial PEM markers (ordered match, not two substrings)', () => {
// END-before-BEGIN, BEGIN-only, and END-only must all fail — a blob that
// merely *contains* both literals (e.g. a proxy-intercept page) is not a cert.
const reversed = '-----END CERTIFICATE-----\nMIIB...\n-----BEGIN CERTIFICATE-----';
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/);
}
});
it('rejects an empty Buffer', () => {
expect(() => buildSeaTlsOptions(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/);
});
it('folds TLS options into the full connection options', () => {
const native = buildSeaConnectionOptions(opts({ checkServerCertificate: false })) as {
checkServerCertificate?: boolean;
};
expect(native.checkServerCertificate).to.equal(false);
});
});
describe('SeaAuth retry options — buildSeaRetryOptions', () => {
// The driver's ClientConfig retry defaults (ms / total-attempt count).
const defaults = {
retryMaxAttempts: 5,
retriesTimeout: 15 * 60 * 1000,
retryDelayMin: 1000,
retryDelayMax: 60 * 1000,
};
it('converts the connector ms knobs to the kernel whole-second kwargs', () => {
const r = buildSeaRetryOptions(defaults);
expect(r.retryMinWaitSecs).to.equal(1); // 1000ms
expect(r.retryMaxWaitSecs).to.equal(60); // 60000ms
expect(r.retryOverallTimeoutSecs).to.equal(900); // 15min
});
it('passes retryMaxAttempts through as a TOTAL attempt count (kernel converts to retries)', () => {
expect(buildSeaRetryOptions({ ...defaults, retryMaxAttempts: 5 }).retryMaxAttempts).to.equal(5);
expect(buildSeaRetryOptions({ ...defaults, retryMaxAttempts: 0 }).retryMaxAttempts).to.equal(0);
});
it('rounds sub-second delays to the nearest second (kernel granularity)', () => {
const r = buildSeaRetryOptions({ ...defaults, retryDelayMin: 1500, retryDelayMax: 2400 });
expect(r.retryMinWaitSecs).to.equal(2); // 1.5s → 2
expect(r.retryMaxWaitSecs).to.equal(2); // 2.4s → 2
});
it('clamps negative/garbage inputs into the napi u32 range', () => {
const r = buildSeaRetryOptions({
retryMaxAttempts: -3,
retriesTimeout: -1,
retryDelayMin: -1000,
retryDelayMax: 0,
});
expect(r.retryMaxAttempts).to.equal(0);
expect(r.retryMinWaitSecs).to.equal(0);
expect(r.retryOverallTimeoutSecs).to.equal(0);
});
});