11/**
22 * SQL Quality Telemetry Tests
33 *
4- * Verifies that the `sql_quality` event is emitted with correct aggregations
5- * when tools report findings, and is NOT emitted when there are no findings.
4+ * Verifies the aggregation logic, event payload shape, and finding
5+ * extraction patterns used for the `sql_quality` telemetry event, and
6+ * that scenarios with no findings result in empty finding arrays (the
7+ * condition used by tool.ts to decide not to emit the event).
68 */
79
810import { describe , expect , test } from "bun:test"
@@ -12,99 +14,91 @@ import { Telemetry } from "../../src/altimate/telemetry"
1214// 1. aggregateFindings
1315// ---------------------------------------------------------------------------
1416describe ( "Telemetry.aggregateFindings" , ( ) => {
15- test ( "aggregates findings by severity and category" , ( ) => {
17+ test ( "aggregates findings by category" , ( ) => {
1618 const findings : Telemetry . Finding [ ] = [
17- { category : "missing_table" , severity : "error" } ,
18- { category : "missing_column" , severity : "error" } ,
19- { category : "cartesian_product" , severity : "warning " } ,
20- { category : "missing_table" , severity : "error" } ,
19+ { category : "missing_table" } ,
20+ { category : "missing_column" } ,
21+ { category : "lint " } ,
22+ { category : "missing_table" } ,
2123 ]
2224 const result = Telemetry . aggregateFindings ( findings )
23- expect ( result . by_severity ) . toEqual ( { error : 3 , warning : 1 } )
24- expect ( result . by_category ) . toEqual ( {
25+ expect ( result ) . toEqual ( {
2526 missing_table : 2 ,
2627 missing_column : 1 ,
27- cartesian_product : 1 ,
28+ lint : 1 ,
2829 } )
2930 } )
3031
31- test ( "returns empty objects for empty findings" , ( ) => {
32+ test ( "returns empty object for empty findings" , ( ) => {
3233 const result = Telemetry . aggregateFindings ( [ ] )
33- expect ( result . by_severity ) . toEqual ( { } )
34- expect ( result . by_category ) . toEqual ( { } )
34+ expect ( result ) . toEqual ( { } )
3535 } )
3636
3737 test ( "handles single finding" , ( ) => {
3838 const findings : Telemetry . Finding [ ] = [
39- { category : "syntax_error" , severity : "error" } ,
39+ { category : "syntax_error" } ,
4040 ]
4141 const result = Telemetry . aggregateFindings ( findings )
42- expect ( result . by_severity ) . toEqual ( { error : 1 } )
43- expect ( result . by_category ) . toEqual ( { syntax_error : 1 } )
42+ expect ( result ) . toEqual ( { syntax_error : 1 } )
4443 } )
4544
46- test ( "handles all same category different severities " , ( ) => {
45+ test ( "handles all same category" , ( ) => {
4746 const findings : Telemetry . Finding [ ] = [
48- { category : "select_star" , severity : "warning " } ,
49- { category : "select_star" , severity : "info " } ,
50- { category : "select_star" , severity : "error " } ,
47+ { category : "lint " } ,
48+ { category : "lint " } ,
49+ { category : "lint " } ,
5150 ]
5251 const result = Telemetry . aggregateFindings ( findings )
53- expect ( result . by_severity ) . toEqual ( { warning : 1 , info : 1 , error : 1 } )
54- expect ( result . by_category ) . toEqual ( { select_star : 3 } )
52+ expect ( result ) . toEqual ( { lint : 3 } )
5553 } )
5654} )
5755
5856// ---------------------------------------------------------------------------
5957// 2. sql_quality event shape validation
6058// ---------------------------------------------------------------------------
6159describe ( "sql_quality event shape" , ( ) => {
62- test ( "by_severity and by_category serialize to valid JSON strings " , ( ) => {
60+ test ( "by_category serializes to valid JSON string " , ( ) => {
6361 const findings : Telemetry . Finding [ ] = [
64- { category : "anti_pattern" , severity : "warning " } ,
65- { category : "anti_pattern" , severity : "warning " } ,
66- { category : "performance_issue" , severity : "info " } ,
62+ { category : "lint " } ,
63+ { category : "lint " } ,
64+ { category : "safety " } ,
6765 ]
68- const { by_severity, by_category } = Telemetry . aggregateFindings ( findings )
69- const severityJson = JSON . stringify ( by_severity )
70- const categoryJson = JSON . stringify ( by_category )
66+ const by_category = Telemetry . aggregateFindings ( findings )
67+ const json = JSON . stringify ( by_category )
7168
7269 // Should round-trip through JSON
73- expect ( JSON . parse ( severityJson ) ) . toEqual ( { warning : 2 , info : 1 } )
74- expect ( JSON . parse ( categoryJson ) ) . toEqual ( { anti_pattern : 2 , performance_issue : 1 } )
70+ expect ( JSON . parse ( json ) ) . toEqual ( { lint : 2 , safety : 1 } )
7571 } )
7672
7773 test ( "aggregated counts match finding_count" , ( ) => {
7874 const findings : Telemetry . Finding [ ] = [
79- { category : "a" , severity : "error" } ,
80- { category : "b" , severity : "warning" } ,
81- { category : "c" , severity : "error" } ,
82- { category : "a" , severity : "info" } ,
75+ { category : "a" } ,
76+ { category : "b" } ,
77+ { category : "c" } ,
78+ { category : "a" } ,
8379 ]
84- const { by_severity, by_category } = Telemetry . aggregateFindings ( findings )
85- const totalBySeverity = Object . values ( by_severity ) . reduce ( ( a , b ) => a + b , 0 )
86- const totalByCategory = Object . values ( by_category ) . reduce ( ( a , b ) => a + b , 0 )
87- expect ( totalBySeverity ) . toBe ( findings . length )
88- expect ( totalByCategory ) . toBe ( findings . length )
80+ const by_category = Telemetry . aggregateFindings ( findings )
81+ const total = Object . values ( by_category ) . reduce ( ( a , b ) => a + b , 0 )
82+ expect ( total ) . toBe ( findings . length )
8983 } )
9084} )
9185
9286// ---------------------------------------------------------------------------
9387// 3. Finding extraction patterns (validates what tools produce)
9488// ---------------------------------------------------------------------------
9589describe ( "tool finding extraction patterns" , ( ) => {
96- test ( "sql_analyze issues map to findings" , ( ) => {
90+ test ( "sql_analyze issues map to findings via issue.type" , ( ) => {
91+ // issue.type is coarse: "lint", "semantic", "safety"
9792 const issues = [
98- { type : "select_star " , severity : "warning" , message : "..." , recommendation : "..." , confidence : "high" } ,
99- { type : "cartesian_product " , severity : "error " , message : "..." , recommendation : "..." , confidence : "high" } ,
93+ { type : "lint " , severity : "warning" , message : "..." , recommendation : "..." , confidence : "high" } ,
94+ { type : "safety " , severity : "high " , message : "..." , recommendation : "..." , confidence : "high" } ,
10095 ]
10196 const findings : Telemetry . Finding [ ] = issues . map ( ( i ) => ( {
10297 category : i . type ,
103- severity : i . severity ,
10498 } ) )
10599 expect ( findings ) . toEqual ( [
106- { category : "select_star" , severity : "warning " } ,
107- { category : "cartesian_product" , severity : "error " } ,
100+ { category : "lint " } ,
101+ { category : "safety " } ,
108102 ] )
109103 } )
110104
@@ -124,31 +118,32 @@ describe("tool finding extraction patterns", () => {
124118 }
125119 const findings : Telemetry . Finding [ ] = errors . map ( ( e ) => ( {
126120 category : classify ( e . message ) ,
127- severity : "error" ,
128121 } ) )
129- const { by_category } = Telemetry . aggregateFindings ( findings )
122+ const by_category = Telemetry . aggregateFindings ( findings )
130123 expect ( by_category ) . toEqual ( {
131124 missing_table : 1 ,
132125 missing_column : 1 ,
133126 syntax_error : 1 ,
134127 } )
135128 } )
136129
137- test ( "semantics issues preserve rule/type as category" , ( ) => {
130+ test ( "semantics issues all map to semantic_issue category" , ( ) => {
131+ // Semantic findings don't have rule/type — always "semantic_issue"
138132 const issues = [
139- { rule : "cartesian_product" , severity : "error" , message : "..." } ,
140- { type : "null_misuse" , severity : "warning" , message : "..." } ,
141- { severity : "warning" , message : "..." } , // no rule or type
133+ { severity : "error" , message : "..." } ,
134+ { severity : "warning" , message : "..." } ,
135+ { severity : "warning" , message : "..." } ,
142136 ]
143- const findings : Telemetry . Finding [ ] = issues . map ( ( i : any ) => ( {
144- category : i . rule ?? i . type ?? "semantic_issue" ,
145- severity : i . severity ?? "warning" ,
137+ const findings : Telemetry . Finding [ ] = issues . map ( ( ) => ( {
138+ category : "semantic_issue" ,
146139 } ) )
147140 expect ( findings ) . toEqual ( [
148- { category : "cartesian_product" , severity : "error " } ,
149- { category : "null_misuse" , severity : "warning " } ,
150- { category : "semantic_issue" , severity : "warning" } ,
141+ { category : "semantic_issue " } ,
142+ { category : "semantic_issue " } ,
143+ { category : "semantic_issue" } ,
151144 ] )
145+ const by_category = Telemetry . aggregateFindings ( findings )
146+ expect ( by_category ) . toEqual ( { semantic_issue : 3 } )
152147 } )
153148
154149 test ( "fix tool produces fix_applied and unfixable_error categories" , ( ) => {
@@ -158,12 +153,12 @@ describe("tool finding extraction patterns", () => {
158153 }
159154 const findings : Telemetry . Finding [ ] = [ ]
160155 for ( const _ of data . fixes_applied ) {
161- findings . push ( { category : "fix_applied" , severity : "warning" } )
156+ findings . push ( { category : "fix_applied" } )
162157 }
163158 for ( const _ of data . unfixable_errors ) {
164- findings . push ( { category : "unfixable_error" , severity : "error" } )
159+ findings . push ( { category : "unfixable_error" } )
165160 }
166- const { by_category } = Telemetry . aggregateFindings ( findings )
161+ const by_category = Telemetry . aggregateFindings ( findings )
167162 expect ( by_category ) . toEqual ( { fix_applied : 2 , unfixable_error : 1 } )
168163 } )
169164
@@ -173,7 +168,7 @@ describe("tool finding extraction patterns", () => {
173168 const equivFindings : Telemetry . Finding [ ] = [ ]
174169 if ( ! equivData . equivalent && equivData . differences ?. length ) {
175170 for ( const _ of equivData . differences ) {
176- equivFindings . push ( { category : "equivalence_difference" , severity : "warning" } )
171+ equivFindings . push ( { category : "equivalence_difference" } )
177172 }
178173 }
179174 expect ( equivFindings ) . toEqual ( [ ] )
@@ -183,22 +178,21 @@ describe("tool finding extraction patterns", () => {
183178 const diffFindings : Telemetry . Finding [ ] = [ ]
184179 if ( ! diffData . equivalent && diffData . differences ?. length ) {
185180 for ( const _ of diffData . differences ) {
186- diffFindings . push ( { category : "equivalence_difference" , severity : "warning" } )
181+ diffFindings . push ( { category : "equivalence_difference" } )
187182 }
188183 }
189184 expect ( diffFindings . length ) . toBe ( 2 )
190- const { by_category } = Telemetry . aggregateFindings ( diffFindings )
185+ const by_category = Telemetry . aggregateFindings ( diffFindings )
191186 expect ( by_category ) . toEqual ( { equivalence_difference : 2 } )
192187 } )
193188
194189 test ( "correct tool changes produce findings" , ( ) => {
195190 const data = { changes : [ { description : "a" } , { description : "b" } ] }
196191 const findings : Telemetry . Finding [ ] = data . changes . map ( ( ) => ( {
197192 category : "correction_applied" ,
198- severity : "warning" ,
199193 } ) )
200194 expect ( findings . length ) . toBe ( 2 )
201- const { by_category } = Telemetry . aggregateFindings ( findings )
195+ const by_category = Telemetry . aggregateFindings ( findings )
202196 expect ( by_category ) . toEqual ( { correction_applied : 2 } )
203197 } )
204198} )
@@ -211,18 +205,16 @@ describe("no findings = no sql_quality event", () => {
211205 const issues : any [ ] = [ ]
212206 const findings : Telemetry . Finding [ ] = issues . map ( ( i : any ) => ( {
213207 category : i . type ,
214- severity : i . severity ,
215208 } ) )
216209 expect ( findings . length ) . toBe ( 0 )
217- // tool.ts checks: Array.isArray(findings) && findings.length > 0
210+ // tool.ts guards: !isSoftFailure && Array.isArray(findings) && findings.length > 0
218211 // So no event would be emitted
219212 } )
220213
221214 test ( "valid SQL with no errors produces no findings" , ( ) => {
222215 const data = { valid : true , errors : [ ] }
223216 const findings : Telemetry . Finding [ ] = ( data . errors ?? [ ] ) . map ( ( ) => ( {
224217 category : "validation_error" ,
225- severity : "error" ,
226218 } ) )
227219 expect ( findings . length ) . toBe ( 0 )
228220 } )
0 commit comments