fix(setup): require explicit no-input database scope (#286)

* test(setup): supply explicit --no-input scope to disabled-mode database tests

* fix(setup): require explicit database scope in --no-input instead of auto-scanning the warehouse

* docs(setup): document --no-input database scope requirement
This commit is contained in:
Andrey Avtomonov 2026-06-10 12:36:53 +02:00 committed by GitHub
parent 036a745fc1
commit 853f39a7c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 138 additions and 47 deletions

View file

@ -2145,17 +2145,11 @@ describe('setup databases step', () => {
expect(listTables).toHaveBeenCalledWith(tempDir, 'postgres-warehouse', ['analytics']);
});
it('auto-selects all discovered Postgres schemas in non-interactive setup', async () => {
it('fails non-interactive setup when a scope-bearing connection has no schema configured', async () => {
const io = makeIo();
const prompts = makePromptAdapter({});
const testConnection = vi.fn(async () => 0);
const scanConnection = vi.fn(async asyncScanProjectDir => {
const config = parseKtxProjectConfig(await readFile(join(asyncScanProjectDir, 'ktx.yaml'), 'utf-8'));
expect(config.connections.warehouse).toMatchObject({
schemas: ['orbit_analytics', 'orbit_raw', 'public'],
});
return 0;
});
const scanConnection = vi.fn(async () => 0);
const listSchemas = vi.fn(async () => ['orbit_analytics', 'orbit_raw', 'public']);
const result = await runKtxSetupDatabasesStep(
@ -2172,13 +2166,93 @@ describe('setup databases step', () => {
{ prompts, testConnection, scanConnection, listSchemas },
);
expect(result.status).toBe('failed');
expect(listSchemas).not.toHaveBeenCalled();
expect(scanConnection).not.toHaveBeenCalled();
expect(io.stderr()).toContain('--database-schema');
expect(io.stderr()).toContain('connections.warehouse.schemas');
});
it('preserves existing BigQuery dataset_ids in non-interactive setup without rediscovering', async () => {
await writeFile(
join(tempDir, 'ktx.yaml'),
[
'connections:',
' bigquery-warehouse:',
' driver: bigquery',
' dataset_ids:',
" - 'sales'",
' credentials_json: env:BIGQUERY_CREDENTIALS_JSON',
'',
].join('\n'),
'utf-8',
);
const io = makeIo();
const testConnection = vi.fn(async () => 0);
const scanConnection = vi.fn(async () => 0);
const listSchemas = vi.fn(async () => ['sales', 'stripe', 'posthog', 'linear']);
const result = await runKtxSetupDatabasesStep(
{
projectDir: tempDir,
inputMode: 'disabled',
databaseConnectionIds: ['bigquery-warehouse'],
databaseSchemas: [],
skipDatabases: false,
},
io.io,
{ testConnection, scanConnection, listSchemas },
);
expect(result.status).toBe('ready');
expect(prompts.multiselect).not.toHaveBeenCalled();
expect(listSchemas).not.toHaveBeenCalled();
expect(scanConnection).toHaveBeenCalledWith(tempDir, 'bigquery-warehouse', expect.anything());
const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
expect(config.connections['bigquery-warehouse']).toMatchObject({
driver: 'bigquery',
dataset_ids: ['sales'],
});
});
it('preserves existing Postgres schemas in non-interactive setup without rediscovering', async () => {
await writeFile(
join(tempDir, 'ktx.yaml'),
[
'connections:',
' warehouse:',
' driver: postgres',
' url: env:DATABASE_URL',
' schemas:',
" - 'analytics'",
'',
].join('\n'),
'utf-8',
);
const io = makeIo();
const testConnection = vi.fn(async () => 0);
const scanConnection = vi.fn(async () => 0);
const listSchemas = vi.fn(async () => ['analytics', 'raw', 'public']);
const result = await runKtxSetupDatabasesStep(
{
projectDir: tempDir,
inputMode: 'disabled',
databaseConnectionIds: ['warehouse'],
databaseSchemas: [],
skipDatabases: false,
},
io.io,
{ testConnection, scanConnection, listSchemas },
);
expect(result.status).toBe('ready');
expect(listSchemas).not.toHaveBeenCalled();
expect(scanConnection).toHaveBeenCalledWith(tempDir, 'warehouse', expect.anything());
const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
expect(config.connections.warehouse).toMatchObject({
schemas: ['orbit_analytics', 'orbit_raw', 'public'],
driver: 'postgres',
schemas: ['analytics'],
});
expect(io.stdout()).toContain('✓ orbit_analytics, orbit_raw, public');
});
it('adds one non-interactive Postgres URL connection, tests it, scans it, and marks databases complete', async () => {
@ -2265,6 +2339,8 @@ describe('setup databases step', () => {
' warehouse:',
' driver: postgres',
' url: env:DATABASE_URL',
' schemas:',
" - 'public'",
' analytics:',
' driver: snowflake',
' authMethod: password',
@ -2312,7 +2388,7 @@ describe('setup databases step', () => {
databaseDrivers: ['postgres'],
databaseConnectionId: 'warehouse',
databaseUrl: 'env:DATABASE_URL',
databaseSchemas: [],
databaseSchemas: ['public'],
skipDatabases: false,
},
io.io,
@ -2342,7 +2418,7 @@ describe('setup databases step', () => {
databaseDrivers: ['postgres'],
databaseConnectionId: 'warehouse',
databaseUrl: 'env:DATABASE_URL',
databaseSchemas: [],
databaseSchemas: ['public'],
skipDatabases: false,
},
io.io,
@ -2409,7 +2485,7 @@ describe('setup databases step', () => {
databaseDrivers: ['postgres'],
databaseConnectionId: 'warehouse',
databaseUrl: 'env:DATABASE_URL',
databaseSchemas: [],
databaseSchemas: ['public'],
skipDatabases: false,
},
io.io,
@ -2470,7 +2546,7 @@ describe('setup databases step', () => {
inputMode: 'disabled',
databaseDrivers: ['snowflake'],
databaseConnectionId: 'snowflake',
databaseSchemas: [],
databaseSchemas: ['PUBLIC'],
enableQueryHistory: true,
queryHistoryWindowDays: 30,
queryHistoryServiceAccountPatterns: ['^svc_'],
@ -2532,7 +2608,7 @@ describe('setup databases step', () => {
inputMode: 'disabled',
databaseDrivers: ['snowflake'],
databaseConnectionId: 'snowflake',
databaseSchemas: [],
databaseSchemas: ['PUBLIC'],
skipDatabases: false,
},
io.io,
@ -2818,6 +2894,8 @@ describe('setup databases step', () => {
' warehouse:',
' driver: postgres',
' url: env:DATABASE_URL',
' schemas:',
" - 'public'",
' context:',
' queryHistory:',
' enabled: true',
@ -3172,6 +3250,8 @@ describe('setup databases step', () => {
' warehouse:',
' driver: postgres',
' url: env:DATABASE_URL',
' schemas:',
" - 'public'",
'',
].join('\n'),
'utf-8',
@ -3228,6 +3308,8 @@ describe('setup databases step', () => {
' warehouse:',
' driver: postgres',
' readonly: true',
' schemas:',
" - 'public'",
' historicSql:',
' enabled: true',
' dialect: postgres',
@ -3324,7 +3406,7 @@ describe('setup databases step', () => {
databaseDrivers: ['postgres'],
databaseConnectionId: 'warehouse',
databaseUrl: 'env:DATABASE_URL',
databaseSchemas: [],
databaseSchemas: ['public'],
enableQueryHistory: true,
skipDatabases: false,
},
@ -3373,7 +3455,7 @@ describe('setup databases step', () => {
inputMode: 'disabled',
databaseDrivers: ['snowflake'],
databaseConnectionId: 'warehouse',
databaseSchemas: [],
databaseSchemas: ['PUBLIC'],
enableQueryHistory: true,
skipDatabases: false,
},
@ -3485,7 +3567,7 @@ describe('setup databases step', () => {
databaseDrivers: ['postgres'],
databaseConnectionId: 'replay',
databaseUrl: 'env:DATABASE_URL',
databaseSchemas: [],
databaseSchemas: ['public'],
skipDatabases: false,
},
io.io,