6 releases

0.4.4 Oct 30, 2024
0.4.3 Oct 30, 2024
0.3.1 Oct 20, 2024

#243 in Web programming

GPL-3.0-only

36KB
687 lines

PostWoman

Actions Status Crates.io Version Crates.io Downloads (latest version) GitHub last commit GitHub Issues or Pull Requests

A CLI api tester and request builder, totally not born out of frustration from some other tool...

Why

I'd much rather edit my test routes in my text editor as bare config files and fire them via a CLI than fumble around some GUI application.

As an example, most API test tools have features to automatically split down query arguments from a built url. PostWoman doesn't bother, as your editor will most likely offer multi-cursors and search/replace to easily split off query parameters.

While PostWoman will never be as fully featured as other graphical tools, it doesn't need to be to provide a solid API testing framework.

Usage

Install with cargo install postwoman

postwoman expects a postwoman.toml collection in your cwd. A different file or path can be specified with the global -c option.

Use postwoman run <filter> to send requests to all routes in current config matching given filter (regex). Use . as filter to run all.

Examples

A collection can be super simple

[route.test]
url = "https://api.alemi.dev/debug"

But more complex options are available, check out provided postwoman.toml for some (ready to run!) examples.

More complex collection trees can be achieved with include top level field. Includes are idempotent and always resolved relative from parent collection's directory.

include = [
	"other.toml",
	"even/more.toml"
]

Running

Show collection summary

$ postwoman
~@ postwoman/0.3.1
-> postwoman.toml
 + PW_TOKEN=set-me-as-and-environment-variable!
 - healthcheck 	GET 	https://api.alemi.dev/
 - debug 	PUT 	https://api.alemi.dev/debug
 - benchmark 	GET 	https://api.alemi.dev/look/into/the/void
 - notfound 	GET 	https://api.alemi.dev/not-found
 - payload 	POST 	https://api.alemi.dev/debug
 - cookie 	GET 	https://api.alemi.dev/getcookie

Run all endpoints matching . (aka all of them)

$ postwoman run .
~@ postwoman/0.3.1
 : [05:14:47.241122] postwoman.toml::healthcheck 	sending request...
 + [05:14:47.411708] postwoman.toml::healthcheck 	done in 170ms
{
  "example": [
    "https://api.alemi.dev/debug",
    "https://api.alemi.dev/msg",
    "https://api.alemi.dev/mumble/ping"
  ],
  "time": "Sunday, 20-Oct-2024 03:14:47 GMT",
  "up": true
}
 : [05:14:47.411807] postwoman.toml::debug 	sending request...
 + [05:14:47.574391] postwoman.toml::debug 	done in 162ms
/debug?body=json&cache=0
 : [05:14:47.574474] postwoman.toml::benchmark 	sending request...
 + [05:14:47.726527] postwoman.toml::benchmark 	done in 152ms
 : [05:14:47.726605] postwoman.toml::notfound 	sending request...
 + [05:14:47.878922] postwoman.toml::notfound 	done in 152ms
nginx/1.26.2
 : [05:14:47.879000] postwoman.toml::payload 	sending request...
 + [05:14:48.039053] postwoman.toml::payload 	done in 160ms
{
  "body": "{\n\t\"complex\": {\n\t\t\"json\": \"payloads\",\n\t\t\"can\": \"be\",\n\t\t\"expressed\": \"this\",\n\t\t\"way\": true\n\t}\n}",
  "headers": {
    "accept": [
      "*/*"
    ],
    "connection": "close",
    "content-length": "94",
    "user-agent": "postwoman@sample/0.3.1",
    "x-forwarded-proto": "https",
    "x-real-ip": "93.34.149.115",
    "x-real-port": 46945,
    "x-user-agent": "postwoman@sample/0.3.1"
  },
  "method": "POST",
  "path": "/debug",
  "time": 1729394088.0156112,
  "version": "HTTP/1.0"
}
 : [05:14:48.039099] postwoman.toml::cookie 	sending request...
 + [05:14:48.168725] postwoman.toml::cookie 	done in 129ms
SGF2ZSBhIENvb2tpZSE=

Debug a specific route passing --debug:

$ postwoman run notfound --debug
~@ postwoman/0.3.1
 : [05:15:16.960147] postwoman.toml::notfound 	sending request...
 + [05:15:17.120647] postwoman.toml::notfound 	done in 160ms
Response {
    url: "https://api.alemi.dev/not-found",
    status: 404,
    headers: {
        "server": "nginx/1.26.2",
        "date": "Sun, 20 Oct 2024 03:15:17 GMT",
        "content-type": "text/html",
        "content-length": "153",
        "connection": "keep-alive",
        "vary": "Accept-Encoding",
    },
}
Body: <html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.26.2</center>
</body>
</html>

Dependencies

~13–26MB
~373K SLoC