Skip to content

Commit

Permalink
new: colorize mock errors
Browse files Browse the repository at this point in the history
  • Loading branch information
lansfy committed Jan 6, 2025
1 parent 2ae7c3b commit 60928da
Show file tree
Hide file tree
Showing 34 changed files with 245 additions and 145 deletions.
2 changes: 1 addition & 1 deletion checker/response_body/response_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func createWrongStatusError(statusCode int, known map[int]string) error {
for code := range known {
knownCodes = append(knownCodes, fmt.Sprintf("%d", code))
}
return colorize.NewNotEqualError("server responded with unexpected ", "status", ":", strings.Join(knownCodes, " / "), statusCode, nil)
return colorize.NewNotEqualError("server responded with unexpected %s:", "status", strings.Join(knownCodes, " / "), statusCode)
}

func (c *responseBodyChecker) Check(t models.TestInterface, result *models.Result) ([]error, error) {
Expand Down
13 changes: 4 additions & 9 deletions checker/response_db/response_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,30 +122,25 @@ func compareDbResponseLength(expected, actual []string, query interface{}) error
diffCfg.Diffable = true
chunks := diff.DiffChunks(strings.Split(diffCfg.Sprint(expected), "\n"), strings.Split(diffCfg.Sprint(actual), "\n"))

tail := []*colorize.Part{
tail := []colorize.Part{
colorize.None("\n\n query: "),
colorize.Cyan(query),
colorize.None("\n diff (--- expected vs +++ actual):\n"),
}
tail = append(tail, colorize.MakeColorDiff(chunks)...)

return colorize.NewNotEqualError(
"quantity of ",
"quantity of %s do not match:",
"items in database",
" do not match:",
len(expected),
len(actual),
tail,
)
).AddParts(tail...)

return colorize.NewError(
colorize.None("quantity of items in database do not match (-expected: "),
"quantity of items in database do not match (-expected: %s +actual: %s)\n test query:\n%s\n result diff:\n%s",
colorize.Cyan(len(expected)),
colorize.None(" +actual: "),
colorize.Cyan(len(actual)),
colorize.None(")\n test query:\n"),
colorize.Cyan(query),
colorize.None("\n result diff:\n"),
colorize.Cyan(pretty.Compare(expected, actual)),
)
}
Expand Down
9 changes: 4 additions & 5 deletions checker/response_header/response_header.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (c *responseHeaderChecker) Check(t models.TestInterface, result *models.Res
actualValues, ok := result.ResponseHeaders[k]
if !ok {
errs = append(errs, colorize.NewError(
colorize.None("response does not include expected header "),
"response does not include expected header %s",
colorize.Cyan(k),
))
continue
Expand All @@ -44,16 +44,15 @@ func (c *responseHeaderChecker) Check(t models.TestInterface, result *models.Res
}
if len(actualValues) == 1 {
errs = append(errs, colorize.NewNotEqualError(
"response header ", k, " value does not match:",
"response header %s value does not match:",
k,
v,
actualValues[0],
nil,
))
} else {
errs = append(errs, colorize.NewError(
colorize.None("response header "),
"response header %s value does not match expected %s",
colorize.Cyan(k),
colorize.None(" value does not match expected "),
colorize.Green(v),
))
}
Expand Down
8 changes: 2 additions & 6 deletions colorize/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ package colorize

import (
"fmt"
"strings"

"github.com/kylelemons/godebug/diff"
)

func MakeColorDiff(chunks []diff.Chunk) []*Part {
parts := []*Part{}
func MakeColorDiff(chunks []diff.Chunk) []Part {
parts := []Part{}
for _, c := range chunks {
for _, line := range c.Added {
parts = append(parts, Red(fmt.Sprintf("+%s\n", line)))
Expand All @@ -20,8 +19,5 @@ func MakeColorDiff(chunks []diff.Chunk) []*Part {
parts = append(parts, None(fmt.Sprintf(" %s\n", line)))
}
}
if len(parts) != 0 {
parts[len(parts)-1].value = strings.TrimRight(parts[len(parts)-1].value, "\n")
}
return parts
}
103 changes: 64 additions & 39 deletions colorize/error.go
Original file line number Diff line number Diff line change
@@ -1,50 +1,51 @@
package colorize

import (
"errors"
"fmt"
"strings"

"github.com/fatih/color"
)

type Color int
func Red(v interface{}) Part {
return &partImpl{color.HiRedString, fmt.Sprintf("%v", v), false}
}

const (
ColorRed Color = iota
ColorCyan
ColorGreen
ColorNone
)
func Cyan(v interface{}) Part {
return &partImpl{color.HiCyanString, fmt.Sprintf("%v", v), true}
}

type Part struct {
attr Color
value string
func Green(v interface{}) Part {
return &partImpl{color.HiGreenString, fmt.Sprintf("%v", v), false}
}

func Red(v interface{}) *Part {
return &Part{ColorRed, fmt.Sprintf("%v", v)}
func None(v interface{}) Part {
return &partImpl{asIsString, fmt.Sprintf("%v", v), false}
}

func Cyan(v interface{}) *Part {
return &Part{ColorCyan, fmt.Sprintf("%v", v)}
func SubError(err error) Part {
return &subErrorImpl{err}
}

func Green(v interface{}) *Part {
return &Part{ColorGreen, fmt.Sprintf("%v", v)}
type Error struct {
parts []Part
}

func None(v interface{}) *Part {
return &Part{ColorNone, fmt.Sprintf("%v", v)}
func (e *Error) AddParts(values ...Part) *Error {
e.parts = append(e.parts, values...)
return e
}

type Error struct {
parts []*Part
func (e *Error) SetSubError(err error) *Error {
e.AddParts(SubError(err))
return e
}

func (e *Error) Error() string {
buf := strings.Builder{}
for _, p := range e.parts {
buf.WriteString(p.value)
buf.WriteString(p.Text())
}
return buf.String()
}
Expand All @@ -56,32 +57,56 @@ func asIsString(format string, a ...interface{}) string {
func (e *Error) ColorError() string {
buf := strings.Builder{}
for _, p := range e.parts {
if p.value == "" {
continue
buf.WriteString(p.ColorText())
}
return buf.String()
}

func alternateJoin(list1, list2 []Part) []Part {
result := []Part{}
i, j := 0, 0
for len(result) != len(list1)+len(list2) {
if i < len(list1) {
result = append(result, list1[i])
i++
}
f := asIsString
switch p.attr {
case ColorRed:
f = color.RedString
case ColorCyan:
f = color.CyanString
case ColorGreen:
f = color.GreenString
if j < len(list2) {
result = append(result, list2[j])
j++
}
}

return result
}

buf.WriteString(f(p.value))
func GetColoredValue(err error) string {
var pErr *Error
if errors.As(err, &pErr) {
return pErr.ColorError()
}
return buf.String()
return err.Error()
}

func NewError(format string, values ...Part) *Error {
plain := []Part{}
for _, s := range strings.Split(format, "%s") {
plain = append(plain, None(s))
}
return &Error{alternateJoin(plain, values)}
}

func NewEntityError(pattern, entity string) *Error {
return NewError(pattern, Cyan(entity))
}

func NewError(parts ...*Part) error {
return &Error{parts}
func NewNotEqualError(pattern, entity string, expected, actual interface{}) *Error {
pattern += "\n expected: %s\n actual: %s"
return NewError(pattern, Cyan(entity), Green(expected), Red(actual))
}

func NewNotEqualError(before, entity, after string, expected, actual interface{}, tail []*Part) error {
parts := []*Part{
func NewNotEqualError2(before, entity, after string, expected, actual interface{}) *Error {
parts := []Part{
None(before), Cyan(entity), None(after + "\n expected: "), Green(expected), None("\n actual: "), Red(actual),
}
parts = append(parts, tail...)
return NewError(parts...)
return NewError("", parts...)
}
35 changes: 35 additions & 0 deletions colorize/part.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package colorize

type Part interface {
Text() string
ColorText() string
}

type partImpl struct {
colorer func(format string, a ...interface{}) string
value string
entity bool
}

func (p *partImpl) Text() string {
if p.entity {
return "'" + p.value + "'"
}
return p.value
}

func (p *partImpl) ColorText() string {
return p.colorer(p.value)
}

type subErrorImpl struct {
err error
}

func (p *subErrorImpl) Text() string {
return ": " + p.err.Error()
}

func (p *subErrorImpl) ColorText() string {
return ": " + GetColoredValue(p.err)
}
10 changes: 4 additions & 6 deletions compare/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ func leafMatchType(expected interface{}) leafsMatchType {
return pure
}

func diffStrings(a, b string) []*colorize.Part {
func diffStrings(a, b string) []colorize.Part {
chunks := diff.DiffChunks(strings.Split(a, "\n"), strings.Split(b, "\n"))
return colorize.MakeColorDiff(chunks)
}
Expand All @@ -232,18 +232,16 @@ func makeValueCompareError(path, msg string, expected, actual interface{}) error
}

// special case for multi-line strings
parts := []*colorize.Part{
colorize.None("at path "),
parts := []colorize.Part{
colorize.Cyan(path),
colorize.None(" " + msg + ":\n diff (--- expected vs +++ actual):\n"),
}

parts = append(parts, diffStrings(actualStr, expectedStr)...)
return colorize.NewError(parts...)
return colorize.NewError("at path %s "+msg+":\n diff (--- expected vs +++ actual):\n", parts...)
}

func makeError(path, msg string, expected, actual interface{}) error {
return colorize.NewNotEqualError("at path ", path, " "+msg+":", expected, actual, nil)
return colorize.NewNotEqualError("at path %s "+msg+":", path, expected, actual)
}

func convertToArray(array interface{}) []interface{} {
Expand Down
2 changes: 1 addition & 1 deletion compare/compare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

func makeErrorString(path, msg string, expected, actual interface{}) string {
return fmt.Sprintf(
"at path %s %s:\n expected: %v\n actual: %v",
"at path '%s' %s:\n expected: %v\n actual: %v",
path,
msg,
expected,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ require (
github.com/tidwall/gjson v1.17.0
golang.org/x/sync v0.4.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand All @@ -22,4 +21,5 @@ require (
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
7 changes: 4 additions & 3 deletions mocks/constraint_body_json_field_matches_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package mocks

import (
"encoding/json"
"fmt"
"net/http"

"github.com/lansfy/gonkex/colorize"
"github.com/lansfy/gonkex/compare"

"github.com/tidwall/gjson"
)

Expand Down Expand Up @@ -58,10 +59,10 @@ func (c *bodyJSONFieldMatchesJSONConstraint) Verify(r *http.Request) []error {

value := gjson.Get(string(body), c.path)
if !value.Exists() {
return []error{fmt.Errorf("json field %s does not exist", c.path)}
return []error{colorize.NewEntityError("json field %s does not exist", c.path)}
}
if value.String() == "" {
return []error{fmt.Errorf("json field %s is empty", c.path)}
return []error{colorize.NewEntityError("json field %s is empty", c.path)}
}

var actual interface{}
Expand Down
4 changes: 2 additions & 2 deletions mocks/constraint_body_matches_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package mocks

import (
"encoding/json"
"fmt"
"errors"
"net/http"

"github.com/lansfy/gonkex/compare"
Expand Down Expand Up @@ -50,7 +50,7 @@ func (c *bodyMatchesJSONConstraint) Verify(r *http.Request) []error {
}

if len(body) == 0 {
return []error{fmt.Errorf("request is empty")}
return []error{errors.New("request is empty")}
}
var actual interface{}
err = json.Unmarshal(body, &actual)
Expand Down
4 changes: 2 additions & 2 deletions mocks/constraint_body_matches_text.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ func (c *bodyMatchesTextConstraint) Verify(r *http.Request) []error {
textBody := string(body)

if c.body != "" && c.body != textBody {
return []error{fmt.Errorf("body value\n%s\ndoesn't match expected\n%s", textBody, c.body)}
return []error{fmt.Errorf("body value\n%s\ndoes not match expected\n%s", textBody, c.body)}
}
if c.regexp != nil && !c.regexp.MatchString(textBody) {
return []error{fmt.Errorf("body value\n%s\ndoesn't match regexp %s", textBody, c.regexp)}
return []error{fmt.Errorf("body value\n%s\ndoes not match regexp %s", textBody, c.regexp)}
}
return nil
}
Loading

0 comments on commit 60928da

Please sign in to comment.