Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/opencode/src/altimate/tools/impact-analysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,14 @@ export const ImpactAnalysisTool = Tool.define("impact_analysis", {
},
})

interface DownstreamModel {
export interface DownstreamModel {
name: string
depth: number
materialized?: string
path: string[]
}

function findDownstream(
export function findDownstream(
targetName: string,
models: Array<{ name: string; depends_on: string[]; materialized?: string }>,
): DownstreamModel[] {
Expand Down Expand Up @@ -188,7 +188,7 @@ function findDownstream(
return results
}

function formatImpactReport(data: {
export function formatImpactReport(data: {
model: string
column?: string
changeType: string
Expand Down
13 changes: 8 additions & 5 deletions packages/opencode/src/altimate/tools/training-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,12 @@ export const TrainingImportTool = Tool.define("training_import", {
},
})

interface MarkdownSection {
export interface MarkdownSection {
name: string
content: string
}

function parseMarkdownSections(markdown: string): MarkdownSection[] {
export function parseMarkdownSections(markdown: string): MarkdownSection[] {
const sections: MarkdownSection[] = []
const lines = markdown.split("\n")
let currentH1 = ""
Expand Down Expand Up @@ -222,11 +222,14 @@ function parseMarkdownSections(markdown: string): MarkdownSection[] {
return sections
}

function slugify(text: string): string {
return text
export function slugify(text: string): string {
const result = text
.normalize("NFKD")
.replace(/[\u0300-\u036f]/g, "")
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, "")
.replace(/\s+/g, "-")
.replace(/^-+|-+$/g, "")
.slice(0, 64)
.replace(/^-+|-+$/g, "")
return result || "untitled"
}
4 changes: 3 additions & 1 deletion packages/opencode/src/command/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ export namespace Command {
const result: string[] = []
const numbered = template.match(/\$\d+/g)
if (numbered) {
for (const match of [...new Set(numbered)].sort()) result.push(match)
// altimate_change start — fix lexicographic sort of multi-digit placeholders ($10 before $2)
for (const match of [...new Set(numbered)].sort((a, b) => parseInt(a.slice(1), 10) - parseInt(b.slice(1), 10))) result.push(match)
// altimate_change end
}
if (template.includes("$ARGUMENTS")) result.push("$ARGUMENTS")
return result
Expand Down
22 changes: 22 additions & 0 deletions packages/opencode/test/altimate/tools/finops-formatting.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,34 @@ describe("formatBytes: normal cases", () => {
})
})

describe("formatBytes: higher units (TB, PB)", () => {
test("TB boundary", () => {
expect(formatBytes(1024 ** 4)).toBe("1.00 TB")
})

test("PB boundary", () => {
expect(formatBytes(1024 ** 5)).toBe("1.00 PB")
})

test("values beyond PB stay at PB (no EB unit)", () => {
expect(formatBytes(1024 ** 6)).toBe("1024.00 PB")
})

test("multi-PB value", () => {
expect(formatBytes(2 * 1024 ** 5)).toBe("2.00 PB")
})
})

describe("formatBytes: edge cases", () => {
test("negative bytes displays with sign", () => {
expect(formatBytes(-100)).toBe("-100 B")
expect(formatBytes(-1536)).toBe("-1.50 KB")
})

test("negative KB", () => {
expect(formatBytes(-1024)).toBe("-1.00 KB")
})

test("fractional bytes clamps to B unit", () => {
expect(formatBytes(0.5)).toBe("1 B")
})
Expand Down
205 changes: 205 additions & 0 deletions packages/opencode/test/altimate/tools/impact-analysis.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import { describe, test, expect } from "bun:test"
import { findDownstream, formatImpactReport } from "../../../src/altimate/tools/impact-analysis"
import type { DownstreamModel } from "../../../src/altimate/tools/impact-analysis"

describe("findDownstream: DAG traversal", () => {
test("returns empty for leaf model with no dependents", () => {
const models = [
{ name: "stg_orders", depends_on: ["source.raw_orders"], materialized: "view" },
{ name: "stg_customers", depends_on: ["source.raw_customers"], materialized: "view" },
]
const result = findDownstream("stg_orders", models)
expect(result).toHaveLength(0)
})

test("finds direct dependents (depth 1)", () => {
const models = [
{ name: "stg_orders", depends_on: ["source.raw_orders"] },
{ name: "fct_orders", depends_on: ["project.stg_orders", "project.stg_customers"] },
{ name: "stg_customers", depends_on: ["source.raw_customers"] },
]
const result = findDownstream("stg_orders", models)
expect(result).toHaveLength(1)
expect(result[0].name).toBe("fct_orders")
expect(result[0].depth).toBe(1)
})

test("finds transitive dependents across multiple depths", () => {
const models = [
{ name: "stg_orders", depends_on: ["source.raw_orders"] },
{ name: "fct_orders", depends_on: ["project.stg_orders"] },
{ name: "dim_orders", depends_on: ["project.fct_orders"] },
{ name: "report_orders", depends_on: ["project.dim_orders"] },
]
const result = findDownstream("stg_orders", models)
expect(result).toHaveLength(3)
expect(result[0]).toMatchObject({ name: "fct_orders", depth: 1 })
expect(result[1]).toMatchObject({ name: "dim_orders", depth: 2 })
expect(result[2]).toMatchObject({ name: "report_orders", depth: 3 })
})

test("tracks dependency paths correctly", () => {
const models = [
{ name: "stg_orders", depends_on: [] as string[] },
{ name: "fct_orders", depends_on: ["project.stg_orders"] },
{ name: "report", depends_on: ["project.fct_orders"] },
]
const result = findDownstream("stg_orders", models)
expect(result[0].path).toEqual(["stg_orders", "fct_orders"])
expect(result[1].path).toEqual(["stg_orders", "fct_orders", "report"])
})

test("handles diamond dependency (A\u2192B, A\u2192C, B\u2192D, C\u2192D)", () => {
const models = [
{ name: "A", depends_on: [] as string[] },
{ name: "B", depends_on: ["project.A"] },
{ name: "C", depends_on: ["project.A"] },
{ name: "D", depends_on: ["project.B", "project.C"] },
]
const result = findDownstream("A", models)
// D should appear only once (visited set prevents duplicates)
const names = result.map((r) => r.name)
expect(names.filter((n) => n === "D")).toHaveLength(1)
expect(result).toHaveLength(3) // B, C, D
})

test("self-referencing model \u2014 behavior documentation only, not a valid dbt graph", () => {
const models = [
{ name: "stg_orders", depends_on: ["project.stg_orders"] },
]
// This assertion exists only to document current behavior, not to endorse it.
// Self-referencing dbt models are invalid and cannot compile, so this edge case
// is not reachable in practice. The visited set prevents infinite recursion.
const result = findDownstream("stg_orders", models)
expect(result).toHaveLength(1)
expect(result[0].name).toBe("stg_orders")
})

test("parses qualified names (strips prefix before last dot)", () => {
const models = [
{ name: "stg_orders", depends_on: [] as string[] },
{ name: "fct_orders", depends_on: ["my_project.stg_orders", "other_project.stg_customers"] },
]
const result = findDownstream("stg_orders", models)
expect(result).toHaveLength(1)
expect(result[0].name).toBe("fct_orders")
})

test("preserves materialization metadata", () => {
const models = [
{ name: "stg_orders", depends_on: [] as string[], materialized: "view" },
{ name: "fct_orders", depends_on: ["project.stg_orders"], materialized: "table" },
{ name: "report", depends_on: ["project.fct_orders"], materialized: "incremental" },
]
const result = findDownstream("stg_orders", models)
expect(result[0].materialized).toBe("table")
expect(result[1].materialized).toBe("incremental")
})

test("model not in graph returns empty", () => {
const models = [
{ name: "stg_orders", depends_on: ["source.raw_orders"] },
{ name: "fct_orders", depends_on: ["project.stg_orders"] },
]
const result = findDownstream("nonexistent_model", models)
expect(result).toHaveLength(0)
})
})

describe("formatImpactReport", () => {
test("safe change with zero downstream", () => {
const report = formatImpactReport({
model: "stg_temp",
changeType: "remove",
direct: [],
transitive: [],
affectedTestCount: 0,
columnImpact: [],
totalModels: 10,
})
expect(report).toContain("REMOVE stg_temp")
expect(report).toContain("Blast radius: 0/10 models (0.0%)")
expect(report).toContain("No downstream models depend on this. Change is safe to make.")
expect(report).not.toContain("WARNING")
})

test("remove with downstream shows BREAKING warning", () => {
const report = formatImpactReport({
model: "stg_orders",
changeType: "remove",
direct: [{ name: "fct_orders", depth: 1, path: ["stg_orders", "fct_orders"] }],
transitive: [],
affectedTestCount: 0,
columnImpact: [],
totalModels: 20,
})
expect(report).toContain("WARNING: This is a BREAKING change")
expect(report).toContain("Blast radius: 1/20 models (5.0%)")
expect(report).toContain("Direct Dependents (1)")
expect(report).toContain("Consider deprecation period before removal")
})

test("rename shows rename-specific warning and actions", () => {
const report = formatImpactReport({
model: "stg_orders",
changeType: "rename",
direct: [{ name: "fct_orders", depth: 1, path: ["stg_orders", "fct_orders"] }],
transitive: [],
affectedTestCount: 5,
columnImpact: [],
totalModels: 10,
})
expect(report).toContain("WARNING: Rename requires updating all downstream references.")
expect(report).toContain("Update all downstream SQL references to new name")
expect(report).toContain("Tests in project: 5")
})

test("column-level impact shows affected columns", () => {
const report = formatImpactReport({
model: "stg_orders",
column: "order_id",
changeType: "retype",
direct: [{ name: "fct_orders", depth: 1, path: ["stg_orders", "fct_orders"] }],
transitive: [],
affectedTestCount: 0,
columnImpact: ["total_amount", "order_count"],
totalModels: 10,
})
expect(report).toContain("RETYPE stg_orders.order_id")
expect(report).toContain("CAUTION: Type change may cause implicit casts")
expect(report).toContain("Affected Output Columns (2)")
expect(report).toContain("total_amount")
expect(report).toContain("order_count")
})

test("percentage calculation with 0 total models does not produce NaN or Infinity", () => {
const report = formatImpactReport({
model: "stg_orders",
changeType: "add",
direct: [],
transitive: [],
affectedTestCount: 0,
columnImpact: [],
totalModels: 0,
})
expect(report).not.toContain("NaN")
expect(report).not.toContain("Infinity")
expect(report).toContain("Blast radius: 0/0 models")
})

test("transitive dependents show dependency path", () => {
const report = formatImpactReport({
model: "stg_orders",
changeType: "modify",
direct: [{ name: "fct_orders", depth: 1, path: ["stg_orders", "fct_orders"] }],
transitive: [{ name: "report", depth: 2, materialized: "table", path: ["stg_orders", "fct_orders", "report"] }],
affectedTestCount: 0,
columnImpact: [],
totalModels: 50,
})
expect(report).toContain("Direct Dependents (1)")
expect(report).toContain("Transitive Dependents (1)")
expect(report).toContain("report [table] (via: stg_orders \u2192 fct_orders \u2192 report)")
expect(report).toContain("Blast radius: 2/50 models (4.0%)")
})
})
Loading
Loading