From 6dc99f53a9f29ea03b281f59ef7a448ebfa928db Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 21:23:10 +0000 Subject: [PATCH] fix(campaign): handle empty string paths in edge case detector - Modified AnalyzeFiles to skip empty string paths. - Updated detectLanguage to return "unknown" for empty path strings. - Modified matchesPath to correctly compare empty strings without panic or false positives. - Removed corresponding TODO comments in the source code. - Added comprehensive unit tests in internal/campaign/edge_case_detector_test.go. - Fixed a broken gap test by adjusting expectations to match the new behavior. Co-authored-by: theRebelliousNerd <187437903+theRebelliousNerd@users.noreply.github.com> --- internal/campaign/edge_case_detector.go | 12 ++++-- .../campaign/edge_case_detector_gaps_test.go | 9 ++--- internal/campaign/edge_case_detector_test.go | 37 +++++++++++++++++-- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/internal/campaign/edge_case_detector.go b/internal/campaign/edge_case_detector.go index 6a84513f..3e793cef 100644 --- a/internal/campaign/edge_case_detector.go +++ b/internal/campaign/edge_case_detector.go @@ -166,6 +166,9 @@ func (d *EdgeCaseDetector) AnalyzeFiles(ctx context.Context, paths []string, int decisions := make([]FileDecision, 0, len(paths)) for _, path := range paths { + if path == "" { + continue + } select { case <-ctx.Done(): logging.Campaign("Edge case analysis interrupted: %d/%d files analyzed before timeout", len(decisions), len(paths)) @@ -541,6 +544,9 @@ func (d *EdgeCaseDetector) actionPriority(action FileAction) int { // detectLanguage determines the language from file extension. func (d *EdgeCaseDetector) detectLanguage(path string) string { + if path == "" { + return "unknown" + } ext := strings.ToLower(filepath.Ext(path)) langMap := map[string]string{ ".go": "go", @@ -558,6 +564,9 @@ func (d *EdgeCaseDetector) detectLanguage(path string) string { } func (d *EdgeCaseDetector) matchesPath(candidate, path string) bool { + if candidate == "" || path == "" { + return candidate == path + } if candidate == path { return true } @@ -800,9 +809,6 @@ func (a *EdgeCaseAnalysis) GetPreworkTasks() []string { // analyzeFile should aggressively verify ctx.Err() before executing heavy operations, // potentially mid-query, to prevent leaking goroutines or excessive delay. -// TODO: Missing Edge Case - Null/Undefined/Empty: Empty string in paths slice. -// AnalyzeFiles should filter or reject `""` paths before processing them. -// detectLanguage, matchesPath, and other file utilities behavior on `""` should be explicit. // TODO: Missing Edge Case - Null/Undefined/Empty: IntelligenceReport fields are empty. // If an empty IntelligenceReport is passed, missing dependencies or metrics could lead diff --git a/internal/campaign/edge_case_detector_gaps_test.go b/internal/campaign/edge_case_detector_gaps_test.go index a9cd9be9..1ae1a831 100644 --- a/internal/campaign/edge_case_detector_gaps_test.go +++ b/internal/campaign/edge_case_detector_gaps_test.go @@ -57,6 +57,7 @@ func TestEdgeCaseDetectorGap_AnalyzeFiles_EmptyIntelligence(t *testing.T) { } } +// TestEdgeCaseDetectorGap_EmptyPathString (Vector 1: Empty Paths) // TestEdgeCaseDetectorGap_EmptyPathString (Vector 1: Empty Paths) func TestEdgeCaseDetectorGap_EmptyPathString(t *testing.T) { detector := NewEdgeCaseDetector(nil, nil) @@ -64,12 +65,8 @@ func TestEdgeCaseDetectorGap_EmptyPathString(t *testing.T) { paths := []string{""} decisions, _ := detector.AnalyzeFiles(context.Background(), paths, nil) - if len(decisions) != 1 { - t.Fatalf("Expected 1 decision, got %d", len(decisions)) - } - - if decisions[0].Language != "unknown" { - t.Errorf("Expected unknown language for empty path, got %s", decisions[0].Language) + if len(decisions) != 0 { + t.Fatalf("Expected 0 decisions for empty path, got %d", len(decisions)) } } diff --git a/internal/campaign/edge_case_detector_test.go b/internal/campaign/edge_case_detector_test.go index 30bceb09..ef6e6b69 100644 --- a/internal/campaign/edge_case_detector_test.go +++ b/internal/campaign/edge_case_detector_test.go @@ -298,10 +298,6 @@ func TestEdgeCaseAnalysis_FileCategories(t *testing.T) { // Test what happens when AnalyzeFiles is called with an already canceled context. // Expected behavior: Should return immediately with a ctx.Err() and empty decisions. -// TODO: Missing Edge Case - Null/Undefined/Empty: Empty string in paths slice. -// Test what happens when `paths` contains `""`. -// Expected behavior: The system should not panic; `detectLanguage` should return "unknown"; -// `matchesPath` behavior with empty strings must be defined and validated. // TODO: Missing Edge Case - Null/Undefined/Empty: IntelligenceReport fields are empty (but not nil). // Test with `IntelligenceReport{FileTopology: map[string]FileInfo{}, GitChurnHotspots: []GitChurn{}}`. @@ -329,3 +325,36 @@ func TestEdgeCaseAnalysis_FileCategories(t *testing.T) { // TODO: Missing Edge Case - Extreme Values: Max file size boundaries. // Test determineAction with `LineCount = math.MaxInt32`. // Expected behavior: Should cleanly suggest ActionModularize without overflow in heuristics (e.g., complexity calc). + +func TestEdgeCaseDetector_AnalyzeFiles_EmptyStringInPaths(t *testing.T) { + detector := NewEdgeCaseDetector(nil, nil) + ctx := context.Background() + + // AnalyzeFiles takes (ctx, paths, *IntelligenceReport) + decisions, err := detector.AnalyzeFiles(ctx, []string{"", "valid/path.go", ""}, nil) + + if err != nil { + t.Errorf("AnalyzeFiles with empty paths should not error: %v", err) + } + if len(decisions) != 1 { + t.Errorf("expected 1 decision for valid path, got %d", len(decisions)) + } else if decisions[0].Path != "valid/path.go" { + t.Errorf("expected decision path to be 'valid/path.go', got '%s'", decisions[0].Path) + } + + // test detectLanguage empty string + if lang := detector.detectLanguage(""); lang != "unknown" { + t.Errorf("detectLanguage(\"\") expected 'unknown', got '%s'", lang) + } + + // test matchesPath empty string + if match := detector.matchesPath("", "path/to/file.go"); match { + t.Errorf("matchesPath(\"\", \"path/to/file.go\") should be false") + } + if match := detector.matchesPath("candidate.go", ""); match { + t.Errorf("matchesPath(\"candidate.go\", \"\") should be false") + } + if match := detector.matchesPath("", ""); !match { + t.Errorf("matchesPath(\"\", \"\") should be true") + } +}