# tbd A small Clojure HTTP API built on [Ring](https://github.com/ring-clojure/ring) (Jetty) and [reitit](https://github.com/metosin/reitit), with Swagger UI docs, unit tests, dockerized integration tests, and joint line coverage. ## Requirements - Docker, for the integration tests and the `script/docker-*` tasks (all CI needs) - For running tasks natively (optional): [Clojure CLI](https://clojure.org/guides/install_clojure) (tools.deps) with JDK 11+, and [Babashka](https://babashka.org/) for `script/coverage-report` ## Running ```bash clojure -X:run ``` The server listens on port `3000` by default; set `PORT` to change it: ```bash PORT=8080 clojure -X:run ``` Or build and run the container: ```bash docker build -t tbd . docker run --rm -p 3000:3000 tbd ``` ## API | Method | Path | Description | |--------|------|-------------| | `GET` | `/` | Plain-text landing message | | `GET` | `/api/ping` | Health check, returns `pong` | | `GET` | `/api/add?x=2&y=3` | Adds two numbers, returns EDN: `{:result 5}` (400 if either is missing or not a number) | | `GET` | `/api-docs` | Swagger UI | | `GET` | `/swagger.json` | OpenAPI/Swagger spec | ```bash curl localhost:3000/api/ping # pong curl 'localhost:3000/api/add?x=2&y=3' # {:result 5} ``` ## Development Every task is a bash script in `script/`. Start an nREPL server (with CIDER middleware) on the port in `.nrepl-port`, or `3000` if that file doesn't exist: ```bash script/repl ``` ### Tests | Task | What it does | |------|--------------| | `script/unit-test` | Runs the Clojure tests in `test/` | | `script/integration-test` | Builds the app image, starts it on a random port, runs the [integration suite](integration-test/README.md) against it, then tears it down | | `script/test` | Unit tests, then integration tests (stops at the first failure) | | `script/docker-unit-test` | Same as `script/unit-test`, but inside the app image (Docker only) | ### Coverage Line coverage via [Cloverage](https://github.com/cloverage/cloverage): | Task | Output | |------|--------| | `script/unit-coverage` | `target/coverage/unit/` | | `script/integration-coverage` | `target/coverage/integration/` | | `script/coverage` | Runs both, then writes the merged report to `target/coverage/merged/` | | `script/docker-unit-coverage`, `script/docker-coverage-report` | Dockerized versions of the unit run and the merge step, so `script/docker-unit-coverage`, `script/integration-coverage`, `script/docker-coverage-report` is `script/coverage` with no local Clojure or Babashka (this is what CI runs) | Each report directory has an `index.html` and an `lcov.info`. See [integration-test/README.md](integration-test/README.md) for how integration coverage is collected. ## Project layout ``` src/tbd/ core.clj Router, Ring app, and server startup /routes.clj Each route group (health, math, ...) owns its route subtree /core.clj Business logic for the group, with no HTTP concerns test/ Unit tests integration-test/ Dockerized integration tests and coverage harness script/ Bash entry points (test, coverage, repl, ...) and coverage helpers ``` To add a new group, create `tbd..routes` with a `routes` fn that returns a reitit route vector, then add it to the router in `src/tbd/core.clj`.