@@ -128,6 +128,9 @@ func TestDetectIndexConsistencyErrors(t *testing.T) {
128128 splitRangeDDL string
129129 // indexDDL is the DDL to create the indexes on the table.
130130 indexDDL []string
131+ // corruptionTargetIndex specifies which secondary index to corrupt (0-based position).
132+ // If not specified, defaults to 0 (first index).
133+ corruptionTargetIndex int
131134 // missingIndexEntrySelector defines a SQL predicate that selects rows
132135 // whose secondary index entries will be manually deleted to simulate
133136 // missing index entries (i.e., present in the primary index but not in the
@@ -247,6 +250,57 @@ func TestDetectIndexConsistencyErrors(t *testing.T) {
247250 danglingIndexEntryInsertQuery : "SELECT 15, 30, 300, 'corrupt', 'e_3', 300.5" , // Add dangling entry after timestamp
248251 useTimestampBeforeCorruption : true , // Use timestamp from before corruption
249252 },
253+ {
254+ desc : "2 indexes, corrupt second index, missing entry" ,
255+ splitRangeDDL : "ALTER TABLE test.t SPLIT AT VALUES (500)" ,
256+ indexDDL : []string {
257+ "CREATE INDEX idx_t_a ON test.t (a)" ,
258+ "CREATE INDEX idx_t_b ON test.t (b) STORING (e)" ,
259+ },
260+ corruptionTargetIndex : 1 , // Target second index (idx_t_b)
261+ missingIndexEntrySelector : "a = 7" ,
262+ expectedIssues : []inspectIssue {
263+ {
264+ ErrorType : "missing_secondary_index_entry" ,
265+ PrimaryKey : "e'(7, \\ 'd_7\\ ')'" ,
266+ Details : map [redact.RedactableString ]interface {}{
267+ "index_name" : "idx_t_b" ,
268+ },
269+ },
270+ },
271+ expectedErrRegex : expectedInspectFoundInconsistencies ,
272+ },
273+ {
274+ desc : "3 indexes, corrupt middle index, dangling entry" ,
275+ splitRangeDDL : "ALTER TABLE test.t SPLIT AT VALUES (333),(666)" ,
276+ indexDDL : []string {
277+ "CREATE INDEX idx_t_a ON test.t (a)" ,
278+ "CREATE INDEX idx_t_b ON test.t (b) STORING (c)" ,
279+ "CREATE INDEX idx_t_c ON test.t (c) STORING (f)" ,
280+ },
281+ corruptionTargetIndex : 1 , // Target second index (middle one)
282+ danglingIndexEntryInsertQuery : "SELECT 25, 50, 500, 'corrupt_middle', 'e_25', 125.5" ,
283+ expectedIssues : []inspectIssue {
284+ {
285+ ErrorType : "dangling_secondary_index_entry" ,
286+ PrimaryKey : "e'(25, \\ 'corrupt_middle\\ ')'" ,
287+ Details : map [redact.RedactableString ]interface {}{
288+ "index_name" : "idx_t_b" ,
289+ },
290+ },
291+ },
292+ expectedErrRegex : expectedInspectFoundInconsistencies ,
293+ },
294+ {
295+ desc : "multiple indexes, no corruption - all should be checked" ,
296+ indexDDL : []string {
297+ "CREATE INDEX idx_t_a ON test.t (a)" ,
298+ "CREATE INDEX idx_t_b ON test.t (b)" ,
299+ "CREATE INDEX idx_t_c ON test.t (c)" ,
300+ },
301+ // No corruptionTargetIndex specified, no corruption
302+ missingIndexEntrySelector : "" , // No corruption
303+ },
250304 } {
251305 t .Run (tc .desc , func (t * testing.T ) {
252306 issueLogger .reset ()
@@ -313,7 +367,14 @@ func TestDetectIndexConsistencyErrors(t *testing.T) {
313367 }
314368
315369 tableDesc := desctestutils .TestingGetPublicTableDescriptor (kvDB , codec , "test" , "t" )
316- secIndex := tableDesc .PublicNonPrimaryIndexes ()[0 ]
370+
371+ // Select target index based on corruptionTargetIndex with bounds checking
372+ indexes := tableDesc .PublicNonPrimaryIndexes ()
373+ targetIndexPos := tc .corruptionTargetIndex
374+ if targetIndexPos < 0 || targetIndexPos >= len (indexes ) {
375+ targetIndexPos = 0 // Default to first index for safety/backward compatibility
376+ }
377+ secIndex := indexes [targetIndexPos ]
317378
318379 // Apply test-specific corruption based on configured selectors:
319380 // - If missingIndexEntrySelector is set, we delete the secondary index entries
@@ -438,6 +499,21 @@ func TestDetectIndexConsistencyErrors(t *testing.T) {
438499 }
439500 }
440501 }
502+
503+ // Validate Details if provided in expected issue
504+ if expectedIssue .Details != nil {
505+ require .NotNil (t , foundIssue .Details , "issue should have details when expected" )
506+
507+ // Check that all expected detail keys and values match
508+ for expectedKey , expectedValue := range expectedIssue .Details {
509+ require .Contains (t , foundIssue .Details , expectedKey ,
510+ "issue should contain detail key: %s" , expectedKey )
511+
512+ actualValue := foundIssue .Details [expectedKey ]
513+ require .Equal (t , expectedValue , actualValue ,
514+ "detail %s should be %v, got %v" , expectedKey , expectedValue , actualValue )
515+ }
516+ }
441517 }
442518
443519 // Validate job status matches expected outcome
0 commit comments