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(rx): add factory connectable tests (#139) #140

Merged
merged 1 commit into from
Apr 4, 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
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"dupl",
"emirpasic",
"errcheck",
"errgroup",
"exportloopref",
"extendio",
"fieldalignment",
Expand All @@ -36,6 +37,7 @@
"goreleaser",
"gosec",
"gosimple",
"gotn",
"goveralls",
"govet",
"graffico",
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ require (
github.com/teivah/onecontext v1.3.0
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
golang.org/x/net v0.20.0 // indirect
golang.org/x/sync v0.6.0
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.17.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
Expand Down
149 changes: 131 additions & 18 deletions rx/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,49 @@ type RxAssert[T any] interface { //nolint:revive // foo
itemsNoOrderedToBeChecked() (bool, []T)
noItemsToBeChecked() bool
someItemsToBeChecked() bool
numbersToBeChecked() (bool, []int)
numbersNoOrderedToBeChecked() (bool, []int)
noNumbersToBeChecked() bool
someNumbersToBeChecked() bool
raisedErrorToBeChecked() (bool, error)
raisedErrorsToBeChecked() (bool, []error)
raisedAnErrorToBeChecked() (bool, error)
notRaisedErrorToBeChecked() bool
itemToBeChecked() (bool, T)
noItemToBeChecked() (bool, T)
numberToBeChecked() (b bool, i int)
noNumberToBeChecked() (b bool, i int)
customPredicatesToBeChecked() (bool, []AssertPredicate[T])
}

type rxAssert[T any] struct {
f func(*rxAssert[T])
checkHasItems bool
checkHasNoItems bool
checkHasSomeItems bool
items []T
checkHasItemsNoOrder bool
itemsNoOrder []T
f func(*rxAssert[T])
checkHasItems bool
checkHasNoItems bool
checkHasSomeItems bool
items []T
checkHasItemsNoOrder bool
itemsNoOrder []T

checkHasNumbers bool
checkHasNoNumbers bool
checkHasSomeNumbers bool
numbers []int
checkHasNumbersNoOrder bool
numbersNoOrder []int

checkHasRaisedError bool
err error
checkHasRaisedErrors bool
errs []error
checkHasRaisedAnError bool
checkHasNotRaisedError bool
checkHasItem bool
checkHasNumber bool
item T
number int
checkHasNoItem bool
checkHasNoNumber bool
checkHasCustomPredicate bool
customPredicates []AssertPredicate[T]
}
Expand All @@ -67,6 +84,23 @@ func (ass *rxAssert[T]) noItemsToBeChecked() bool {
func (ass *rxAssert[T]) someItemsToBeChecked() bool {
return ass.checkHasSomeItems
}

func (ass *rxAssert[T]) numbersToBeChecked() (b bool, i []int) {
return ass.checkHasNumbers, ass.numbers
}

func (ass *rxAssert[T]) numbersNoOrderedToBeChecked() (b bool, i []int) {
return ass.checkHasNumbersNoOrder, ass.numbersNoOrder
}

func (ass *rxAssert[T]) noNumbersToBeChecked() bool {
return ass.checkHasNoNumbers
}

func (ass *rxAssert[T]) someNumbersToBeChecked() bool {
return ass.checkHasSomeNumbers
}

func (ass *rxAssert[T]) raisedErrorToBeChecked() (bool, error) {
return ass.checkHasRaisedError, ass.err
}
Expand All @@ -91,6 +125,14 @@ func (ass *rxAssert[T]) noItemToBeChecked() (b bool, i T) {
return ass.checkHasNoItem, ass.item
}

func (ass *rxAssert[T]) numberToBeChecked() (b bool, i int) {
return ass.checkHasNumber, ass.number
}

func (ass *rxAssert[T]) noNumberToBeChecked() (b bool, i int) {
return ass.checkHasNoNumber, ass.number
}

func (ass *rxAssert[T]) customPredicatesToBeChecked() (bool, []AssertPredicate[T]) {
return ass.checkHasCustomPredicate, ass.customPredicates
}
Expand All @@ -111,9 +153,12 @@ func parseAssertions[T any](assertions ...RxAssert[T]) RxAssert[T] {
return ass
}

func Assert[T any](ctx context.Context, iterable Iterable[T], assertions ...RxAssert[T]) {
func Assert[T any](ctx context.Context, iterable Iterable[T], assertions ...RxAssert[T]) { //nolint:gocyclo // to be fixed
// TODO(fix): cyclo complexity of this function is too high, needs a refactoring
//
ass := parseAssertions(assertions...)
got := make([]T, 0)
gotN := make([]int, 0)
errs := make([]error, 0)
observe := iterable.Observe()

Expand All @@ -127,19 +172,26 @@ loop:
break loop
}

// TODO: needs to accommodate item.N, ie the numeric aux value
// and also should be modified to support all the other
// new ways of interpreting an item (Ch, Tick, Tv), possibly
// with new assertions, ie: HasCh, HasTick, HasTv.
//
if item.IsError() {
switch {
case item.IsError():
errs = append(errs, item.E)
} else {

case item.IsNumeric():
gotN = append(gotN, item.N)

default:
got = append(got, item.V)
}
}
}

// TODO: I wonder if we can re-design this somewhat. The problem with this current
// implementation, is that it speculatively checks the conditions on the Assert
// object to determine wether or not to invoke the check. If we delegated the
// checking to the assertions and then iterate the assertions, that would
// surely be a better option. For now, there is quite a bit of cloned code
// just waiting to be cleaned up.

if checked, predicates := ass.customPredicatesToBeChecked(); checked {
for _, predicate := range predicates {
err := predicate(got)
Expand All @@ -153,6 +205,10 @@ loop:
Expect(got).To(ContainElements(expectedItems))
}

if checkHasNumbers, expectedItems := ass.numbersToBeChecked(); checkHasNumbers {
Expect(gotN).To(ContainElements(expectedItems))
}

if checkHasItemsNoOrder, itemsNoOrder := ass.itemsNoOrderedToBeChecked(); checkHasItemsNoOrder {
m := make(map[interface{}]interface{})
for _, v := range itemsNoOrder {
Expand All @@ -168,6 +224,21 @@ loop:
}
}

if checkHasNumbersNoOrder, numbersNoOrder := ass.numbersNoOrderedToBeChecked(); checkHasNumbersNoOrder {
m := make(map[int]int)
for _, v := range numbersNoOrder { // what is this loop doing?
m[v] = 0 // ?? nil
}

for _, v := range gotN {
delete(m, v)
}

if len(m) != 0 {
Fail(fmt.Sprintf("missing elements: '%v'", gotN))
}
}

if checkHasItem, value := ass.itemToBeChecked(); checkHasItem {
length := len(got)
if length != 1 {
Expand All @@ -179,14 +250,33 @@ loop:
}
}

if checkHasNumber, value := ass.numberToBeChecked(); checkHasNumber {
length := len(gotN)
if length != 1 {
Fail(fmt.Sprintf("wrong number of items, expected 1, got %d", length))
}

if length > 0 {
Expect(gotN[0]).To(Equal(value))
}
}

if ass.noItemsToBeChecked() {
Expect(got).To(BeEmpty())
}

if ass.noNumbersToBeChecked() {
Expect(gotN).To(BeEmpty())
}

if ass.someItemsToBeChecked() {
Expect(got).NotTo(BeEmpty())
}

if ass.someNumbersToBeChecked() {
Expect(gotN).NotTo(BeEmpty())
}

if checkHasRaisedError, expectedError := ass.raisedErrorToBeChecked(); checkHasRaisedError {
if expectedError == nil {
Expect(errs).To(BeEmpty())
Expand Down Expand Up @@ -231,11 +321,34 @@ func HasItem[T any](i T) RxAssert[T] {
})
}

func HasNumbers[T any](expectedNumbers []int) RxAssert[T] {
return newAssertion(func(ra *rxAssert[T]) {
ra.checkHasNumbers = true
ra.numbers = expectedNumbers
})
}

// HasItem checks if a single or optional single has a specific item.
func HasNumber[T any](i int) RxAssert[T] {
return newAssertion(func(a *rxAssert[T]) {
a.checkHasNumber = true
a.number = i
})
}

// HasItemsNoOrder checks that an observable produces the corresponding items regardless of the order.
func HasItemsNoOrder[T any](items ...T) RxAssert[T] {
func HasItemsNoOrder[T any](numbers ...T) RxAssert[T] {
return newAssertion(func(a *rxAssert[T]) {
a.checkHasItemsNoOrder = true
a.itemsNoOrder = items
a.itemsNoOrder = numbers
})
}

// HasNumbersNoOrder checks that an observable produces the corresponding items regardless of the order.
func HasNumbersNoOrder[T any](numbers ...int) RxAssert[T] {
return newAssertion(func(a *rxAssert[T]) {
a.checkHasNumbersNoOrder = true
a.numbersNoOrder = numbers
})
}

Expand All @@ -246,7 +359,7 @@ func IsNotEmpty[T any]() RxAssert[T] {
})
}

// IsEmpty checks that the observable has not produce any item.
// IsEmpty checks that the observable has not produced any items.
func IsEmpty[T any]() RxAssert[T] {
return newAssertion(func(a *rxAssert[T]) {
a.checkHasNoItems = true
Expand Down
Loading
Loading