ci(api-tests): add API test to CI

This commit is contained in:
Edouard Vanbelle
2026-05-11 09:54:35 +02:00
parent 8ab537797a
commit cc14ec53ee
8 changed files with 158 additions and 83 deletions
+7 -7
View File
@@ -9,7 +9,7 @@ Tests are written using [Hurl](https://hurl.dev) — a plain-text, CLI-first HTT
## Configuration
Edit `.env` to match your local instance. This file is the single source of
Edit `test.env` to match your local instance. This file is the single source of
truth: `run.sh` sources it for shell variables and passes it to Hurl as
`--variables-file`.
@@ -24,19 +24,19 @@ password=TestPassword1!
```bash
# First-time setup (run once on a fresh instance)
hurl --variables-file tests/api/hurl.vars --test tests/api/setup.hurl
hurl --variables-file tests/api/test.env --test tests/api/setup.hurl
# Contacts CRUD scenario
hurl --variables-file tests/api/hurl.vars --test tests/api/contacts.hurl
hurl --variables-file tests/api/test.env --test tests/api/contacts.hurl
# All scenarios at once
hurl --variables-file tests/api/hurl.vars --test tests/api/setup.hurl tests/api/contacts.hurl
hurl --variables-file tests/api/test.env --test tests/api/setup.hurl tests/api/contacts.hurl
# With full request/response output
hurl --variables-file tests/api/hurl.vars --test --verbose tests/api/contacts.hurl
hurl --variables-file tests/api/test.env --test --verbose tests/api/contacts.hurl
# Generate an HTML report
hurl --variables-file tests/api/hurl.vars --test --report-html /tmp/hurl-report tests/api/contacts.hurl
hurl --variables-file tests/api/test.env --test --report-html /tmp/hurl-report tests/api/contacts.hurl
```
## Test files
@@ -45,7 +45,7 @@ hurl --variables-file tests/api/hurl.vars --test --report-html /tmp/hurl-report
|---|---|
| `setup.hurl` | One-time admin account creation; also asserts the endpoint is locked afterwards |
| `contacts.hurl` | Full contacts CRUD scenario (13 steps, see below) |
| `.env` | Variables: `base_url`, `username`, `email`, `password` — used by both Hurl and `run.sh` |
| `test.env` | Variables: `base_url`, `username`, `email`, `password` — used by both Hurl and `run.sh` |
## Scenario: `contacts.hurl`
+27 -7
View File
@@ -165,6 +165,8 @@ jsonpath "$.phone[0].type" == "mobile"
# ─────────────────────────────────────────────────────────────
# Step 8 – Update John Doe: add nickname, notes, and organisation
# Uses If-Match for optimistic concurrency
# Captures the refreshed ETag into etag_updated so that
# the original etag remains available as a stale value.
# ─────────────────────────────────────────────────────────────
PUT {{base_url}}/api/address-books/{{book_id}}/contacts/{{contact_id}}
Authorization: Bearer {{token}}
@@ -195,7 +197,7 @@ If-Match: {{etag}}
HTTP 200
[Captures]
etag: header "ETag"
etag_updated: header "ETag"
[Asserts]
header "ETag" exists
jsonpath "$.id" == {{contact_id}}
@@ -207,17 +209,35 @@ jsonpath "$.notes" == "Updated via Hurl test"
# ─────────────────────────────────────────────────────────────
# Step 9 – Delete John Doe (uses refreshed ETag from step 8)
# Step 9 – Stale ETag rejection: update with the pre-step-8 ETag
# The contact was already modified so this must return 412
# ─────────────────────────────────────────────────────────────
PUT {{base_url}}/api/address-books/{{book_id}}/contacts/{{contact_id}}
Authorization: Bearer {{token}}
Content-Type: application/json
If-Match: {{etag}}
{
"first_name": "John",
"last_name": "Doe",
"full_name": "John Doe",
"nickname": "should-not-be-saved"
}
HTTP 412
# ─────────────────────────────────────────────────────────────
# Step 10 – Delete John Doe (uses refreshed ETag from step 8)
# ─────────────────────────────────────────────────────────────
DELETE {{base_url}}/api/address-books/{{book_id}}/contacts/{{contact_id}}
Authorization: Bearer {{token}}
If-Match: {{etag}}
If-Match: {{etag_updated}}
HTTP 204
# ─────────────────────────────────────────────────────────────
# Step 10 – Address book must be empty again
# Step 11 – Address book must be empty again
# ─────────────────────────────────────────────────────────────
GET {{base_url}}/api/address-books/{{book_id}}/contacts
Authorization: Bearer {{token}}
@@ -228,7 +248,7 @@ jsonpath "$" count == 0
# ─────────────────────────────────────────────────────────────
# Step 11 – Delete the personal address book
# Step 12 – Delete the personal address book
# ─────────────────────────────────────────────────────────────
DELETE {{base_url}}/api/address-books/{{book_id}}
Authorization: Bearer {{token}}
@@ -237,7 +257,7 @@ HTTP 204
# ─────────────────────────────────────────────────────────────
# Step 12 – Verify the address book is gone from the list
# Step 13 – Verify the address book is gone from the list
# ─────────────────────────────────────────────────────────────
GET {{base_url}}/api/address-books
Authorization: Bearer {{token}}
@@ -248,7 +268,7 @@ jsonpath "$[*].id" not contains {{book_id}}
# ─────────────────────────────────────────────────────────────
# Step 13 – List the system address book (OxiCloud users)
# Step 14 – List the system address book (OxiCloud users)
# ─────────────────────────────────────────────────────────────
GET {{base_url}}/api/address-books/system/contacts
Authorization: Bearer {{token}}
+4 -4
View File
@@ -13,9 +13,9 @@ REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
COMMON="$REPO_ROOT/tests/common"
API_DIR="$REPO_ROOT/tests/api"
# .env is the single source of truth for connection details and credentials.
# shellcheck source=.env
source "$API_DIR/.env"
# test.env is the single source of truth for connection details and credentials.
# shellcheck source=test.env
source "$API_DIR/test.env"
# Derive server port from base_url (e.g. http://localhost:8087 → 8087)
SERVER_PORT="${base_url##*:}"
@@ -77,7 +77,7 @@ log "Server is ready."
# ── 4. Run Hurl tests ─────────────────────────────────────────────────────────
log "Running Hurl tests..."
hurl --variables-file "$API_DIR/.env" --test --jobs 1 \
hurl --variables-file "$API_DIR/test.env" --test --jobs 1 \
"$API_DIR/setup.hurl" \
"$API_DIR/contacts.hurl"
+6
View File
@@ -0,0 +1,6 @@
# Test credentials for local/CI API tests — NOT real secrets.
base_url=http://localhost:8087
username=admin
email=admin@example.com
# gitguardian:ignore
password=TestPassword1!