Skip to content

test: dbt helpers — direct unit tests for shared manifest utilities#694

Open
anandgupta42 wants to merge 1 commit intomainfrom
test/hourly-20260411-0017
Open

test: dbt helpers — direct unit tests for shared manifest utilities#694
anandgupta42 wants to merge 1 commit intomainfrom
test/hourly-20260411-0017

Conversation

@anandgupta42
Copy link
Copy Markdown
Contributor

@anandgupta42 anandgupta42 commented Apr 11, 2026

Summary

What does this PR do?

1. findModel, getUniqueId, detectDialect, buildSchemaContext, extractColumns, listModelNames, loadRawManifestsrc/altimate/native/dbt/helpers.ts (32 new tests)

These 7 exported pure functions are the shared foundation for dbt-lineage, dbt-unit-test-gen, and dbt-manifest handlers. Zero direct unit tests existed — they were only tested indirectly through dbtLineage() in dbt-lineage-helpers.test.ts, which cannot isolate individual function regressions.

A silent bug in any of these helpers cascades across multiple dbt tools:

  • findModel returning null for a valid model → lineage and unit-test generation fail silently
  • detectDialect returning wrong dialect → altimate-core SQL analysis produces garbage
  • buildSchemaContext dropping upstream columns → dbt unit-test generation creates empty schemas
  • loadRawManifest cache corruption → stale manifest data used across requests

Specific scenarios covered:

  • findModel (6 tests): lookup by unique_id, lookup by name, source/test node rejection, empty nodes, duplicate model names
  • getUniqueId (4 tests): direct key match, name-based scan, source node rejection, missing model
  • detectDialect (5 tests): all 11 known adapter→dialect mappings, unmapped adapter passthrough, empty/null/missing metadata defaults to snowflake
  • buildSchemaContext (5 tests): alias-over-name precedence (with toBeUndefined() guard), empty column skip, source resolution, empty/unresolvable upstream IDs
  • extractColumns (4 tests): data_type extraction, type field fallback, dict-key-as-name fallback, empty dict
  • listModelNames (2 tests): model-only filtering, empty nodes
  • loadRawManifest (6 tests): missing file returns null, valid parse, invalid JSON throws, primitive (non-object) throws, path+mtime caching (reference equality), cache invalidation on rewrite

Type of change

  • New feature (non-breaking change which adds functionality)

Issue for this PR

N/A — proactive test coverage for shared dbt utilities with zero direct tests

How did you verify your code works?

bun test test/altimate/dbt-helpers.test.ts   # 32 pass, 54 expect() calls
bun run script/upstream/analyze.ts --markers --base main --strict   # ok
turbo typecheck   # 5/5 packages pass

Checklist

  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

https://claude.ai/code/session_01G3L4LeyQGJ2ox4ssaqb3Kb


Summary by cubic

Add 32 direct unit tests for shared dbt helpers in src/altimate/native/dbt/helpers.ts to catch regressions and improve reliability across dbt-lineage, dbt-unit-test-gen, and manifest handlers. Tests cover model lookup, unique_id resolution, dialect detection, schema context building, column extraction, model name listing, and manifest load/caching (including invalidation).

Written for commit ccac8f4. Summary will update on new commits.

Summary by CodeRabbit

  • Tests
    • Added comprehensive test suite for dbt helper functions, covering model lookups, dialect detection, schema context building, column extraction, and manifest loading across various scenarios and edge cases.

The 7 exported functions in src/altimate/native/dbt/helpers.ts power
dbt-lineage, dbt-unit-test-gen, and dbt-manifest handlers but had zero
direct unit tests. A silent regression in findModel or detectDialect
cascades across multiple dbt tools. 32 new tests cover model lookup,
dialect mapping, schema context building, column extraction, and
manifest caching/invalidation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

https://claude.ai/code/session_01G3L4LeyQGJ2ox4ssaqb3Kb
Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 11, 2026

