Repository navigation
feat: reject empty-input-only SQL tests and fix SQL test helper scope - #803
Merged
Merged
Conversation
Helper CTEs are now in scope for __expected__ and __assert__ SQL in both the SQL-analysis and textual planner paths, for comparison and difference sample queries. Referenced helpers, the helpers they read, and mocks they read by CTE name are emitted once at top level in dependency order. Models using __cursor_start()/__cursor_end() are rendered for tests with a wide typed window (1900-01-01 to 2999-12-31, or +/-1e15 for integer cursors) using the adapter's execution bound literal. TEST headers accept cursor_start (inclusive) and cursor_end (exclusive), validated against each intrinsic-using chain model's cursor_type and cursor_grain.
When rule errors withhold compile artifacts, still report ERROR-severity SQL test planning diagnostics in text and JSON output. The background prepared-artifacts path reuses its staged planning result without publishing; the inline path plans uncached tests without writing artifacts or cache records. Planning input errors become diagnostics so rule errors are not lost.
audit_completed events were published from the end-of-run audit batch projection, so a run emitted every audit event in one burst after the last audit finished. A slow lifecycle sink then lost the tail at the fixed two-second exit drain, and nothing on the console reported the loss. - Publish each audit_completed once as its outcome is confirmed: per audit for sqb audit (serial and concurrent), source audits and end audits, and per model result for model audits in build. Payloads, result ids, envelope correlation and persisted audit-result rows are unchanged; the batch projection reuses the already published records and still writes history once. - Add sinks.lifecycle.shutdown_timeout (fixed duration "0s".."10m", default "2s") to configure the exit drain for lifecycle sinks. The per-invocation timeout and queue priorities are unchanged. - Print one stderr warning when the final export summary reports dropped or failed lifecycle deliveries, stating when the command itself succeeded. Exit codes and machine-readable stdout are unchanged.
Helper scope is now the transitive closure over helper and mock CTEs, where a mock body's references to helpers or other mocks count as dependencies. Generated mocks in the analysis path and inlined mocks in the textual path bring the mocks they read into top-level scope once, in dependency order, and those mocks count as reachable. Marker replacement helpers move to their own module.
Share quote- and comment-aware scanning (sql_scan) between SQL-test extraction and marker resolution, and CTE assembly (cte_sql) between planning and rendering. Every mock CTE body now comes from the test's ScopeGraph in analysis generation, textual inlining and helper scope; helper injection into mock and direct-test bodies uses one function. Test and execution cursor intrinsics share one adapter bound renderer, and the compile writer shares planning diagnostic conversion with the rules-failed path. Compiled test SQL is unchanged.
Add SQBRTEST203 (empty-input-only-test), enabled by default, which flags model-mode SQL tests whose mocks are all empty and whose expected outputs and assertions can only prove that no rows are produced. Reviewed exceptions live in the typed allowed_tests rule option, and stale entries are reported. The native engine classifies each test once; SQBRTEST203 and the SQBRTEST202 min_tests_per_model count both read that result, so filler never satisfies the minimum whether or not SQBRTEST203 is selected. Allowlisted tests still count.
Add a default `clones` mode that extracts Python functions and methods and Rust fn items, normalises their token streams, finds candidates with winnowed k-gram fingerprints, scores them with a token-sequence ratio, and reports exact, renamed, and near-miss clusters with path:start-end members. Supports --since (clusters touching lines changed since a revision, including uncommitted and untracked files), --lang, --path, --min-tokens, --min-similarity, --include-tests, --json, and a [[clone_allowlist]] with required reasons. `make dupscore` now runs the clone mode; `report` and `pair` are unchanged. Helpers are regrouped into package_pairs, clones, and inputs buckets to keep module fan-out within fensu limits.
An assertion whose projection or ORDER BY calls a function (for example an UNNEST over an aggregate check) can verify concrete output, and a WHERE FALSE fixture that calls a function anywhere (for example ORDER BY COUNT(*)) can still produce a row. Treat both as not empty-input-only.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
SQL unit tests could not use helper CTEs in assertions or expected rows, and models using cursor intrinsics could not be tested. Compile hid SQL test planning errors whenever a rule failed. Audit lifecycle events arrived in one end-of-run burst, so slow sinks silently lost them. Empty-input-only filler tests counted as coverage, and dupscore did not point at concrete duplicated code.
Changes
__expected__/__assert__SQL, including mocks reached through helpers; cursor intrinsics render a wide typed window, overridable per test withTEST(cursor_start …, cursor_end …).audit_completedis published as audits finish; newsinks.lifecycle.shutdown_timeout; stderr warning when lifecycle deliveries are dropped or fail (exit code unchanged).SQBRTEST203empty-input-only-test rule withallowed_tests;SQBRTEST202no longer counts such tests.dupscore clones: function-level clone detection for Python and Rust, with--since.Upgrade note: projects relying on empty-input filler tests to meet
min_tests_per_modelwill seeSQBRTEST202/SQBRTEST203errors; replace the filler or list reviewed cases inallowed_tests.Verification
sqb compile/sqb test/sqb audit/sqb buildfor helper scope, cursor windows, rule-gated diagnostics, incremental audit events, sink drain and warning, and SQBRTEST202/203.make test;make check-ci.