From 768118448065056236e1e3067d6703a0ff34178c Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 24 Jan 2024 01:32:28 +0200 Subject: [PATCH] updates on docs --- README.md | 207 ++++++++++++++++++---------- be_ctx/README.md | 41 ++++++ be_ctx/matchers_ctx.go | 1 + be_http/README.md | 127 ++++++++++++++++++ be_http/be.md | 0 be_http/matchers_http.go | 2 + be_json/README.md | 50 +++++++ be_json/matchers_json.go | 2 + be_jwt/README.md | 100 ++++++++++++++ be_math/README.md | 137 +++++++++++++++++++ be_math/matchers_math.go | 1 + be_reflected/README.md | 209 +++++++++++++++++++++++++++++ be_reflected/matchers_reflected.go | 4 +- be_strings/README.md | 134 ++++++++++++++++++ be_strings/matchers_string.go | 2 +- be_time/README.md | 142 ++++++++++++++++++++ be_time/matchers_time.go | 1 + be_url/README.md | 162 ++++++++++++++++++++++ be_url/matchers_url.go | 1 + core-be-matchers.md | 82 +++++++++++ matchers.go | 4 + 21 files changed, 1333 insertions(+), 76 deletions(-) create mode 100644 be_ctx/README.md create mode 100644 be_http/README.md create mode 100644 be_http/be.md create mode 100644 be_json/README.md create mode 100644 be_jwt/README.md create mode 100644 be_math/README.md create mode 100644 be_reflected/README.md create mode 100644 be_strings/README.md create mode 100644 be_time/README.md create mode 100644 be_url/README.md create mode 100644 core-be-matchers.md diff --git a/README.md b/README.md index 51dce82..f60dd9c 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,10 @@ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/expectto/be/blob/main/LICENSE) [![Go Reference](https://pkg.go.dev/badge/github.com/expectto/be.svg)](https://pkg.go.dev/github.com/expectto/be) -`expectto/be` is a Golang package that offers a substantial collection of `Be` matchers. Every `Be` matcher is compatible with both [Ginkgo](https://github.com/onsi/ginkgo)/[Gomega](https://github.com/onsi/gomega) -and [Gomock](https://github.com/uber-go/mock). Where possible, arguments of matchers can be either finite values or matchers (Be/Gomega/Gomock).
+`expectto/be` is a Golang package that offers a substantial collection of `Be` matchers. Every `Be` matcher is +compatible with both [Ginkgo](https://github.com/onsi/ginkgo)/[Gomega](https://github.com/onsi/gomega) +and [Gomock](https://github.com/uber-go/mock). Where possible, arguments of matchers can be either finite values or +matchers (Be/Gomega/Gomock).
Employing `expectto/be` matchers enables you to create straightforward, readable, and maintainable unit or integration tests in Golang. Tasks such as testing HTTP requests, validating JSON responses, and more become remarkably comprehensive and straightforward. @@ -47,111 +49,170 @@ Expect(err).To(Succeed()) // Matching an HTTP request Expect(req).To(be_http.Request( - // Matching the URL - be_http.HavingURL(be_url.URL( - be_url.WithHttps(), - be_url.HavingHost("example.com"), - be_url.HavingPath("/path"), - be_url.HavingSearchParam("status", "active"), - be_url.HavingSearchParam("v", be_reflected.AsNumericString()), - be_url.HavingSearchParam("q", "Hello World"), - )), - - // Matching the HTTP method - be_http.HavingMethod(http.MethodPost), - - // Matching request's context - be_http.HavingCtx(be_ctx.Ctx( - be_ctx.WithDeadline(be_time.LaterThan(time.Now().Add(30*time.Minute))), - be_ctx.WithValue("foobar", 100), - )) - - // Matching the request body using JSON matchers - be_http.HavingBody( - be_json.Matcher( - be_json.JsonAsReader, - be_json.HaveKeyValue("hello", "world"), - be_json.HaveKeyValue("n", be_reflected.AsIntish()), - be_json.HaveKeyValue("ids", be_reflected.AsSliceOf[string]), - be_json.HaveKeyValue("details", And( - be_reflected.AsObjects(), - be.HaveLength(2), - ContainElements( - be_json.HaveKeyValue("key", "foo"), - be_json.HaveKeyValue("key", "bar"), - ), - )), - ), - - // Matching HTTP headers - be_http.HavingHeader("X-Custom", "Hey-There"), - be_http.HavingHeader("Authorization", - be_strings.Template("Bearer {{jwt}}", - be_strings.MatchingPart("jwt", - be_jwt.Token( - be_jwt.BeingValid(), - be_jwt.HavingClaims("name", "John Doe"), - ), - ), - ), - ), - ), +// Matching the URL +be_http.HavingURL(be_url.URL( +be_url.WithHttps(), +be_url.HavingHost("example.com"), +be_url.HavingPath("/path"), +be_url.HavingSearchParam("status", "active"), +be_url.HavingSearchParam("v", be_reflected.AsNumericString()), +be_url.HavingSearchParam("q", "Hello World"), +)), + +// Matching the HTTP method +be_http.HavingMethod(http.MethodPost), + +// Matching request's context +be_http.HavingCtx(be_ctx.Ctx( +be_ctx.WithDeadline(be_time.LaterThan(time.Now().Add(30*time.Minute))), +be_ctx.WithValue("foobar", 100), +)) + +// Matching the request body using JSON matchers +be_http.HavingBody( +be_json.Matcher( +be_json.JsonAsReader, +be_json.HaveKeyValue("hello", "world"), +be_json.HaveKeyValue("n", be_reflected.AsIntish()), +be_json.HaveKeyValue("ids", be_reflected.AsSliceOf[string]), +be_json.HaveKeyValue("details", And( +be_reflected.AsObjects(), +be.HaveLength(2), +ContainElements( +be_json.HaveKeyValue("key", "foo"), +be_json.HaveKeyValue("key", "bar"), +), +)), +), + +// Matching HTTP headers +be_http.HavingHeader("X-Custom", "Hey-There"), +be_http.HavingHeader("Authorization", +be_strings.Template("Bearer {{jwt}}", +be_strings.MatchingPart("jwt", +be_jwt.Token( +be_jwt.BeingValid(), +be_jwt.HavingClaims("name", "John Doe"), +), +), +), +), +), )) ``` ## Matchers -### (core) be -
- Be provides a set of core matchers for common testing scenarios: +### Core Be matchers + +📦 `be` provides a set of core matchers for common testing scenarios.
[See detailed docs](core-be-matchers.md) -| Matcher | Example Usage | Description | -|------------------------------|---------------------------------------------------------------|---------------------------------------------------------------------------------------| -| `be.Always()` | `Expect(anything()).To(be.Always())` | Always succeeds (passes). | -| `be.Never(err)` | `Expect(anything()).To(be.Never(errors.New("custom error")))` | Never succeeds and always fails with a specified error | -| `be.All(ms ...any)` | `Expect(m).To(be.All(HaveKey("foo"), HaveKey("bar"), ...))` | Logical AND for multiple matchers. _Similar to Ginkgo's`And()`_ | -| `be.Any(ms ...any)` | `Expect(m).To(be.Any(HaveKey("foo"), HaveKey("bar"), ...)` | Logical OR for multiple matchers. _Similar to Ginkgo's `Or()`_ | -| `be.Eq(expected)` | `Expect(v).To(be.Eq(expectedValue))` | Checks for equality. _Similar to Ginkgo's `Equal` _ | -| `be.Not(matcher)` | `Expect(v).To(be.Not(anotherMatcher))` | Negates the result of another matcher. _Similar to Ginkgo's `Not()`_ | -| `be.HaveLength(args ...any)` | `Expect(collection).To(be.HaveLength(lengthMatcher))` | Matches the length of slices, arrays, strings, or maps. Supports matchers as argument | -
+#### Core matchers: +`Always`, `Never`, `All`, `Any`, `Eq`, `Not`, `HaveLength` ### be_reflected -AssignableTo(), Implementing(), AsKind(), ... +📦 `be_reflected` provides Be matchers that use reflection, enabling expressive assertions on values' reflect kinds and +types.
[See detailed docs](be_reflected/README.md) + +#### General Matchers based on reflect.Kind: + +`AsKind`, `AsFunc`, `AsChan`, `AsPointer`, `AsFinalPointer`, `AsStruct`, `AsPointerToStruct`, `AsSlice`, `AsPointerToSlice`, `AsSliceOf`, `AsMap`, `AsPointerToMap`, `AsObject`, `AsObjects`, `AsPointerToObject` + +#### Data Type Matchers based on reflect.Kind + +`AsString`, `AsBytes`, `AsNumeric`, `AsNumericString`, `AsIntish`, `AsIntishString`, `AsFloatish`, `AsFloatishString`, + +#### Interface Matchers based on reflect.Kind + +`AsReader`,`AsStringer` + +#### Matchers based on types compatibility: + +`AssignableTo`, `Implementing` ### be_math -GreaterThan(), GreaterLessThan(), ... +📦 `be_math` provides Be matchers for mathematical operations.
[See detailed docs](be_math/README.md) + +#### Matchers on math: + +`GreaterThan`, `GreaterThanEqual`, `LessThan`, `LessThanEqual`, `Approx`, `InRange`, `Odd`, `Even`, `Negative`, `Positive`, `Zero`, `Integral`, `DivisibleBy` + +#### Shortcut aliases for math matchers: + +`Gt`, `Gte`, `Lt`, `Lte` ### be_strings -EmptyString(), NonEmptyString(), Alpha(), ... +📦 `be_strings` provides Be matchers for string-related assertions.
[See detailed docs](be_strings/README.md) + +#### Matchers on strings + +`NonEmptyString`, `EmptyString`, `Alpha`, `Numeric`, `AlphaNumeric`, `AlphaNumericWithDots`, `Float`, `Titled`, `LowerCaseOnly`, `MatchWildcard`, `ValidEmail` + +#### Template matchers + +`MatchTemplate` ### be_time -LaterThan(), LaterThanEqual(), EarlierThan(), ... +📦 `be_time` provides Be matchers on time.Time.
[See detailed docs](be_time/README.md) + +#### Time Matchers + +`LaterThan`, `LaterThanEqual`, `EarlierThan`, `EarlierThanEqual`, `Approx`, `SameNano`, `SameSecond`, `SameMinute`, `SameHour`, `SameTimezone`, `SameOffset`, `IsDST`, `SameDay`, `SameWeekday`, `SameWeek`, `SameMonth`, `SameYear` ### be_jwt -Token(), HavingClaims(), ... +📦 `be_jwt` provides Be matchers for handling JSON Web Tokens (JWT). It includes matchers for transforming and validating +JWT tokens. Matchers corresponds to specific +golang [jwt implementation](https://github.com/golang-jwt/jwt/v5).
[See detailed docs](be_jwt/README.md) + +#### Transformers for JWT matching: + +`TransformSignedJwtFromString`, `TransformJwtFromString` + +#### Matchers on JWT: + +`Token`, `Valid`, `HavingClaims`, `HavingMethodAlg`, `SignedVia` ### be_url -URL() HavingHost(), HavingHostname(), ... +📦 `be_url` provides Be matchers on url.URL.
[See detailed docs](be_jwt/README.md) + +#### Transformers for URL Matchers: + +`TransformUrlFromString`, `TransformSchemelessUrlFromString` + +#### URL Matchers: + +`URL`, `HavingHost`, `HavingHostname`, `HavingScheme`, `NotHavingScheme`, `WithHttps`, `WithHttp`, `HavingPort`, `NotHavingPort`, `HavingPath`, `HavingRawQuery`, `HavingSearchParam`, `HavingMultipleSearchParam`, `HavingUsername`, `HavingUserinfo`, `HavingPassword` ### be_ctx -Ctx(), CtxWithValue(), CtxWithDeadline(), ... +📦 `be_ctx` provides Be matchers on context.Context.
[See detailed docs](be_ctx/README.md) + +#### Context Matchers: + +`Ctx`, `CtxWithValue`, `CtxWithDeadline`, `CtxWithError` ### be_json -Matcher(), HaveKeyWithValue(), ... +📦 `be_json` provides Be matchers for expressive assertions on JSON.
[See detailed docs](be_json/README.md) + +#### JSON Matchers: + +`Matcher`, `HaveKeyValue` ### be_http -Request(), HavingMethod(), HavingUrl(), ... +📦 `be_http` provides Be matchers for expressive assertions on http.Request.
[See detailed docs](be_http/README.md) + +#### Matchers on HTTP: + +`Request`, `HavingMethod`, `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, `HavingURL`, `HavingBody`, `HavingHost`, `HavingProto`, `HavingHeader` # Contributing diff --git a/be_ctx/README.md b/be_ctx/README.md new file mode 100644 index 0000000..59f47d2 --- /dev/null +++ b/be_ctx/README.md @@ -0,0 +1,41 @@ +# be_ctx +-- + import "github.com/expectto/be/be_ctx" + +Package be_ctx provides Be matchers on context.Context + +## Usage + +#### func Ctx + +```go +func Ctx(args ...any) types.BeMatcher +``` +Ctx succeeds if the actual value is a context.Context. If no arguments are +provided, it matches any context.Context. Otherwise, it uses the Psi matcher to +match the provided arguments against the actual context's values. + +#### func CtxWithDeadline + +```go +func CtxWithDeadline(deadline any) types.BeMatcher +``` +CtxWithDeadline succeeds if the actual value is a context.Context and its +deadline matches the provided deadline. + +#### func CtxWithError + +```go +func CtxWithError(err any) types.BeMatcher +``` +CtxWithError succeeds if the actual value is a context.Context and its error +matches the provided error value. + +#### func CtxWithValue + +```go +func CtxWithValue(key any, vs ...any) types.BeMatcher +``` +CtxWithValue succeeds if the actual value is a context.Context and contains a +key-value pair where the key matches the provided key and the value matches the +provided arguments using any other matchers. diff --git a/be_ctx/matchers_ctx.go b/be_ctx/matchers_ctx.go index 4998d94..73264cd 100644 --- a/be_ctx/matchers_ctx.go +++ b/be_ctx/matchers_ctx.go @@ -1,3 +1,4 @@ +// Package be_ctx provides Be matchers on context.Context package be_ctx import ( diff --git a/be_http/README.md b/be_http/README.md new file mode 100644 index 0000000..007aec0 --- /dev/null +++ b/be_http/README.md @@ -0,0 +1,127 @@ +# be_http +-- + import "github.com/expectto/be/be_http" + +Package be_http provides matchers for url.Request todo: more detailed +documentation here is required + +## Usage + +#### func DELETE + +```go +func DELETE() types.BeMatcher +``` +DELETE returns a matcher that succeeds if the actual *http.Request has a method +"DELETE". + +#### func GET + +```go +func GET() types.BeMatcher +``` +GET returns a matcher that succeeds if the actual *http.Request has a method +"GET". + +#### func HavingBody + +```go +func HavingBody(args ...any) types.BeMatcher +``` +HavingBody succeeds if the actual value is a *http.Request and its body matches +the provided arguments. Note: The body is not re-streamed, so it's not available +after matching. + +#### func HavingHeader + +```go +func HavingHeader(key string, args ...any) types.BeMatcher +``` +HavingHeader matches requests that have header with a given key. (1) If no args +are given, it simply matches a request with existed header by key. (2) If +len(args) == 1 && args[0] is a stringish, it matches a request with header `Key: +Args[0]` (3) if len(args) == 1 && args[0] is not stringish, it is considered to +be matcher for header's value Examples: - HavingHeader("X-Header") matches +request with non-empty X-Header header - HavingHeader("X-Header", "X-Value") +matches request with X-Header: X-Value - HavingHeader("X-Header", +HavePrefix("Bearer ")) matchers request with header(X-Header)'s value matching +given HavePrefix matcher - todo: support multiple header values todo: fixme I'm +ugly for now + +#### func HavingHost + +```go +func HavingHost(args ...any) types.BeMatcher +``` +HavingHost succeeds if the actual value is a *http.Request and its Host matches +the provided arguments. + +#### func HavingMethod + +```go +func HavingMethod(args ...any) types.BeMatcher +``` +HavingMethod succeeds if the actual value is a *http.Request and its HTTP method +matches the provided arguments. + +#### func HavingProto + +```go +func HavingProto(args ...any) types.BeMatcher +``` +HavingProto succeeds if the actual value is a *http.Request and its Proto +matches the provided arguments. + +#### func HavingURL + +```go +func HavingURL(args ...any) types.BeMatcher +``` +HavingURL succeeds if the actual value is a *http.Request and its URL matches +the provided arguments. + +#### func OPTIONS + +```go +func OPTIONS() types.BeMatcher +``` +OPTIONS returns a matcher that succeeds if the actual *http.Request has a method +"OPTIONS". + +#### func PATCH + +```go +func PATCH() types.BeMatcher +``` +PATCH returns a matcher that succeeds if the actual *http.Request has a method +"PATCH". + +#### func POST + +```go +func POST() types.BeMatcher +``` +POST returns a matcher that succeeds if the actual *http.Request has a method +"POST". + +#### func PUT + +```go +func PUT() types.BeMatcher +``` +PUT returns a matcher that succeeds if the actual *http.Request has a method +"PUT". + +#### func Request + +```go +func Request(args ...any) types.BeMatcher +``` +Request matches an actual value to be a valid *http.Request corresponding to +given inputs. Possible inputs: 1. Nil args -> so actual value MUST be any valid +*http.Request. 2. Single arg . Actual value MUST be a *http.Request, +whose .URL.String() is compared against args[0]. 3. List of Omega/Gomock/Psi +matchers, that are applied to *http.Request object. + + - Supports matching http.Request properties like method, URL, body, host, proto, and headers. + - Additional arguments can be used for matching specific headers, e.g., WithHeader("Content-Type", "application/json"). diff --git a/be_http/be.md b/be_http/be.md new file mode 100644 index 0000000..e69de29 diff --git a/be_http/matchers_http.go b/be_http/matchers_http.go index 7220244..4a13c1c 100644 --- a/be_http/matchers_http.go +++ b/be_http/matchers_http.go @@ -1,3 +1,5 @@ +// Package be_http provides matchers for url.Request +// todo: more detailed documentation here is required package be_http import ( diff --git a/be_json/README.md b/be_json/README.md new file mode 100644 index 0000000..d7b7de4 --- /dev/null +++ b/be_json/README.md @@ -0,0 +1,50 @@ +# be_json +-- + import "github.com/expectto/be/be_json" + +Package be_json provides Be matchers for expressive assertions on JSON TODO: +more detailed explanation what is considered to be JSON here + +## Usage + +#### func HaveKeyValue + +```go +func HaveKeyValue(key any, args ...any) types.BeMatcher +``` +HaveKeyValue is a facade to gomega.HaveKey & gomega.HaveKeyWithValue + +#### func Matcher + +```go +func Matcher(args ...any) types.BeMatcher +``` +Matcher is a JSON matcher. "JSON" here means a []byte with JSON data in it By +default several input types are available: string(*) / []byte(*), fmt.Stringer, +io.Reader + + - custom string-based or []byte-based types are available as well + +To make it stricter and to specify which format JSON we should expect, you must +pass one of transforms as first argument: + + - JsonAsBytes/ JsonAsString / JsonAsStringer / JsonAsReader (for string-like representation) + - JsonAsObject / JsonAsObjects (for map[string]any representation) + +#### type JsonInputType + +```go +type JsonInputType uint32 +``` + + +```go +const ( + JsonAsBytes JsonInputType = 1 << iota + JsonAsString + JsonAsStringer + JsonAsReader + JsonAsObject + JsonAsObjects +) +``` diff --git a/be_json/matchers_json.go b/be_json/matchers_json.go index 04e7424..183c60e 100644 --- a/be_json/matchers_json.go +++ b/be_json/matchers_json.go @@ -1,3 +1,5 @@ +// Package be_json provides Be matchers for expressive assertions on JSON +// TODO: more detailed explanation what is considered to be JSON here package be_json import ( diff --git a/be_jwt/README.md b/be_jwt/README.md new file mode 100644 index 0000000..18a8b11 --- /dev/null +++ b/be_jwt/README.md @@ -0,0 +1,100 @@ +# be_jwt +-- + import "github.com/expectto/be/be_jwt" + +Package be_jwt provides Be matchers for handling JSON Web Tokens (JWT). It +includes matchers for transforming and validating JWT tokens. Matchers +corresponds to specific golang jwt implementation: +https://github.com/golang-jwt/jwt/v5 + +## Usage + +```go +var TransformJwtFromString = func(input string) any { + p := jwt.NewParser() + + t, parts, err := p.ParseUnverified(input, jwt.MapClaims{}) + if err != nil { + return NewTransformError(err, input) + } + + t.Signature, err = p.DecodeSegment(parts[2]) + if err != nil { + return NewTransformError(fmt.Errorf("corrupted signature part: %w", err), input) + } + + return t +} +``` +TransformJwtFromString is a transform function (string->*jwt.Token) without a +secret. It parses the input string as a JWT and returns the resulting +*jwt.Token. + +```go +var TransformSignedJwtFromString = func(secret string) func(string) any { + return func(input string) any { + parsed, err := jwt.Parse(input, func(token *jwt.Token) (any, error) { + return []byte(secret), nil + }) + if err != nil { + return NewTransformError(fmt.Errorf("to parse jwt token (with secret=%s): %w", secret, err), input) + } + + return parsed + } +} +``` +TransformSignedJwtFromString returns a transform function (string->*jwt.Token) +for a given secret. + +#### func HavingClaims + +```go +func HavingClaims(args ...any) types.BeMatcher +``` +HavingClaims succeeds if the actual value is a JWT token and its claims match +the provided value or matchers. + +#### func HavingMethodAlg + +```go +func HavingMethodAlg(args ...any) types.BeMatcher +``` +HavingMethodAlg succeeds if the actual value is a JWT token and its method +algorithm match the provided value or matchers. + +#### func SignedVia + +```go +func SignedVia(secret string) types.BeMatcher +``` +SignedVia succeeds if the actual value is a valid and signed JWT token, verified +using the specified secret key. It's intended for matching against a secret-less +token and applying the secret only for this specific matching. + +Example: + +Token(TransformJwtFromString, SignedVia(secret)) // works similar to: +Token(TransformSignedJwtFromString(secret), Valid()) + +#### func Token + +```go +func Token(args ...any) types.BeMatcher +``` +Token matches the actual value to be a valid *jwt.Token corresponding to given +inputs. Possible inputs: 1. No args -> the actual value MUST be any valid +*jwt.Token. 2. Single arg . The actual value MUST be a *jwt.Token, whose +.String() is compared against args[0]. 3. Single arg <*jwt.Token>. The actual +value MUST be a *jwt.Token. 4. List of Omega/Gomock/Psi matchers that are +applied to *jwt.Token object. + + - TransformJwtFromString/TransformSignedJwtFromString(secret) transforms can be given as the first argument, + so the string->*jwt.Token transform is applied. + +#### func Valid + +```go +func Valid() types.BeMatcher +``` +Valid succeeds if the actual value is a JWT token and it's valid diff --git a/be_math/README.md b/be_math/README.md new file mode 100644 index 0000000..c3fe0ed --- /dev/null +++ b/be_math/README.md @@ -0,0 +1,137 @@ +# be_math +-- + import "github.com/expectto/be/be_math" + +Package be_math provides Be matchers for mathematical operations + +## Usage + +#### func Approx + +```go +func Approx(compareTo, threshold any) types.BeMatcher +``` +Approx succeeds if actual is numerically approximately equal to the passed-in +value within the specified threshold. + +#### func DivisibleBy + +```go +func DivisibleBy(divisor any) types.BeMatcher +``` +DivisibleBy succeeds if actual is numerically divisible by the passed-in value. + +#### func Even + +```go +func Even() types.BeMatcher +``` +Even succeeds if actual is an even numeric value. + +#### func GreaterThan + +```go +func GreaterThan(arg any) types.BeMatcher +``` +GreaterThan succeeds if actual is numerically greater than the passed-in value. + +#### func GreaterThanEqual + +```go +func GreaterThanEqual(arg any) types.BeMatcher +``` +GreaterThanEqual succeeds if actual is numerically greater than or equal to the +passed-in value. + +#### func Gt + +```go +func Gt(arg any) types.BeMatcher +``` +Gt is an alias for GreaterThan, succeeding if actual is numerically greater than +the passed-in value. + +#### func Gte + +```go +func Gte(arg any) types.BeMatcher +``` +Gte is an alias for GreaterThanEqual, succeeding if actual is numerically +greater than or equal to the passed-in value. + +#### func InRange + +```go +func InRange(from any, fromInclusive bool, until any, untilInclusive bool) types.BeMatcher +``` +InRange succeeds if actual is numerically within the specified range. The range +is defined by the 'from' and 'until' values, and inclusivity is determined by +the 'fromInclusive' and 'untilInclusive' flags. + +#### func Integral + +```go +func Integral() types.BeMatcher +``` +Integral succeeds if actual is an integral float, meaning it has zero decimal +places. This matcher checks if the numeric value has no fractional component. + +#### func LessThan + +```go +func LessThan(arg any) types.BeMatcher +``` +LessThan succeeds if actual is numerically less than the passed-in value. + +#### func LessThanEqual + +```go +func LessThanEqual(arg any) types.BeMatcher +``` +LessThanEqual succeeds if actual is numerically less than or equal to the +passed-in value. + +#### func Lt + +```go +func Lt(arg any) types.BeMatcher +``` +Lt is an alias for LessThan, succeeding if actual is numerically less than the +passed-in value. + +#### func Lte + +```go +func Lte(arg any) types.BeMatcher +``` +Lte is an alias for LessThanEqual, succeeding if actual is numerically less than +or equal to the passed-in value. + +#### func Negative + +```go +func Negative() types.BeMatcher +``` +Negative succeeds if actual is a negative numeric value. + +#### func Odd + +```go +func Odd() types.BeMatcher +``` +Odd succeeds if actual is an odd numeric value. + +#### func Positive + +```go +func Positive() types.BeMatcher +``` +Positive succeeds if actual is a positive numeric value. + +#### func Zero + +```go +func Zero() types.BeMatcher +``` +Zero succeeds if actual is numerically approximately equal to zero. Any type of +int/float will work for comparison. diff --git a/be_math/matchers_math.go b/be_math/matchers_math.go index b7cc7f7..7e30459 100644 --- a/be_math/matchers_math.go +++ b/be_math/matchers_math.go @@ -1,3 +1,4 @@ +// Package be_math provides Be matchers for mathematical operations package be_math import ( diff --git a/be_reflected/README.md b/be_reflected/README.md new file mode 100644 index 0000000..c46dc44 --- /dev/null +++ b/be_reflected/README.md @@ -0,0 +1,209 @@ +# be_reflected +-- + import "github.com/expectto/be/be_reflected" + +Package be_reflected provides Be matchers that use reflection, enabling +expressive assertions on values' reflect kinds and types. + +## Usage + +#### func AsBytes + +```go +func AsBytes() types.BeMatcher +``` +AsBytes succeeds if actual is assignable to a slice of bytes ([]byte). + +#### func AsChan + +```go +func AsChan() types.BeMatcher +``` +AsChan succeeds if actual is of kind reflect.Chan. + +#### func AsFinalPointer + +```go +func AsFinalPointer() types.BeMatcher +``` +AsFinalPointer succeeds if the actual value is a final pointer, meaning it's a +pointer to a non-pointer type. + +#### func AsFloatish + +```go +func AsFloatish() types.BeMatcher +``` +AsFloatish succeeds if actual is a numeric value that represents a +floating-point value. + +#### func AsFloatishString + +```go +func AsFloatishString() types.BeMatcher +``` +AsFloatishString succeeds if actual is a string that can be parsed into a valid +floating-point value. + +#### func AsFunc + +```go +func AsFunc() types.BeMatcher +``` +AsFunc succeeds if actual is of kind reflect.Func. + +#### func AsIntish + +```go +func AsIntish() types.BeMatcher +``` +AsIntish succeeds if actual is a numeric value that represents an integer (from +reflect.Int up to reflect.Uint64). + +#### func AsIntishString + +```go +func AsIntishString() types.BeMatcher +``` +AsIntishString succeeds if actual is a string that can be parsed into a valid +integer value. + +#### func AsKind + +```go +func AsKind(args ...any) types.BeMatcher +``` +AsKind succeeds if actual is assignable to any of the specified kinds or matches +the provided matchers. + +#### func AsMap + +```go +func AsMap() types.BeMatcher +``` +AsMap succeeds if actual is of kind reflect.Map. + +#### func AsNumeric + +```go +func AsNumeric() types.BeMatcher +``` +AsNumeric succeeds if actual is a numeric value, supporting various integer +kinds: reflect.Int, ... reflect.Int64, and floating-point kinds: +reflect.Float32, reflect.Float64 + +#### func AsNumericString + +```go +func AsNumericString() types.BeMatcher +``` +AsNumericString succeeds if actual is a string that can be parsed into a valid +numeric value. + +#### func AsObject + +```go +func AsObject() types.BeMatcher +``` +AsObject is more specific than AsMap. It checks if the given `actual` value is a +map with string keys and values of any type. This is particularly useful in the +context of BeJson matcher, where the term 'Object' aligns with JSON notation. + +#### func AsObjects + +```go +func AsObjects() types.BeMatcher +``` + +#### func AsPointer + +```go +func AsPointer() types.BeMatcher +``` +AsPointer succeeds if the actual value is a pointer. + +#### func AsPointerToMap + +```go +func AsPointerToMap() types.BeMatcher +``` +AsPointerToMap succeeds if actual is a pointer to a map. + +#### func AsPointerToObject + +```go +func AsPointerToObject() types.BeMatcher +``` +AsPointerToObject succeeds if actual is a pointer to a value that matches +AsObject after applying dereference. + +#### func AsPointerToSlice + +```go +func AsPointerToSlice() types.BeMatcher +``` +AsPointerToSlice succeeds if actual is a pointer to a slice. + +#### func AsPointerToStruct + +```go +func AsPointerToStruct() types.BeMatcher +``` +AsPointerToStruct succeeds if actual is a pointer to a struct. + +#### func AsReader + +```go +func AsReader() types.BeMatcher +``` +AsReader succeeds if actual implements the io.Reader interface. + +#### func AsSlice + +```go +func AsSlice() types.BeMatcher +``` +AsSlice succeeds if actual is of kind reflect.Slice. + +#### func AsSliceOf + +```go +func AsSliceOf[T any]() types.BeMatcher +``` +AsSliceOf succeeds if actual is of kind reflect.Slice and each element of the +slice is assignable to the specified type T. + +#### func AsString + +```go +func AsString() types.BeMatcher +``` +AsString succeeds if actual is of kind reflect.String. + +#### func AsStringer + +```go +func AsStringer() types.BeMatcher +``` +AsStringer succeeds if actual implements the fmt.Stringer interface. + +#### func AsStruct + +```go +func AsStruct() types.BeMatcher +``` +AsStruct succeeds if actual is of kind reflect.Struct. + +#### func AssignableTo + +```go +func AssignableTo[T any]() types.BeMatcher +``` +AssignableTo succeeds if actual is assignable to the specified type T. + +#### func Implementing + +```go +func Implementing[T any]() types.BeMatcher +``` +Implementing succeeds if actual implements the specified interface type T. diff --git a/be_reflected/matchers_reflected.go b/be_reflected/matchers_reflected.go index 30055ee..2015153 100644 --- a/be_reflected/matchers_reflected.go +++ b/be_reflected/matchers_reflected.go @@ -1,5 +1,5 @@ -// Package be_reflected offers Be matchers for reflection in Go, -// enabling expressive and versatile assertions on values' reflect kinds. +// Package be_reflected provides Be matchers that use reflection, +// enabling expressive assertions on values' reflect kinds and types. package be_reflected import ( diff --git a/be_strings/README.md b/be_strings/README.md new file mode 100644 index 0000000..dc6bb2f --- /dev/null +++ b/be_strings/README.md @@ -0,0 +1,134 @@ +# be_strings +-- + import "github.com/expectto/be/be_strings" + +Package be_strings provides Be matchers for string-related assertions. + +## Usage + +#### func Alpha + +```go +func Alpha() types.BeMatcher +``` +Alpha succeeds if actual is a string containing only alphabetical characters. +Actual must be a string-like value (can be adjusted via SetStringFormat method). + +#### func AlphaNumeric + +```go +func AlphaNumeric() types.BeMatcher +``` +AlphaNumeric succeeds if actual is a string containing only alphanumeric +characters. Actual must be a string-like value (can be adjusted via +SetStringFormat method). As Numeric() matcher is considered to match on +integers, AlphaNumeric() doesn't match on dots So, consider +AlphaNumericWithDots() then + +#### func AlphaNumericWithDots + +```go +func AlphaNumericWithDots() types.BeMatcher +``` +AlphaNumericWithDots succeeds if actual is a string containing only alphanumeric +characters and dots. Actual must be a string-like value (can be adjusted via +SetStringFormat method). + +#### func EmptyString + +```go +func EmptyString() types.BeMatcher +``` +EmptyString succeeds if actual is an empty string. Actual must be a string-like +value (can be adjusted via SetStringFormat method). + +#### func Float + +```go +func Float() types.BeMatcher +``` +Float succeeds if actual is a string representing a valid floating-point number. +Actual must be a string-like value (can be adjusted via SetStringFormat method). + +#### func LowerCaseOnly + +```go +func LowerCaseOnly() types.BeMatcher +``` +LowerCaseOnly succeeds if actual is a string containing only lowercase +characters. Actual must be a string-like value (can be adjusted via +SetStringFormat method). + +#### func MatchTemplate + +```go +func MatchTemplate(template string, vars ...*V) types.BeMatcher +``` +MatchTemplate succeeds if actual matches given template pattern. Provided +template must have `{{Field}}` placeholders. Each distinct placeholder from +template requires a var to be passed in list of `vars`. Var can be a raw value +or a matcher + +E.g. + + Expect(someString).To(be_strings.MatchTemplate("Hello {{Name}}. Your number is {{Number}}", be_strings.Var("Name", "John"), be_strings.Var("Number", 3))) + Expect(someString).To(be_strings.MatchTemplate("Hello {{Name}}. Good bye, {{Name}}.", be_strings.Var("Name", be_strings.Titled())) + +#### func MatchWildcard + +```go +func MatchWildcard(pattern string) types.BeMatcher +``` +MatchWildcard succeeds if actual matches given wildcard pattern. Actual must be +a string-like value (can be adjusted via SetStringFormat method). + +#### func NonEmptyString + +```go +func NonEmptyString() types.BeMatcher +``` +NonEmptyString succeeds if actual is not an empty string. Actual must be a +string-like value (can be adjusted via SetStringFormat method). + +#### func Numeric + +```go +func Numeric() types.BeMatcher +``` +Numeric succeeds if actual is a string representing a valid numeric integer. +Actual must be a string-like value (can be adjusted via SetStringFormat method). + +#### func Titled + +```go +func Titled(languageArg ...language.Tag) types.BeMatcher +``` +Titled succeeds if actual is a string with the first letter of each word +capitalized. Actual must be a string-like value (can be adjusted via +SetStringFormat method). + +#### func ValidEmail + +```go +func ValidEmail() types.BeMatcher +``` +ValidEmail succeeds if actual is a valid email. Actual must be a string-like +value (can be adjusted via SetStringFormat method). + +#### type V + +```go +type V struct { + Name string + Matcher types.BeMatcher +} +``` + + +#### func Var + +```go +func Var(name string, matching any) *V +``` +Var creates a var used for replacing placeholders for templates in +`MatchTemplate` diff --git a/be_strings/matchers_string.go b/be_strings/matchers_string.go index 5000a5a..3ee9d13 100644 --- a/be_strings/matchers_string.go +++ b/be_strings/matchers_string.go @@ -1,4 +1,4 @@ -// Package be_strings provides Be matchers for string-related assertions +// Package be_strings provides Be matchers for string-related assertions. package be_strings import ( diff --git a/be_time/README.md b/be_time/README.md new file mode 100644 index 0000000..55b5c30 --- /dev/null +++ b/be_time/README.md @@ -0,0 +1,142 @@ +# be_time +-- + import "github.com/expectto/be/be_time" + +Package be_time provides Be matchers on time.Time + +## Usage + +#### func Approx + +```go +func Approx(compareTo time.Time, threshold time.Duration) types.BeMatcher +``` +Approx succeeds if actual time is approximately equal to the specified time +`compareTo` within the given time duration threshold. + +#### func EarlierThan + +```go +func EarlierThan(compareTo time.Time) types.BeMatcher +``` +EarlierThan succeeds if actual time is earlier than the specified time +`compareTo`. + +#### func EarlierThanEqual + +```go +func EarlierThanEqual(compareTo time.Time) types.BeMatcher +``` +EarlierThanEqual succeeds if actual time is earlier than or equal to the +specified time `compareTo`. + +#### func IsDST + +```go +func IsDST(compareTo time.Time) types.BeMatcher +``` +IsDST checks if actual time is DST + +#### func LaterThan + +```go +func LaterThan(compareTo time.Time) types.BeMatcher +``` +LaterThan succeeds if actual time is later than the specified time `compareTo`. + +#### func LaterThanEqual + +```go +func LaterThanEqual(compareTo time.Time) types.BeMatcher +``` +LaterThanEqual succeeds if actual time is later than or equal to the specified +time `compareTo`. + +#### func SameDay + +```go +func SameDay(compareTo time.Time) types.BeMatcher +``` +SameDay succeeds if the day component of the actual time is equal to the day +component of the specified time `compareTo`. + +#### func SameHour + +```go +func SameHour(compareTo time.Time) types.BeMatcher +``` +SameHour succeeds if actual time is approximately equal to the specified time +`compareTo` with the precision of one hour. + +#### func SameMinute + +```go +func SameMinute(compareTo time.Time) types.BeMatcher +``` +SameMinute succeeds if actual time is approximately equal to the specified time +`compareTo` with the precision of one minute. + +#### func SameMonth + +```go +func SameMonth(compareTo time.Time) types.BeMatcher +``` +SameMonth succeeds if the month component of the actual time is equal to the +month component of the specified time `compareTo`. + +#### func SameNano + +```go +func SameNano(compareTo time.Time) types.BeMatcher +``` +SameNano succeeds if actual time is approximately equal to the specified time +`compareTo` with the precision of one nanosecond. + +#### func SameOffset + +```go +func SameOffset(compareTo time.Time) types.BeMatcher +``` +SameOffset checks if actual time is the same timezone offset as specified time +`compareTo` Note: times can have different timezone names, but same offset, e.g. +America/New_York and Canada/Toronto + +#### func SameSecond + +```go +func SameSecond(compareTo time.Time) types.BeMatcher +``` +SameSecond succeeds if actual time is approximately equal to the specified time +`compareTo` with the precision of one second. + +#### func SameTimezone + +```go +func SameTimezone(compareTo time.Time) types.BeMatcher +``` +SameTimezone checks if actual time is the same timezone as specified time +`compareTo` + +#### func SameWeek + +```go +func SameWeek(compareTo time.Time) types.BeMatcher +``` +SameWeek succeeds if the ISO week and year components of the actual time are +equal to the ISO week and year components of the specified time `compareTo`. + +#### func SameWeekday + +```go +func SameWeekday(compareTo time.Time) types.BeMatcher +``` +SameWeekday succeeds if the weekday component of the actual time is equal to the +weekday component of the specified time `compareTo`. + +#### func SameYear + +```go +func SameYear(compareTo time.Time) types.BeMatcher +``` +SameYear succeeds if the year component of the actual time is equal to the year +component of the specified time `compareTo`. diff --git a/be_time/matchers_time.go b/be_time/matchers_time.go index a00f3f6..3b7ff36 100644 --- a/be_time/matchers_time.go +++ b/be_time/matchers_time.go @@ -1,3 +1,4 @@ +// Package be_time provides Be matchers on time.Time package be_time import ( diff --git a/be_url/README.md b/be_url/README.md new file mode 100644 index 0000000..11007d9 --- /dev/null +++ b/be_url/README.md @@ -0,0 +1,162 @@ +# be_url +-- + import "github.com/expectto/be/be_url" + +Package be_url provides Be matchers on url.URL + +## Usage + +```go +var TransformSchemelessUrlFromString = func(rawURL string) (*url.URL, error) { + result, err := url.Parse(rawURL) + if err == nil && result.Scheme == "" { + result, err = url.Parse("http://" + rawURL) + if err == nil { + result.Scheme = "" + } + } + return result, err +} +``` +TransformSchemelessUrlFromString returns string->*url.Url transform It allows +string to be a scheme-less url + +```go +var TransformUrlFromString = url.Parse +``` +TransformUrlFromString returns string->*url.Url transform + +#### func HavingHost + +```go +func HavingHost(args ...any) types.BeMatcher +``` +HavingHost succeeds if the actual value is a *url.URL and its Host matches the +provided one (via direct value or matchers) + +#### func HavingHostname + +```go +func HavingHostname(args ...any) types.BeMatcher +``` +HavingHostname succeeds if the actual value is a *url.URL and its Hostname +matches the provided one (via direct value or matchers) + +#### func HavingMultipleSearchParam + +```go +func HavingMultipleSearchParam(searchParamName string, args ...any) types.BeMatcher +``` +HavingMultipleSearchParam succeeds if the actual value is a *url.URL and its +specified search parameter (all its values via slice) matches the provided +arguments. + +#### func HavingPassword + +```go +func HavingPassword(args ...any) types.BeMatcher +``` +HavingPassword succeeds if the actual value is a *url.URL and its Password +matches the provided one. + +#### func HavingPath + +```go +func HavingPath(args ...any) types.BeMatcher +``` +HavingPath succeeds if the actual value is a *url.URL and its Path matches the +given one. + +#### func HavingPort + +```go +func HavingPort(args ...any) types.BeMatcher +``` +HavingPort succeeds if the actual value is a *url.URL and its Port matches the +provided one. + +#### func HavingRawQuery + +```go +func HavingRawQuery(args ...any) types.BeMatcher +``` +HavingRawQuery succeeds if the actual value is a *url.URL and its RawQuery +matches the given one. + +#### func HavingScheme + +```go +func HavingScheme(args ...any) types.BeMatcher +``` +HavingScheme succeeds if the actual value is a *url.URL and its Scheme matches +the provided one (via direct value or matchers) + +#### func HavingSearchParam + +```go +func HavingSearchParam(searchParamName string, args ...any) types.BeMatcher +``` +HavingSearchParam succeeds if the actual value is a *url.URL and its specified +search parameter matches the provided arguments. + +#### func HavingUserinfo + +```go +func HavingUserinfo(args ...any) types.BeMatcher +``` +HavingUserinfo succeeds if the actual value is a *url.URL and its User.String() +matches the provided one. + +#### func HavingUsername + +```go +func HavingUsername(args ...any) types.BeMatcher +``` +HavingUsername succeeds if the actual value is a *url.URL and its Username +matches the provided one. + +#### func NotHavingPort + +```go +func NotHavingPort(args ...any) types.BeMatcher +``` +NotHavingPort succeeds if the actual value is a *url.URL and its Port does not +match the given one. Example: `Expect(u).To(NotHavingPort())` matches port-less +url + +#### func NotHavingScheme + +```go +func NotHavingScheme(args ...any) types.BeMatcher +``` +NotHavingScheme succeeds if the actual value is a *url.URL and its Scheme +negatively matches given value Example: `Expect(u).To(NotHavingScheme())` +matches url without a scheme + +#### func URL + +```go +func URL(args ...any) types.BeMatcher +``` +URL matches actual value to be a valid URL corresponding to given inputs +Possible inputs: 1. Nil args -> so actual value MUST be any valid *url.URL 2. +Single arg . Actual value MUST be a *url.URL, whose .String() compared +against args[0] 3. Single arg <*url.Url>. Actual value MUST be a *url.URL, whose +.String() compared against args[0].String() 4. List of Omega/Gomock/Psi +matchers, that are applied to *url.URL object + + - TransformUrlFromString() transform can be given as first argument, so string->*url.URL transform is applied + +#### func WithHttp + +```go +func WithHttp() types.BeMatcher +``` +WithHttp succeeds if the actual value is a *url.URL and its scheme is "http". + +#### func WithHttps + +```go +func WithHttps() types.BeMatcher +``` +WithHttps succeeds if the actual value is a *url.URL and its scheme is "https". diff --git a/be_url/matchers_url.go b/be_url/matchers_url.go index 7417650..1939e17 100644 --- a/be_url/matchers_url.go +++ b/be_url/matchers_url.go @@ -1,3 +1,4 @@ +// Package be_url provides Be matchers on url.URL package be_url import ( diff --git a/core-be-matchers.md b/core-be-matchers.md new file mode 100644 index 0000000..ef6c036 --- /dev/null +++ b/core-be-matchers.md @@ -0,0 +1,82 @@ +# be +-- + import "github.com/expectto/be" + + +## Usage + +```go +var Ctx = be_ctx.Ctx +``` +Ctx is an alias for be_ctx.Ctx + +```go +var HttpRequest = be_http.Request +``` +HttpRequest is an alias for be_http.Request matcher + +```go +var JwtToken = be_jwt.Token +``` +JwtToken is an alias for be_jwt.Token matcher + +```go +var StringAsTemplate = be_strings.MatchTemplate +``` +StringAsTemplate is an alias for be_strings.MatchTemplate matcher + +```go +var URL = be_url.URL +``` +URL is an alias for be_url.URL matcher + +#### func All + +```go +func All(ms ...any) types.BeMatcher +``` +All is like gomega.And() + +#### func Always + +```go +func Always() types.BeMatcher +``` +Always does always match + +#### func Any + +```go +func Any(ms ...any) types.BeMatcher +``` +Any is like gomega.Or() + +#### func Eq + +```go +func Eq(expected any) types.BeMatcher +``` +Eq is like gomega.Equal() + +#### func HaveLength + +```go +func HaveLength(args ...any) types.BeMatcher +``` +HaveLength is like gomega.HaveLen() HaveLength succeeds if the actual value has +a length that matches the provided conditions. It accepts either a count value +or one or more Gomega matchers to specify the desired length conditions. + +#### func Never + +```go +func Never(err error) types.BeMatcher +``` +Never does never succeed (does always fail) + +#### func Not + +```go +func Not(expected any) types.BeMatcher +``` +Not is like gomega.Not() diff --git a/matchers.go b/matchers.go index 530ae8f..729f7ff 100644 --- a/matchers.go +++ b/matchers.go @@ -1,6 +1,7 @@ package be import ( + "github.com/expectto/be/be_ctx" "github.com/expectto/be/be_http" "github.com/expectto/be/be_jwt" "github.com/expectto/be/be_strings" @@ -18,3 +19,6 @@ var JwtToken = be_jwt.Token // StringAsTemplate is an alias for be_strings.MatchTemplate matcher var StringAsTemplate = be_strings.MatchTemplate + +// Ctx is an alias for be_ctx.Ctx +var Ctx = be_ctx.Ctx