Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add file assertion #147

Merged
merged 7 commits into from
Oct 23, 2020
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Add ability to test suite from a url
- Add ability to test suite from stdin
- Add `file` assertion to `stdout` and `stderr`

# v2.3.0

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ tests:
object.attr: hello # Make assertions on json objects
xml:
"//book//auhtor": Steven King # Make assertions on xml documents
file: correct-output.txt
exit-code: 127
skip: false

Expand Down
5 changes: 3 additions & 2 deletions commander_unix.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@ tests:
contains:
- ✓ [local] it should exit with error code
- "- [local] it should skip, was skipped"
line-count: 17
line-count: 19
exit-code: 0

it should assert that commander will fail:
command: ./commander test ./integration/unix/failing_suite.yaml
stdout:
contains:
- ✗ [local] 'file matcher should fail when file and output are different', on property 'Stdout'
- ✗ [local] 'it will fail', on property 'ExitCode'
- ✗ [local] 'test timeout' could not be executed with error message
- Command timed out after 10ms
- "Count: 2, Failed: 2"
- "Count: 3, Failed: 3"
exit-code: 1

it should validate a big output:
Expand Down
2 changes: 2 additions & 0 deletions integration/unix/_fixtures/file_output_0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
first line
second line
3 changes: 3 additions & 0 deletions integration/unix/_fixtures/file_output_1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
first line
second line
third line
10 changes: 10 additions & 0 deletions integration/unix/commander_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ tests:
/books/0/author: J. R. R. Tokien
/books/1/author: Joanne K. Rowling

it should assert file contents on stdout:
command: cat ./integration/unix/_fixtures/big_out.txt
stdout:
file: ./integration/unix/_fixtures/big_out.txt

it should assert file contents on stderr:
command: cat ./integration/unix/_fixtures/big_out.txt >&2
stderr:
file: ./integration/unix/_fixtures/big_out.txt

it should inherit from parent env:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind adding a failing case as well?

config:
inherit-env: true
Expand Down
7 changes: 6 additions & 1 deletion integration/unix/failing_suite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ tests:
command: sleep 1
config:
timeout: 10ms
exit-code: 0
exit-code: 0

file matcher should fail when file and output are different:
command: cat ./integration/unix/_fixtures/file_output_1.txt
stdout:
file: ./integration/unix/_fixtures/file_output_0.txt
37 changes: 37 additions & 0 deletions pkg/matcher/matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/antchfx/xmlquery"
"github.com/pmezard/go-difflib/difflib"
"github.com/tidwall/gjson"
"io/ioutil"
"log"
"strings"
)
Expand All @@ -20,8 +21,13 @@ const (
NotContains = "notcontains"
JSON = "json"
XML = "xml"
File = "file"
)

// The function used to open files when necessary for matching
dylanhitt marked this conversation as resolved.
Show resolved Hide resolved
// Allows the file IO to be overridden during tests
var ReadFile = ioutil.ReadFile

