Skip to content

Commit 8b0cfce

Browse files
committed
Add basic testing setup
1 parent cbc5063 commit 8b0cfce

File tree

10 files changed

+244
-8
lines changed

10 files changed

+244
-8
lines changed

.github/workflows/test.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Test
2+
3+
on:
4+
push: {}
5+
6+
jobs:
7+
test:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v4
11+
12+
- name: Set up Go
13+
uses: actions/setup-go@v5
14+
with:
15+
go-version: '1.25'
16+
17+
- name: Install dependencies
18+
run: go mod download
19+
20+
- name: Run tests
21+
run: make test
22+
23+
- name: Build
24+
run: make build
25+

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ dev: $(AIR)
5151

5252
# Run tests
5353
test:
54-
go test -v ./...
54+
go test -v -timeout 30s ./...
5555

5656
# Clean generated files and binaries
5757
clean:

PATTERNS.md

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,26 +236,91 @@ make generate-all
236236

237237
## Testing Pattern
238238

239-
(TODO: To be established when implementing actual functionality)
239+
### Handler-Level Tests
240240

241+
Test by calling `ApiService` methods directly (not via HTTP). This is simple, fast, and matches the kernel API pattern.
242+
243+
**Test Helper (cmd/api/api/api_test.go):**
241244
```go
242-
package images_test
245+
package api
243246

244247
import (
245248
"context"
246249
"testing"
247250

251+
"github.com/onkernel/cloud-hypervisor-dataplane/cmd/api/config"
248252
"github.com/onkernel/cloud-hypervisor-dataplane/lib/images"
253+
"github.com/onkernel/cloud-hypervisor-dataplane/lib/instances"
254+
"github.com/onkernel/cloud-hypervisor-dataplane/lib/volumes"
255+
)
256+
257+
func newTestService(t *testing.T) *ApiService {
258+
cfg := &config.Config{
259+
DataDir: t.TempDir(), // Creates isolated temp directory per test
260+
}
261+
262+
return &ApiService{
263+
Config: cfg,
264+
ImageManager: images.NewManager(cfg.DataDir),
265+
InstanceManager: instances.NewManager(cfg.DataDir),
266+
VolumeManager: volumes.NewManager(cfg.DataDir),
267+
}
268+
}
269+
270+
func ctx() context.Context {
271+
return context.Background()
272+
}
273+
```
274+
275+
**Example Test (cmd/api/api/images_test.go):**
276+
```go
277+
package api
278+
279+
import (
280+
"testing"
281+
282+
"github.com/onkernel/cloud-hypervisor-dataplane/lib/oapi"
283+
"github.com/stretchr/testify/assert"
249284
"github.com/stretchr/testify/require"
250285
)
251286

252287
func TestGetImage_NotFound(t *testing.T) {
253-
mgr := images.NewManager("/tmp/test")
254-
_, err := mgr.GetImage(context.Background(), "fake-id")
255-
require.ErrorIs(t, err, images.ErrNotFound)
288+
svc := newTestService(t)
289+
290+
resp, err := svc.GetImage(ctx(), oapi.GetImageRequestObject{
291+
Id: "non-existent",
292+
})
293+
require.NoError(t, err)
294+
295+
notFound, ok := resp.(oapi.GetImage404JSONResponse)
296+
require.True(t, ok, "expected 404 response")
297+
assert.Equal(t, "not_found", notFound.Code)
298+
assert.Equal(t, "image not found", notFound.Message)
299+
}
300+
301+
func TestListImages_Empty(t *testing.T) {
302+
svc := newTestService(t)
303+
304+
resp, err := svc.ListImages(ctx(), oapi.ListImagesRequestObject{})
305+
require.NoError(t, err)
306+
307+
list, ok := resp.(oapi.ListImages200JSONResponse)
308+
require.True(t, ok, "expected 200 response")
309+
assert.Empty(t, list)
256310
}
257311
```
258312

313+
### Running Tests
314+
315+
```bash
316+
make test # Run all tests
317+
go test -v ./cmd/api/api/ # Run handler tests only
318+
```
319+
320+
### CI Integration
321+
322+
Tests run automatically on GitHub via `.github/workflows/test.yml` on push and PR.
323+
259324
## Comments
260325

261326
From workspace rules:

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Cloud Hypervisor Dataplane API
22

