Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions matchers/have_patterns_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package matchers

import (
"bytes"
"strings"
"testing"
)

// TestHavePatternsMatchString verifies that HavePatterns correctly matches
// string content (the type it receives after the validate-layer materialization fix).
func TestHavePatternsMatchString(t *testing.T) {
content := "Banner /etc/issue.net\nLogLevel INFO\n"

m := HavePatterns([]interface{}{"Banner /etc/issue.net"})
success, err := m.Match(content)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !success {
t.Fatal("expected Match to return true")
}
}

// TestHavePatternsFailureResultString verifies that FailureResult returns the
// actual content as a string — not a Go type repr — when given string input.
// After the validate-layer fix, FailureResult always receives a materialized string.
func TestHavePatternsFailureResultString(t *testing.T) {
content := "Banner /etc/issue.net\nLogLevel INFO\n"
pattern := "nonexistent-pattern"

m := HavePatterns([]interface{}{pattern})
success, err := m.Match(content)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if success {
t.Fatal("expected Match to return false for missing pattern")
}

result := m.FailureResult(content)

actual, ok := result.Actual.(string)
if !ok {
t.Fatalf("FailureResult.Actual must be a string, got %T: %v", result.Actual, result.Actual)
}
if strings.Contains(actual, "object:") {
t.Errorf("FailureResult.Actual must not contain Go type repr, got: %q", actual)
}
if !strings.Contains(actual, "Banner") {
t.Errorf("FailureResult.Actual must contain the file content, got: %q", actual)
}
}

// TestHavePatternsMatchBytesReader verifies that HavePatterns still accepts
// io.Reader directly (backwards-compat; the validate layer now sends strings).
func TestHavePatternsMatchBytesReader(t *testing.T) {
content := "pam_faillock.so preauth\n"

m := HavePatterns([]interface{}{"pam_faillock.so"})
reader := bytes.NewReader([]byte(content))
success, err := m.Match(reader)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !success {
t.Fatal("expected Match to return true")
}
}
10 changes: 9 additions & 1 deletion resource/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,15 @@ func ValidateGomegaValue(res ResourceRead, property string, expectedValue any, a
case func() (any, error):
foundValue, err = f()
case func() (io.Reader, error):
foundValue, err = f()
var r io.Reader
r, err = f()
if err == nil {
var i interface{}
i, err = matchers.ReaderToString{}.Transform(r)
if err == nil {
foundValue = i.(string)
}
}
gomegaMatcher = matchers.HavePatterns(expectedValue)
default:
err = fmt.Errorf("Unknown method signature: %t", f)
Expand Down
27 changes: 27 additions & 0 deletions resource/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,33 @@ func TestValidateContainsBadRegexErr(t *testing.T) {
}
}

// TestValidateContainsFailureActual asserts that when a file-contents check fails,
// the MatcherResult.Actual field contains the readable file content — not the
// Go internal type representation "object: *bytes.Reader" / "object: *os.File".
func TestValidateContainsFailureActual(t *testing.T) {
fileContent := "Banner /etc/issue.net\nLogLevel INFO\n"
missingPattern := "nonexistent-pattern-xyz"

inFunc := func() (io.Reader, error) {
return strings.NewReader(fileContent), nil
}
got := ValidateValue(&FakeResource{""}, "contents", []interface{}{missingPattern}, inFunc, false)

if got.Result != FAIL {
t.Fatalf("expected FAIL, got %v", got.Result)
}
actual, ok := got.MatcherResult.Actual.(string)
if !ok {
t.Fatalf("MatcherResult.Actual must be a string, got %T: %v", got.MatcherResult.Actual, got.MatcherResult.Actual)
}
if strings.Contains(actual, "object:") {
t.Errorf("MatcherResult.Actual must not contain Go type repr, got: %q", actual)
}
if !strings.Contains(actual, "Banner") {
t.Errorf("MatcherResult.Actual must contain file content, got: %q", actual)
}
}

func TestValidateContainsSkip(t *testing.T) {
for _, c := range containsTests {
inFunc := func() (io.Reader, error) {
Expand Down
4 changes: 2 additions & 2 deletions testdata/out_matching_basic_failing.1.documentation
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ to satisfy at least one of these matchers
[{"have-len":2}]
Matching: basic_reader: matches:
Expected
"object: *strings.Reader"
"foo bar\nmoo cow\n"
to have patterns
["fox","/^t.*w$/","!foo","!/^foo/"]
the missing elements were
Expand Down Expand Up @@ -253,7 +253,7 @@ to satisfy at least one of these matchers

Matching: basic_reader: matches:
Expected
"object: *strings.Reader"
"foo bar\nmoo cow\n"
to have patterns
["fox","/^t.*w$/","!foo","!/^foo/"]
the missing elements were
Expand Down
2 changes: 1 addition & 1 deletion testdata/out_matching_basic_failing.1.rspecish
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ to satisfy at least one of these matchers

Matching: basic_reader: matches:
Expected
"object: *strings.Reader"
"foo bar\nmoo cow\n"
to have patterns
["fox","/^t.*w$/","!foo","!/^foo/"]
the missing elements were
Expand Down
2 changes: 1 addition & 1 deletion testdata/out_matching_basic_failing.1.tap
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ not ok 2 - Matching: basic_array_consists_of: matches: Expected ["foo","bar","mo
not ok 3 - Matching: basic_array_matchers: matches: Expected ["foo","bar","moo"] to satisfy at least one of these matchers [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}},{"contain-element":{"have-suffix":"x"}}]
not ok 4 - Matching: basic_int: matches: Expected 42 to be numerically eq 43
not ok 5 - Matching: basic_len: matches: Expected "123" to satisfy at least one of these matchers [{"have-len":2}]
not ok 6 - Matching: basic_reader: matches: Expected "object: *strings.Reader" to have patterns ["fox","/^t.*w$/","!foo","!/^foo/"] the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"]
not ok 6 - Matching: basic_reader: matches: Expected "foo bar\nmoo cow\n" to have patterns ["fox","/^t.*w$/","!foo","!/^foo/"] the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"]
not ok 7 - Matching: basic_semver: matches: Expected "1.2.3" to satisfy at least one of these matchers [{"semver-constraint":">=9.9.0"}]
not ok 8 - Matching: basic_string: matches: Expected "this is a test" to equal "this is a failing test"
not ok 9 - Matching: basic_string_contain_substring: matches: Expected "foo" to contain substring "x"
Expand Down