This commit is contained in:
Tomas Dvorak
2026-02-22 15:41:27 +01:00
parent 0b88627e54
commit 409acd2e08
84 changed files with 65382 additions and 27475 deletions
+342
View File
@@ -0,0 +1,342 @@
package quality
import (
"context"
"testing"
)
// DetectorMock implements the Detector interface for testing
type DetectorMock struct {
name string
severity Severity
}
func (m *DetectorMock) Name() string {
return m.name
}
func (m *DetectorMock) Detect(ctx context.Context, path string, config *Config) ([]Finding, error) {
return []Finding{
{
Type: "mock_finding",
Severity: m.severity,
Status: StatusOpen,
Score: 5,
},
}, nil
}
func (m *DetectorMock) Severity() Severity {
return m.severity
}
// LanguageDetectorMock implements both Detector and LanguageDetector interfaces
type LanguageDetectorMock struct {
*DetectorMock
supportedLanguages []string
}
func (m *LanguageDetectorMock) SupportedLanguages() []string {
return m.supportedLanguages
}
func (m *LanguageDetectorMock) ExtractFunctions(ctx context.Context, files []string) ([]FunctionInfo, error) {
var functions []FunctionInfo
for _, file := range files {
functions = append(functions, FunctionInfo{
Name: "test_func",
File: file,
})
}
return functions, nil
}
func (m *LanguageDetectorMock) ExtractClasses(ctx context.Context, files []string) ([]ClassInfo, error) {
var classes []ClassInfo
for _, file := range files {
classes = append(classes, ClassInfo{
Name: "TestClass",
File: file,
})
}
return classes, nil
}
// FileFinderMock implements the FileFinder interface for testing
type FileFinderMock struct {
files []string
}
func (m *FileFinderMock) FindFiles(path string, language string) ([]string, error) {
return m.files, nil
}
func (m *FileFinderMock) IsSourceFile(path string, language string) bool {
return true
}
func TestNewBaseDetector(t *testing.T) {
finder := &FileFinderMock{files: []string{"test.go"}}
detector := NewBaseDetector("test-detector", SeverityT2, finder)
if detector == nil {
t.Error("NewBaseDetector() should not return nil")
}
if detector.name != "test-detector" {
t.Errorf("NewBaseDetector() name = %v, want test-detector", detector.name)
}
if detector.severity != SeverityT2 {
t.Errorf("NewBaseDetector() severity = %v, want T2", detector.severity)
}
if detector.finder != finder {
t.Error("NewBaseDetector() finder not set correctly")
}
}
func TestBaseDetector_Name(t *testing.T) {
detector := NewBaseDetector("test-name", SeverityT1, nil)
if detector.Name() != "test-name" {
t.Errorf("Name() = %v, want test-name", detector.Name())
}
}
func TestBaseDetector_Severity(t *testing.T) {
tests := []struct {
name string
severity Severity
}{
{"T1 severity", SeverityT1},
{"T2 severity", SeverityT2},
{"T3 severity", SeverityT3},
{"T4 severity", SeverityT4},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
detector := NewBaseDetector("test", tt.severity, nil)
if detector.Severity() != tt.severity {
t.Errorf("Severity() = %v, want %v", detector.Severity(), tt.severity)
}
})
}
}
func TestBaseDetector_FindFiles(t *testing.T) {
tests := []struct {
name string
finder FileFinder
expected []string
}{
{
name: "with finder",
finder: &FileFinderMock{files: []string{"file1.go", "file2.go"}},
expected: []string{"file1.go", "file2.go"},
},
{
name: "without finder",
finder: nil,
expected: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
detector := NewBaseDetector("test", SeverityT1, tt.finder)
files, err := detector.FindFiles("/test/path", "go")
if err != nil {
t.Errorf("FindFiles() unexpected error: %v", err)
}
if len(files) != len(tt.expected) {
t.Errorf("FindFiles() expected %d files, got %d", len(tt.expected), len(files))
}
for i, file := range files {
if i < len(tt.expected) && file != tt.expected[i] {
t.Errorf("FindFiles() file %d = %v, want %v", i, file, tt.expected[i])
}
}
})
}
}
func TestShouldExclude(t *testing.T) {
tests := []struct {
name string
path string
excludes []string
expected bool
}{
{"no excludes", "test.go", []string{}, false},
{"empty excludes", "test.go", []string{""}, false},
{"exact match", "test.go", []string{"test.go"}, true},
{"pattern match", "test_*.go", []string{"test_*.go"}, true},
{"no match", "other.go", []string{"test.go"}, false},
{
name: "directory match",
path: "vendor/lib.go",
excludes: []string{"vendor"},
expected: false,
}, // filepath.Match doesn't match directories this way
{"base directory match", "lib.go", []string{"lib.go"}, true},
{"multiple patterns", "test.go", []string{"*.py", "test.go"}, true},
{"invalid pattern", "test.go", []string{"[invalid"}, false},
{"complex pattern", "internal/test/file.go", []string{"internal/*/file.go"}, true},
{"case sensitive", "Test.go", []string{"test.go"}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := ShouldExclude(tt.path, tt.excludes)
if result != tt.expected {
t.Errorf("ShouldExclude(%s, %v) = %v, want %v", tt.path, tt.excludes, result, tt.expected)
}
})
}
}
func TestShouldExclude_EdgeCases(t *testing.T) {
tests := []struct {
name string
path string
excludes []string
expected bool
}{
{"empty path", "", []string{"*"}, true},
{"empty pattern", "test.go", []string{""}, false},
{"star pattern", "any_file.go", []string{"*"}, true}, {
name: "question mark",
path: "file.go",
excludes: []string{"file.?"},
expected: false}, // filepath.Match doesn't support ? this way
{
name: "character class",
path: "file.go",
excludes: []string{"file.[go]"},
expected: false}, // filepath.Match doesn't support character classes
{"nested pattern", "a/b/c/file.go", []string{"a/*/c/file.go"}, true},
{"absolute path", "/absolute/path/file.go", []string{"*.go"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := ShouldExclude(tt.path, tt.excludes)
if result != tt.expected {
t.Errorf("ShouldExclude(%s, %v) = %v, want %v", tt.path, tt.excludes, result, tt.expected)
}
})
}
}
func TestMockDetector_Interface(t *testing.T) {
// Verify that DetectorMock implements Detector interface
var _ Detector = &DetectorMock{name: "test", severity: SeverityT1}
detector := &DetectorMock{name: "test-detector", severity: SeverityT2}
ctx := context.Background()
findings, err := detector.Detect(ctx, "/test/path", &Config{})
if err != nil {
t.Errorf("MockDetector.Detect() unexpected error: %v", err)
}
if len(findings) != 1 {
t.Errorf("DetectorMock.Detect() expected 1 finding, got %d", len(findings))
}
if findings[0].Type != "mock_finding" {
t.Errorf("DetectorMock.Detect() finding type = %v, want mock_finding", findings[0].Type)
}
if findings[0].Severity != SeverityT2 {
t.Errorf("DetectorMock.Detect() finding severity = %v, want T2", findings[0].Severity)
}
}
func TestLanguageDetectorMock_Interface(t *testing.T) {
// Verify that LanguageDetectorMock implements LanguageDetector interface
var _ LanguageDetector = &LanguageDetectorMock{
DetectorMock: &DetectorMock{name: "test", severity: SeverityT1},
supportedLanguages: []string{"go", "python"},
}
detector := &LanguageDetectorMock{
DetectorMock: &DetectorMock{name: "test-lang", severity: SeverityT3},
supportedLanguages: []string{"go", "python", "javascript"},
}
if len(detector.SupportedLanguages()) != 3 {
t.Errorf("LanguageDetectorMock.SupportedLanguages() expected 3 languages, got %d", len(detector.SupportedLanguages()))
}
ctx := context.Background()
files := []string{"file1.go", "file2.py"}
functions, err := detector.ExtractFunctions(ctx, files)
if err != nil {
t.Errorf("LanguageDetectorMock.ExtractFunctions() unexpected error: %v", err)
}
if len(functions) != 2 {
t.Errorf("LanguageDetectorMock.ExtractFunctions() expected 2 functions, got %d", len(functions))
}
classes, err := detector.ExtractClasses(ctx, files)
if err != nil {
t.Errorf("LanguageDetectorMock.ExtractClasses() unexpected error: %v", err)
}
if len(classes) != 2 {
t.Errorf("LanguageDetectorMock.ExtractClasses() expected 2 classes, got %d", len(classes))
}
}
func TestMockFileFinder_Interface(t *testing.T) {
// Verify that FileFinderMock implements FileFinder interface
var _ FileFinder = &FileFinderMock{files: []string{"test.go"}}
finder := &FileFinderMock{files: []string{"file1.go", "file2.go"}}
files, err := finder.FindFiles("/test/path", "go")
if err != nil {
t.Errorf("FileFinderMock.FindFiles() unexpected error: %v", err)
}
if len(files) != 2 {
t.Errorf("FileFinderMock.FindFiles() expected 2 files, got %d", len(files))
}
if !finder.IsSourceFile("test.go", "go") {
t.Error("FileFinderMock.IsSourceFile() should return true")
}
}
func TestBaseDetector_Integration(t *testing.T) {
// Test BaseDetector with real mock implementations
finder := &FileFinderMock{files: []string{"main.go", "utils.go"}}
detector := NewBaseDetector("integration-test", SeverityT2, finder)
// Test all methods
if detector.Name() != "integration-test" {
t.Errorf("Integration test: Name() = %v, want integration-test", detector.Name())
}
if detector.Severity() != SeverityT2 {
t.Errorf("Integration test: Severity() = %v, want T2", detector.Severity())
}
files, err := detector.FindFiles("/project", "go")
if err != nil {
t.Errorf("Integration test: FindFiles() unexpected error: %v", err)
}
if len(files) != 2 {
t.Errorf("Integration test: FindFiles() expected 2 files, got %d", len(files))
}
}