3+
[![Test](https://github.com/onkernel/cloud-hypervisor-dataplane/actions/workflows/test.yml/badge.svg)](https://github.com/onkernel/cloud-hypervisor-dataplane/actions/workflows/test.yml)
4+
35
A generic dataplane API for managing VM lifecycle using Cloud Hypervisor with OCI-based workloads. This project provides a clean, REST-ful interface for creating, managing, and orchestrating VMs from container images.
46

57
## Features
@@ -189,10 +191,18 @@ make generate-all
189191

190192
### Testing
191193

194+
Run all tests:
192195
```bash
193196
make test
194197
```
195198

199+
Run specific package:
200+
```bash
201+
go test -v ./cmd/api/api/
202+
```
203+
204+
Tests are handler-level tests that call `ApiService` methods directly (no HTTP server needed).
205+
196206
### Building
197207

198208
```bash

cmd/api/api/api_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package api
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/onkernel/cloud-hypervisor-dataplane/cmd/api/config"
8+
"github.com/onkernel/cloud-hypervisor-dataplane/lib/images"
9+
"github.com/onkernel/cloud-hypervisor-dataplane/lib/instances"
10+
"github.com/onkernel/cloud-hypervisor-dataplane/lib/volumes"
11+
)
12+
13+
// newTestService creates an ApiService for testing with temporary data directory
14+
func newTestService(t *testing.T) *ApiService {
15+
cfg := &config.Config{
16+
DataDir: t.TempDir(),
17+
}
18+
19+
return &ApiService{
20+
Config: cfg,
21+
ImageManager: images.NewManager(cfg.DataDir),
22+
InstanceManager: instances.NewManager(cfg.DataDir),
23+
VolumeManager: volumes.NewManager(cfg.DataDir),
24+
}
25+
}
26+
27+
func ctx() context.Context {
28+
return context.Background()
29+
}
30+

cmd/api/api/images_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package api
2+
3+
import (
4+
"testing"
5+
6+
"github.com/onkernel/cloud-hypervisor-dataplane/lib/oapi"
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestListImages_Empty(t *testing.T) {
12+
svc := newTestService(t)
13+
14+
resp, err := svc.ListImages(ctx(), oapi.ListImagesRequestObject{})
15+
require.NoError(t, err)
16+
17+
list, ok := resp.(oapi.ListImages200JSONResponse)
18+
require.True(t, ok, "expected 200 response")
19+
assert.Empty(t, list)
20+
}
21+
22+
func TestGetImage_NotFound(t *testing.T) {
23+
svc := newTestService(t)
24+
25+
resp, err := svc.GetImage(ctx(), oapi.GetImageRequestObject{
26+
Id: "non-existent",
27+
})
28+
require.NoError(t, err)
29+
30+
notFound, ok := resp.(oapi.GetImage404JSONResponse)
31+
require.True(t, ok, "expected 404 response")
32+
assert.Equal(t, "not_found", notFound.Code)
33+
assert.Equal(t, "image not found", notFound.Message)
34+
}
35+

cmd/api/api/instances_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package api
2+
3+
import (
4+
"testing"
5+
6+
"github.com/onkernel/cloud-hypervisor-dataplane/lib/oapi"
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestListInstances_Empty(t *testing.T) {
12+
svc := newTestService(t)
13+
14+
resp, err := svc.ListInstances(ctx(), oapi.ListInstancesRequestObject{})
15+
require.NoError(t, err)
16+
17+
list, ok := resp.(oapi.ListInstances200JSONResponse)
18+
require.True(t, ok, "expected 200 response")
19+
assert.Empty(t, list)
20+
}
21+
22+
func TestGetInstance_NotFound(t *testing.T) {
23+
svc := newTestService(t)
24+
25+
resp, err := svc.GetInstance(ctx(), oapi.GetInstanceRequestObject{
26+
Id: "non-existent",
27+
})
28+
require.NoError(t, err)
29+
30+
notFound, ok := resp.(oapi.GetInstance404JSONResponse)
31+
require.True(t, ok, "expected 404 response")
32+
assert.Equal(t, "not_found", notFound.Code)
33+
}
34+

cmd/api/api/volumes_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package api
2+
3+
import (
4+
"testing"
5+
6+
"github.com/onkernel/cloud-hypervisor-dataplane/lib/oapi"
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestListVolumes_Empty(t *testing.T) {
12+
svc := newTestService(t)
13+
14+
resp, err := svc.ListVolumes(ctx(), oapi.ListVolumesRequestObject{})
15+
require.NoError(t, err)
16+
17+
list, ok := resp.(oapi.ListVolumes200JSONResponse)
18+
require.True(t, ok, "expected 200 response")
19+
assert.Empty(t, list)
20+
}
21+
22+
func TestGetVolume_NotFound(t *testing.T) {
23+
svc := newTestService(t)
24+
25+
resp, err := svc.GetVolume(ctx(), oapi.GetVolumeRequestObject{
26+
Id: "non-existent",
27+
})
28+
require.NoError(t, err)
29+
30+
notFound, ok := resp.(oapi.GetVolume404JSONResponse)
31+
require.True(t, ok, "expected 404 response")
32+
assert.Equal(t, "not_found", notFound.Code)
33+
}
34+

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ require (
88
github.com/go-chi/chi/v5 v5.2.3
99
github.com/google/wire v0.7.0
1010
github.com/oapi-codegen/runtime v1.1.2
11+
github.com/stretchr/testify v1.11.1
1112
golang.org/x/sync v0.17.0
1213
)
1314

1415
require (
1516
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
17+
github.com/davecgh/go-spew v1.1.1 // indirect
1618
github.com/go-openapi/jsonpointer v0.21.0 // indirect
1719
github.com/go-openapi/swag v0.23.0 // indirect
1820
github.com/google/uuid v1.5.0 // indirect
@@ -22,6 +24,7 @@ require (
2224
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
2325
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
2426
github.com/perimeterx/marshmallow v1.1.5 // indirect
27+
github.com/pmezard/go-difflib v1.0.0 // indirect
2528
github.com/woodsbury/decimal128 v1.3.0 // indirect
2629
gopkg.in/yaml.v2 v2.4.0 // indirect
2730
gopkg.in/yaml.v3 v3.0.1 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99
4747
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
4848
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
4949
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
50-
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
51-
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
50+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
51+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
5252
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
5353
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
5454
github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0=

0 commit comments

Comments
 (0)