go-process/pkg/api/provider_test.go
Virgil cd16b014da fix(api): include health-check reason payload
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-31 19:53:19 +00:00

150 lines
3.8 KiB
Go

// SPDX-Licence-Identifier: EUPL-1.2
package api_test
import (
"net/http"
"net/http/httptest"
"os"
"testing"
process "dappco.re/go/core/process"
processapi "dappco.re/go/core/process/pkg/api"
goapi "forge.lthn.ai/core/api"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func init() {
gin.SetMode(gin.TestMode)
}
func TestProcessProvider_Name_Good(t *testing.T) {
p := processapi.NewProvider(nil, nil)
assert.Equal(t, "process", p.Name())
}
func TestProcessProvider_BasePath_Good(t *testing.T) {
p := processapi.NewProvider(nil, nil)
assert.Equal(t, "/api/process", p.BasePath())
}
func TestProcessProvider_Channels_Good(t *testing.T) {
p := processapi.NewProvider(nil, nil)
channels := p.Channels()
assert.Contains(t, channels, "process.daemon.started")
assert.Contains(t, channels, "process.daemon.stopped")
assert.Contains(t, channels, "process.daemon.health")
}
func TestProcessProvider_Describe_Good(t *testing.T) {
p := processapi.NewProvider(nil, nil)
descs := p.Describe()
assert.GreaterOrEqual(t, len(descs), 4)
// Verify all descriptions have required fields
for _, d := range descs {
assert.NotEmpty(t, d.Method)
assert.NotEmpty(t, d.Path)
assert.NotEmpty(t, d.Summary)
assert.NotEmpty(t, d.Tags)
}
for _, d := range descs {
if d.Path == "/daemons/:code/:daemon/health" {
props, ok := d.Response["properties"].(map[string]any)
require.True(t, ok)
assert.Contains(t, props, "reason")
}
}
}
func TestProcessProvider_ListDaemons_Good(t *testing.T) {
// Use a temp directory so the registry has no daemons
dir := t.TempDir()
registry := newTestRegistry(dir)
p := processapi.NewProvider(registry, nil)
r := setupRouter(p)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/api/process/daemons", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
body := w.Body.String()
assert.NotEmpty(t, body)
}
func TestProcessProvider_GetDaemon_Bad(t *testing.T) {
dir := t.TempDir()
registry := newTestRegistry(dir)
p := processapi.NewProvider(registry, nil)
r := setupRouter(p)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/api/process/daemons/test/nonexistent", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}
func TestProcessProvider_HealthCheck_NoEndpoint_Good(t *testing.T) {
dir := t.TempDir()
registry := newTestRegistry(dir)
require.NoError(t, registry.Register(process.DaemonEntry{
Code: "test",
Daemon: "nohealth",
PID: os.Getpid(),
}))
p := processapi.NewProvider(registry, nil)
r := setupRouter(p)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/api/process/daemons/test/nohealth/health", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Contains(t, w.Body.String(), "no health endpoint configured")
assert.Contains(t, w.Body.String(), "\"reason\"")
}
func TestProcessProvider_RegistersAsRouteGroup_Good(t *testing.T) {
p := processapi.NewProvider(nil, nil)
engine, err := goapi.New()
require.NoError(t, err)
engine.Register(p)
assert.Len(t, engine.Groups(), 1)
assert.Equal(t, "process", engine.Groups()[0].Name())
}
func TestProcessProvider_StreamGroup_Good(t *testing.T) {
p := processapi.NewProvider(nil, nil)
engine, err := goapi.New()
require.NoError(t, err)
engine.Register(p)
// Engine.Channels() discovers StreamGroups
channels := engine.Channels()
assert.Contains(t, channels, "process.daemon.started")
}
// -- Test helpers -------------------------------------------------------------
func setupRouter(p *processapi.ProcessProvider) *gin.Engine {
r := gin.New()
rg := r.Group(p.BasePath())
p.RegisterRoutes(rg)
return r
}
// newTestRegistry creates a process.Registry backed by a test directory.
func newTestRegistry(dir string) *process.Registry {
return process.NewRegistry(dir)
}