Skip to content

Commit

Permalink
Merge pull request #147 from glesica/add-file-assertion
Browse files Browse the repository at this point in the history
Add file assertion
  • Loading branch information
dylanhitt authored Oct 23, 2020
2 parents 75391aa + 571c5e4 commit 10db468
Show file tree
Hide file tree
Showing 15 changed files with 150 additions and 8 deletions.
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
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ For more information take a look at the [quick start](#quick-start), the [exampl
* [line-count](#line-count)
* [not-contains](#not-contains)
* [xml](#xml)
* [file](#file)
- [stderr](#stderr)
- [skip](#skip)
+ [Config](#user-content-config-config)
Expand Down Expand Up @@ -167,6 +168,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 Expand Up @@ -529,6 +531,21 @@ cat some.xml:
</book>
```

##### file

`file` is a file path, relative to the working directory that will have
its entire contents matched against the command output. Other than
reading from a file this works the same as [exactly](#exactly).

The example below will always pass.

```yaml
output should match file:
command: cat output.txt
stdout:
file: output.txt
```

#### stderr

See [stdout](#stdout) for more information.
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 examples/_fixtures/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
line one
line two
7 changes: 6 additions & 1 deletion examples/commander.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ tests:
it should skip:
command: echo "I should be skipped"
stdout: I should be skipped
skip: true
skip: true

it should match file output:
command: printf "line one\nline two"
stdout:
file: ../../examples/_fixtures/output.txt
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:
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
// 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")
}

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

0 comments on commit 10db468

Please sign in to comment.