Skip to content

Commit

Permalink
test(helpers): define custom matchers for assert metrics (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
plastikfan committed Sep 8, 2024
1 parent b389c7f commit 266b0f3
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 119 deletions.
37 changes: 14 additions & 23 deletions internal/helpers/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,29 +87,20 @@ func AssertNavigation(entry *NaviTE, to *TestOptions) {
}

func assertMetrics(entry *NaviTE, to *TestOptions) {
Expect(to.Result.Metrics().Count(enums.MetricNoFilesInvoked)).To(
Equal(entry.ExpectedNoOf.Files),
BecauseQuantity("Incorrect no of files",
int(entry.ExpectedNoOf.Files), //nolint:gosec // ok
int(to.Result.Metrics().Count(enums.MetricNoFilesInvoked)), //nolint:gosec // ok
),
)

Expect(to.Result.Metrics().Count(enums.MetricNoFoldersInvoked)).To(
Equal(entry.ExpectedNoOf.Folders),
BecauseQuantity("Incorrect no of folders",
int(entry.ExpectedNoOf.Folders), //nolint:gosec // ok
int(to.Result.Metrics().Count(enums.MetricNoFoldersInvoked)), //nolint:gosec // ok
),
)

sum := lo.Sum(lo.Values(entry.ExpectedNoOf.Children))

Expect(to.Result.Metrics().Count(enums.MetricNoChildFilesFound)).To(
Equal(uint(sum)),
BecauseQuantity("Incorrect total no of child files",
sum,
int(to.Result.Metrics().Count(enums.MetricNoChildFilesFound)), //nolint:gosec // ok
Expect(to.Result).To(
And(
HaveMetricCountOf(ExpectedMetric{
Type: enums.MetricNoFilesInvoked,
Count: entry.ExpectedNoOf.Files,
}),
HaveMetricCountOf(ExpectedMetric{
Type: enums.MetricNoFoldersInvoked,
Count: entry.ExpectedNoOf.Folders,
}),
HaveMetricCountOf(ExpectedMetric{
Type: enums.MetricNoChildFilesFound,
Count: uint(lo.Sum(lo.Values(entry.ExpectedNoOf.Children))),
}),
),
)
}
Expand Down
9 changes: 9 additions & 0 deletions internal/helpers/helper-defs.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,16 @@ type (
Children map[string]int
}

MatcherExpectation[T comparable] struct {
Expected T
Actual T
}

RecordingMap map[string]int
RecordingScopeMap map[string]enums.FilterScope
RecordingOrderMap map[string]int
)

func (x MatcherExpectation[T]) IsEqual() bool {
return x.Actual == x.Expected
}
210 changes: 114 additions & 96 deletions internal/helpers/matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import (
"strings"

. "github.com/onsi/gomega/types" //nolint:stylecheck,revive // ok
"github.com/snivilised/traverse/core"
"github.com/snivilised/traverse/enums"
"github.com/snivilised/traverse/internal/third/lo"
)

type DirectoryContentsMatcher struct {
expected interface{}
expected interface{}
expectedNames []string
actualNames []string
}

func HaveDirectoryContents(expected interface{}) GomegaMatcher {
Expand All @@ -23,53 +27,39 @@ func HaveDirectoryContents(expected interface{}) GomegaMatcher {
func (m *DirectoryContentsMatcher) Match(actual interface{}) (bool, error) {
entries, entriesOk := actual.([]fs.DirEntry)
if !entriesOk {
return false, fmt.Errorf("matcher expected []fs.DirEntry (%T)", entries)
return false, fmt.Errorf("πŸ”₯ matcher expected []fs.DirEntry (%T)", entries)
}

names := lo.Map(entries, func(entry fs.DirEntry, _ int) string {
m.actualNames = lo.Map(entries, func(entry fs.DirEntry, _ int) string {
return entry.Name()
})

expected, expectedOk := m.expected.([]string)
if !expectedOk {
return false, fmt.Errorf("matcher expected []string (%T)", expected)
return false, fmt.Errorf("πŸ”₯ matcher expected []string (%T)", expected)
}
m.expectedNames = expected

return slices.Compare(names, expected) == 0, nil
return slices.Compare(m.actualNames, m.expectedNames) == 0, nil
}

func (m *DirectoryContentsMatcher) FailureMessage(actual interface{}) string {
entries, _ := actual.([]fs.DirEntry)
names := lo.Map(entries, func(entry fs.DirEntry, _ int) string {
return entry.Name()
})
slices.Sort(names)

expected, _ := m.expected.([]string)
slices.Sort(expected)

return fmt.Sprintf("❌ Expected\n\t%v\nto match contents\n\t%v\n",
strings.Join(names, ", "), strings.Join(expected, ", "),
func (m *DirectoryContentsMatcher) FailureMessage(_ interface{}) string {
return fmt.Sprintf(
"❌ DirectoryContentsMatcher Expected\n\t%v\nto match contents\n\t%v\n",
strings.Join(m.expectedNames, ", "), strings.Join(m.actualNames, ", "),
)
}

func (m *DirectoryContentsMatcher) NegatedFailureMessage(actual interface{}) string {
entries, _ := actual.([]fs.DirEntry)
names := lo.Map(entries, func(entry fs.DirEntry, _ int) string {
return entry.Name()
})
slices.Sort(names)

expected, _ := m.expected.([]string)
slices.Sort(expected)

return fmt.Sprintf("❌ Expected\n\t%v\nNOT to match contents\n\t%v\n",
strings.Join(names, ", "), strings.Join(expected, ", "),
func (m *DirectoryContentsMatcher) NegatedFailureMessage(_ interface{}) string {
return fmt.Sprintf(
"❌ DirectoryContentsMatcher Expected\n\t%v\nNOT to match contents\n\t%v\n",
strings.Join(m.expectedNames, ", "), strings.Join(m.actualNames, ", "),
)
}

type InvokeNodeMatcher struct {
expected interface{}
expected interface{}
mandatory string
}

func HaveInvokedNode(expected interface{}) GomegaMatcher {
Expand All @@ -81,47 +71,38 @@ func HaveInvokedNode(expected interface{}) GomegaMatcher {
func (m *InvokeNodeMatcher) Match(actual interface{}) (bool, error) {
recording, ok := actual.(RecordingMap)
if !ok {
return false, fmt.Errorf("matcher expected actual to be a RecordingMap (%T)", actual)
return false, fmt.Errorf(
"InvokeNodeMatcher expected actual to be a RecordingMap (%T)",
actual,
)
}

mandatory, ok := m.expected.(string)
if !ok {
return false, fmt.Errorf("matcher expected string (%T)", actual)
return false, fmt.Errorf("InvokeNodeMatcher expected string (%T)", actual)
}
m.mandatory = mandatory

_, found := recording[mandatory]

if !found {
return false, fmt.Errorf("❌ missing invoke for node: '%v'", mandatory)
}
_, found := recording[m.mandatory]

return true, nil
return found, nil
}

func (m *InvokeNodeMatcher) FailureMessage(_ interface{}) string {
mandatory, ok := m.expected.(string)
if !ok {
return fmt.Sprintf("πŸ”₯ matcher expected string (%T)", m.expected)
}

return fmt.Sprintf("❌ Expected\n\t%v\nnode to be invoked\n",
mandatory,
m.mandatory,
)
}

func (m *InvokeNodeMatcher) NegatedFailureMessage(_ interface{}) string {
mandatory, ok := m.expected.(string)
if !ok {
return fmt.Sprintf("πŸ”₯ matcher expected string (%T)", m.expected)
}

return fmt.Sprintf("❌ Expected\n\t%v\nnode NOT to be invoked\n",
mandatory,
m.mandatory,
)
}

type NotInvokeNodeMatcher struct {
expected interface{}
expected interface{}
mandatory string
}

func HaveNotInvokedNode(expected interface{}) GomegaMatcher {
Expand All @@ -140,46 +121,37 @@ func (m *NotInvokeNodeMatcher) Match(actual interface{}) (bool, error) {
if !ok {
return false, fmt.Errorf("matcher expected string (%T)", actual)
}
m.mandatory = mandatory

_, found := recording[mandatory]
_, found := recording[m.mandatory]

if found {
return false, fmt.Errorf("❌ prohibited invoke occurred for node: '%v'", mandatory)
}

return true, nil
return !found, nil
}

func (m *NotInvokeNodeMatcher) FailureMessage(_ interface{}) string {
mandatory, ok := m.expected.(string)
if !ok {
return fmt.Sprintf("πŸ”₯ matcher expected string (%T)", m.expected)
}

return fmt.Sprintf("❌ Expected\n\t%v\nnode to NOT be invoked\n",
mandatory,
m.mandatory,
)
}

func (m *NotInvokeNodeMatcher) NegatedFailureMessage(_ interface{}) string {
mandatory, ok := m.expected.(string)
if !ok {
return fmt.Sprintf("πŸ”₯ matcher expected string (%T)", m.expected)
}

return fmt.Sprintf("❌ Expected\n\t%v\nnode to be invoked\n",
mandatory,
m.mandatory,
)
}

type ExpectedCount struct {
Name string
Count int
}
type (
ExpectedCount struct {
Name string
Count int
}

type ChildCountMatcher struct {
expected interface{}
}
ChildCountMatcher struct {
expected interface{}
expectation MatcherExpectation[uint]
name string
}
)

func HaveChildCountOf(expected interface{}) GomegaMatcher {
return &ChildCountMatcher{
Expand All @@ -190,48 +162,94 @@ func HaveChildCountOf(expected interface{}) GomegaMatcher {
func (m *ChildCountMatcher) Match(actual interface{}) (bool, error) {
recording, ok := actual.(RecordingMap)
if !ok {
return false, fmt.Errorf("matcher expected actual to be a RecordingMap (%T)", actual)
return false, fmt.Errorf("ChildCountMatcher expected actual to be a RecordingMap (%T)", actual)
}

expected, ok := m.expected.(ExpectedCount)
if !ok {
return false, fmt.Errorf("matcher expected ExpectedCount (%T)", actual)
return false, fmt.Errorf("ChildCountMatcher expected ExpectedCount (%T)", actual)
}

count, ok := recording[expected.Name]
if !ok {
return false, fmt.Errorf("πŸ”₯ not found: '%v'", expected.Name)
}

if count != expected.Count {
return false, fmt.Errorf(
"❌ incorrect child count for: '%v', actual: '%v', expected: '%v'",
expected.Name,
count, expected.Count,
)
m.expectation = MatcherExpectation[uint]{
Expected: uint(expected.Count),
Actual: uint(count),
}
m.name = expected.Name

return true, nil
return m.expectation.IsEqual(), nil
}

func (m *ChildCountMatcher) FailureMessage(_ interface{}) string {
expected, ok := m.expected.(ExpectedCount)
if !ok {
return fmt.Sprintf("πŸ”₯ matcher expected ExpectedCount (%T)", m.expected)
}

return fmt.Sprintf("❌ Expected\n\t%v\nnode to be invoked\n",
expected,
return fmt.Sprintf(
"❌ Expected child count for node: '%v' to be equal; expected: '%v', actual: '%v'\n",
m.name, m.expectation.Expected, m.expectation.Actual,
)
}

func (m *ChildCountMatcher) NegatedFailureMessage(_ interface{}) string {
expected, ok := m.expected.(ExpectedCount)
return fmt.Sprintf(
"❌ Expected child count for node: '%v' NOT to be equal; expected: '%v', actual: '%v'\n",
m.name, m.expectation.Expected, m.expectation.Actual,
)
}

type (
ExpectedMetric struct {
Type enums.Metric
Count uint
}

MetricMatcher struct {
expected interface{}
expectation MatcherExpectation[uint]
typ enums.Metric
}
)

func HaveMetricCountOf(expected interface{}) GomegaMatcher {
return &MetricMatcher{
expected: expected,
}
}

func (m *MetricMatcher) Match(actual interface{}) (bool, error) {
result, ok := actual.(core.TraverseResult)
if !ok {
return fmt.Sprintf("πŸ”₯ matcher expected ExpectedCount (%T)", m.expected)
return false, fmt.Errorf(
"πŸ”₯ MetricMatcher expected actual to be a core.TraverseResult (%T)",
actual,
)
}

return fmt.Sprintf("❌ Expected\n\t%v\nnode NOT to be invoked\n",
expected,
expected, ok := m.expected.(ExpectedMetric)
if !ok {
return false, fmt.Errorf("πŸ”₯ MetricMatcher expected ExpectedMetric (%T)", actual)
}

m.expectation = MatcherExpectation[uint]{
Expected: expected.Count,
Actual: result.Metrics().Count(expected.Type),
}
m.typ = expected.Type

return m.expectation.IsEqual(), nil
}

func (m *MetricMatcher) FailureMessage(_ interface{}) string {
return fmt.Sprintf(
"❌ Expected metric '%v' to be equal; expected:'%v', actual: '%v'\n",
m.typ.String(), m.expectation.Expected, m.expectation.Actual,
)
}

func (m *MetricMatcher) NegatedFailureMessage(_ interface{}) string {
return fmt.Sprintf(
"❌ Expected metric '%v' NOT to be equal; expected:'%v', actual: '%v'\n",
m.typ.String(), m.expectation.Expected, m.expectation.Actual,
)
}

0 comments on commit 266b0f3

Please sign in to comment.