📝 Walkthrough

Walkthrough

A new test suite has been added for dbt helper functions, covering unit tests for model lookup, dialect detection, schema context generation, column extraction, model name listing, and raw manifest loading with caching behavior validation.

Changes

Cohort / File(s) Summary
DBT Helpers Test Suite
packages/opencode/test/altimate/dbt-helpers.test.ts
New comprehensive test suite with 305 lines covering unit tests for findModel, getUniqueId, detectDialect, buildSchemaContext, extractColumns, listModelNames, and loadRawManifest functions, including edge cases, null handling, cache behavior, and error scenarios.

Suggested labels

contributor

Suggested reviewers

  • suryaiyer95

Poem

🐰 Hop, hop, tests so fine,
Helpers now have their shine,
Dialects and schemas aligned,
Manifests cached, no need to rewind—
Quality code, perfectly divine!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is comprehensive and well-structured, covering what changed, why it matters, specific test scenarios, and verification steps. However, it does not include the required 'PINEAPPLE' keyword at the top as mandated by the template for AI-generated contributions. Add the word 'PINEAPPLE' at the very top of the PR description before any other content, as required by the repository template for AI-generated contributions.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding direct unit tests for dbt helper functions, which is the primary focus of the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/hourly-20260411-0017

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/opencode/test/altimate/dbt-helpers.test.ts`:
- Around line 294-304: The test "invalidates cache when file content is
rewritten" is flaky because it assumes a rewrite always changes mtimeMs; update
the test to force a deterministic mtime change using fs.utimesSync (or
fs.utimes) after the second write, then call loadRawManifest again and assert
the returned object differs (or that second.v === 2) and that the cache identity
changed; locate the test by its title and the use of
loadRawManifest/manifestPath to implement the utimes bump and the explicit
identity assertion.
- Around line 252-258: Replace the manual temp-dir lifecycle that uses
fs.mkdtempSync and fs.rmSync (tmpDir variable set in beforeEach/afterEach) with
the provided tmpdir() helper from fixture/fixture.ts and the `await using`
pattern; locate the setup that defines tmpDir and the beforeEach/afterEach
blocks, remove mkdtempSync/rmSync, import tmpdir, and in each test scope acquire
the temp directory via `await using (const tmpDir = await tmpdir(...))` so
cleanup happens automatically when the using block exits.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f1df5cfb-9d94-4641-ac5a-0b383b5a2fc0

📥 Commits

Reviewing files that changed from the base of the PR and between f030bf8 and ccac8f4.

📒 Files selected for processing (1)
  • packages/opencode/test/altimate/dbt-helpers.test.ts

Comment on lines +252 to +258
beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbt-helpers-test-"))
})

afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true })
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Use tmpdir() + await using instead of manual temp-dir lifecycle.

Line 252–258 manually manages temp directories with mkdtempSync/rmSync. In this test path, repo rules require tmpdir() from fixture/fixture.ts with await using.

♻️ Suggested refactor pattern
 import { describe, test, expect, beforeEach, afterEach } from "bun:test"
 import * as fs from "fs"
 import * as path from "path"
 import * as os from "os"
+import { tmpdir } from "../fixture/fixture"

 describe("loadRawManifest", () => {
-  let tmpDir: string
-
-  beforeEach(() => {
-    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbt-helpers-test-"))
-  })
-
-  afterEach(() => {
-    fs.rmSync(tmpDir, { recursive: true, force: true })
-  })
-
-  test("returns null for non-existent file", () => {
-    expect(loadRawManifest(path.join(tmpDir, "nonexistent.json"))).toBeNull()
+  test("returns null for non-existent file", async () => {
+    await using tmp = await tmpdir()
+    expect(loadRawManifest(path.join(tmp.path, "nonexistent.json"))).toBeNull()
   })

As per coding guidelines: "Use the tmpdir function from fixture/fixture.ts to create temporary directories for tests with automatic cleanup in test files" and "Always use await using syntax with tmpdir() for automatic cleanup when the variable goes out of scope".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/altimate/dbt-helpers.test.ts` around lines 252 - 258,
Replace the manual temp-dir lifecycle that uses fs.mkdtempSync and fs.rmSync
(tmpDir variable set in beforeEach/afterEach) with the provided tmpdir() helper
from fixture/fixture.ts and the `await using` pattern; locate the setup that
defines tmpDir and the beforeEach/afterEach blocks, remove mkdtempSync/rmSync,
import tmpdir, and in each test scope acquire the temp directory via `await
using (const tmpDir = await tmpdir(...))` so cleanup happens automatically when
the using block exits.

