blob: 100d9f71afb5fbae6c74b4404d7e4dff621eb3fe [file] [log] [blame]
package codereview
import (
"testing"
"time"
)
// TestCompareTestResults_NilPointerPanic tests the fix for the nil pointer panic
// that occurred when comparing test results between commits where a package
// exists in the after state but not in the before state.
func TestCompareTestResults_NilPointerPanic(t *testing.T) {
// Create a CodeReviewer instance (minimal setup)
reviewer := &CodeReviewer{
sketchBaseRef: "main",
}
// Create test results where:
// - before: empty (no packages)
// - after: has a package with tests
beforeResults := []testJSON{} // Empty - no packages existed before
afterResults := []testJSON{
{
Time: time.Now(),
Package: "sketch.dev/newpkg",
Action: "run",
Test: "TestNewFunction",
},
{
Time: time.Now(),
Package: "sketch.dev/newpkg",
Action: "pass",
Test: "TestNewFunction",
Elapsed: 0.001,
},
{
Time: time.Now(),
Package: "sketch.dev/newpkg",
Action: "pass",
Test: "", // package-level pass
Elapsed: 0.001,
},
}
// This should not panic - before the fix, this would cause a nil pointer dereference
// when trying to access beforeResult.TestStatus[test] where beforeResult is nil
regressions, err := reviewer.compareTestResults(beforeResults, afterResults)
if err != nil {
t.Fatalf("compareTestResults failed: %v", err)
}
// We expect no regressions since the new test is passing
if len(regressions) != 0 {
t.Errorf("Expected no regressions for passing new test, got %d", len(regressions))
}
}
// TestCompareTestResults_NilPointerPanic_FailingTest tests the same scenario
// but with a failing test to ensure we properly detect regressions
func TestCompareTestResults_NilPointerPanic_FailingTest(t *testing.T) {
reviewer := &CodeReviewer{
sketchBaseRef: "main",
}
beforeResults := []testJSON{} // Empty - no packages existed before
afterResults := []testJSON{
{
Time: time.Now(),
Package: "sketch.dev/newpkg",
Action: "run",
Test: "TestNewFunction",
},
{
Time: time.Now(),
Package: "sketch.dev/newpkg",
Action: "fail",
Test: "TestNewFunction",
Elapsed: 0.001,
},
{
Time: time.Now(),
Package: "sketch.dev/newpkg",
Action: "fail",
Test: "", // package-level fail
Elapsed: 0.001,
},
}
// This should not panic and should detect the regression
regressions, err := reviewer.compareTestResults(beforeResults, afterResults)
if err != nil {
t.Fatalf("compareTestResults failed: %v", err)
}
// We expect 1 regression for the failing new test
if len(regressions) != 1 {
t.Errorf("Expected 1 regression for failing new test, got %d", len(regressions))
}
if len(regressions) > 0 {
regression := regressions[0]
if regression.Package != "sketch.dev/newpkg" {
t.Errorf("Expected package 'sketch.dev/newpkg', got %q", regression.Package)
}
if regression.Test != "TestNewFunction" {
t.Errorf("Expected test 'TestNewFunction', got %q", regression.Test)
}
if regression.BeforeStatus != testStatusUnknown {
t.Errorf("Expected before status testStatusUnknown, got %v", regression.BeforeStatus)
}
if regression.AfterStatus != testStatusFail {
t.Errorf("Expected after status testStatusFail, got %v", regression.AfterStatus)
}
}
}
// TestCompareTestResults_ExistingPackage tests the normal case where
// a package exists in both before and after states
func TestCompareTestResults_ExistingPackage(t *testing.T) {
reviewer := &CodeReviewer{
sketchBaseRef: "main",
}
// Package exists in both before and after
beforeResults := []testJSON{
{
Time: time.Now(),
Package: "sketch.dev/existing",
Action: "pass",
Test: "TestExisting",
Elapsed: 0.001,
},
{
Time: time.Now(),
Package: "sketch.dev/existing",
Action: "pass",
Test: "",
Elapsed: 0.001,
},
}
afterResults := []testJSON{
{
Time: time.Now(),
Package: "sketch.dev/existing",
Action: "fail",
Test: "TestExisting",
Elapsed: 0.001,
},
{
Time: time.Now(),
Package: "sketch.dev/existing",
Action: "fail",
Test: "",
Elapsed: 0.001,
},
}
// This should detect the regression from pass to fail
regressions, err := reviewer.compareTestResults(beforeResults, afterResults)
if err != nil {
t.Fatalf("compareTestResults failed: %v", err)
}
// We expect 1 regression
if len(regressions) != 1 {
t.Errorf("Expected 1 regression, got %d", len(regressions))
}
if len(regressions) > 0 {
regression := regressions[0]
if regression.BeforeStatus != testStatusPass {
t.Errorf("Expected before status testStatusPass, got %v", regression.BeforeStatus)
}
if regression.AfterStatus != testStatusFail {
t.Errorf("Expected after status testStatusFail, got %v", regression.AfterStatus)
}
}
}
// TestHashChangedFiles tests the hash function for changed files
func TestHashChangedFiles(t *testing.T) {
reviewer := &CodeReviewer{
repoRoot: "/app",
processedChangedFileSets: make(map[string]bool),
reportedRelatedFiles: make(map[string]bool),
}
// Test that same files in different order produce same hash
files1 := []string{"/app/a.go", "/app/b.go"}
files2 := []string{"/app/b.go", "/app/a.go"}
hash1 := reviewer.hashChangedFiles(files1)
hash2 := reviewer.hashChangedFiles(files2)
if hash1 != hash2 {
t.Errorf("Expected same hash for same files in different order, got %q vs %q", hash1, hash2)
}
// Test that different files produce different hash
files3 := []string{"/app/a.go", "/app/c.go"}
hash3 := reviewer.hashChangedFiles(files3)
if hash1 == hash3 {
t.Errorf("Expected different hash for different files, got same hash %q", hash1)
}
// Test single file
files4 := []string{"/app/a.go"}
hash4 := reviewer.hashChangedFiles(files4)
if hash1 == hash4 {
t.Errorf("Expected different hash for different number of files, got same hash %q", hash1)
}
}