mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-07 07:55:13 +02:00
Merge remote-tracking branch 'origin/main' into explore-connection-mapping
# Conflicts: # packages/cli/src/connection.test.ts
This commit is contained in:
commit
01680d7b89
70 changed files with 185 additions and 326 deletions
|
|
@ -22,7 +22,6 @@ ktx dev <subcommand> [options]
|
|||
|
||||
| Flag | Description | Default |
|
||||
|------|-------------|---------|
|
||||
| `--name <name>` | Project name written to `ktx.yaml` | — |
|
||||
| `--force` | Rewrite `ktx.yaml` and scaffold files in an existing project | `false` |
|
||||
|
||||
## `dev runtime`
|
||||
|
|
@ -40,7 +39,7 @@ ktx dev <subcommand> [options]
|
|||
|
||||
```bash
|
||||
ktx dev init
|
||||
ktx dev init ./my-project --name "Analytics Context"
|
||||
ktx dev init ./my-project
|
||||
ktx dev init --force
|
||||
|
||||
ktx dev runtime install --yes
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
project: local-warehouse
|
||||
connections:
|
||||
warehouse:
|
||||
driver: postgres
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
project: orbit-relationship-verification
|
||||
connections:
|
||||
orbit:
|
||||
driver: sqlite
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export interface KtxCliCommandContext {
|
|||
deps: KtxCliDeps;
|
||||
packageInfo: KtxCliPackageInfo;
|
||||
setExitCode: (code: number) => void;
|
||||
runInit: (args: { projectDir: string; projectName?: string; force: boolean }, io: KtxCliIo) => Promise<number>;
|
||||
runInit: (args: { projectDir: string; force: boolean }, io: KtxCliIo) => Promise<number>;
|
||||
writeDebug?: (command: string, commandContext: CommandWithGlobalOptions) => void;
|
||||
}
|
||||
|
||||
|
|
@ -32,14 +32,14 @@ export interface OutputModeOptions {
|
|||
}
|
||||
|
||||
interface KtxCommanderProgramOptions {
|
||||
runInit: (args: { projectDir: string; projectName?: string; force: boolean }, io: KtxCliIo) => Promise<number>;
|
||||
runInit: (args: { projectDir: string; force: boolean }, io: KtxCliIo) => Promise<number>;
|
||||
}
|
||||
|
||||
export interface BuildKtxProgramOptions {
|
||||
io: KtxCliIo;
|
||||
deps: KtxCliDeps;
|
||||
packageInfo: KtxCliPackageInfo;
|
||||
runInit: (args: { projectDir: string; projectName?: string; force: boolean }, io: KtxCliIo) => Promise<number>;
|
||||
runInit: (args: { projectDir: string; force: boolean }, io: KtxCliIo) => Promise<number>;
|
||||
setExitCode?: (code: number) => void;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,14 +59,10 @@ export function packageInfoFromJson(packageJson: unknown): KtxCliPackageInfo {
|
|||
};
|
||||
}
|
||||
|
||||
async function runInit(
|
||||
args: { projectDir: string; projectName?: string; force: boolean },
|
||||
io: KtxCliIo,
|
||||
): Promise<number> {
|
||||
async function runInit(args: { projectDir: string; force: boolean }, io: KtxCliIo): Promise<number> {
|
||||
const { initKtxProject } = await import('@ktx/context/project');
|
||||
const result = await initKtxProject({
|
||||
projectDir: args.projectDir,
|
||||
projectName: args.projectName,
|
||||
force: args.force,
|
||||
});
|
||||
|
||||
|
|
@ -77,7 +73,7 @@ async function runInit(
|
|||
}
|
||||
|
||||
export async function runInitForCommander(
|
||||
args: { projectDir: string; projectName?: string; force: boolean },
|
||||
args: { projectDir: string; force: boolean },
|
||||
io: KtxCliIo,
|
||||
): Promise<number> {
|
||||
return await runInit(args, io);
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('lists configured connections without resolving secrets', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
warehouse: { driver: 'postgres', url: 'env:DATABASE_URL' },
|
||||
docs: { driver: 'notion', auth_token_ref: 'env:NOTION_TOKEN', crawl_mode: 'all_accessible' },
|
||||
|
|
@ -100,7 +100,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('prints an empty-state message that points at setup instead of removed connection add', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
const io = makeIo();
|
||||
|
||||
await expect(runKtxConnection({ command: 'list', projectDir }, io.io)).resolves.toBe(0);
|
||||
|
|
@ -111,7 +111,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('tests a native connection by calling connector.testConnection (not introspect)', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
warehouse: { driver: 'sqlite' },
|
||||
});
|
||||
|
|
@ -136,7 +136,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('reports the connector error and still cleans up when native testConnection fails', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
warehouse: { driver: 'sqlite' },
|
||||
});
|
||||
|
|
@ -155,7 +155,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('tests a configured Metabase connection through the Metabase runtime client', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
prod_metabase: {
|
||||
driver: 'metabase',
|
||||
|
|
@ -201,7 +201,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('tests a Looker connection through the Looker client', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
bi_looker: {
|
||||
driver: 'looker',
|
||||
|
|
@ -230,7 +230,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('falls back to userId when Looker metadata has no display name', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
bi_looker: {
|
||||
driver: 'looker',
|
||||
|
|
@ -255,7 +255,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('reports the Looker error when testConnection fails', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
bi_looker: {
|
||||
driver: 'looker',
|
||||
|
|
@ -277,7 +277,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('tests a Notion connection by retrieving the bot user', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
docs: {
|
||||
driver: 'notion',
|
||||
|
|
@ -302,7 +302,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('falls back to bot id when Notion bot has no name', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
docs: {
|
||||
driver: 'notion',
|
||||
|
|
@ -323,7 +323,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('tests a dbt connection via testRepoConnection (success)', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
process.env.DBT_TOKEN = 'gh_token_abc'; // pragma: allowlist secret
|
||||
await writeConnections(projectDir, {
|
||||
'dbt-main': {
|
||||
|
|
@ -354,7 +354,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('reports the git error when testRepoConnection fails for dbt', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
'dbt-main': {
|
||||
driver: 'dbt',
|
||||
|
|
@ -377,7 +377,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('tests a LookML connection via testRepoConnection with camelCase repoUrl', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
lookml_main: {
|
||||
driver: 'lookml',
|
||||
|
|
@ -400,7 +400,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('tests a MetricFlow connection via the nested metricflow block', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
mf_main: {
|
||||
driver: 'metricflow',
|
||||
|
|
@ -422,7 +422,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('--all: prints a single coherent list with one row per connection', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
warehouse: { driver: 'sqlite' },
|
||||
docs: { driver: 'notion', auth_token: 'secret_token', crawl_mode: 'all_accessible' }, // pragma: allowlist secret
|
||||
|
|
@ -450,7 +450,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('--all: marks failing connections, keeps passing ones, and returns non-zero', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeConnections(projectDir, {
|
||||
warehouse: { driver: 'sqlite' },
|
||||
broken: { driver: 'sqlite' },
|
||||
|
|
@ -476,7 +476,7 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('--all: shows an empty-state message when no connections are configured', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
const io = makeIo();
|
||||
|
||||
await expect(runKtxConnection({ command: 'test-all', projectDir }, io.io)).resolves.toBe(0);
|
||||
|
|
@ -488,14 +488,18 @@ describe('runKtxConnection', () => {
|
|||
|
||||
it('rejects unknown drivers with a helpful error', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await writeFile(join(projectDir, 'ktx.yaml'), 'project: warehouse\nconnections:\n mystery:\n driver: duckdb\n', 'utf-8');
|
||||
await initKtxProject({ projectDir });
|
||||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
'connections:\n mystery:\n driver: duckdb\n',
|
||||
'utf-8',
|
||||
);
|
||||
const io = makeIo();
|
||||
|
||||
await expect(
|
||||
runKtxConnection({ command: 'test', projectDir, connectionId: 'mystery' }, io.io),
|
||||
).resolves.toBe(1);
|
||||
expect(io.stderr()).toContain('uses driver "duckdb"');
|
||||
expect(io.stderr()).toContain('Supported:');
|
||||
expect(io.stderr()).toContain('connections.mystery.driver');
|
||||
expect(io.stderr()).toContain('postgres');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ function projectWithConnections(connections: KtxProjectConfig['connections']): K
|
|||
return {
|
||||
projectDir: '/tmp/project',
|
||||
config: {
|
||||
...buildDefaultKtxProjectConfig('warehouse'),
|
||||
...buildDefaultKtxProjectConfig(),
|
||||
connections,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ export function defaultDemoProjectDir(): string {
|
|||
|
||||
function demoConfig(databasePath: string): string {
|
||||
return [
|
||||
'project: ktx-demo-orbit',
|
||||
'connections:',
|
||||
` ${DEMO_CONNECTION_ID}:`,
|
||||
' driver: sqlite',
|
||||
|
|
|
|||
|
|
@ -72,10 +72,10 @@ describe('dev Commander tree', () => {
|
|||
const testIo = makeIo();
|
||||
|
||||
try {
|
||||
await expect(runKtxCli(['dev', 'init', projectDir, '--name', 'warehouse'], testIo.io)).resolves.toBe(0);
|
||||
await expect(runKtxCli(['dev', 'init', projectDir], testIo.io)).resolves.toBe(0);
|
||||
|
||||
expect(testIo.stdout()).toContain(`Initialized KTX project at ${projectDir}`);
|
||||
await expect(readFile(join(projectDir, 'ktx.yaml'), 'utf-8')).resolves.toContain('project: warehouse');
|
||||
await expect(readFile(join(projectDir, 'ktx.yaml'), 'utf-8')).resolves.not.toContain('project:');
|
||||
expect(testIo.stderr()).toBe('');
|
||||
} finally {
|
||||
await rm(tempDir, { recursive: true, force: true });
|
||||
|
|
@ -92,7 +92,7 @@ describe('dev Commander tree', () => {
|
|||
|
||||
try {
|
||||
await expect(
|
||||
runKtxCli(['--project-dir', projectDir, 'dev', 'init', '--name', 'global-init'], testIo.io),
|
||||
runKtxCli(['--project-dir', projectDir, 'dev', 'init'], testIo.io),
|
||||
).resolves.toBe(0);
|
||||
|
||||
expect(testIo.stdout()).toContain(`Initialized KTX project at ${projectDir}`);
|
||||
|
|
|
|||
|
|
@ -25,19 +25,17 @@ export function registerDevCommands(program: Command, context: KtxCliCommandCont
|
|||
.command('init')
|
||||
.description('Initialize a Git-backed KTX project directory for maintenance scripts')
|
||||
.argument('[directory]', 'Project directory')
|
||||
.option('--name <name>', 'Project name written to ktx.yaml')
|
||||
.option('--force', 'Rewrite ktx.yaml and scaffold files in an existing project', false)
|
||||
.action(
|
||||
async (
|
||||
projectDir: string | undefined,
|
||||
commandOptions: { name?: string; force?: boolean },
|
||||
commandOptions: { force?: boolean },
|
||||
command: CommandWithGlobalOptions,
|
||||
) => {
|
||||
context.setExitCode(
|
||||
await context.runInit(
|
||||
{
|
||||
projectDir: projectDir ? resolve(projectDir) : resolveCommandProjectDir(command),
|
||||
...(commandOptions.name ? { projectName: commandOptions.name } : {}),
|
||||
force: commandOptions.force === true,
|
||||
},
|
||||
context.io,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { basename, join } from 'node:path';
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
import {
|
||||
formatDoctorReport,
|
||||
|
|
@ -328,7 +328,6 @@ describe('runKtxDoctor', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'storrage:',
|
||||
' state: sqlite',
|
||||
'ingest:',
|
||||
|
|
@ -359,7 +358,7 @@ describe('runKtxDoctor', () => {
|
|||
it('emits structured JSON when ktx.yaml fails Zod validation', async () => {
|
||||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
['project: warehouse', 'storrage: {}', ''].join('\n'),
|
||||
['storrage: {}', ''].join('\n'),
|
||||
'utf-8',
|
||||
);
|
||||
const testIo = makeIo();
|
||||
|
|
@ -387,7 +386,6 @@ describe('runKtxDoctor', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
@ -418,7 +416,6 @@ describe('runKtxDoctor', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
@ -452,7 +449,7 @@ describe('runKtxDoctor', () => {
|
|||
|
||||
const out = testIo.stdout();
|
||||
expect(out).toContain('KTX status');
|
||||
expect(out).toContain('· warehouse');
|
||||
expect(out).toContain(`· ${basename(tempDir)}`);
|
||||
expect(out).toContain('Connections (1)');
|
||||
expect(out).toContain('LLM');
|
||||
expect(out).toContain('anthropic');
|
||||
|
|
@ -468,7 +465,6 @@ describe('runKtxDoctor', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -528,7 +524,6 @@ describe('runKtxDoctor', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
@ -558,7 +553,6 @@ describe('runKtxDoctor', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -620,7 +614,6 @@ describe('runKtxDoctor', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
@ -660,7 +653,6 @@ describe('runKtxDoctor', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
@ -695,7 +687,6 @@ describe('runKtxDoctor', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
@ -724,7 +715,6 @@ describe('runKtxDoctor', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'storrage:',
|
||||
' state: sqlite',
|
||||
'ingest:',
|
||||
|
|
@ -752,7 +742,7 @@ describe('runKtxDoctor', () => {
|
|||
it('emits structured JSON issues when validation fails', async () => {
|
||||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
['project: warehouse', 'storrage: {}', ''].join('\n'),
|
||||
['storrage: {}', ''].join('\n'),
|
||||
'utf-8',
|
||||
);
|
||||
const testIo = makeIo();
|
||||
|
|
@ -788,7 +778,6 @@ describe('runKtxDoctor', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ describe('runKtxCli', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-cli-'));
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'project: cli-dispatch-fixture\n', 'utf-8');
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), '{}\n', 'utf-8');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
@ -502,7 +502,7 @@ describe('runKtxCli', () => {
|
|||
|
||||
it('keeps representative JSON command stdout parseable', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
const commands = [
|
||||
['--project-dir', projectDir, 'status', '--json'],
|
||||
['--project-dir', projectDir, 'sl', 'list', '--json'],
|
||||
|
|
@ -580,7 +580,7 @@ describe('runKtxCli', () => {
|
|||
|
||||
try {
|
||||
delete process.env.KTX_PROJECT_DIR;
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'project: revenue\nconnections: {}\n', 'utf-8');
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
|
||||
process.chdir(tempDir);
|
||||
|
||||
await expect(runKtxCli([], testIo.io, { setup })).resolves.toBe(0);
|
||||
|
|
@ -1482,7 +1482,7 @@ describe('runKtxCli', () => {
|
|||
|
||||
it('dispatches public connection subcommands through the existing connection implementation', async () => {
|
||||
const tempDir = await mkdtemp(join(tmpdir(), 'ktx-connection-dispatch-'));
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'project: connection-dispatch\n', 'utf-8');
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), '{}\n', 'utf-8');
|
||||
const connection = vi.fn(async () => 0);
|
||||
|
||||
await expect(
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ export async function writeWarehouseConfig(projectDir: string): Promise<void> {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' prod-metabase:',
|
||||
' driver: metabase',
|
||||
|
|
@ -126,7 +125,6 @@ export async function writeMetabaseConfig(projectDir: string): Promise<void> {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -488,7 +486,6 @@ export async function runPublicMetabaseSyncModeCase(tempDir: string, input: Sync
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
`project: metabase-sync-mode-${input.name}`,
|
||||
'connections:',
|
||||
' prod-metabase:',
|
||||
' driver: metabase',
|
||||
|
|
|
|||
|
|
@ -633,7 +633,6 @@ describe('runKtxIngest', () => {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: metabase-cli',
|
||||
'connections:',
|
||||
' prod-metabase:',
|
||||
' driver: metabase',
|
||||
|
|
@ -1099,7 +1098,7 @@ describe('runKtxIngest', () => {
|
|||
|
||||
it('passes managed daemon options to adapters and pull-config options when no explicit daemon URL is set', async () => {
|
||||
const projectDir = join(tempDir, 'managed-daemon-ingest-project');
|
||||
await initKtxProject({ projectDir, projectName: 'managed-daemon-ingest-project' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeWarehouseConfig(projectDir);
|
||||
const createdAdapters: SourceAdapter[] = [
|
||||
{ source: 'fake', skillNames: [], detect: async () => true, chunk: async () => ({ workUnits: [] }) },
|
||||
|
|
@ -1159,7 +1158,6 @@ describe('runKtxIngest', () => {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: historic-sql-project',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -1224,7 +1222,6 @@ describe('runKtxIngest', () => {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: historic-sql-progress-project',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -1353,7 +1350,6 @@ describe('runKtxIngest', () => {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: historic-sql-step-progress-project',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -1446,7 +1442,6 @@ describe('runKtxIngest', () => {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: historic-sql-concurrent-progress-project',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -1596,7 +1591,6 @@ describe('runKtxIngest', () => {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: looker-cli',
|
||||
'connections:',
|
||||
' prod-looker:',
|
||||
' driver: looker',
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ describe('runKtxKnowledge', () => {
|
|||
|
||||
it('writes, reads, lists, and searches wiki pages', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
|
||||
const writeIo = makeIo();
|
||||
await expect(
|
||||
|
|
@ -95,7 +95,7 @@ describe('runKtxKnowledge', () => {
|
|||
|
||||
it('prints wiki list, search, and read as public JSON envelopes', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
|
||||
await expect(
|
||||
runKtxKnowledge(
|
||||
|
|
@ -154,7 +154,7 @@ describe('runKtxKnowledge', () => {
|
|||
|
||||
it('rejects slash-delimited write keys with a flat-key suggestion', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
|
||||
const writeIo = makeIo();
|
||||
await expect(
|
||||
|
|
@ -183,7 +183,7 @@ describe('runKtxKnowledge', () => {
|
|||
|
||||
it('explains empty search results for a project without wiki pages', async () => {
|
||||
const projectDir = join(tempDir, 'empty-project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
|
||||
const searchIo = makeIo();
|
||||
await expect(
|
||||
|
|
@ -197,7 +197,7 @@ describe('runKtxKnowledge', () => {
|
|||
|
||||
it('uses configured embeddings for semantic wiki search', async () => {
|
||||
const projectDir = join(tempDir, 'semantic-project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
|
||||
await expect(
|
||||
runKtxKnowledge(
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ describe('CLI local ingest adapters', () => {
|
|||
await writeProject(
|
||||
tempDir,
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -71,7 +70,6 @@ describe('CLI local ingest adapters', () => {
|
|||
await writeProject(
|
||||
tempDir,
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -103,7 +101,6 @@ describe('CLI local ingest adapters', () => {
|
|||
await writeProject(
|
||||
tempDir,
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' bq:',
|
||||
' driver: bigquery',
|
||||
|
|
@ -136,7 +133,6 @@ describe('CLI local ingest adapters', () => {
|
|||
await writeProject(
|
||||
tempDir,
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' sf:',
|
||||
' driver: snowflake',
|
||||
|
|
@ -172,7 +168,6 @@ describe('CLI local ingest adapters', () => {
|
|||
await writeProject(
|
||||
tempDir,
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' bq:',
|
||||
' driver: bigquery',
|
||||
|
|
|
|||
|
|
@ -39,11 +39,10 @@ describe('createKtxCliScanConnector', () => {
|
|||
});
|
||||
|
||||
it('creates a native sqlite connector from standalone config', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
@ -61,11 +60,10 @@ describe('createKtxCliScanConnector', () => {
|
|||
});
|
||||
|
||||
it('passes canonical BigQuery YAML scan limits through to the connector', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: bigquery',
|
||||
|
|
@ -95,11 +93,10 @@ describe('createKtxCliScanConnector', () => {
|
|||
});
|
||||
|
||||
it('throws for structural daemon-only fallback configs', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: duckdb',
|
||||
|
|
@ -116,11 +113,10 @@ describe('createKtxCliScanConnector', () => {
|
|||
});
|
||||
|
||||
it('throws a clear error when the connection block has no driver field', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' type: postgres',
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { runKtxCli, type KtxCliDeps } from './index.js';
|
|||
|
||||
async function makeFixtureProject(prefix: string): Promise<string> {
|
||||
const dir = await mkdtemp(join(tmpdir(), prefix));
|
||||
await writeFile(join(dir, 'ktx.yaml'), 'project: project-dir-fixture\n', 'utf-8');
|
||||
await writeFile(join(dir, 'ktx.yaml'), '{}\n', 'utf-8');
|
||||
return dir;
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +138,7 @@ describe('project directory defaults', () => {
|
|||
const projectDir = join(root, 'warehouse');
|
||||
const nestedDir = join(projectDir, 'nested', 'deeper');
|
||||
await mkdir(nestedDir, { recursive: true });
|
||||
await writeFile(join(projectDir, 'ktx.yaml'), 'project: warehouse\n', 'utf-8');
|
||||
await writeFile(join(projectDir, 'ktx.yaml'), '{}\n', 'utf-8');
|
||||
const expectedProjectDir = await realpath(projectDir);
|
||||
|
||||
const publicIngest = vi.fn(async () => 0);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ describe('resolveKtxProjectDir', () => {
|
|||
const project = join(tempDir, 'warehouse');
|
||||
const nested = join(project, 'nested', 'deeper');
|
||||
await mkdir(nested, { recursive: true });
|
||||
await writeFile(join(project, 'ktx.yaml'), 'project: warehouse\n', 'utf-8');
|
||||
await writeFile(join(project, 'ktx.yaml'), '{}\n', 'utf-8');
|
||||
|
||||
expect(resolveKtxProjectDir({ env: {}, cwd: nested })).toBe(resolve(project));
|
||||
expect(findNearestKtxProjectDir(nested)).toBe(resolve(project));
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ function projectWithConnections(connections: KtxProjectConfig['connections']): K
|
|||
return {
|
||||
projectDir: '/tmp/project',
|
||||
config: {
|
||||
...buildDefaultKtxProjectConfig('warehouse'),
|
||||
...buildDefaultKtxProjectConfig(),
|
||||
connections,
|
||||
},
|
||||
};
|
||||
|
|
@ -51,7 +51,7 @@ function deepReadyProject(
|
|||
connections: KtxProjectConfig['connections'],
|
||||
relationshipsEnabled = true,
|
||||
): KtxPublicIngestProject {
|
||||
const config = buildDefaultKtxProjectConfig('warehouse');
|
||||
const config = buildDefaultKtxProjectConfig();
|
||||
return {
|
||||
projectDir: '/tmp/project',
|
||||
config: {
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ describe('runKtxScan', () => {
|
|||
});
|
||||
|
||||
it('runs structural scans and prints a dev-friendly plain summary', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
const runLocalScan = vi.fn(
|
||||
async (_input: RunLocalScanOptions): Promise<LocalScanRunResult> => ({
|
||||
runId: 'scan-run-1',
|
||||
|
|
@ -377,7 +377,7 @@ describe('runKtxScan', () => {
|
|||
});
|
||||
|
||||
it('passes managed daemon options to local ingest adapters when no explicit daemon URL is set', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
const createLocalIngestAdapters = vi.fn(() => []);
|
||||
const runLocalScan = vi.fn(
|
||||
async (_input: RunLocalScanOptions): Promise<LocalScanRunResult> => ({
|
||||
|
|
@ -421,7 +421,7 @@ describe('runKtxScan', () => {
|
|||
});
|
||||
|
||||
it('explains warnings, capability gaps, and relationships in human scan summaries', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
const runLocalScan = vi.fn(
|
||||
async (_input: RunLocalScanOptions): Promise<LocalScanRunResult> => ({
|
||||
runId: 'scan-run-1',
|
||||
|
|
@ -472,7 +472,7 @@ describe('runKtxScan', () => {
|
|||
});
|
||||
|
||||
it('prints review-only relationship summaries and validation capability warnings', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
const reviewOnlyReport: KtxScanReport = {
|
||||
...reportWithAttention,
|
||||
capabilityGaps: [],
|
||||
|
|
@ -525,7 +525,7 @@ describe('runKtxScan', () => {
|
|||
});
|
||||
|
||||
it('passes a scan progress port and prints TTY progress messages', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
const runLocalScan = vi.fn(async (input: RunLocalScanOptions): Promise<LocalScanRunResult> => {
|
||||
await input.progress?.update(0.15, 'Inspecting database schema');
|
||||
await input.progress?.update(0.55, 'Semantic layer comparison found 5 changes across 18 tables');
|
||||
|
|
@ -572,7 +572,7 @@ describe('runKtxScan', () => {
|
|||
});
|
||||
|
||||
it('uses injected structured progress without requiring TTY progress output', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
const progressEvents: Array<{ progress: number; message?: string; transient?: boolean }> = [];
|
||||
const structuredProgress = {
|
||||
async update(progress: number, message?: string, options?: { transient?: boolean }) {
|
||||
|
|
@ -674,7 +674,7 @@ describe('runKtxScan', () => {
|
|||
});
|
||||
|
||||
it('flushes transient TTY progress messages before printing scan failures', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
const runLocalScan = vi.fn(async (input: RunLocalScanOptions): Promise<LocalScanRunResult> => {
|
||||
await input.progress?.update(0.42, 'Generating descriptions 3/35 tables', { transient: true });
|
||||
throw new Error('scan failed');
|
||||
|
|
@ -711,7 +711,7 @@ describe('runKtxScan', () => {
|
|||
});
|
||||
|
||||
it('does not print live progress messages for non-TTY output', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
const runLocalScan = vi.fn(async (input: RunLocalScanOptions): Promise<LocalScanRunResult> => {
|
||||
await input.progress?.update(0.15, 'Inspecting database schema');
|
||||
return {
|
||||
|
|
@ -747,7 +747,7 @@ describe('runKtxScan', () => {
|
|||
});
|
||||
|
||||
it('uses terminal-aware visual styling only for TTY output', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
const runLocalScan = vi.fn(
|
||||
async (_input: RunLocalScanOptions): Promise<LocalScanRunResult> => ({
|
||||
runId: 'scan-run-1',
|
||||
|
|
@ -807,7 +807,7 @@ describe('runKtxScan', () => {
|
|||
});
|
||||
|
||||
it('honors NO_COLOR for TTY scan summaries', async () => {
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
const runLocalScan = vi.fn(
|
||||
async (_input: RunLocalScanOptions): Promise<LocalScanRunResult> => ({
|
||||
runId: 'scan-run-1',
|
||||
|
|
@ -853,11 +853,10 @@ describe('runKtxScan', () => {
|
|||
|
||||
it('passes native CLI adapters into local scan runs for mysql configs', async () => {
|
||||
const tempProject = await mkdtemp(join(tmpdir(), 'ktx-scan-cli-native-'));
|
||||
await initKtxProject({ projectDir: tempProject, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempProject });
|
||||
await writeFile(
|
||||
join(tempProject, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: mysql',
|
||||
|
|
@ -901,11 +900,10 @@ describe('runKtxScan', () => {
|
|||
|
||||
it('creates a native connector for standalone relationship scans', async () => {
|
||||
const tempProject = await mkdtemp(join(tmpdir(), 'ktx-scan-cli-relationships-'));
|
||||
await initKtxProject({ projectDir: tempProject, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempProject });
|
||||
await writeFile(
|
||||
join(tempProject, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
@ -955,11 +953,10 @@ describe('runKtxScan', () => {
|
|||
|
||||
it('routes standalone postgres scans through the native connector before daemon fallback', async () => {
|
||||
const tempProject = await mkdtemp(join(tmpdir(), 'ktx-scan-cli-native-postgres-'));
|
||||
await initKtxProject({ projectDir: tempProject, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempProject });
|
||||
await writeFile(
|
||||
join(tempProject, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -1021,11 +1018,10 @@ describe('runKtxScan', () => {
|
|||
|
||||
it('passes native CLI adapters into local scan runs for clickhouse configs', async () => {
|
||||
const tempProject = await mkdtemp(join(tmpdir(), 'ktx-scan-cli-native-clickhouse-'));
|
||||
await initKtxProject({ projectDir: tempProject, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempProject });
|
||||
await writeFile(
|
||||
join(tempProject, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: clickhouse',
|
||||
|
|
@ -1072,11 +1068,10 @@ describe('runKtxScan', () => {
|
|||
|
||||
it('passes native CLI adapters into local scan runs for sqlserver configs', async () => {
|
||||
const tempProject = await mkdtemp(join(tmpdir(), 'ktx-scan-cli-native-sqlserver-'));
|
||||
await initKtxProject({ projectDir: tempProject, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempProject });
|
||||
await writeFile(
|
||||
join(tempProject, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlserver',
|
||||
|
|
@ -1138,11 +1133,10 @@ describe('runKtxScan', () => {
|
|||
|
||||
it('passes native CLI adapters into local scan runs for bigquery configs', async () => {
|
||||
const tempProject = await mkdtemp(join(tmpdir(), 'ktx-scan-cli-native-bigquery-'));
|
||||
await initKtxProject({ projectDir: tempProject, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempProject });
|
||||
await writeFile(
|
||||
join(tempProject, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: bigquery',
|
||||
|
|
@ -1203,11 +1197,10 @@ describe('runKtxScan', () => {
|
|||
|
||||
it('passes native CLI adapters into local scan runs for snowflake configs', async () => {
|
||||
const tempProject = await mkdtemp(join(tmpdir(), 'ktx-scan-cli-native-snowflake-'));
|
||||
await initKtxProject({ projectDir: tempProject, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempProject });
|
||||
await writeFile(
|
||||
join(tempProject, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: snowflake',
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ describe('setup agents', () => {
|
|||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-setup-agents-'));
|
||||
await mkdir(join(tempDir, '.ktx', 'agents'), { recursive: true });
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'project: revenue\nconnections: {}\n', 'utf-8');
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ type ReadyProjectOverrides = Omit<Partial<KtxProjectConfig>, 'ingest' | 'llm' |
|
|||
};
|
||||
|
||||
async function writeReadyProject(projectDir: string, overrides: ReadyProjectOverrides = {}) {
|
||||
const defaults = buildDefaultKtxProjectConfig('revenue');
|
||||
const defaults = buildDefaultKtxProjectConfig();
|
||||
const readyConfig: KtxProjectConfig = {
|
||||
...defaults,
|
||||
setup: { database_connection_ids: ['warehouse'] },
|
||||
|
|
@ -595,7 +595,6 @@ describe('setup context build state', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'connections: {}',
|
||||
'llm:',
|
||||
' provider:',
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ describe('setup databases step', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-setup-databases-'));
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
@ -242,7 +242,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -575,7 +574,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -622,7 +620,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -770,7 +767,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -815,7 +811,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -864,7 +859,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -936,7 +930,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -1010,7 +1003,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -1079,7 +1071,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -1146,7 +1137,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -1646,7 +1636,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -1939,7 +1928,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -2019,7 +2007,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' analytics:',
|
||||
' driver: bigquery',
|
||||
|
|
@ -2074,7 +2061,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -2123,7 +2109,6 @@ describe('setup databases step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ describe('setup embeddings step', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-setup-embeddings-'));
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
@ -446,11 +446,10 @@ describe('setup embeddings step', () => {
|
|||
|
||||
it('preserves already completed embeddings setup when no embedding args request changes', async () => {
|
||||
await mkdir(join(tempDir, '.ktx'), { recursive: true });
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse', force: true });
|
||||
await initKtxProject({ projectDir: tempDir, force: true });
|
||||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'setup:',
|
||||
' database_connection_ids: []',
|
||||
'connections: {}',
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ describe('setup Anthropic model step', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-setup-models-'));
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir: tempDir });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
@ -1049,11 +1049,10 @@ describe('setup Anthropic model step', () => {
|
|||
|
||||
it('preserves already completed llm setup when no model args request changes', async () => {
|
||||
await mkdir(join(tempDir, '.ktx'), { recursive: true });
|
||||
await initKtxProject({ projectDir: tempDir, projectName: 'warehouse', force: true });
|
||||
await initKtxProject({ projectDir: tempDir, force: true });
|
||||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'setup:',
|
||||
' database_connection_ids: []',
|
||||
'connections: {}',
|
||||
|
|
@ -1099,7 +1098,6 @@ describe('setup Anthropic model step', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'setup:',
|
||||
' database_connection_ids: []',
|
||||
'connections: {}',
|
||||
|
|
|
|||
|
|
@ -76,11 +76,10 @@ describe('setup project step', () => {
|
|||
|
||||
it('loads an existing project with --existing and drops config setup progress', async () => {
|
||||
const projectDir = join(tempDir, 'warehouse');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'setup:',
|
||||
' database_connection_ids:',
|
||||
' - warehouse',
|
||||
|
|
@ -196,7 +195,7 @@ describe('setup project step', () => {
|
|||
expect.objectContaining({ message: `Create KTX project at ${projectDir}?` }),
|
||||
);
|
||||
expect(prompts.text).not.toHaveBeenCalled();
|
||||
expect(result.status === 'ready' ? result.project.config.project : '').toBe('ktx-project');
|
||||
expect(result.status === 'ready' ? result.project.configPath : '').toBe(join(projectDir, 'ktx.yaml'));
|
||||
expect(testIo.stdout()).toContain(`│ KTX will create:\n│ ${projectDir}`);
|
||||
await expect(stat(join(projectDir, 'ktx.yaml'))).resolves.toBeDefined();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { existsSync } from 'node:fs';
|
||||
import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
||||
import { homedir } from 'node:os';
|
||||
import { basename, join, resolve } from 'node:path';
|
||||
import { join, resolve } from 'node:path';
|
||||
import {
|
||||
initKtxProject,
|
||||
type KtxLocalProject,
|
||||
|
|
@ -156,7 +156,7 @@ async function persistProjectStep(project: KtxLocalProject): Promise<KtxLocalPro
|
|||
|
||||
async function createProject(projectDir: string, deps: KtxSetupProjectDeps): Promise<KtxLocalProject> {
|
||||
const initProject = deps.initProject ?? initKtxProject;
|
||||
const initialized = await initProject({ projectDir, projectName: basename(projectDir) || 'ktx-project' });
|
||||
const initialized = await initProject({ projectDir });
|
||||
return await persistProjectStep(initialized);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ describe('setup sources step', () => {
|
|||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-setup-sources-'));
|
||||
projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'sources' });
|
||||
await initKtxProject({ projectDir });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'llm:',
|
||||
' provider:',
|
||||
' backend: anthropic',
|
||||
|
|
@ -109,7 +108,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'llm:',
|
||||
' provider:',
|
||||
...fixture.providerLines,
|
||||
|
|
@ -129,7 +127,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'setup:',
|
||||
' database_connection_ids:',
|
||||
' - warehouse',
|
||||
|
|
@ -162,7 +159,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'setup:',
|
||||
' database_connection_ids:',
|
||||
' - warehouse',
|
||||
|
|
@ -183,7 +179,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'setup:',
|
||||
' database_connection_ids:',
|
||||
' - warehouse',
|
||||
|
|
@ -206,7 +201,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'setup:',
|
||||
' database_connection_ids: []',
|
||||
'connections:',
|
||||
|
|
@ -230,7 +224,7 @@ describe('setup status', () => {
|
|||
|
||||
it('reports agent status from the install manifest', async () => {
|
||||
await mkdir(join(tempDir, '.ktx', 'agents'), { recursive: true });
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'project: revenue\nconnections: {}\n', 'utf-8');
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
|
||||
await writeFile(
|
||||
join(tempDir, '.ktx/agents/install-manifest.json'),
|
||||
JSON.stringify(
|
||||
|
|
@ -256,7 +250,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'setup:',
|
||||
' database_connection_ids:',
|
||||
' - warehouse',
|
||||
|
|
@ -309,7 +302,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'setup:',
|
||||
' database_connection_ids:',
|
||||
' - warehouse',
|
||||
|
|
@ -370,7 +362,7 @@ describe('setup status', () => {
|
|||
});
|
||||
|
||||
it('prints the readiness checklist for an existing project', async () => {
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'project: revenue\nconnections: {}\n', 'utf-8');
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
|
||||
|
||||
const rendered = formatKtxSetupStatus(await readKtxSetupStatus(tempDir));
|
||||
|
||||
|
|
@ -503,7 +495,7 @@ describe('setup status', () => {
|
|||
),
|
||||
).resolves.toBe(0);
|
||||
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'project: revenue\nconnections: {}\n', 'utf-8');
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
|
||||
|
||||
await expect(
|
||||
runKtxSetup(
|
||||
|
|
@ -589,7 +581,7 @@ describe('setup status', () => {
|
|||
});
|
||||
|
||||
it('lets Back from new project creation return to the first setup intent menu', async () => {
|
||||
const existingConfig = 'project: revenue\nconnections: {}\n';
|
||||
const existingConfig = 'connections: {}\n';
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), existingConfig, 'utf-8');
|
||||
|
||||
const entryChoices = ['new-project', 'exit'];
|
||||
|
|
@ -645,7 +637,7 @@ describe('setup status', () => {
|
|||
const existingProjectDir = join(tempDir, 'existing');
|
||||
const newProjectDir = join(tempDir, 'fresh');
|
||||
await mkdir(existingProjectDir, { recursive: true });
|
||||
const existingConfig = 'project: revenue\nconnections: {}\n';
|
||||
const existingConfig = 'connections: {}\n';
|
||||
await writeFile(join(existingProjectDir, 'ktx.yaml'), existingConfig, 'utf-8');
|
||||
|
||||
const projectChoices = ['custom', 'create'];
|
||||
|
|
@ -722,7 +714,7 @@ describe('setup status', () => {
|
|||
const existingProjectDir = join(tempDir, 'existing');
|
||||
const newProjectDir = join(tempDir, 'fresh');
|
||||
await mkdir(existingProjectDir, { recursive: true });
|
||||
await writeFile(join(existingProjectDir, 'ktx.yaml'), 'project: revenue\nconnections: {}\n', 'utf-8');
|
||||
await writeFile(join(existingProjectDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
|
||||
|
||||
const projectChoices = ['custom', 'create'];
|
||||
const projectPrompts = {
|
||||
|
|
@ -1147,7 +1139,7 @@ describe('setup status', () => {
|
|||
});
|
||||
|
||||
it('lets Back from the first setup step return to the entry menu instead of exiting', async () => {
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'project: test\nconnections: {}\n', 'utf-8');
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), 'connections: {}\n', 'utf-8');
|
||||
const testIo = makeIo();
|
||||
|
||||
const entryChoices = ['setup', 'exit'];
|
||||
|
|
@ -1254,7 +1246,7 @@ describe('setup status', () => {
|
|||
it('runs sources after database setup', async () => {
|
||||
const calls: string[] = [];
|
||||
const io = makeIo();
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), ['project: revenue', 'connections: {}', ''].join('\n'), 'utf-8');
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), ['connections: {}', ''].join('\n'), 'utf-8');
|
||||
|
||||
await expect(
|
||||
runKtxSetup(
|
||||
|
|
@ -1315,7 +1307,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'setup:',
|
||||
' database_connection_ids:',
|
||||
' - warehouse',
|
||||
|
|
@ -1374,7 +1365,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -1430,7 +1420,7 @@ describe('setup status', () => {
|
|||
it('runs context after sources and before agents in full setup', async () => {
|
||||
const calls: string[] = [];
|
||||
const io = makeIo();
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), ['project: revenue', 'connections: {}', ''].join('\n'), 'utf-8');
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), ['connections: {}', ''].join('\n'), 'utf-8');
|
||||
|
||||
await expect(
|
||||
runKtxSetup(
|
||||
|
|
@ -1543,7 +1533,7 @@ describe('setup status', () => {
|
|||
it('runs agent setup after context succeeds in --agents mode', async () => {
|
||||
const calls: string[] = [];
|
||||
const io = makeIo();
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), ['project: revenue', 'connections: {}', ''].join('\n'), 'utf-8');
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), ['connections: {}', ''].join('\n'), 'utf-8');
|
||||
|
||||
await expect(
|
||||
runKtxSetup(
|
||||
|
|
@ -1596,7 +1586,7 @@ describe('setup status', () => {
|
|||
projectDir: tempDir,
|
||||
installs: [{ target: 'codex' as const, scope: 'project' as const, mode: 'cli' as const }],
|
||||
}));
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), ['project: revenue', 'connections: {}', ''].join('\n'), 'utf-8');
|
||||
await writeFile(join(tempDir, 'ktx.yaml'), ['connections: {}', ''].join('\n'), 'utf-8');
|
||||
|
||||
await expect(
|
||||
runKtxSetup(
|
||||
|
|
@ -1633,7 +1623,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'setup:',
|
||||
' database_connection_ids:',
|
||||
' - warehouse',
|
||||
|
|
@ -1671,7 +1660,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'setup:',
|
||||
' database_connection_ids: []',
|
||||
'connections: {}',
|
||||
|
|
@ -1778,7 +1766,6 @@ describe('setup status', () => {
|
|||
await writeFile(
|
||||
join(tempDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: revenue',
|
||||
'setup:',
|
||||
' database_connection_ids: []',
|
||||
'connections: {}',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { existsSync } from 'node:fs';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { basename, join, resolve } from 'node:path';
|
||||
import { getLatestLocalIngestStatus, savedMemoryCountsForReport } from '@ktx/context/ingest';
|
||||
import {
|
||||
ktxLocalStateDbPath,
|
||||
|
|
@ -317,7 +317,7 @@ export async function readKtxSetupStatus(projectDir: string): Promise<KtxSetupSt
|
|||
})) ?? [];
|
||||
|
||||
return {
|
||||
project: { path: resolvedProjectDir, ready: true, name: project.config.project },
|
||||
project: { path: resolvedProjectDir, ready: true, name: basename(project.projectDir) || project.projectDir },
|
||||
llm,
|
||||
embeddings,
|
||||
databases: databaseIds.map((connectionId) => ({
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ async function seedSlSource(input: {
|
|||
sourceName?: string;
|
||||
yaml?: string;
|
||||
}): Promise<void> {
|
||||
const project = await initKtxProject({ projectDir: input.projectDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: input.projectDir });
|
||||
await project.fileStore.writeFile(
|
||||
`semantic-layer/${input.connectionId ?? 'warehouse'}/${input.sourceName ?? 'orders'}.yaml`,
|
||||
input.yaml ?? ORDERS_YAML,
|
||||
|
|
@ -139,7 +139,7 @@ describe('runKtxSl', () => {
|
|||
|
||||
it('fails validation when a table-backed source declares columns absent from a matching warehouse manifest', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
const project = await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir });
|
||||
await project.fileStore.writeFile(
|
||||
'semantic-layer/postgres-warehouse/_schema/orbit_analytics.yaml',
|
||||
`tables:
|
||||
|
|
@ -189,7 +189,7 @@ joins: []
|
|||
|
||||
it('runs sl query and prints SQL output', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
const project = await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir });
|
||||
project.config.connections.warehouse = { driver: 'postgres' };
|
||||
await project.fileStore.writeFile(
|
||||
'semantic-layer/warehouse/orders.yaml',
|
||||
|
|
@ -246,7 +246,7 @@ joins: []
|
|||
|
||||
it('runs sl query from a JSON query file', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
const project = await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir });
|
||||
project.config.connections.warehouse = { driver: 'postgres' };
|
||||
await project.fileStore.writeFile(
|
||||
'semantic-layer/warehouse/orders.yaml',
|
||||
|
|
@ -313,7 +313,7 @@ joins: []
|
|||
|
||||
it('creates default sl query compute through the managed runtime helper', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
const project = await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir });
|
||||
project.config.connections.warehouse = { driver: 'postgres' };
|
||||
await project.fileStore.writeFile(
|
||||
'semantic-layer/warehouse/orders.yaml',
|
||||
|
|
@ -374,7 +374,7 @@ joins: []
|
|||
|
||||
it('executes sl query through the injected query executor', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
const project = await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir });
|
||||
project.config.connections.warehouse = { driver: 'postgres', url: 'postgres://example/db' };
|
||||
await project.fileStore.writeFile(
|
||||
'semantic-layer/warehouse/orders.yaml',
|
||||
|
|
@ -459,7 +459,7 @@ joins: []
|
|||
|
||||
it('executes sl query against a local SQLite connection through the default executor', async () => {
|
||||
const projectDir = join(tempDir, 'project');
|
||||
const project = await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir });
|
||||
const dbPath = join(projectDir, 'warehouse.db');
|
||||
const db = new Database(dbPath);
|
||||
db.exec(`
|
||||
|
|
@ -475,7 +475,6 @@ joins: []
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ async function writeSqliteScanConfig(projectDir: string, dbPath: string, enrich
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
@ -218,7 +217,6 @@ describe('standalone built ktx CLI smoke', () => {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: gateway-smoke',
|
||||
'llm:',
|
||||
' provider:',
|
||||
' backend: gateway',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { basename } from 'node:path';
|
||||
import type {
|
||||
KtxConfigIssue,
|
||||
KtxLocalProject,
|
||||
|
|
@ -650,7 +651,7 @@ export async function buildProjectStatus(project: KtxLocalProject, options: Buil
|
|||
const { verdict, reason, nextActions } = buildVerdict(llm, embeddings, connections, queryHistory, warnings);
|
||||
|
||||
return {
|
||||
projectName: config.project,
|
||||
projectName: basename(project.projectDir) || project.projectDir,
|
||||
projectDir: project.projectDir,
|
||||
config: configStatus,
|
||||
llm,
|
||||
|
|
|
|||
|
|
@ -166,7 +166,6 @@ async function writeHistoricSqlProject(project: KtxLocalProject): Promise<KtxLoc
|
|||
await writeFile(
|
||||
join(project.projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -231,7 +230,7 @@ describe('historic-SQL local ingest retrieval acceptance', () => {
|
|||
});
|
||||
|
||||
it('projects table and pattern evidence into semantic-layer and wiki retrieval surfaces', async () => {
|
||||
const initialized = await initKtxProject({ projectDir: join(tempDir, 'project'), projectName: 'warehouse' });
|
||||
const initialized = await initKtxProject({ projectDir: join(tempDir, 'project') });
|
||||
const project = await writeHistoricSqlProject(initialized);
|
||||
const sqlAnalysis = acceptanceSqlAnalysis();
|
||||
const agentRunner = new HistoricSqlAcceptanceAgentRunner();
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ describe('Metabase YAML source state and discovery cache', () => {
|
|||
function projectWithMetabaseMappings(mappings: Record<string, unknown>) {
|
||||
return {
|
||||
config: {
|
||||
...buildDefaultKtxProjectConfig('metabase-cache-test'),
|
||||
...buildDefaultKtxProjectConfig(),
|
||||
connections: {
|
||||
'prod-metabase': connectionConfigSchema.parse({
|
||||
driver: 'metabase',
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ describe('local ingest adapters', () => {
|
|||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-local-adapters-'));
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
project = await loadKtxProject({ projectDir });
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -314,11 +314,10 @@ describe('canonical local ingest', () => {
|
|||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-local-full-ingest-'));
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -443,7 +442,6 @@ describe('canonical local ingest', () => {
|
|||
await writeFile(
|
||||
join(project.projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -521,11 +519,10 @@ describe('canonical local ingest', () => {
|
|||
|
||||
it('runs historic-SQL evidence projection through the local bundle post-processor', async () => {
|
||||
const projectDir = join(tempDir, 'historic-sql-project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -605,11 +602,10 @@ describe('canonical local ingest', () => {
|
|||
|
||||
it('rejects direct Metabase scheduled pulls before requiring a local ingest LLM provider', async () => {
|
||||
const projectDir = join(tempDir, 'metabase-project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -637,7 +633,7 @@ describe('canonical local ingest', () => {
|
|||
|
||||
it('runs full MetricFlow local ingest from a dbt repo fixture through the canonical runner', async () => {
|
||||
const projectDir = join(tempDir, 'metricflow-run-project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
|
||||
const fixtureDir = join(tempDir, 'metricflow-fixture');
|
||||
await mkdir(join(fixtureDir, 'models'), { recursive: true });
|
||||
|
|
@ -685,7 +681,6 @@ describe('canonical local ingest', () => {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -767,7 +762,6 @@ describe('canonical local ingest', () => {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: local-mf',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -801,11 +795,10 @@ describe('canonical local ingest', () => {
|
|||
|
||||
it('runs scheduled Looker ingest through the canonical local runner and records SL target evidence', async () => {
|
||||
const projectDir = join(tempDir, 'looker-project');
|
||||
await initKtxProject({ projectDir, projectName: 'looker-runtime' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: looker-runtime',
|
||||
'connections:',
|
||||
' prod-looker:',
|
||||
' driver: looker',
|
||||
|
|
|
|||
|
|
@ -24,11 +24,10 @@ describe('createLocalBundleIngestRuntime', () => {
|
|||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-local-bundle-runtime-'));
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -149,7 +148,6 @@ describe('createLocalBundleIngestRuntime', () => {
|
|||
await writeFile(
|
||||
join(project.projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ async function writeWarehouseConfig(projectDir: string): Promise<void> {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -34,7 +33,6 @@ async function writeLiveDatabaseConfig(projectDir: string): Promise<void> {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -88,7 +86,7 @@ describe('local ingest', () => {
|
|||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-local-ingest-'));
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeWarehouseConfig(projectDir);
|
||||
project = await loadKtxProject({ projectDir });
|
||||
});
|
||||
|
|
@ -574,7 +572,6 @@ describe('local ingest', () => {
|
|||
await writeFile(
|
||||
join(project.projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' notion-main:',
|
||||
' driver: notion',
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ describe('EntityDetailsTool', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-entity-details-'));
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project'), projectName: 'warehouse' });
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project') });
|
||||
await seedLiveDatabaseScan();
|
||||
tool = new EntityDetailsTool(() => new WarehouseCatalogService({ fileStore: project.fileStore }));
|
||||
context = {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ describe('WarehouseCatalogService', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-warehouse-catalog-'));
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project'), projectName: 'warehouse' });
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project') });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ describe('local KTX embedding config', () => {
|
|||
it('constructs deterministic embeddings from the default project config', () => {
|
||||
const createKtxEmbeddingProvider = vi.fn(() => ({}) as never);
|
||||
const provider = createLocalKtxEmbeddingProviderFromConfig(
|
||||
buildDefaultKtxProjectConfig('warehouse').ingest.embeddings,
|
||||
buildDefaultKtxProjectConfig().ingest.embeddings,
|
||||
{ createKtxEmbeddingProvider },
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
}
|
||||
|
||||
it('lists local project connections from ktx.yaml', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.connections.warehouse = {
|
||||
driver: 'postgres',
|
||||
url: 'env:DATABASE_URL',
|
||||
|
|
@ -84,7 +84,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('tests a local project connection through the native scan connector factory', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.connections.warehouse = {
|
||||
driver: 'postgres',
|
||||
url: 'env:DATABASE_URL',
|
||||
|
|
@ -120,7 +120,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('triggers canonical bundle ingest and reads status, report, and replay through MCP ports', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.connections.warehouse = {
|
||||
driver: 'postgres',
|
||||
};
|
||||
|
|
@ -216,7 +216,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('returns child run metadata for local Metabase fan-out triggers', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.connections = {
|
||||
'prod-metabase': {
|
||||
driver: 'metabase',
|
||||
|
|
@ -339,7 +339,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('writes, reads, and searches global wiki pages', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
const ports = createLocalProjectMcpContextPorts(project);
|
||||
|
||||
await expect(
|
||||
|
|
@ -383,7 +383,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('writes, lists, reads, and validates semantic-layer sources', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
const ports = createLocalProjectMcpContextPorts(project);
|
||||
|
||||
await expect(
|
||||
|
|
@ -449,7 +449,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('returns semantic-layer hybrid search metadata through local project ports', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
await writeLocalSlSource(project, {
|
||||
connectionId: 'warehouse',
|
||||
sourceName: 'orders',
|
||||
|
|
@ -518,7 +518,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('returns historic SQL usage frequency and snippet through semantic-layer list search', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
await project.fileStore.writeFile(
|
||||
'semantic-layer/warehouse/_schema/public.yaml',
|
||||
`tables:
|
||||
|
|
@ -566,7 +566,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('uses configured local embeddings for semantic-layer search when available', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.ingest.embeddings = { backend: 'none', dimensions: 2 };
|
||||
await writeLocalSlSource(project, {
|
||||
connectionId: 'warehouse',
|
||||
|
|
@ -607,7 +607,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('rejects path traversal keys before touching the project directory', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
const ports = createLocalProjectMcpContextPorts(project);
|
||||
|
||||
await expect(
|
||||
|
|
@ -626,7 +626,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('uses semantic compute for validation and compile-only sl_query when supplied', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.connections.warehouse = {
|
||||
driver: 'postgres',
|
||||
url: 'env:DATABASE_URL',
|
||||
|
|
@ -712,7 +712,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('executes local MCP sl_query when a query executor is configured', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.connections.warehouse = {
|
||||
driver: 'postgres',
|
||||
url: 'env:DATABASE_URL',
|
||||
|
|
@ -770,7 +770,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('exposes detailed local ingest trigger and status ports when local ingest is enabled', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.connections.warehouse = { driver: 'postgres' };
|
||||
project.config.ingest.adapters = ['fake'];
|
||||
project.config.ingest.embeddings = {
|
||||
|
|
@ -890,7 +890,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('passes local ingest pull-config options into runLocalIngest', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.connections.warehouse = { driver: 'postgres' };
|
||||
project.config.ingest.adapters = ['looker'];
|
||||
const runLocalIngest = vi.fn(async () => ({
|
||||
|
|
@ -949,7 +949,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('triggers fetch-capable local ingest without sourceDir config', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.connections.warehouse = {
|
||||
driver: 'postgres',
|
||||
url: 'postgres://localhost:5432/warehouse',
|
||||
|
|
@ -1024,7 +1024,7 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
});
|
||||
|
||||
it('lists and reads only artifacts that belong to a local scan report', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.connections.warehouse = {
|
||||
driver: 'postgres',
|
||||
url: 'env:DATABASE_URL',
|
||||
|
|
@ -1140,6 +1140,6 @@ describe('createLocalProjectMcpContextPorts', () => {
|
|||
}),
|
||||
).resolves.toBeNull();
|
||||
await expect(ports.scan?.listArtifacts?.({ runId: 'missing' })).resolves.toBeNull();
|
||||
await expect(readFile(join(project.projectDir, 'ktx.yaml'), 'utf-8')).resolves.toContain('project: warehouse');
|
||||
await expect(readFile(join(project.projectDir, 'ktx.yaml'), 'utf-8')).resolves.not.toContain('project:');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ describe('createKtxMcpServer', () => {
|
|||
it('runs MCP memory_capture against a local project memory port', async () => {
|
||||
const tempDir = await mkdtemp(join(tmpdir(), 'ktx-mcp-local-memory-'));
|
||||
try {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
const agentRunner = {
|
||||
runLoop: async ({
|
||||
toolSet,
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ describe('createLocalProjectMemoryCapture', () => {
|
|||
});
|
||||
|
||||
it('captures a wiki page through the local memory agent and persists pollable status', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
const agentRunner = {
|
||||
runLoop: async ({
|
||||
toolSet,
|
||||
|
|
@ -144,7 +144,7 @@ describe('createLocalProjectMemoryCapture', () => {
|
|||
});
|
||||
|
||||
it('captures a semantic-layer source for a named local connection id', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' });
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.connections.warehouse = { driver: 'postgres' };
|
||||
const agentRunner = {
|
||||
runLoop: async ({
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ describe('KTX project config', () => {
|
|||
it.each(['status', 'replay', 'run', 'watch'])('accepts former ingest subcommand name "%s" as a connection id', (connectionId) => {
|
||||
expect(
|
||||
parseKtxProjectConfig(`
|
||||
project: reserved-test
|
||||
connections:
|
||||
${connectionId}:
|
||||
driver: postgres
|
||||
|
|
@ -24,8 +23,7 @@ connections:
|
|||
});
|
||||
|
||||
it('builds the default standalone project config', () => {
|
||||
expect(buildDefaultKtxProjectConfig('warehouse')).toEqual({
|
||||
project: 'warehouse',
|
||||
expect(buildDefaultKtxProjectConfig()).toEqual({
|
||||
connections: {},
|
||||
storage: {
|
||||
state: 'sqlite',
|
||||
|
|
@ -84,15 +82,14 @@ connections:
|
|||
});
|
||||
|
||||
it('round-trips through YAML with stable defaults', () => {
|
||||
const serialized = serializeKtxProjectConfig(buildDefaultKtxProjectConfig('warehouse'));
|
||||
const serialized = serializeKtxProjectConfig(buildDefaultKtxProjectConfig());
|
||||
const parsed = parseKtxProjectConfig(serialized);
|
||||
|
||||
expect(serialized).toContain('project: warehouse');
|
||||
expect(serialized).not.toContain('project:');
|
||||
expect(serialized).not.toContain('live-database');
|
||||
expect(serialized).toContain(
|
||||
' embeddings:\n backend: deterministic\n model: deterministic\n dimensions: 8',
|
||||
);
|
||||
expect(parsed.project).toBe('warehouse');
|
||||
expect(parsed.ingest.adapters).toEqual([]);
|
||||
expect(parsed.ingest.embeddings).toEqual({
|
||||
backend: 'deterministic',
|
||||
|
|
@ -103,7 +100,6 @@ connections:
|
|||
|
||||
it('parses and serializes setup warehouse metadata without setup progress', () => {
|
||||
const config = parseKtxProjectConfig(`
|
||||
project: revenue
|
||||
setup:
|
||||
database_connection_ids:
|
||||
- warehouse
|
||||
|
|
@ -126,7 +122,6 @@ connections:
|
|||
|
||||
it('parses global direct Anthropic LLM config', () => {
|
||||
const config = parseKtxProjectConfig(`
|
||||
project: demo
|
||||
llm:
|
||||
provider:
|
||||
backend: anthropic
|
||||
|
|
@ -166,7 +161,6 @@ ingest:
|
|||
|
||||
it('parses global Vertex LLM config', () => {
|
||||
const config = parseKtxProjectConfig(`
|
||||
project: demo
|
||||
llm:
|
||||
provider:
|
||||
backend: vertex
|
||||
|
|
@ -188,7 +182,6 @@ llm:
|
|||
|
||||
it('parses gateway LLM, OpenAI scan embeddings, and sentence-transformers ingest embeddings', () => {
|
||||
const config = parseKtxProjectConfig(`
|
||||
project: demo
|
||||
llm:
|
||||
provider:
|
||||
backend: gateway
|
||||
|
|
@ -232,7 +225,6 @@ scan:
|
|||
|
||||
it('parses scan relationship settings', () => {
|
||||
const config = parseKtxProjectConfig(`
|
||||
project: demo
|
||||
scan:
|
||||
relationships:
|
||||
enabled: false
|
||||
|
|
@ -273,7 +265,6 @@ scan:
|
|||
|
||||
it('parses the scan relationship validation budget sentinel', () => {
|
||||
const config = parseKtxProjectConfig(`
|
||||
project: demo
|
||||
scan:
|
||||
relationships:
|
||||
validationBudget: all
|
||||
|
|
@ -285,7 +276,6 @@ scan:
|
|||
|
||||
it('rejects out-of-range scan relationship numeric settings', () => {
|
||||
const yaml = `
|
||||
project: demo
|
||||
scan:
|
||||
relationships:
|
||||
acceptThreshold: 2
|
||||
|
|
@ -316,7 +306,6 @@ scan:
|
|||
|
||||
it('rejects invalid scan relationship validation budget strings', () => {
|
||||
const yaml = `
|
||||
project: demo
|
||||
scan:
|
||||
relationships:
|
||||
validationBudget: infinite
|
||||
|
|
@ -327,7 +316,6 @@ scan:
|
|||
it('rejects unsupported local LLM and embedding fields', () => {
|
||||
expect(() =>
|
||||
parseKtxProjectConfig(`
|
||||
project: demo
|
||||
ingest:
|
||||
llm:
|
||||
backend: anthropic
|
||||
|
|
@ -336,7 +324,6 @@ ingest:
|
|||
|
||||
expect(() =>
|
||||
parseKtxProjectConfig(`
|
||||
project: demo
|
||||
scan:
|
||||
enrichment:
|
||||
backend: gateway
|
||||
|
|
@ -345,7 +332,6 @@ scan:
|
|||
|
||||
expect(() =>
|
||||
parseKtxProjectConfig(`
|
||||
project: demo
|
||||
scan:
|
||||
enrichment:
|
||||
mode: llm
|
||||
|
|
@ -356,7 +342,6 @@ scan:
|
|||
|
||||
expect(() =>
|
||||
parseKtxProjectConfig(`
|
||||
project: demo
|
||||
ingest:
|
||||
embeddings:
|
||||
provider: gateway
|
||||
|
|
@ -368,7 +353,6 @@ ingest:
|
|||
it('rejects gateway embedding configs', () => {
|
||||
expect(() =>
|
||||
parseKtxProjectConfig(`
|
||||
project: demo
|
||||
ingest:
|
||||
embeddings:
|
||||
backend: gateway
|
||||
|
|
@ -379,7 +363,6 @@ ingest:
|
|||
|
||||
expect(() =>
|
||||
parseKtxProjectConfig(`
|
||||
project: demo
|
||||
scan:
|
||||
enrichment:
|
||||
mode: llm
|
||||
|
|
@ -392,9 +375,9 @@ scan:
|
|||
});
|
||||
|
||||
it('fills optional sections when a minimal config is loaded', () => {
|
||||
const config = parseKtxProjectConfig('project: local\n');
|
||||
const config = parseKtxProjectConfig('{}\n');
|
||||
|
||||
expect(config).toEqual(buildDefaultKtxProjectConfig('local'));
|
||||
expect(config).toEqual(buildDefaultKtxProjectConfig());
|
||||
expect(config.ingest.embeddings).toEqual({
|
||||
backend: 'deterministic',
|
||||
model: 'deterministic',
|
||||
|
|
@ -406,14 +389,15 @@ scan:
|
|||
expect(() => parseKtxProjectConfig('- nope\n')).toThrow('ktx.yaml must contain a YAML object');
|
||||
});
|
||||
|
||||
it('rejects configs with a missing project name', () => {
|
||||
expect(() => parseKtxProjectConfig('connections: {}\n')).toThrow('ktx.yaml field "project" is required');
|
||||
it('accepts configs without a project name', () => {
|
||||
expect(parseKtxProjectConfig('connections: {}\n')).toMatchObject({
|
||||
connections: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects unknown top-level fields under strict mode', () => {
|
||||
expect(() =>
|
||||
parseKtxProjectConfig(`
|
||||
project: demo
|
||||
storrage:
|
||||
state: sqlite
|
||||
`),
|
||||
|
|
@ -423,13 +407,12 @@ storrage:
|
|||
|
||||
describe('validateKtxProjectConfig', () => {
|
||||
it('returns ok: true with no issues for a valid config', () => {
|
||||
const result = validateKtxProjectConfig('project: warehouse\n');
|
||||
const result = validateKtxProjectConfig('connections: {}\n');
|
||||
expect(result).toEqual({ ok: true, issues: [] });
|
||||
});
|
||||
|
||||
it('collects every schema issue without throwing', () => {
|
||||
const result = validateKtxProjectConfig(`
|
||||
project: ""
|
||||
storage:
|
||||
search: not-a-real-backend
|
||||
scan:
|
||||
|
|
@ -441,7 +424,6 @@ scan:
|
|||
const paths = result.issues.map((issue) => issue.path);
|
||||
expect(paths).toEqual(
|
||||
expect.arrayContaining([
|
||||
'project',
|
||||
'storage.search',
|
||||
'scan.relationships.acceptThreshold',
|
||||
]),
|
||||
|
|
@ -450,7 +432,6 @@ scan:
|
|||
|
||||
it('attaches migration hints for known deprecated keys', () => {
|
||||
const result = validateKtxProjectConfig(`
|
||||
project: demo
|
||||
ingest:
|
||||
llm:
|
||||
backend: anthropic
|
||||
|
|
@ -499,18 +480,15 @@ describe('generateKtxProjectConfigJsonSchema', () => {
|
|||
|
||||
it('exposes every top-level ktx.yaml section under properties', () => {
|
||||
const properties = schema.properties as Record<string, unknown>;
|
||||
expect(Object.keys(properties).sort()).toEqual(
|
||||
['agent', 'connections', 'ingest', 'llm', 'memory', 'project', 'scan', 'setup', 'storage'].sort(),
|
||||
);
|
||||
expect(Object.keys(properties).sort()).toEqual(['agent', 'connections', 'ingest', 'llm', 'memory', 'scan', 'setup', 'storage'].sort());
|
||||
});
|
||||
|
||||
it('marks "project" as required', () => {
|
||||
expect(schema.required).toEqual(expect.arrayContaining(['project']));
|
||||
it('does not require any top-level fields', () => {
|
||||
expect(schema.required).toBeUndefined();
|
||||
});
|
||||
|
||||
it('carries .describe() text on top-level fields', () => {
|
||||
const properties = schema.properties as Record<string, { description?: string }>;
|
||||
expect(properties.project?.description).toMatch(/Project identifier/);
|
||||
expect(properties.llm?.description).toMatch(/LLM/);
|
||||
expect(properties.scan?.description).toMatch(/Schema-scan/);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -238,11 +238,6 @@ const memorySchema = z
|
|||
|
||||
const ktxProjectConfigSchema = z
|
||||
.strictObject({
|
||||
project: z
|
||||
.string({ error: 'ktx.yaml field "project" is required' })
|
||||
.trim()
|
||||
.min(1, 'ktx.yaml field "project" is required')
|
||||
.describe('Project identifier; used in logs, ktx state files, and as the default workspace name.'),
|
||||
setup: setupSchema.optional().describe('Setup-wizard state. Written by `ktx setup`; may be omitted.'),
|
||||
connections: z
|
||||
.record(z.string(), connectionSchema)
|
||||
|
|
@ -332,8 +327,8 @@ function formatZodError(error: z.ZodError, input: unknown): string {
|
|||
.join('\n');
|
||||
}
|
||||
|
||||
export function buildDefaultKtxProjectConfig(projectName = 'ktx-project'): KtxProjectConfig {
|
||||
return ktxProjectConfigSchema.parse({ project: projectName });
|
||||
export function buildDefaultKtxProjectConfig(): KtxProjectConfig {
|
||||
return ktxProjectConfigSchema.parse({});
|
||||
}
|
||||
|
||||
export function parseKtxProjectConfig(raw: string): KtxProjectConfig {
|
||||
|
|
|
|||
|
|
@ -20,15 +20,13 @@ describe('KTX local project runtime', () => {
|
|||
|
||||
const result = await initKtxProject({
|
||||
projectDir,
|
||||
projectName: 'warehouse',
|
||||
authorName: 'Agent',
|
||||
authorEmail: 'agent@example.com',
|
||||
});
|
||||
|
||||
expect(result.projectDir).toBe(projectDir);
|
||||
expect(result.config.project).toBe('warehouse');
|
||||
expect(result.commitHash).toMatch(/^[0-9a-f]{40}$/);
|
||||
await expect(readFile(join(projectDir, 'ktx.yaml'), 'utf-8')).resolves.toContain('project: warehouse');
|
||||
await expect(readFile(join(projectDir, 'ktx.yaml'), 'utf-8')).resolves.not.toContain('project:');
|
||||
const gitignore = await readFile(join(projectDir, '.ktx/.gitignore'), 'utf-8');
|
||||
expect(gitignore).toContain('cache/');
|
||||
expect(gitignore).toContain('db.sqlite');
|
||||
|
|
@ -46,7 +44,7 @@ describe('KTX local project runtime', () => {
|
|||
|
||||
it('loads an initialized project with a working file store', async () => {
|
||||
const projectDir = join(tempDir, 'warehouse');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
|
||||
const loaded = await loadKtxProject({ projectDir });
|
||||
await loaded.fileStore.writeFile(
|
||||
|
|
@ -57,7 +55,6 @@ describe('KTX local project runtime', () => {
|
|||
'Add revenue page',
|
||||
);
|
||||
|
||||
expect(loaded.config.project).toBe('warehouse');
|
||||
await expect(loaded.fileStore.readFile('wiki/global/revenue.md')).resolves.toMatchObject({
|
||||
content: '# Revenue\n',
|
||||
});
|
||||
|
|
@ -65,16 +62,12 @@ describe('KTX local project runtime', () => {
|
|||
|
||||
it('rejects reinitializing an existing project unless force is set', async () => {
|
||||
const projectDir = join(tempDir, 'warehouse');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
|
||||
await expect(initKtxProject({ projectDir, projectName: 'warehouse' })).rejects.toThrow(
|
||||
'Project already contains ktx.yaml',
|
||||
);
|
||||
await expect(initKtxProject({ projectDir })).rejects.toThrow('Project already contains ktx.yaml');
|
||||
|
||||
await expect(initKtxProject({ projectDir, projectName: 'warehouse-v2', force: true })).resolves.toMatchObject({
|
||||
config: {
|
||||
project: 'warehouse-v2',
|
||||
},
|
||||
await expect(initKtxProject({ projectDir, force: true })).resolves.toMatchObject({
|
||||
configPath: join(projectDir, 'ktx.yaml'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { LocalGitFileStore } from './local-git-file-store.js';
|
|||
|
||||
export interface InitKtxProjectOptions {
|
||||
projectDir: string;
|
||||
projectName?: string;
|
||||
force?: boolean;
|
||||
authorName?: string;
|
||||
authorEmail?: string;
|
||||
|
|
@ -101,7 +100,7 @@ async function createRuntime(
|
|||
|
||||
export async function initKtxProject(options: InitKtxProjectOptions): Promise<InitKtxProjectResult> {
|
||||
const projectDir = resolve(options.projectDir);
|
||||
const projectName = options.projectName?.trim() || basename(projectDir) || 'ktx-project';
|
||||
const projectName = basename(projectDir) || 'ktx-project';
|
||||
const authorName = options.authorName ?? 'ktx';
|
||||
const authorEmail = options.authorEmail ?? 'ktx@example.com';
|
||||
const logger = options.logger ?? noopLogger;
|
||||
|
|
@ -112,7 +111,7 @@ export async function initKtxProject(options: InitKtxProjectOptions): Promise<In
|
|||
throw new Error(`Project already contains ktx.yaml: ${configPath}`);
|
||||
}
|
||||
|
||||
const config = buildDefaultKtxProjectConfig(projectName);
|
||||
const config = buildDefaultKtxProjectConfig();
|
||||
const runtime = await createRuntime(projectDir, config, authorName, authorEmail, logger);
|
||||
|
||||
await writeProjectFile(projectDir, 'ktx.yaml', serializeKtxProjectConfig(config));
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ describe('KTX setup config helpers', () => {
|
|||
});
|
||||
|
||||
it('sets setup database connection ids without duplicates', () => {
|
||||
const config = buildDefaultKtxProjectConfig('warehouse');
|
||||
const config = buildDefaultKtxProjectConfig();
|
||||
|
||||
const withDatabases = setKtxSetupDatabaseConnectionIds(config, ['warehouse', 'analytics', 'warehouse']);
|
||||
|
||||
|
|
|
|||
|
|
@ -231,7 +231,6 @@ describe('writeLocalScanEnrichmentArtifacts', () => {
|
|||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-local-enrichment-artifacts-'));
|
||||
project = await initKtxProject({
|
||||
projectDir: join(tempDir, 'project'),
|
||||
projectName: 'warehouse',
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -371,7 +371,7 @@ describe('local scan enrichment', () => {
|
|||
},
|
||||
},
|
||||
relationshipSettings: {
|
||||
...buildDefaultKtxProjectConfig('warehouse').scan.relationships,
|
||||
...buildDefaultKtxProjectConfig().scan.relationships,
|
||||
llmProposals: false,
|
||||
maxLlmTablesPerBatch: 40,
|
||||
},
|
||||
|
|
@ -383,7 +383,7 @@ describe('local scan enrichment', () => {
|
|||
|
||||
it('skips relationship detection when scan relationships are disabled', async () => {
|
||||
const settings = {
|
||||
...buildDefaultKtxProjectConfig('warehouse').scan.relationships,
|
||||
...buildDefaultKtxProjectConfig().scan.relationships,
|
||||
enabled: false,
|
||||
};
|
||||
const result = await runLocalScanEnrichment({
|
||||
|
|
@ -474,7 +474,7 @@ describe('local scan enrichment', () => {
|
|||
})),
|
||||
};
|
||||
const settings = {
|
||||
...buildDefaultKtxProjectConfig('test').scan.relationships,
|
||||
...buildDefaultKtxProjectConfig().scan.relationships,
|
||||
enabled: false,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -515,8 +515,7 @@ export async function runLocalScanEnrichment(
|
|||
const now = input.now ?? (() => new Date());
|
||||
const state = completedKtxScanEnrichmentStateSummary();
|
||||
const syncId = input.syncId ?? input.context.runId;
|
||||
const relationshipSettings =
|
||||
input.relationshipSettings ?? buildDefaultKtxProjectConfig(input.connectionId).scan.relationships;
|
||||
const relationshipSettings = input.relationshipSettings ?? buildDefaultKtxProjectConfig().scan.relationships;
|
||||
const inputHash = computeKtxScanEnrichmentInputHash({
|
||||
snapshot,
|
||||
mode: input.mode,
|
||||
|
|
|
|||
|
|
@ -105,7 +105,6 @@ async function writeLiveDatabaseConfig(projectDir: string): Promise<void> {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -123,7 +122,6 @@ async function writeDatabaseConfigWithoutIngestAdapters(projectDir: string): Pro
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -184,7 +182,7 @@ describe('local scan', () => {
|
|||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-local-scan-'));
|
||||
const projectDir = join(tempDir, 'project');
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeLiveDatabaseConfig(projectDir);
|
||||
project = await loadKtxProject({ projectDir });
|
||||
});
|
||||
|
|
@ -1037,7 +1035,6 @@ describe('local scan', () => {
|
|||
await writeFile(
|
||||
join(project.projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
@ -1393,7 +1390,6 @@ describe('local scan', () => {
|
|||
await writeFile(
|
||||
join(project.projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
@ -1425,7 +1421,6 @@ describe('local scan', () => {
|
|||
await writeFile(
|
||||
join(project.projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: mysql',
|
||||
|
|
@ -1457,7 +1452,6 @@ describe('local scan', () => {
|
|||
await writeFile(
|
||||
join(project.projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: clickhouse',
|
||||
|
|
@ -1492,7 +1486,6 @@ describe('local scan', () => {
|
|||
await writeFile(
|
||||
join(project.projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlserver',
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ describe('readLocalScanStructuralSnapshot', () => {
|
|||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-local-structural-artifacts-'));
|
||||
project = await initKtxProject({
|
||||
projectDir: join(tempDir, 'project'),
|
||||
projectName: 'warehouse',
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ async function writeWarehouseConfig(projectDir: string): Promise<void> {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
@ -67,7 +66,7 @@ function liveDatabaseAdapter(): SourceAdapter {
|
|||
}
|
||||
|
||||
async function createLiveDatabaseRun(projectDir: string, runId: string) {
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeWarehouseConfig(projectDir);
|
||||
const project = await loadKtxProject({ projectDir });
|
||||
await runLocalStageOnlyIngest({
|
||||
|
|
@ -283,7 +282,7 @@ describe('local scan relationship artifact reader', () => {
|
|||
it('returns null when the scan run has no report', async () => {
|
||||
const projectDir = await mkdtemp(join(tmpdir(), 'ktx-relationship-artifacts-missing-run-'));
|
||||
try {
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
const project = await loadKtxProject({ projectDir });
|
||||
|
||||
await expect(readLocalScanRelationshipArtifacts(project, 'missing-run')).resolves.toBeNull();
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ function llmProvider(): KtxLlmProvider {
|
|||
}
|
||||
|
||||
function relationshipSettings() {
|
||||
return buildDefaultKtxProjectConfig('warehouse').scan.relationships;
|
||||
return buildDefaultKtxProjectConfig().scan.relationships;
|
||||
}
|
||||
|
||||
function llmOnlyRelationshipSnapshot(): KtxSchemaSnapshot {
|
||||
|
|
@ -557,7 +557,7 @@ describe('production relationship discovery', () => {
|
|||
`);
|
||||
|
||||
const settings = {
|
||||
...buildDefaultKtxProjectConfig('warehouse').scan.relationships,
|
||||
...buildDefaultKtxProjectConfig().scan.relationships,
|
||||
acceptThreshold: 0.99,
|
||||
reviewThreshold: 0.55,
|
||||
};
|
||||
|
|
@ -633,7 +633,7 @@ describe('production relationship discovery', () => {
|
|||
schema: snapshotToKtxEnrichedSchema(richSnapshot),
|
||||
context: { runId: 'candidate-cap' },
|
||||
settings: {
|
||||
...buildDefaultKtxProjectConfig('warehouse').scan.relationships,
|
||||
...buildDefaultKtxProjectConfig().scan.relationships,
|
||||
maxCandidatesPerColumn: 1,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -202,7 +202,6 @@ async function projectWithDecisions(
|
|||
const tempDir = await mkdtemp(join(tmpdir(), 'ktx-relationship-review-apply-'));
|
||||
const project = await initKtxProject({
|
||||
projectDir: join(tempDir, 'project'),
|
||||
projectName: 'warehouse',
|
||||
});
|
||||
await project.fileStore.writeFile(
|
||||
'raw-sources/warehouse/live-database/sync-a/enrichment/relationship-review-decisions.json',
|
||||
|
|
|
|||
|
|
@ -19,11 +19,10 @@ async function writeProjectFile(projectDir: string, relativePath: string, conten
|
|||
}
|
||||
|
||||
async function createProject(projectDir: string): Promise<void> {
|
||||
await initKtxProject({ projectDir, projectName: 'warehouse' });
|
||||
await initKtxProject({ projectDir });
|
||||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
|
|||
|
|
@ -241,7 +241,7 @@ describe('SQLite hybrid search backend conformance', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-search-conformance-'));
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project'), projectName: 'warehouse' });
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project') });
|
||||
dbPath = join(tempDir, '.ktx', 'db.sqlite');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ describe('compileLocalSlQuery', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-local-query-'));
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project'), projectName: 'warehouse' });
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project') });
|
||||
project.config.connections.warehouse = { driver: 'postgres' };
|
||||
await project.fileStore.writeFile(
|
||||
'semantic-layer/warehouse/orders.yaml',
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ describe('local semantic-layer helpers', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-local-sl-'));
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project'), projectName: 'warehouse' });
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project') });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ describe('PGlite semantic-layer search prototype', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-pglite-sl-prototype-'));
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project'), projectName: 'warehouse' });
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project') });
|
||||
project.config.ingest.embeddings.dimensions = 3;
|
||||
pgliteDataDir = join(tempDir, 'pglite-search');
|
||||
port = await allocatePort();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ describe('loadLatestSlDictionaryEntries', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-sl-dictionary-profile-'));
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project'), projectName: 'warehouse' });
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project') });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ describe('local knowledge helpers', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), 'ktx-local-knowledge-'));
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project'), projectName: 'warehouse' });
|
||||
project = await initKtxProject({ projectDir: join(tempDir, 'project') });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ describe('standalone example docs', () => {
|
|||
assert.match(readme, /Accepted: 9/);
|
||||
assert.match(readme, /Review: 0/);
|
||||
assert.match(readme, /Rejected: 0/);
|
||||
assert.match(config, /project: orbit-relationship-verification/);
|
||||
assert.doesNotMatch(config, /^project:/m);
|
||||
assert.match(config, /orbit:/);
|
||||
assert.match(config, /driver: sqlite/);
|
||||
assert.match(
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ export function buildSeedSql() {
|
|||
|
||||
export function buildKtxYaml(postgresUrl) {
|
||||
return [
|
||||
'project: artifact-live-database',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ describe('installed live-database artifact smoke helpers', () => {
|
|||
assert.equal(
|
||||
buildKtxYaml('postgresql://ktx:postgres@127.0.0.1:15432/warehouse'), // pragma: allowlist secret
|
||||
[
|
||||
'project: artifact-live-database',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: postgres',
|
||||
|
|
|
|||
|
|
@ -642,7 +642,6 @@ try {
|
|||
await writeFile(
|
||||
join(projectDir, 'ktx.yaml'),
|
||||
[
|
||||
'project: warehouse',
|
||||
'connections:',
|
||||
' warehouse:',
|
||||
' driver: sqlite',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue