Skip to content

Commit bb722b8

Browse files
anandgupta42claude
andauthored
test: consolidate 11 test PRs — 305 new tests, deduplicated (#498)
* test: consolidate 11 test PRs into single suite — 305 new tests Consolidates tests from PRs #440, #441, #479, #483, #484, #485, #490, #491, #492, #495, #496. Deduplicates overlapping MongoDB normalize tests (3 PRs → 1), `keybind` tests (2 PRs → 1), and `connections.test.ts` additions (4 PRs → 1 merged file). New test files: - `sql-analyze-tool.test.ts` — `SqlAnalyzeTool` formatting (6 tests) - `sql-analyze-format.test.ts` — `sql.analyze` success semantics + dispatcher (5 tests) - `builtin-commands.test.ts` — altimate builtin command registration (10 tests) - `hints-discover-mcps.test.ts` — `Command.hints()` + discover-and-add-mcps (11 tests) - `fingerprint-detect.test.ts` — file-based project detection rules (11 tests) - `dbt-lineage-helpers.test.ts` — dbt lineage helper functions (13 tests) - `registry-env-loading.test.ts` — `ALTIMATE_CODE_CONN_*` env var loading (8 tests) - `warehouse-telemetry.test.ts` — MongoDB auth detection telemetry (4 tests) - `bus-event.test.ts` — bus event registry payloads (5 tests) - `git.test.ts` — git utility functions (5 tests) - `keybind.test.ts` — keybind parsing, matching, `fromParsedKey` (22 tests) Merged into existing files: - `connections.test.ts` — `loadFromEnv` (5), `detectAuthMethod` (14), credential stripping (1), `containerToConfig` (1), dbt profiles edge cases (3) - `driver-normalize.test.ts` — MongoDB alias resolution (13 tests) Fixes: - `fixture.ts` — disable `commit.gpgsign` in test tmpdir to prevent CI failures - `hints-discover-mcps.test.ts` — corrected sort order expectation - `registry-env-loading.test.ts` — fixed flaky assertion that assumed isolated env Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address CodeRabbit review comments - `bus-event.test.ts`: move `BusEvent.define` into tests instead of module scope - `builtin-commands.test.ts`: fix misleading "lexicographic" → "numeric" sort label - `git.test.ts`: use `tmpdir({ git: true })` fixture instead of manual `git init` - `registry-env-loading.test.ts`: remove unused `fs`, `os`, `path` imports Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent abcaa1d commit bb722b8

14 files changed

Lines changed: 1874 additions & 2 deletions

packages/opencode/test/altimate/connections.test.ts

Lines changed: 297 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { describe, expect, test, beforeEach, beforeAll, afterAll } from "bun:test"
1+
import { describe, expect, test, beforeEach, afterEach, beforeAll, afterAll } from "bun:test"
22
import * as Dispatcher from "../../src/altimate/native/dispatcher"
33

44
// Disable telemetry via env var instead of mock.module
@@ -10,9 +10,10 @@ afterAll(() => { delete process.env.ALTIMATE_TELEMETRY_DISABLED })
1010
// ---------------------------------------------------------------------------
1111

1212
import * as Registry from "../../src/altimate/native/connections/registry"
13+
import { detectAuthMethod } from "../../src/altimate/native/connections/registry"
1314
import * as CredentialStore from "../../src/altimate/native/connections/credential-store"
1415
import { parseDbtProfiles } from "../../src/altimate/native/connections/dbt-profiles"
15-
import { discoverContainers } from "../../src/altimate/native/connections/docker-discovery"
16+
import { discoverContainers, containerToConfig } from "../../src/altimate/native/connections/docker-discovery"
1617
import { registerAll } from "../../src/altimate/native/connections/register"
1718

1819
// ---------------------------------------------------------------------------
@@ -74,6 +75,132 @@ describe("ConnectionRegistry", () => {
7475
})
7576
})
7677

78+
// ---------------------------------------------------------------------------
79+
// loadFromEnv — env-var-based connection config loading
80+
// ---------------------------------------------------------------------------
81+
82+
describe("loadFromEnv via Registry.load()", () => {
83+
const saved: Record<string, string | undefined> = {}
84+
85+
function setEnv(key: string, value: string) {
86+
saved[key] = process.env[key]
87+
process.env[key] = value
88+
}
89+
90+
beforeEach(() => {
91+
Registry.reset()
92+
})
93+
94+
afterEach(() => {
95+
for (const [key, orig] of Object.entries(saved)) {
96+
if (orig === undefined) delete process.env[key]
97+
else process.env[key] = orig
98+
}
99+
for (const key of Object.keys(saved)) delete saved[key]
100+
})
101+
102+
test("parses valid JSON from ALTIMATE_CODE_CONN_* env vars", () => {
103+
setEnv("ALTIMATE_CODE_CONN_MYDB", JSON.stringify({ type: "postgres", host: "localhost", port: 5432 }))
104+
Registry.load()
105+
const config = Registry.getConfig("mydb")
106+
expect(config).toBeDefined()
107+
expect(config?.type).toBe("postgres")
108+
expect(config?.host).toBe("localhost")
109+
})
110+
111+
test("lowercases connection name from env var suffix", () => {
112+
setEnv("ALTIMATE_CODE_CONN_PROD_DB", JSON.stringify({ type: "snowflake", account: "abc" }))
113+
Registry.load()
114+
expect(Registry.getConfig("prod_db")).toBeDefined()
115+
expect(Registry.getConfig("PROD_DB")).toBeUndefined()
116+
})
117+
118+
test("ignores env var with invalid JSON", () => {
119+
setEnv("ALTIMATE_CODE_CONN_BAD", "not-valid-json{")
120+
Registry.load()
121+
expect(Registry.getConfig("bad")).toBeUndefined()
122+
})
123+
124+
test("ignores env var config without type field", () => {
125+
setEnv("ALTIMATE_CODE_CONN_NOTYPE", JSON.stringify({ host: "localhost", port: 5432 }))
126+
Registry.load()
127+
expect(Registry.getConfig("notype")).toBeUndefined()
128+
})
129+
130+
test("ignores non-object JSON values (string, number, array)", () => {
131+
setEnv("ALTIMATE_CODE_CONN_STR", JSON.stringify("just a string"))
132+
setEnv("ALTIMATE_CODE_CONN_NUM", JSON.stringify(42))
133+
setEnv("ALTIMATE_CODE_CONN_ARR", JSON.stringify([1, 2, 3]))
134+
Registry.load()
135+
expect(Registry.getConfig("str")).toBeUndefined()
136+
expect(Registry.getConfig("num")).toBeUndefined()
137+
expect(Registry.getConfig("arr")).toBeUndefined()
138+
})
139+
})
140+
141+
// ---------------------------------------------------------------------------
142+
// detectAuthMethod
143+
// ---------------------------------------------------------------------------
144+
145+
describe("detectAuthMethod", () => {
146+
test("returns 'connection_string' for config with connection_string", () => {
147+
expect(detectAuthMethod({ type: "postgres", connection_string: "postgresql://..." } as any)).toBe("connection_string")
148+
})
149+
150+
test("returns 'key_pair' for Snowflake private_key_path", () => {
151+
expect(detectAuthMethod({ type: "snowflake", private_key_path: "/path/to/key.p8" } as any)).toBe("key_pair")
152+
})
153+
154+
test("returns 'key_pair' for camelCase privateKeyPath", () => {
155+
expect(detectAuthMethod({ type: "snowflake", privateKeyPath: "/path/to/key.p8" } as any)).toBe("key_pair")
156+
})
157+
158+
test("returns 'sso' for Snowflake externalbrowser", () => {
159+
expect(detectAuthMethod({ type: "snowflake", authenticator: "EXTERNALBROWSER" } as any)).toBe("sso")
160+
})
161+
162+
test("returns 'sso' for Okta URL authenticator", () => {
163+
expect(detectAuthMethod({ type: "snowflake", authenticator: "https://myorg.okta.com" } as any)).toBe("sso")
164+
})
165+
166+
test("returns 'oauth' for OAuth authenticator", () => {
167+
expect(detectAuthMethod({ type: "snowflake", authenticator: "OAUTH" } as any)).toBe("oauth")
168+
})
169+
170+
test("returns 'token' for access_token", () => {
171+
expect(detectAuthMethod({ type: "databricks", access_token: "dapi..." } as any)).toBe("token")
172+
})
173+
174+
test("returns 'password' for config with password", () => {
175+
expect(detectAuthMethod({ type: "postgres", password: "secret" } as any)).toBe("password")
176+
})
177+
178+
test("returns 'file' for duckdb", () => {
179+
expect(detectAuthMethod({ type: "duckdb", path: "/data/my.db" } as any)).toBe("file")
180+
})
181+
182+
test("returns 'file' for sqlite", () => {
183+
expect(detectAuthMethod({ type: "sqlite", path: "/data/my.sqlite" } as any)).toBe("file")
184+
})
185+
186+
test("returns 'connection_string' for mongodb without password", () => {
187+
expect(detectAuthMethod({ type: "mongodb" } as any)).toBe("connection_string")
188+
})
189+
190+
test("returns 'password' for mongo with password", () => {
191+
expect(detectAuthMethod({ type: "mongo", password: "secret" } as any)).toBe("password")
192+
})
193+
194+
test("returns 'unknown' for null/undefined", () => {
195+
expect(detectAuthMethod(null)).toBe("unknown")
196+
expect(detectAuthMethod(undefined)).toBe("unknown")
197+
})
198+
199+
test("returns 'unknown' for empty config with no identifiable auth", () => {
200+
expect(detectAuthMethod({ type: "postgres" } as any)).toBe("unknown")
201+
})
202+
})
203+
77204
// ---------------------------------------------------------------------------
78205
// CredentialStore (keytar not available in test environment)
79206
// ---------------------------------------------------------------------------
@@ -135,6 +262,36 @@ describe("CredentialStore", () => {
135262
expect(sanitized.oauth_client_secret).toBeUndefined()
136263
expect(sanitized.authenticator).toBe("oauth")
137264
})
265+
266+
test("saveConnection strips all sensitive fields from complex config", async () => {
267+
const config = {
268+
type: "snowflake",
269+
account: "abc123",
270+
user: "svc_user",
271+
password: "pw123",
272+
private_key: "-----BEGIN PRIVATE KEY-----",
273+
private_key_passphrase: "passphrase",
274+
token: "oauth-token",
275+
oauth_client_secret: "client-secret",
276+
ssh_password: "ssh-pw",
277+
connection_string: "mongodb://...",
278+
} as any
279+
const { sanitized, warnings } = await CredentialStore.saveConnection("complex", config)
280+
281+
expect(sanitized.password).toBeUndefined()
282+
expect(sanitized.private_key).toBeUndefined()
283+
expect(sanitized.private_key_passphrase).toBeUndefined()
284+
expect(sanitized.token).toBeUndefined()
285+
expect(sanitized.oauth_client_secret).toBeUndefined()
286+
expect(sanitized.ssh_password).toBeUndefined()
287+
expect(sanitized.connection_string).toBeUndefined()
288+
289+
expect(sanitized.type).toBe("snowflake")
290+
expect(sanitized.account).toBe("abc123")
291+
expect(sanitized.user).toBe("svc_user")
292+
293+
expect(warnings).toHaveLength(7)
294+
})
138295
})
139296

