package dsl import ( "strings" "testing" ) func TestExactMatcherPass(t *testing.T) { m := ExactMatcher{Value: "hello\n"} if errs := m.Match("stdout", "hello\n"); errs != nil { t.Errorf("expected pass, got %v", errs) } } func TestExactMatcherMismatch(t *testing.T) { m := ExactMatcher{Value: "hello\n"} errs := m.Match("stdout", "world\n") if len(errs) != 1 { t.Fatalf("want 1 error, got %d: %v", len(errs), errs) } if !strings.Contains(errs[0], "stdout mismatch") { t.Errorf("error missing label: %q", errs[0]) } if !strings.Contains(errs[0], "hello") || !strings.Contains(errs[0], "world") { t.Errorf("error missing values: %q", errs[0]) } } func TestContainsMatcherPass(t *testing.T) { m := ContainsMatcher{Substr: "needle"} if errs := m.Match("stdout", "haystack with a needle inside"); errs != nil { t.Errorf("expected pass, got %v", errs) } } func TestContainsMatcherMissing(t *testing.T) { m := ContainsMatcher{Substr: "needle"} errs := m.Match("stdout", "only hay here") if len(errs) != 1 { t.Fatalf("want 1 error, got %d: %v", len(errs), errs) } if !strings.Contains(errs[0], "needle") { t.Errorf("error missing substring: %q", errs[0]) } } func TestRegexMatcherPass(t *testing.T) { m := RegexMatcher{Pattern: `^hello .*!$`} if errs := m.Match("stdout", "hello world!"); errs != nil { t.Errorf("expected pass, got %v", errs) } } func TestRegexMatcherMismatch(t *testing.T) { m := RegexMatcher{Pattern: `^\d+$`} errs := m.Match("stdout", "not-a-number") if len(errs) != 1 { t.Fatalf("want 1 error, got %d: %v", len(errs), errs) } if !strings.Contains(errs[0], "does not match") { t.Errorf("error unexpected: %q", errs[0]) } } func TestRegexMatcherInvalidPattern(t *testing.T) { m := RegexMatcher{Pattern: `[`} errs := m.Match("stdout", "anything") if len(errs) != 1 { t.Fatalf("want 1 error, got %d: %v", len(errs), errs) } if !strings.Contains(errs[0], "invalid regex") { t.Errorf("error unexpected: %q", errs[0]) } } func TestNumericEpsMatcherPassWithinEps(t *testing.T) { m := NumericEpsMatcher{Epsilon: 0.01, Value: "1.0 2.0 3.0"} if errs := m.Match("stdout", "1.005 1.999 3.0"); errs != nil { t.Errorf("expected pass, got %v", errs) } } func TestNumericEpsMatcherExceedsEps(t *testing.T) { m := NumericEpsMatcher{Epsilon: 0.01, Value: "1.0 2.0"} errs := m.Match("stdout", "1.0 2.5") if len(errs) != 1 { t.Fatalf("want 1 error, got %d: %v", len(errs), errs) } if !strings.Contains(errs[0], "number[1]") { t.Errorf("error missing index: %q", errs[0]) } } func TestNumericEpsMatcherCountMismatch(t *testing.T) { m := NumericEpsMatcher{Epsilon: 0.01, Value: "1 2 3"} errs := m.Match("stdout", "1 2") if len(errs) != 1 { t.Fatalf("want 1 error, got %d: %v", len(errs), errs) } if !strings.Contains(errs[0], "expected 3 numbers, got 2") { t.Errorf("error unexpected: %q", errs[0]) } } func TestNumericEpsMatcherBadExpected(t *testing.T) { m := NumericEpsMatcher{Epsilon: 0.01, Value: "1 foo"} errs := m.Match("stdout", "1 2") if len(errs) != 1 || !strings.Contains(errs[0], "cannot parse expected") { t.Errorf("want expected-parse error, got %v", errs) } } func TestNumericEpsMatcherBadActual(t *testing.T) { m := NumericEpsMatcher{Epsilon: 0.01, Value: "1 2"} errs := m.Match("stdout", "1 bar") if len(errs) != 1 || !strings.Contains(errs[0], "cannot parse actual") { t.Errorf("want actual-parse error, got %v", errs) } } func TestNumericEpsMatcherMultipleErrors(t *testing.T) { m := NumericEpsMatcher{Epsilon: 0.01, Value: "1 2 3"} errs := m.Match("stdout", "9 8 7") if len(errs) != 3 { t.Errorf("want 3 errors, got %d: %v", len(errs), errs) } } func TestAnyOrderMatcherPassReordered(t *testing.T) { m := AnyOrderMatcher{Lines: []string{"a", "b", "c"}} if errs := m.Match("stdout", "c\nb\na\n"); errs != nil { t.Errorf("expected pass, got %v", errs) } } func TestAnyOrderMatcherPassNoTrailingNewline(t *testing.T) { m := AnyOrderMatcher{Lines: []string{"x", "y"}} if errs := m.Match("stdout", "y\nx"); errs != nil { t.Errorf("expected pass, got %v", errs) } } func TestAnyOrderMatcherCountMismatch(t *testing.T) { m := AnyOrderMatcher{Lines: []string{"a", "b"}} errs := m.Match("stdout", "a\nb\nc\n") if len(errs) != 1 || !strings.Contains(errs[0], "expected 2 lines, got 3") { t.Errorf("want count error, got %v", errs) } } func TestAnyOrderMatcherLineMismatch(t *testing.T) { m := AnyOrderMatcher{Lines: []string{"a", "b"}} errs := m.Match("stdout", "a\nz\n") if len(errs) != 1 || !strings.Contains(errs[0], "line mismatch") { t.Errorf("want line mismatch, got %v", errs) } } func TestAnyOrderMatcherEmptyBoth(t *testing.T) { m := AnyOrderMatcher{Lines: []string{}} if errs := m.Match("stdout", ""); errs != nil { t.Errorf("expected pass on empty, got %v", errs) } } func TestNoMatcherAlwaysPasses(t *testing.T) { m := NoMatcher{} if errs := m.Match("stdout", "anything at all\n"); errs != nil { t.Errorf("NoMatcher should never fail, got %v", errs) } if errs := m.Match("stderr", ""); errs != nil { t.Errorf("NoMatcher should never fail on empty, got %v", errs) } } func TestSplitLinesEmpty(t *testing.T) { if got := splitLines(""); len(got) != 0 { t.Errorf("splitLines(\"\") = %v, want empty", got) } } func TestSplitLinesTrailingNewline(t *testing.T) { got := splitLines("a\nb\n") if len(got) != 2 || got[0] != "a" || got[1] != "b" { t.Errorf("splitLines trailing = %v, want [a b]", got) } }