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

test(helpers): define custom matchers for assert metrics (#172) #175

Merged
merged 1 commit into from
Sep 8, 2024
Merged
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
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,
)
}
Loading