package rules import ( "context" "testing" ) func TestEngine_StopsAfterFirstMatch(t *testing.T) { // Create a config with multiple rules config := &RuleConfig{ Rules: []Rule{ // First rule: matches text/html and returns obelisk &MimetypeRule{ Mimetype: "text/html", Archivers: []ArchiverConfig{ {Key: "obelisk"}, }, }, // Second rule: matches application/* and returns direct_download // This should NEVER be evaluated if first rule matches &MimetypeRule{ Mimetype: "application/*", Archivers: []ArchiverConfig{ {Key: "direct_download"}, }, }, }, DefaultArchivers: []ArchiverConfig{{Key: "obelisk"}}, } engine := NewEngine(config) ctx := context.Background() // Test with text/html - should match first rule and stop metadata := &URLMetadata{ MimeType: "text/html", Domain: "example.com", } extractors, err := engine.Evaluate(ctx, metadata) if err != nil { t.Fatalf("Evaluate() error = %v", err) } // Should only return obelisk (from first rule), NOT direct_download if len(extractors) != 1 { t.Errorf("Expected 1 extractor, got %d", len(extractors)) } if extractors[0].Key != "obelisk" { t.Errorf("Expected extractor 'obelisk', got '%s'", extractors[0].Key) } } func TestEngine_StopsAfterFirstMatch_WithEmptyArchivers(t *testing.T) { // Test that engine stops even when first rule returns empty extractors config := &RuleConfig{ Rules: []Rule{ // First rule: matches and returns empty extractors (skip archiving) &HostnameRule{ Hostname: "example.com", Archivers: []ArchiverConfig{}, // Empty = skip }, // Second rule: should never be evaluated &MimetypeRule{ Mimetype: "text/html", Archivers: []ArchiverConfig{ {Key: "obelisk"}, }, }, }, DefaultArchivers: []ArchiverConfig{{Key: "obelisk"}}, } engine := NewEngine(config) ctx := context.Background() metadata := &URLMetadata{ MimeType: "text/html", Domain: "example.com", // Matches first rule } extractors, err := engine.Evaluate(ctx, metadata) if err != nil { t.Fatalf("Evaluate() error = %v", err) } // Should return empty extractors (skip archiving), NOT obelisk from second rule if len(extractors) != 0 { t.Errorf("Expected 0 extractors (skip archiving), got %d: %v", len(extractors), extractors) } } func TestEngine_MultipleArchiversInSingleRule(t *testing.T) { // Test that a single rule can return multiple extractors config := &RuleConfig{ Rules: []Rule{ &MimetypeRule{ Mimetype: "text/html", Archivers: []ArchiverConfig{ {Key: "obelisk"}, {Key: "direct_download"}, // Multiple extractors in one rule }, }, }, DefaultArchivers: []ArchiverConfig{{Key: "obelisk"}}, } engine := NewEngine(config) ctx := context.Background() metadata := &URLMetadata{ MimeType: "text/html", Domain: "example.com", } extractors, err := engine.Evaluate(ctx, metadata) if err != nil { t.Fatalf("Evaluate() error = %v", err) } // Should return both extractors from the single rule if len(extractors) != 2 { t.Errorf("Expected 2 extractors, got %d", len(extractors)) } if extractors[0].Key != "obelisk" || extractors[1].Key != "direct_download" { t.Errorf("Expected [obelisk, direct_download], got %v", extractors) } }