// NewMatcher creates a new matcher by type
func NewMatcher(matcher string) Matcher {
switch matcher {
Expand All @@ -37,6 +43,8 @@ func NewMatcher(matcher string) Matcher {
return JSONMatcher{}
case XML:
return XMLMatcher{}
case File:
return FileMatcher{}
default:
panic(fmt.Sprintf("Validator '%s' does not exist!", matcher))
}
Expand Down Expand Up @@ -240,3 +248,32 @@ to be equal to

return result
}

// FileMatcher matches output captured from stdout or stderr
// against the contents of a file
type FileMatcher struct {
}

func (m FileMatcher) Match(got interface{}, expected interface{}) MatcherResult {
expectedText, err := ReadFile(expected.(string))
if err != nil {
panic(err.Error())
}
expectedString := string(expectedText)

result := got == expectedString

diff := difflib.UnifiedDiff{
A: difflib.SplitLines(got.(string)),
B: difflib.SplitLines(expectedString),
FromFile: "Got",
ToFile: "Expected",
Context: 3,
}
diffText, _ := difflib.GetUnifiedDiffString(diff)

return MatcherResult{
Diff: diffText,
Success: result,
}
}
29 changes: 25 additions & 4 deletions pkg/matcher/matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,16 @@ func TestTextMatcher_ValidateFails(t *testing.T) {

func TestEqualMatcher_Validate(t *testing.T) {
m := EqualMatcher{}
got := m.Match(1, 1)
got := m.Match(2, 2)
assert.True(t, got.Success)
}

func TestEqualMatcher_ValidateFails(t *testing.T) {
m := EqualMatcher{}
got := m.Match(1, 0)
got := m.Match(2, 3)
assert.False(t, got.Success)
assert.Contains(t, got.Diff, "+0")
assert.Contains(t, got.Diff, "-1")
assert.Contains(t, got.Diff, "+3")
assert.Contains(t, got.Diff, "-2")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that there was always a "-1" in the diff output because it tells the user how many lines changed, so this makes the test a tiny bit less brittle.

}

func TestContainsMatcher_Match(t *testing.T) {
Expand Down Expand Up @@ -183,3 +183,24 @@ another`
assert.False(t, r.Success)
assert.Equal(t, diff, r.Diff)
}

func TestFileMatcher_Match(t *testing.T) {
ReadFile = func(filename string) ([]byte, error) {
return []byte("line one\nline two"), nil
}
m := FileMatcher{}
got := m.Match("line one\nline two", "fake.txt")
assert.True(t, got.Success)
assert.Equal(t, "", got.Diff)
}

func TestFileMatcher_ValidateFails(t *testing.T) {
ReadFile = func(filename string) ([]byte, error) {
return []byte("line one\nline two"), nil
}
m := FileMatcher{}
got := m.Match("line one\nline three", "fake.txt")
assert.False(t, got.Success)
assert.Contains(t, got.Diff, "+line two")
assert.Contains(t, got.Diff, "-line three")
}
1 change: 1 addition & 0 deletions pkg/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ type ExpectedOut struct {
NotContains []string `yaml:"not-contains,omitempty"`
JSON map[string]string `yaml:"json,omitempty"`
XML map[string]string `yaml:"xml,omitempty"`
File string `yaml:"file,omitempty"`
}

// CommandUnderTest represents the command under test
Expand Down
7 changes: 7 additions & 0 deletions pkg/runtime/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ func validateExpectedOut(got string, expected ExpectedOut) matcher.MatcherResult
}
}

if expected.File != "" {
m = matcher.NewMatcher(matcher.File)
if result = m.Match(got, expected.File); !result.Success {
return result
}
}

return result
}

Expand Down
24 changes: 24 additions & 0 deletions pkg/runtime/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package runtime
import (
"github.com/commander-cli/commander/pkg/matcher"
"github.com/stretchr/testify/assert"
"io/ioutil"
"testing"
)

Expand Down Expand Up @@ -192,6 +193,29 @@ test`
assert.Equal(t, diff, r.Diff)
}

func Test_ValidateExpectedOut_ValidateFile(t *testing.T) {
content := "line one"
matcher.ReadFile = func(filename string) ([]byte, error) {
return []byte(content), nil
}
r := validateExpectedOut(content, ExpectedOut{File: "fake.txt"})
assert.True(t, r.Success)
assert.Equal(t, "", r.Diff)

diff := `--- Got
+++ Expected
@@ -1,2 +1 @@
line one
-line two
`

r = validateExpectedOut(content+"\nline two", ExpectedOut{File: "fake.txt"})
assert.False(t, r.Success)
assert.Equal(t, diff, r.Diff)

matcher.ReadFile = ioutil.ReadFile
}

func Test_ValidateExpectedOut_ValidateXML(t *testing.T) {
xml := `<book>
<author>J. R. R. Tolkien</author>
Expand Down
6 changes: 6 additions & 0 deletions pkg/suite/yaml_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ func (y *YAMLSuiteConf) convertToExpectedOut(value interface{}) runtime.Expected
"lines",
"json",
"xml",
"file",
"not-contains":
break
default:
Expand All @@ -242,6 +243,11 @@ func (y *YAMLSuiteConf) convertToExpectedOut(value interface{}) runtime.Expected
exp.Exactly = toString(exactly)
}

// Parse file key
if file := v["file"]; file != nil {
exp.File = toString(file)
}

//Parse line-count key
if lc := v["line-count"]; lc != nil {
exp.LineCount = lc.(int)
Expand Down