Repository: influxdata/influxdb
File: tsdb/engine/tsm1/compact_property_test.go (Go)

Code before:
			groupToFiles[groupIdx] = append(groupToFiles[groupIdx], info)
		}
	}

	// For each group, check if there are gaps in the file sequence
	for groupIdx, filesInGroup := range groupToFiles {
		if len(filesInGroup) < 2 {
			continue // No gaps possible with less than 2 files
		}

		// Check for gaps between files in this group
		for i := 0; i < len(filesInGroup)-1; i++ {
			current := filesInGroup[i]
			next := filesInGroup[i+1]

			// Find all files between current and next in the sorted sequence
			currentPos := findFilePosition(current, fileInfos)
			nextPos := findFilePosition(next, fileInfos)

			// Check if there are files between current and next that are in different groups
			for pos := currentPos + 1; pos < nextPos; pos++ {
				betweenFile := fileInfos[pos]
				betweenGroup, inGroup := fileToGroup[betweenFile.filename]

				if inGroup && betweenGroup != groupIdx {
					return fmt.Errorf("gap detected: files %s and %s are in group %d, but file %s (between them) is in group %d",
						current.filename, next.filename, groupIdx, betweenFile.filename, betweenGroup)
				}
			}
		}
	}

	return nil
}

// findFilePosition returns the position of a file in the sorted fileInfos slice
func findFilePosition(target fileInfo, fileInfos []fileInfo) int {
	for i, info := range fileInfos {
		if info.filename == target.filename {
			return i
		}
	}
	return -1
}

// ValidateCompactionProperties validates that compaction results satisfy all properties
func ValidateCompactionProperties(allFiles []string, results TestLevelResults, properties ...CompactionProperty) error {
	var errs []error

	// Collect all compaction groups from results
	var allGroups []tsm1.CompactionGroup

Review comment (bug):
`expected results: No Gaps Rule violation: gap detected: files 04-04.tsm and 09-04.tsm are in group 1, but file 05-03.tsm (between them) is in group 0`

This error message is tough as the group indices don't tell me anything about which level group it came from. I think it should know which level group its talking about. 

also: "no gaps rule" is odd description of the property. Maybe its me: the rule is that tsm files within a group must be adjacent in the master list of files. "Adjacency rule" seems more accurate. a "gap" sounds like a _missing_ tsm file instead of one in the wrong place. 

Code after:
			}

			if lastIndex == -1 {
				lastIndex = f.index
			} else {
				// Check lastIndex wrt f.index
				if lastIndex+1 != f.index {
					return fmt.Errorf("file %s in compaction group %d violates adjacency policy", file, groupIndex+1)
				}
				lastIndex = f.index
			}
		}
	}

	return nil
}

// ValidateCompactionProperties validates that compaction results satisfy all properties
func ValidateCompactionProperties(allFiles []string, results TestLevelResults, properties ...CompactionProperty) error {
	var errs []error

	// Collect all compaction groups from results
	var allGroups []tsm1.CompactionGroup
	allGroups = append(allGroups, extractGroups(results.level1Groups)...)
	allGroups = append(allGroups, extractGroups(results.level2Groups)...)
	allGroups = append(allGroups, extractGroups(results.level3Groups)...)
	allGroups = append(allGroups, extractGroups(results.level4Groups)...)
	allGroups = append(allGroups, extractGroups(results.level5Groups)...)

	// Validate each property
	for _, property := range properties {
		if err := property.Validator(allFiles, allGroups); err != nil {
			errs = append(errs, fmt.Errorf("%s violation: %v", property.Name, err))
		}
	}

	return errors.Join(errs...)
}

// extractGroups extracts CompactionGroup from PlannedCompactionGroup
func extractGroups(plannedGroups []tsm1.PlannedCompactionGroup) []tsm1.CompactionGroup {
	var groups []tsm1.CompactionGroup
	for _, planned := range plannedGroups {
		groups = append(groups, planned.Group)
	}
	return groups
}

// ValidateTestCase validates both expected results and actual planner output
func ValidateTestCase(testCase TestEnginePlanCompactionsRunner, actualResults TestLevelResults) error {
	var errs []error