hutc 0.2.0

Simple af rest api testing client using lua
hutc-0.2.0 is not a library.

hutc

Lua-driven HTTP API test runner built in Rust.

Installation

Install from crates.io:

cargo install hutc

Quick Start

  1. Generate Lua language-server definitions:
hutc init

This creates tests/hutc.defs.lua.

  1. Create a test file at tests/health.lua:
local client = http()
client:base_url("https://example.com")

test("health endpoint is up", function()
  local res = client:req():path("/health"):get()

  expect(res.status):to_equal(200)
  expect(res.ok):to_equal(true)
end)
  1. Run tests:
hutc test

Default Paths

  • hutc test reads .lua files from tests
  • hutc init writes tests/hutc.defs.lua

Lua API

Globals

Function Purpose
test(name, fn) Registers a test case
expect(value) Creates an assertion object
http() Creates an HTTP client
log(...) Prints debug values

Assertions

expect(value) returns an Expect object:

Method Description
:msg("message") Adds custom message prefix on assertion failure
:to_equal(expected) Asserts equality
:to_not_equal(expected) Asserts inequality
:to_exist() Asserts value is not nil

Example:

expect(res.status):msg("status mismatch"):to_equal(200)
expect(res.json.user):to_exist()

HTTP Client

Create a client:

local client = http()
client:base_url("https://api.example.com")

Create a request with client:req(), then chain request-builder methods:

Method Description
:path("/users") Relative path (uses base_url)
:url("https://...") Absolute URL (overrides path/base_url)
:header("k", "v") Single header
:headers({ k = "v" }) Multiple headers
:query("k", "v") Single query param
:queries({ k = "v" }) Multiple query params
:body("text") Plain text body
:body_bytes("raw") Raw bytes body
:json('{"k":"v"}') JSON body as raw JSON string
:form({ k = "v" }) Form body
:timeout_ms(5000) Request timeout in milliseconds
:bearer("token") Sets Authorization: Bearer <token>

Execute with one of:

get, post, put, patch, delete, send

send uses current/default method (GET unless set by another verb).

HTTP Response Shape

Each request returns a response table:

Field Type Description
status integer HTTP status code
ok boolean true for 2xx responses
body string Raw response body
url string Final response URL
duration_ms integer Request duration
headers table<string, string> Response headers
json any? Parsed JSON (if body is valid JSON)

End-to-End Example

local client = http()
client:base_url("https://jsonplaceholder.typicode.com")

test("GET /posts returns data", function()
  local res = client
    :req()
    :path("/posts")
    :query("_limit", "1")
    :get()

  expect(res.status):to_equal(200)
  expect(res.json):to_exist()
  expect(res.json[1].id):to_exist()
end)

test("POST /posts creates resource", function()
  local res = client
    :req()
    :path("/posts")
    :header("content-type", "application/json")
    :json('{"title":"hello","body":"world","userId":1}')
    :post()

  expect(res.status):to_equal(201)
  expect(res.json.id):to_exist()
end)

Development

Run from source:

cargo run -- test