Releases: korya/http-assert
Release list
v0.4.0
Added
- A reusable Go package at
github.com/korya/http-assertexposes the HTTP
client, structured results and all response assertion constructors. The
library invokes its configured HTTP client once and leaves retries, logging
and presentation to its caller. - Generic
ha.Must(...)keeps values built from static input inline, including
HTTP requests and assertions, while preserving explicit errors for runtime
input.
Changed
- Breaking for source installs: the CLI now lives at
github.com/korya/http-assert/cmd/http-assert; use
go install github.com/korya/http-assert/cmd/http-assert@latest. Published
release archives and thehttp-assertbinary name are unchanged. - Assertion failures and evaluation errors are structured library data. Each
outcome is exclusively a pass, a failed assertion or an evaluation error,
and its assertion family is consistently typed. The CLI owns human-readable
formatting and preserves its existing output. - Assertion families use the exported
AssertionKindtype and constants
instead of requiring consumers to compare raw strings. - The library's zero-value client uses a 20-second total request timeout instead
of the unboundedhttp.DefaultClient. Callers can still inject an HTTP client
or apply a shorter request-context deadline.
Fixed
- jq evaluation stops when its response request's context is cancelled while
retaining the package's ten-second safety ceiling.
v0.3.0
v0.3.0 teaches --assert-status to describe a set of codes rather than one, decodes the two compression formats CDNs actually serve, and colours the verdict so a failure stops looking like a success in a scroll-past.
It also ships binaries built with a current Go. v0.2.0 was compiled with go1.25.5 — eight months and seven patch releases stale, every one of them carrying security fixes, several to crypto/tls and net/http. If you run the released binaries, this is the upgrade that closes that.
⚠️ Breaking: go install now needs Go 1.26
The module's minimum moved from Go 1.25 to 1.26, so building from source requires it. Go supports a release until two newer ones exist, and 1.27 is imminent, which would have put 1.25 out of support.
Released binaries need no Go toolchain and are unaffected. If you download from the assets below, this changes nothing for you.
--assert-status takes a set of codes
Previously one exact code, so "did this succeed" meant naming every acceptable status in a separate run, or falling back to --assert-ok — which also passes on a 3xx.
$ http-assert --assert-status 2xx https://example.com # any 200-299
$ http-assert --assert-status 200,204 https://example.com # either
$ http-assert --assert-status 401-403 https://example.com # any of three
$ http-assert --assert-status 301,2xx,500-503 https://example.com # mixedThe failure quotes the spec as you wrote it — status: expected 2xx, got 404 ("404 Not Found") — rather than an expansion you would have to translate back.
A code no response can carry (-1, 1000, 099) is now rejected before the request is made, exiting 71 instead of failing an assertion at 93. A typo in your command line is not a fact about the service, and a CI job reading exit codes should not conclude otherwise.
Brotli and zstd bodies are decoded
A Content-Encoding: br or zstd response could not have its payload asserted on at all — the body assertions refused by name while the status and header assertions carried on. Both are what CDNs serve, so testing content negotiation was the one thing the tool could not follow through on.
$ http-assert -H 'Accept-Encoding: br' \
--assert-header-eq 'content-encoding: br' \
--assert-body 'jQuery' https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js
[+] PASSEDBoth codings decode payloads produced by the reference implementations, not just by Go's own writers. Cost: two dependencies with no transitive deps of their own, and +419 KB on the binary.
Colour
[+] PASSED and [-] FAILED differed by one character, so a failure looked exactly like a success in a CI log.
The verdict is now green or red, Error: is red, and the [.] [:] [>] trace lines are dimmed. [~] is yellow — a retry reports trouble without being the verdict, and a check that passed on the fourth attempt is not the same news as one that passed on the first.
--color takes auto (default), always or never. auto colours only when stderr is a terminal, so pipes and CI logs stay plain without being asked. NO_COLOR is honoured; --color=always overrides it.
Fixes
- Header failures no longer leak Go syntax.
got ["abc123"]now readsgot "abc123", and a multi-valued header readsgot "first", "second". The same defect was in three assertions, not just the reported one. - Multi-value header matching is documented.
--assert-headerand--assert-header-eqhold when any value matches — so--assert-header-eq 'Set-Cookie: session=abc'passes against a response setting three cookies. That was always the behaviour; nothing said so.
Under the hood
Assertions now return structured failures carrying kind, target, expected and actual rather than only a sentence, which is what --json output (#45) needs. No output changed: every failure message is byte-identical, verified by diffing both binaries across thirteen failing scenarios.
Install
go install github.com/korya/http-assert@v0.3.0 # needs Go 1.26+
Or take a binary from the assets below — no toolchain required.
Full commit log
- 7a3a536: chore(go): Raise the module to Go 1.26 and pin the build toolchain (@korya)
- de0b5c9: chore(ci): Report when the pinned Go toolchain trails its patch line (@korya)
- 114dfca: docs(readme): State the Go requirement for a source build (@korya)
- 91d2930: chore(deps): Bump actions/setup-go from 5 to 7 (@dependabot[bot])
- fda82fe: refactor(assert): Give assertions a kind and failures their parts (@korya)
- 5fa25cd: test(assert): Pin the structure a failure now carries (@korya)
- 9b69b33: feat(compress): Decode brotli and zstd response bodies (@korya)
- a1ec603: fix(assert): Render header values readably; document multi-value (@korya)
- de0e7a5: feat(cli): Colour the verdict when stderr is a terminal (@korya)
- e000584: feat(cli): Colour the retry line yellow rather than dimming it (@korya)
- 1fb2dfb: feat(assert): Accept a status class, list or range in --assert-status (@korya)
v0.2.0
v0.2.0 closes the gap between what http-assert promised and what it did. Exit codes now say who is at fault, three new capabilities land (--assert-jq, --retry, -L), and ten fixes correct assertions that could quietly pass or fail for the wrong reason.
If you script against exit codes, read the breaking change below before upgrading. If you only test for non-zero, nothing changes for you.
⚠️ Breaking: exit codes now name the category, not the layer
Five codes with accidental boundaries became three, each answering a different question:
| Code | Meaning |
|---|---|
0 |
every assertion passed |
71 |
the invocation was rejected — no request was attempted |
92 |
the request produced no usable response |
93 |
a response arrived and at least one assertion failed |
91 and 103 are gone (both are now 71), and transport failures moved from 93 to 92, so 93 now means assertion failed and nothing else. Previously the same typo exited 103 with a 40-line usage dump from the command line but 71 with one line from the environment, and running with zero assertions reported the transport-failure code without ever sending a request (#25).
New
--assert-jq— assert a jq expression against a JSON body, instead of pointing a regex at JSON and hoping key order holds. Repeatable; each expression must yieldtrue, and one that yields nothing has checked nothing and fails. Queries compile before the request is sent, so a broken one exits71without touching the network.--retry— re-send the request after a failed attempt, with--retry-delay(1s, fixed) between them. A failure is any failure: a transport error or an assertion that did not hold — which is the case that matters when you are waiting for a service to come up and it answers perfectly well with the wrong thing.--retry-max-timebounds the run;-mbounds each attempt.-L/--location— follow redirects, with--max-redirsbounding the chain. Assertions then apply to the response at the end of it. Without-La 3xx is asserted on as it stands, which is what--assert-redirectinspects — so the two cannot be combined.
Fixed
Correctness, mostly in places where an assertion could pass or fail for the wrong reason:
- A compressed response body is now decoded before assertions run, so
--assert-bodysees the payload rather than gzip bytes. - An empty body can now satisfy a body assertion instead of being treated as absent.
- Both boolean assertions negate the same way:
=falseselects the opposite assertion rather than cancelling the flag. -dnow impliesPOST, matching curl.- A repeated single-value assertion flag is rejected instead of silently keeping the last one.
- A
-Hvalue with no colon is rejected rather than accepted as nonsense. - Conflicting verbosity settings resolve by documented priority instead of silently.
- The failure dump is formatted rather than serialized, includes the request body, and no longer mistakes a whitespace-only body for binary.
Docs and CI
--help now documents the environment variables, exit codes, proxy support and redirect behaviour, and the README was restructured to lead with what the tool is for. Several flag descriptions that contradicted the code were corrected. CI now runs across Linux, macOS and Windows on two Go versions, gated on the static checks.
Install
Grab a binary from the assets below, or:
go install github.com/korya/http-assert@v0.2.0
Full commit log
- d065060: chore(deps): Bump extractions/setup-just from 3 to 4 (@dependabot[bot])
- f0329d6: chore(deps): Bump actions/checkout from 4 to 7 (@dependabot[bot])
- aaeb6e0: chore(deps): Bump golangci/golangci-lint-action from 8 to 9 (@dependabot[bot])
- 5205fbf: docs(godoc): Document the command in the package comment (@korya)
- 928694d: docs(readme): Complete the exit code list (@korya)
- 4d3fc12: chore(ci): Test across macOS, Windows and two Go versions (@korya)
- ba799c7: chore(ci): Gate the matrix on the static checks (@korya)
- 6cf32b1: fix(render): Format the failure dump instead of serializing it (@korya)
- c955587: fix(render): Treat whitespace as text when choosing the body renderer (@korya)
- 24464e7: docs(cli): Correct the flag descriptions that contradict the code (@korya)
- 883edaa: docs(cli): Document environment, exit codes and proxy in --help (@korya)
- 1277e7c: docs(readme): Correct --assert-ok and document proxy support (@korya)
- 88ac957: fix(cli): Reject a repeated assertion flag instead of dropping the earlier one (@korya)
- 7a624e0: docs(cli): Say that assertion flags take a single value (@korya)
- 77cb909: docs: Say that redirects are not followed (@korya)
- 4ef0107: feat(cli): Add -L/--location to follow redirects (@korya)
- b3dc4af: docs(readme): Name every command-line-only option (@korya)
- 14540fd: feat(cli): Add --retry to re-send a failed check (@korya)
- f2fdfb5: fix(client): Decode the response body before assertions run (@korya)
- a9e5693: docs(readme): Drop implementation notes the reader cannot act on (@korya)
- 11df18b: fix(assert): Let an empty body satisfy a body assertion (@korya)
- 2ff7841: fix(assert): Make both boolean assertions negate the same way (@korya)
- afc4f6a: fix(cli): Reject a -H value with no colon (@korya)
- 06d99d3: fix(render): Show the request body in the failure dump (@korya)
- d6e5c43: fix(cli): Default the method to POST when -d is given (@korya)
- 7180c20: test(e2e): Cover every case the -d POST implication adds (@korya)
- 167b11d: test(e2e): Move the flipped issue tests out of the known-issues file (@korya)
- 7c9d098: feat(assert): Add --assert-jq for JSON responses (@korya)
- ef0db33: docs: Correct the claims the docs do not honour (@korya)
- c327f98: docs(readme): Document the contract the tool already keeps (@korya)
- d5f47db: docs(readme): Restructure for readers before contributors (@korya)
- ebc4377: docs(readme): Take the em dashes and tics out of the new copy (@korya)
- cf1a8c8: docs(readme): Bring back the Go Reference badge (@korya)
- ca1fe7b: docs(readme): Rewrite the opening for the reader who knows curl (@korya)
- 091addd: docs(readme): Open with what the tool is, in one sentence (@korya)
- 74563a7: docs(readme): Align the body with the one-sentence opening (@korya)
- 2f4b3e1: feat(cli)!: Reduce exit codes to invocation, transport and assertion (@korya)
- 01c2761: fix(cli): Resolve verbosity conflicts by priority instead of silently (@korya)
- 9e2f8f9: docs(cli): Move the verbosity precedence into the flag descriptions (@korya)
v0.1.0
Changelog
- c3091b2: Bump github.com/onsi/gomega from 1.38.3 to 1.39.1 (@dependabot[bot])
- 1b891c0: chore(gosec): Suppress the G704 SSRF false positive in Client.Do (@korya)
- 2852077: Bump golang.org/x/net in the go_modules group across 1 directory (@dependabot[bot])
- d954611: test(e2e): Add black-box suite pinning the CLI config contract (@korya)
- 0f44633: docs(readme): Correct which options read the environment (@korya)
- 9df95a8: test(e2e): Make the end-to-end suite opt-in and a separate CI step (@korya)
- 29fa30b: test(e2e): Pin value-level environment semantics (@korya)
- cd6c168: refactor(config): Replace viper with an explicit environment lookup (@korya)
- 9773690: fix(config): Reject unparseable environment values (@korya)
- 3830c63: refactor(log): Delete the unused logWarn and logError helpers (@korya)
- 508da4a: chore(ci): Fail the build when go.mod is not tidy (@korya)
- 276c277: test(unit): Replace gomega with stdlib assertions and subtests (@korya)
- 1ed5759: test(unit): Check both return values in Test_parseHostMappings (@korya)
- 4d5dc06: fix(assert): Reject unparseable regexp patterns instead of panicking (@korya)
- 15b79af: test(e2e): Assert no flag panics on malformed input (@korya)
- 3b2ca72: fix(utils): Guard printPayload against a negative crop size (@korya)
- 2a6e00b: test(unit): Add fuzz targets for the input parsers (@korya)
- 1152c0e: chore(lint): Forbid regexp.MustCompile (@korya)
- 2f9afd5: chore(ci): Assert the standard linters stay enabled (@korya)
- 4ffe282: refactor(cli): Rename die to dief (@korya)
- 965d8f6: chore(ci): Pin the CI Go toolchain to go.mod (@korya)
- d0fe3bc: feat(cli): Add a --version flag (@korya)
- 838f82b: chore(release): Add a goreleaser configuration (@korya)
- f0368a8: chore(ci): Build the release artifacts on every pull request (@korya)
- ce79253: chore(ci): Publish binaries and a GitHub Release on tag push (@korya)
- 3b66625: chore(ci): Let Dependabot track GitHub Actions versions (@korya)
- 0ef37ba: docs(readme): Document binary downloads and --version (@korya)