140297
// ---------------------------------------------------------------------------
@@ -261,6 +418,122 @@ snow:
261418
fs.rmSync(tmpDir, { recursive: true })
262419
}
263420
})
421+
422+
// altimate_change start — tests for untested dbt profiles parser edge cases
423+
test("resolves env_var with default fallback when env var is missing", async () => {
424+
const fs = await import("fs")
425+
const os = await import("os")
426+
const path = await import("path")
427+
428+
delete process.env.__TEST_DBT_MISSING_VAR_12345
429+
430+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbt-test-"))
431+
const profilesPath = path.join(tmpDir, "profiles.yml")
432+
433+
fs.writeFileSync(
434+
profilesPath,
435+
`
436+
myproject:
437+
outputs:
438+
dev:
439+
type: postgres
440+
host: "{{ env_var('__TEST_DBT_MISSING_VAR_12345', 'localhost') }}"
441+
port: 5432
442+
user: "{{ env_var('__TEST_DBT_MISSING_USER_12345', 'default_user') }}"
443+
password: secret
444+
dbname: mydb
445+
`,
446+
)
447+
448+
try {
449+
const connections = await parseDbtProfiles(profilesPath)
450+
expect(connections).toHaveLength(1)
451+
expect(connections[0].config.host).toBe("localhost")
452+
expect(connections[0].config.user).toBe("default_user")
453+
} finally {
454+
fs.rmSync(tmpDir, { recursive: true })
455+
}
456+
})
457+
458+
test("skips 'config' top-level key (dbt global settings)", async () => {
459+
const fs = await import("fs")
460+
const os = await import("os")
461+
const path = await import("path")
462+
463+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbt-test-"))
464+
const profilesPath = path.join(tmpDir, "profiles.yml")
465+
466+
fs.writeFileSync(
467+
profilesPath,
468+
`
469+
config:
470+
send_anonymous_usage_stats: false
471+
use_colors: true
472+
473+
real_project:
474+
outputs:
475+
dev:
476+
type: postgres
477+
host: localhost
478+
dbname: analytics
479+
`,
480+
)
481+
482+
try {
483+
const connections = await parseDbtProfiles(profilesPath)
484+
expect(connections).toHaveLength(1)
485+
expect(connections[0].name).toBe("real_project_dev")
486+
expect(connections.find((c) => c.name.startsWith("config"))).toBeUndefined()
487+
} finally {
488+
fs.rmSync(tmpDir, { recursive: true })
489+
}
490+
})
491+
492+
test("handles multiple profiles with multiple outputs", async () => {
493+
const fs = await import("fs")
494+
const os = await import("os")
495+
const path = await import("path")
496+
497+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbt-test-"))
498+
const profilesPath = path.join(tmpDir, "profiles.yml")
499+
500+
fs.writeFileSync(
501+
profilesPath,
502+
`
503+
warehouse_a:
504+
outputs:
505+
dev:
506+
type: postgres
507+
host: localhost
508+
dbname: dev_db
509+
prod:
510+
type: postgres
511+
host: prod.example.com
512+
dbname: prod_db
513+
514+
warehouse_b:
515+
outputs:
516+
staging:
517+
type: snowflake
518+
account: abc123
519+
user: admin
520+
password: pw
521+
database: STAGING
522+
warehouse: COMPUTE_WH
523+
schema: PUBLIC
524+
`,
525+
)
526+
527+
try {
528+
const connections = await parseDbtProfiles(profilesPath)
529+
expect(connections).toHaveLength(3)
530+
const names = connections.map((c) => c.name).sort()
531+
expect(names).toEqual(["warehouse_a_dev", "warehouse_a_prod", "warehouse_b_staging"])
532+
} finally {
533+
fs.rmSync(tmpDir, { recursive: true })
534+
}
535+
})
536+
// altimate_change end
264537
})
265538

266539
// ---------------------------------------------------------------------------
@@ -272,6 +545,28 @@ describe("Docker discovery", () => {
272545
const containers = await discoverContainers()
273546
expect(containers).toEqual([])
274547
})
548+
549+
test("containerToConfig omits undefined optional fields", () => {
550+
const container = {
551+
container_id: "def456",
552+
name: "mysql_dev",
553+
image: "mysql:8",
554+
db_type: "mysql",
555+
host: "127.0.0.1",
556+
port: 3306,
557+
user: undefined as string | undefined,
558+
password: undefined as string | undefined,
559+
database: undefined as string | undefined,
560+
status: "running",
561+
}
562+
const config = containerToConfig(container as any)
563+
expect(config.type).toBe("mysql")
564+
expect(config.host).toBe("127.0.0.1")
565+
expect(config.port).toBe(3306)
566+
expect(config.user).toBeUndefined()
567+
expect(config.password).toBeUndefined()
568+
expect(config.database).toBeUndefined()
569+
})
275570
})
276571

277572
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)