Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/go-int.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
name: Go Integration

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
integration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: '1.25'
- name: Install just
uses: extractions/setup-just@v3
- name: Fetch justfiles
run: just fetch
- name: Linux tuning
run: just linux-tune
- name: Integration
run: just go::unit-int
1 change: 0 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ jobs:
run: |
just go::deps
just go::mod
just bats::deps
- name: Test
run: just test
- name: Upload coverage reports to Codecov
Expand Down
34 changes: 23 additions & 11 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ Quick reference for common commands:

```bash
just deps # Install all dependencies
just test # Run all tests (lint + unit + coverage + bats)
just test # Run all tests (lint + unit + coverage)
just go::unit # Run unit tests only
just go::unit-int # Run integration tests (requires running osapi)
just go::vet # Run golangci-lint
just go::fmt # Auto-format (gofumpt + golines)
go test -run TestName -v ./internal/job/... # Run a single test
Expand All @@ -32,7 +33,7 @@ go test -run TestName -v ./internal/job/... # Run a single test
## Architecture (Quick Reference)

- **`cmd/`** - Cobra CLI commands (`client`, `node agent`, `api server`, `nats server`)
- **`internal/api/`** - Echo REST API by domain (`node/`, `job/`, `health/`, `audit/`, `common/`). Types are OpenAPI-generated (`*.gen.go`)
- **`internal/api/`** - Echo REST API by domain (`node/`, `job/`, `health/`, `audit/`, `common/`). Types are OpenAPI-generated (`*.gen.go`). Combined OpenAPI spec: `internal/api/gen/api.yaml`
- **`internal/job/`** - Job domain types, subject routing. `client/` for high-level ops
- **`internal/agent/`** - Node agent: consumer/handler/processor pipeline for job execution
- **`internal/provider/`** - Operation implementations: `node/{host,disk,mem,load}`, `network/{dns,ping}`
Expand Down Expand Up @@ -123,8 +124,9 @@ input must be validated, and the spec must declare how:
`validation.Struct(request.Body)` for request bodies
3. A `400` response defined in the OpenAPI spec for endpoints that
accept user input
4. An integration test (`*_integration_test.go`) that sends raw HTTP
through the full Echo middleware stack and verifies:
4. HTTP wiring tests (`TestXxxHTTP` / `TestXxxRBACHTTP` methods in the
`*_public_test.go` suite) that send raw HTTP through the full Echo
middleware stack and verify:
- Validation errors return correct status codes and error messages
- RBAC: 401 (no token), 403 (wrong permissions), 200 (valid token)

Expand All @@ -140,13 +142,13 @@ Create `internal/api/{domain}/`:
a 400 on failure.
- Tests: `{operation}_get_public_test.go` (testify/suite, table-driven).
Must cover validation failures (400), success, and error paths.
- Integration tests: `{operation}_get_integration_test.go` — sends raw
HTTP through the full Echo middleware stack. Every integration test
MUST include:
- **Validation tests**: valid input, invalid input (400 responses)
- **RBAC tests**: no token (401), wrong permissions (403), valid
token (200). Uses `api.New()` + `server.GetXxxHandler()` +
`server.RegisterHandlers()` to wire through `scopeMiddleware`.
Each public test suite also includes HTTP wiring methods:
- `TestXxxHTTP` — sends raw HTTP through the full Echo middleware
stack to verify validation (valid input, invalid input → 400).
- `TestXxxRBACHTTP` — verifies auth middleware: no token (401),
wrong permissions (403), valid token (200). Uses `api.New()` +
`server.GetXxxHandler()` + `server.RegisterHandlers()` to wire
through `scopeMiddleware`.
See existing examples in `internal/api/job/` and
`internal/api/audit/`.

Expand Down Expand Up @@ -248,6 +250,16 @@ func FunctionName(

### Testing

Three test layers:
- **Unit tests** (`*_test.go`, `*_public_test.go`) — fast, mocked
dependencies, run with `just go::unit`. Includes `TestXxxHTTP` /
`TestXxxRBACHTTP` methods that send raw HTTP through real Echo
middleware with mocked backends.
- **Integration tests** (`test/integration/`) — build and start a real
`osapi` binary, exercise CLI commands end-to-end. Guarded by
`//go:build integration` tag, run with `just go::unit-int`.

Conventions:
- ALL tests in `internal/job/` MUST use `testify/suite` with table-driven patterns
- Internal tests: `*_test.go` in same package (e.g., `package job`) for private functions
- Public tests: `*_public_test.go` in test package (e.g., `package job_test`) for exported functions
Expand Down
8 changes: 4 additions & 4 deletions docs/docs/sidebar/development/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,15 @@ See the [Testing](testing.md) page for details on running tests and listing just
recipes.

```bash
just test # Run all tests (lint + unit + coverage + bats)
just test # Run all tests (lint + unit + coverage)
just go::unit # Run unit tests only
just bats::test # Run integration tests only
just go::unit-int # Run integration tests (requires running osapi)
```

Unit tests should follow the Go convention of being located in a file named
`*_test.go` in the same package as the code being tested. Integration tests are
located in the `test` directory and executed by [Bats][].
located in `test/integration/` and use a `//go:build integration` tag. They
build and start a real `osapi` binary, so they require no external setup.

### File naming

Expand Down Expand Up @@ -143,6 +144,5 @@ be reasonable to split it in a few). Git squash and rebase is your friend!
[Prettier]: https://prettier.io/
[Docusaurus]: https://docusaurus.io
[Conventional Commits]: https://www.conventionalcommits.org
[Bats]: https://github.com/bats-core/bats-core
[NATS CLI]: https://github.com/nats-io/natscli
<!-- prettier-ignore-end -->
32 changes: 30 additions & 2 deletions docs/docs/sidebar/development/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,46 @@ Install dependencies:
$ just deps
```

To execute tests:
## Unit Tests

Unit tests run with mocked dependencies and require no external services:

```bash
$ just go::unit # Run unit tests
$ just go::unit-cov # Run with coverage report
$ just test # Run all checks (lint + unit + coverage)
```

Unit tests follow the Go convention of being located in `*_test.go` files in the
same package as the code being tested. Public API tests use the `_test` package
suffix in `*_public_test.go` files. Public test suites also include HTTP wiring
methods (`TestXxxHTTP`, `TestXxxRBACHTTP`) that send raw HTTP through the full
Echo middleware stack with mocked backends.

## Integration Tests

Integration tests build a real `osapi` binary, start all three components (NATS,
API server, agent), and exercise CLI commands end-to-end. They are guarded by a
`//go:build integration` tag and located in `test/integration/`:

```bash
$ just test
$ just go::unit-int # Run integration tests
```

The test harness allocates random ports, generates a JWT, and starts the server
automatically — no manual setup required. Tests validate JSON responses from CLI
commands with `--json` output.

## Formatting

Auto format code:

```bash
$ just go::fmt
```

## Listing Recipes

List helpful targets:

```bash
Expand Down
Loading