Comment on lines +294 to +304
test("invalidates cache when file content is rewritten", () => {
const manifestPath = path.join(tmpDir, "updated.json")
fs.writeFileSync(manifestPath, JSON.stringify({ v: 1 }))
const first = loadRawManifest(manifestPath)

// Rewrite — OS updates mtime to current time, which differs from
// the first write's mtime (millisecond-resolution on modern Linux).
fs.writeFileSync(manifestPath, JSON.stringify({ v: 2 }))
const second = loadRawManifest(manifestPath)
expect(second.v).toBe(2)
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make the cache-invalidation test deterministic for mtime-based caching.

Line 299–301 assumes rewrite always changes mtimeMs. On fast filesystems/writes, this can be flaky. Force an mtime bump and assert identity changed.

🛠️ Deterministic assertion update
   test("invalidates cache when file content is rewritten", () => {
     const manifestPath = path.join(tmpDir, "updated.json")
     fs.writeFileSync(manifestPath, JSON.stringify({ v: 1 }))
     const first = loadRawManifest(manifestPath)

-    // Rewrite — OS updates mtime to current time, which differs from
-    // the first write's mtime (millisecond-resolution on modern Linux).
     fs.writeFileSync(manifestPath, JSON.stringify({ v: 2 }))
+    const bumped = new Date(Date.now() + 1000)
+    fs.utimesSync(manifestPath, bumped, bumped)
     const second = loadRawManifest(manifestPath)
     expect(second.v).toBe(2)
+    expect(second).not.toBe(first)
   })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test("invalidates cache when file content is rewritten", () => {
const manifestPath = path.join(tmpDir, "updated.json")
fs.writeFileSync(manifestPath, JSON.stringify({ v: 1 }))
const first = loadRawManifest(manifestPath)
// Rewrite — OS updates mtime to current time, which differs from
// the first write's mtime (millisecond-resolution on modern Linux).
fs.writeFileSync(manifestPath, JSON.stringify({ v: 2 }))
const second = loadRawManifest(manifestPath)
expect(second.v).toBe(2)
})
test("invalidates cache when file content is rewritten", () => {
const manifestPath = path.join(tmpDir, "updated.json")
fs.writeFileSync(manifestPath, JSON.stringify({ v: 1 }))
const first = loadRawManifest(manifestPath)
fs.writeFileSync(manifestPath, JSON.stringify({ v: 2 }))
const bumped = new Date(Date.now() + 1000)
fs.utimesSync(manifestPath, bumped, bumped)
const second = loadRawManifest(manifestPath)
expect(second.v).toBe(2)
expect(second).not.toBe(first)
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/altimate/dbt-helpers.test.ts` around lines 294 - 304,
The test "invalidates cache when file content is rewritten" is flaky because it
assumes a rewrite always changes mtimeMs; update the test to force a
deterministic mtime change using fs.utimesSync (or fs.utimes) after the second
write, then call loadRawManifest again and assert the returned object differs
(or that second.v === 2) and that the cache identity changed; locate the test by
its title and the use of loadRawManifest/manifestPath to implement the utimes
bump and the explicit identity assertion.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants