rdev/internal/domain/worker_test.go
jordan bc47e426b0 feat: Add CI pipeline proxy, DNS alias management, and worker executor system
- Add ListPipelines/GetPipeline to CIProvider port with Woodpecker adapter
- Add DNS alias endpoints: GET/POST/DELETE /projects/{id}/domains
- Implement worker executor daemon, build executor, and git operations
- Add build service, worker service, and build audit tracking
- Add worker registry with PostgreSQL adapter and migration
- Add multi-provider code agent interface (Claude Code + OpenCode)
- Add create-and-build combo endpoint
- Update landing-page cookbook to reflect all gaps closed
- Fix tech debt: unified validation, auth scopes, error wrapping, slog patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 21:05:28 -07:00

147 lines
3.0 KiB
Go

package domain
import (
"errors"
"testing"
"time"
)
func TestWorker_IsAvailable(t *testing.T) {
tests := []struct {
status WorkerStatus
want bool
}{
{WorkerStatusIdle, true},
{WorkerStatusBusy, false},
{WorkerStatusDraining, false},
{WorkerStatusOffline, false},
}
for _, tt := range tests {
t.Run(string(tt.status), func(t *testing.T) {
w := &Worker{Status: tt.status}
if got := w.IsAvailable(); got != tt.want {
t.Errorf("IsAvailable() = %v, want %v", got, tt.want)
}
})
}
}
func TestWorker_IsHealthy(t *testing.T) {
threshold := 90 * time.Second
tests := []struct {
name string
lastHeartbeat time.Time
want bool
}{
{
name: "recent heartbeat",
lastHeartbeat: time.Now().Add(-30 * time.Second),
want: true,
},
{
name: "stale heartbeat",
lastHeartbeat: time.Now().Add(-2 * time.Minute),
want: false,
},
{
name: "exactly at threshold",
lastHeartbeat: time.Now().Add(-90 * time.Second),
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &Worker{LastHeartbeat: tt.lastHeartbeat}
if got := w.IsHealthy(threshold); got != tt.want {
t.Errorf("IsHealthy() = %v, want %v", got, tt.want)
}
})
}
}
func TestWorkerStatus_IsValid(t *testing.T) {
tests := []struct {
status WorkerStatus
want bool
}{
{WorkerStatusIdle, true},
{WorkerStatusBusy, true},
{WorkerStatusDraining, true},
{WorkerStatusOffline, true},
{"unknown", false},
{"", false},
}
for _, tt := range tests {
t.Run(string(tt.status), func(t *testing.T) {
if got := tt.status.IsValid(); got != tt.want {
t.Errorf("IsValid() = %v, want %v", got, tt.want)
}
})
}
}
func TestWorker_Validate(t *testing.T) {
tests := []struct {
name string
worker Worker
wantErr error
}{
{
name: "valid worker",
worker: Worker{ID: "worker-1", Hostname: "host-1"},
wantErr: nil,
},
{
name: "missing ID",
worker: Worker{Hostname: "host-1"},
wantErr: ErrWorkerIDRequired,
},
{
name: "missing hostname",
worker: Worker{ID: "worker-1"},
wantErr: ErrWorkerHostnameRequired,
},
{
name: "both missing",
worker: Worker{},
wantErr: ErrWorkerIDRequired,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.worker.Validate()
if tt.wantErr != nil {
if !errors.Is(err, tt.wantErr) {
t.Errorf("Validate() error = %v, want %v", err, tt.wantErr)
}
} else if err != nil {
t.Errorf("Validate() unexpected error = %v", err)
}
})
}
}
func TestValidWorkerStatuses(t *testing.T) {
statuses := ValidWorkerStatuses()
if len(statuses) != 4 {
t.Errorf("expected 4 statuses, got %d", len(statuses))
}
found := make(map[WorkerStatus]bool)
for _, s := range statuses {
found[s] = true
}
expected := []WorkerStatus{WorkerStatusIdle, WorkerStatusBusy, WorkerStatusDraining, WorkerStatusOffline}
for _, s := range expected {
if !found[s] {
t.Errorf("expected %s in valid statuses", s)
}
}
}