Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bdb1c1d
test: add comprehensive tests for sandbox-host, control-plane, and fl…
aspectrr Feb 15, 2026
22d9565
fix: add in sandbox-host and control-plane
aspectrr Feb 16, 2026
823d003
fix: remove landing-page
aspectrr Feb 17, 2026
5694220
fix: fix ci stuff
aspectrr Feb 17, 2026
c002ec8
fix: fix linting
aspectrr Feb 17, 2026
68d021a
feat: replace WebSocket demo with scripted self-playing terminal anim…
aspectrr Feb 17, 2026
4867573
fix: delete demo server, make cli demo better
aspectrr Feb 17, 2026
883044e
feat: add comprehensive api test suite, billing, sdk, and web features
aspectrr Feb 18, 2026
62d6366
fix: replace hardcoded OAuth state with crypto nonce to prevent CSRF
aspectrr Feb 18, 2026
04980e3
fix: scope DeleteOrgMember by org_id to prevent cross-org IDOR
aspectrr Feb 18, 2026
4b6ea70
feat: add live boolean to CreateSandbox, remove base_image from user-…
aspectrr Feb 18, 2026
2478d74
fix: address PR #65 review findings - security, error handling, type …
aspectrr Feb 18, 2026
6fa6843
fix: delete landing-page ci, update demo scripts
aspectrr Feb 18, 2026
b66477b
fix: address remaining PR #65 review findings
aspectrr Feb 18, 2026
ecd5005
fix: address all PR #65 review findings - billing stubs, race conditi…
aspectrr Feb 18, 2026
c19853e
fix: harden API security, billing accuracy, and placement logic
aspectrr Feb 19, 2026
afce088
fix: resolve eslint errors in landing page components
aspectrr Feb 19, 2026
5a164d6
fix: consolidate billing routes and fix calculator pricing
aspectrr Feb 19, 2026
d30db5e
feat: add telemetry, harden auth/billing, improve orchestrator placement
aspectrr Feb 19, 2026
709cb28
fix: resolve eslint errors in posthog and billing page
aspectrr Feb 19, 2026
6e0ec3d
feat: wire daemon connection spinner, remove install script, update d…
aspectrr Feb 19, 2026
f28c13f
fix: address 26+ PR review findings across security, correctness, bil…
aspectrr Feb 19, 2026
173662a
fix: address 39 PR review findings across security, correctness, and …
aspectrr Feb 20, 2026
3e95723
fix: add scripts and internal host exec
aspectrr Feb 20, 2026
6a39598
feat: add onboarding endpoint, randomize host selection, improve web …
aspectrr Feb 20, 2026
0ac5c2c
fix: address 5 PR review findings - stream race, placement scoring, d…
aspectrr Feb 21, 2026
f1aa9ee
fix: address 9 PR review findings - id.Generate panic, type assertion…
aspectrr Feb 21, 2026
a57335a
fix: enforce org-scoped sandbox lookups at DB layer, fix zero-billing…
aspectrr Feb 21, 2026
3699286
feat: replace DB polling with registry-based billing metering
aspectrr Feb 21, 2026
14fa38d
fix: update docs, goreleaser
aspectrr Feb 21, 2026
cf23b3f
fix: nits on swagger types
aspectrr Feb 21, 2026
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
128 changes: 128 additions & 0 deletions control-plane/internal/api/handlers_host_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package api

import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"

"github.com/go-chi/chi/v5"
)

func TestHandleListHosts(t *testing.T) {
mock := &mockOrchestrator{
listHostsFn: func(_ context.Context) ([]*HostInfo, error) {
return []*HostInfo{
{HostID: "host-1", Hostname: "node-1", Status: "ONLINE", ActiveSandboxes: 3},
{HostID: "host-2", Hostname: "node-2", Status: "OFFLINE", ActiveSandboxes: 0},
}, nil
},
}

s := NewServer(mock, nil)
req := httptest.NewRequest(http.MethodGet, "/v1/hosts", nil)
rr := httptest.NewRecorder()

s.Router.ServeHTTP(rr, req)

if rr.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d: %s", rr.Code, rr.Body.String())
}

var resp map[string]json.RawMessage
if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil {
t.Fatalf("decode response: %v", err)
}

var count float64
if err := json.Unmarshal(resp["count"], &count); err != nil {
t.Fatalf("decode count: %v", err)
}
if int(count) != 2 {
t.Fatalf("expected count 2, got %d", int(count))
}

var hosts []HostInfo
if err := json.Unmarshal(resp["hosts"], &hosts); err != nil {
t.Fatalf("decode hosts: %v", err)
}
if hosts[0].HostID != "host-1" {
t.Fatalf("expected first host_id host-1, got %s", hosts[0].HostID)
}
}

func TestHandleGetHost(t *testing.T) {
mock := &mockOrchestrator{
getHostFn: func(_ context.Context, id string) (*HostInfo, error) {
if id == "host-1" {
return &HostInfo{
HostID: "host-1",
Hostname: "node-1",
Status: "ONLINE",
ActiveSandboxes: 5,
AvailableCPUs: 8,
AvailableMemMB: 16384,
}, nil
}
return nil, fmt.Errorf("host not found: %s", id)
},
}

s := NewServer(mock, nil)
req := httptest.NewRequest(http.MethodGet, "/v1/hosts/host-1", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("id", "host-1")
req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx))
rr := httptest.NewRecorder()

s.Router.ServeHTTP(rr, req)

if rr.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d: %s", rr.Code, rr.Body.String())
}

var resp HostInfo
if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil {
t.Fatalf("decode response: %v", err)
}
if resp.HostID != "host-1" {
t.Fatalf("expected host_id host-1, got %s", resp.HostID)
}
if resp.Hostname != "node-1" {
t.Fatalf("expected hostname node-1, got %s", resp.Hostname)
}
if resp.AvailableCPUs != 8 {
t.Fatalf("expected available_cpus 8, got %d", resp.AvailableCPUs)
}
}

func TestHandleGetHost_NotFound(t *testing.T) {
mock := &mockOrchestrator{
getHostFn: func(_ context.Context, id string) (*HostInfo, error) {
return nil, fmt.Errorf("host not found: %s", id)
},
}

s := NewServer(mock, nil)
req := httptest.NewRequest(http.MethodGet, "/v1/hosts/host-nope", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("id", "host-nope")
req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx))
rr := httptest.NewRecorder()

s.Router.ServeHTTP(rr, req)

if rr.Code != http.StatusNotFound {
t.Fatalf("expected status 404, got %d: %s", rr.Code, rr.Body.String())
}

var resp map[string]string
if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil {
t.Fatalf("decode response: %v", err)
}
if resp["error"] == "" {
t.Fatal("expected error message in response")
}
}
Loading
Loading