blob: 0315dfcd950c8cf6cd16de90a00cc7c48e16a320 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 { test } from 'node:test'
import assert from 'node:assert/strict'
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs'
import { join, isAbsolute } from 'node:path'
import { tmpdir } from 'node:os'
import { AdbcDatabase, AdbcError } from '../lib/index.js'
const testLib = process.env.ADBC_DRIVER_MANAGER_TEST_LIB
function tomlPath(p: string): string {
return p.replaceAll('\\', '/')
}
function writeProfileToml(dir: string, name: string, driver: string, options: Record<string, string> = {}): void {
const optLines = Object.entries(options)
.map(([k, v]) => `${k} = "${tomlPath(v)}"`)
.join('\n')
writeFileSync(
join(dir, `${name}.toml`),
`profile_version = 1\ndriver = "${tomlPath(driver)}"\n\n[Options]\n${optLines}\n`,
)
}
test('profile: load database from profile:// URI', async () => {
const driver = testLib ?? 'sqlite'
const tmpDir = mkdtempSync(join(tmpdir(), 'adbc-profile-test-'))
try {
// Create a SQLite file with a known table so we can confirm the profile
// is actually pointing us at the right database.
const dbPath = join(tmpDir, 'test.db')
const setupDb = new AdbcDatabase({ driver, databaseOptions: { uri: dbPath } })
const setupConn = await setupDb.connect()
await setupConn.execute('CREATE TABLE profile_marker (id INTEGER, value TEXT)')
await setupConn.execute("INSERT INTO profile_marker VALUES (42, 'from_profile')")
await setupConn.close()
await setupDb.close()
writeProfileToml(tmpDir, 'test_sqlite', driver, { uri: dbPath })
const db = new AdbcDatabase({
driver: 'profile://test_sqlite',
profileSearchPaths: [tmpDir],
})
const conn = await db.connect()
const table = await conn.query('SELECT id, value FROM profile_marker')
assert.strictEqual(table.numRows, 1)
assert.strictEqual(table.getChild('id')?.get(0), 42n)
assert.strictEqual(table.getChild('value')?.get(0), 'from_profile')
await conn.close()
await db.close()
} finally {
rmSync(tmpDir, { recursive: true })
}
})
// URI-style driver strings require a short name as the scheme — skip when the
// test env var is set to an absolute path (e.g. on Windows CI).
test('profile: load database from sqlite: URI', { skip: testLib !== undefined && isAbsolute(testLib) }, async () => {
const driver = testLib ?? 'sqlite'
const db = new AdbcDatabase({ driver: `${driver}::memory:` })
const conn = await db.connect()
await conn.query('SELECT 1 AS n')
await conn.close()
await db.close()
})
test(
'profile: omit driver, load from URI in databaseOptions.uri',
{ skip: testLib !== undefined && isAbsolute(testLib) },
async () => {
const driver = testLib ?? 'sqlite'
const db = new AdbcDatabase({
databaseOptions: { uri: `${driver}::memory:` },
})
const conn = await db.connect()
await conn.query('SELECT 1 AS n')
await conn.close()
await db.close()
},
)
test('profile: omit driver, load from profile:// URI in databaseOptions.uri', async () => {
const driver = testLib ?? 'sqlite'
const tmpDir = mkdtempSync(join(tmpdir(), 'adbc-profile-test-'))
try {
writeProfileToml(tmpDir, 'my_profile', driver)
const db = new AdbcDatabase({
databaseOptions: { uri: 'profile://my_profile' },
profileSearchPaths: [tmpDir],
})
const conn = await db.connect()
await conn.query('SELECT 1 AS n')
await conn.close()
await db.close()
} finally {
rmSync(tmpDir, { recursive: true })
}
})
test('profile: omit driver, load from bare profile name in databaseOptions.profile', async () => {
const driver = testLib ?? 'sqlite'
const tmpDir = mkdtempSync(join(tmpdir(), 'adbc-profile-test-'))
try {
writeProfileToml(tmpDir, 'my_profile', driver)
const db = new AdbcDatabase({
databaseOptions: { profile: 'my_profile' },
profileSearchPaths: [tmpDir],
})
const conn = await db.connect()
await conn.query('SELECT 1 AS n')
await conn.close()
await db.close()
} finally {
rmSync(tmpDir, { recursive: true })
}
})
test('profile: driver + databaseOptions.profile loads from profile', async () => {
const driver = tomlPath(testLib ?? 'sqlite')
const tmpDir = mkdtempSync(join(tmpdir(), 'adbc-profile-test-'))
try {
writeProfileToml(tmpDir, 'my_profile', driver)
const db = new AdbcDatabase({
driver,
databaseOptions: { profile: 'my_profile' },
profileSearchPaths: [tmpDir],
})
const conn = await db.connect()
await conn.query('SELECT 1 AS n')
await conn.close()
await db.close()
} finally {
rmSync(tmpDir, { recursive: true })
}
})
test('profile: driver + databaseOptions.uri profile:// loads from profile', async () => {
const driver = tomlPath(testLib ?? 'sqlite')
const tmpDir = mkdtempSync(join(tmpdir(), 'adbc-profile-test-'))
try {
writeProfileToml(tmpDir, 'my_profile', driver)
const db = new AdbcDatabase({
driver,
databaseOptions: { uri: 'profile://my_profile' },
profileSearchPaths: [tmpDir],
})
const conn = await db.connect()
await conn.query('SELECT 1 AS n')
await conn.close()
await db.close()
} finally {
rmSync(tmpDir, { recursive: true })
}
})
test('profile: error when profile key and profile:// uri are both provided', async () => {
assert.throws(
() =>
new AdbcDatabase({
databaseOptions: { profile: 'p1', uri: 'profile://p2' },
}),
(err: unknown) => {
assert.ok(err instanceof Error)
assert.ok(err.message.includes('mutually exclusive'), `unexpected message: ${err.message}`)
return true
},
)
})
test('profile: driver disagreeing with profile errors', async () => {
const driver = tomlPath(testLib ?? 'sqlite')
const tmpDir = mkdtempSync(join(tmpdir(), 'adbc-profile-test-'))
try {
writeProfileToml(tmpDir, 'my_profile', driver)
assert.throws(
() =>
new AdbcDatabase({
driver: 'some_other_driver',
databaseOptions: { profile: 'my_profile' },
profileSearchPaths: [tmpDir],
}),
(err: unknown) => {
assert.ok(err instanceof AdbcError)
assert.strictEqual(err.code, 'InvalidArguments')
assert.strictEqual(
err.message,
`profile specifies driver \`${tomlPath(driver)}\` which does not match requested driver \`some_other_driver\``,
)
return true
},
)
} finally {
rmSync(tmpDir, { recursive: true })
}
})
test('profile: error when driver is omitted and neither uri nor profile is present', async () => {
assert.throws(
// @ts-expect-error — intentionally invalid: neither uri nor profile present
() => new AdbcDatabase({ databaseOptions: { username: 'foo' } }),
(err: unknown) => {
assert.ok(err instanceof Error)
assert.ok(err.message.includes('driver is required'), `unexpected message: ${err.message}`)
return true
},
)
})