first commit

This commit is contained in:
robojerk 2025-08-18 23:45:01 -07:00
commit 1751544440
28 changed files with 6994 additions and 0 deletions

View file

@ -0,0 +1,45 @@
package database
import (
"testing"
"github.com/debian/deb-orchestrator/internal/models"
)
// BenchmarkTaskCreation benchmarks task creation performance
func BenchmarkTaskCreation(b *testing.B) {
service := NewMockService()
b.ResetTimer()
for i := 0; i < b.N; i++ {
taskReq := models.TaskRequest{
Method: "build",
Args: []string{"package1"},
Priority: 1,
Arch: "amd64",
Channel: "stable",
}
task := models.NewTask(taskReq)
service.CreateTask(task)
}
}
// BenchmarkHostCreation benchmarks host creation performance
func BenchmarkHostCreation(b *testing.B) {
service := NewMockService()
b.ResetTimer()
for i := 0; i < b.N; i++ {
hostReq := models.HostRequest{
Name: "benchmark-host",
Arch: "amd64",
Channel: "stable",
Capacity: 10,
Hostname: "192.168.1.100",
Port: 22,
Capabilities: map[string]interface{}{"build": true},
}
host := models.NewHost(hostReq)
service.CreateHost(host)
}
}

View file

@ -0,0 +1,132 @@
package database
import (
"fmt"
"testing"
"github.com/debian/deb-orchestrator/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestServiceIntegration tests the complete service layer integration
func TestServiceIntegration(t *testing.T) {
service := NewMockService()
require.NotNil(t, service)
t.Run("Complete Task Lifecycle", func(t *testing.T) {
// Create a task
taskReq := models.TaskRequest{
Method: "build",
Args: []string{"package1", "package2"},
Priority: 1,
Arch: "amd64",
Channel: "stable",
}
task := models.NewTask(taskReq)
// Test task creation
err := service.CreateTask(task)
assert.NoError(t, err)
assert.Equal(t, int64(1), task.ID)
// Test task assignment
err = service.AssignTask(task.ID, 1)
assert.NoError(t, err)
// Test task start
err = service.StartTask(task.ID)
assert.NoError(t, err)
// Test task completion
err = service.CompleteTask(task.ID, "Build completed successfully")
assert.NoError(t, err)
// Verify final state
retrievedTask, err := service.GetTaskByID(task.ID)
assert.NoError(t, err)
assert.Equal(t, models.TaskStateCompleted, retrievedTask.State)
assert.Equal(t, "Build completed successfully", retrievedTask.Result)
})
t.Run("Complete Host Lifecycle", func(t *testing.T) {
// Create a host
hostReq := models.HostRequest{
Name: "integration-test-host",
Arch: "amd64",
Channel: "stable",
Capacity: 10,
Hostname: "192.168.1.100",
Port: 22,
Capabilities: map[string]interface{}{"docker": true, "build": true},
}
host := models.NewHost(hostReq)
// Test host creation
err := service.CreateHost(host)
assert.NoError(t, err)
assert.Equal(t, int64(1), host.ID)
// Test host load update
err = service.UpdateHostLoad(host.ID, 5)
assert.NoError(t, err)
// Test host heartbeat (using Heartbeat method)
err = service.Heartbeat(host.ID)
assert.NoError(t, err)
// Test host state update
err = service.UpdateHostState(host.ID, models.HostStateBusy)
assert.NoError(t, err)
// Verify final state
retrievedHost, err := service.GetHostByID(host.ID)
assert.NoError(t, err)
assert.Equal(t, models.HostStateBusy, retrievedHost.State)
assert.Equal(t, 5, retrievedHost.CurrentLoad)
assert.True(t, retrievedHost.IsAvailable())
})
}
// TestServiceConcurrency tests concurrent access patterns
func TestServiceConcurrency(t *testing.T) {
service := NewMockService()
require.NotNil(t, service)
// Test concurrent host creation
const numHosts = 20
errors := make(chan error, numHosts)
for i := 0; i < numHosts; i++ {
go func(id int) {
hostReq := models.HostRequest{
Name: fmt.Sprintf("concurrent-host-%d", id),
Arch: "amd64",
Channel: "stable",
Capacity: id%10 + 1,
Hostname: fmt.Sprintf("192.168.1.%d", 200+id),
Port: 22,
Capabilities: map[string]interface{}{"build": true},
}
host := models.NewHost(hostReq)
err := service.CreateHost(host)
errors <- err
}(i)
}
// Collect results
for i := 0; i < numHosts; i++ {
err := <-errors
assert.NoError(t, err)
}
// Verify all hosts were created
stats, err := service.GetHostStats()
assert.NoError(t, err)
// Check if stats is a map and has the expected structure
if hostStats, ok := stats.(map[string]int); ok {
assert.GreaterOrEqual(t, hostStats["total_hosts"], numHosts)
} else {
t.Logf("Stats type: %T, value: %v", stats, stats)
}
}

View file

@ -0,0 +1,234 @@
package e2e
import (
"testing"
"github.com/debian/deb-orchestrator/internal/database"
"github.com/debian/deb-orchestrator/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestCompleteBuildWorkflow tests the complete build workflow
func TestCompleteBuildWorkflow(t *testing.T) {
service := database.NewMockService()
require.NotNil(t, service)
// Step 1: Register a build host
hostReq := models.HostRequest{
Name: "e2e-build-host",
Arch: "amd64",
Channel: "stable",
Capacity: 5,
Hostname: "192.168.1.200",
Port: 22,
Capabilities: map[string]interface{}{"build": true},
}
host := models.NewHost(hostReq)
err := service.CreateHost(host)
require.NoError(t, err)
// Step 2: Create a build task
taskReq := models.TaskRequest{
Method: "build",
Args: []string{"test-package"},
Priority: 1,
Arch: "amd64",
Channel: "stable",
}
task := models.NewTask(taskReq)
err = service.CreateTask(task)
require.NoError(t, err)
// Step 3: Complete the workflow
err = service.AssignTask(task.ID, host.ID)
require.NoError(t, err)
err = service.StartTask(task.ID)
require.NoError(t, err)
err = service.CompleteTask(task.ID, "Build completed successfully")
require.NoError(t, err)
// Verify final state
retrievedTask, err := service.GetTaskByID(task.ID)
require.NoError(t, err)
assert.Equal(t, models.TaskStateCompleted, retrievedTask.State)
assert.Equal(t, host.ID, retrievedTask.HostID)
}
// TestHostFailoverWorkflow tests the failover and recovery workflow
func TestHostFailoverWorkflow(t *testing.T) {
service := database.NewMockService()
require.NotNil(t, service)
t.Run("Host Failure and Task Recovery", func(t *testing.T) {
// Create two hosts
host1 := models.NewHost(models.HostRequest{
Name: "primary-host",
Arch: "amd64",
Channel: "stable",
Capacity: 3,
Hostname: "192.168.1.100",
Port: 22,
Capabilities: map[string]interface{}{"build": true},
})
err := service.CreateHost(host1)
require.NoError(t, err)
host2 := models.NewHost(models.HostRequest{
Name: "backup-host",
Arch: "amd64",
Channel: "stable",
Capacity: 3,
Hostname: "192.168.1.101",
Port: 22,
Capabilities: map[string]interface{}{"build": true},
})
err = service.CreateHost(host2)
require.NoError(t, err)
// Create and assign tasks to primary host
task1 := models.NewTask(models.TaskRequest{
Method: "build",
Args: []string{"package1"},
Priority: 1,
Arch: "amd64",
Channel: "stable",
})
err = service.CreateTask(task1)
require.NoError(t, err)
task2 := models.NewTask(models.TaskRequest{
Method: "build",
Args: []string{"package2"},
Priority: 1,
Arch: "amd64",
Channel: "stable",
})
err = service.CreateTask(task2)
require.NoError(t, err)
// Assign tasks to primary host
err = service.AssignTask(task1.ID, host1.ID)
require.NoError(t, err)
err = service.AssignTask(task2.ID, host1.ID)
require.NoError(t, err)
// Start tasks
err = service.StartTask(task1.ID)
require.NoError(t, err)
err = service.StartTask(task2.ID)
require.NoError(t, err)
// Simulate primary host failure by marking it as offline
err = service.UpdateHostState(host1.ID, models.HostStateOffline)
require.NoError(t, err)
// Verify tasks are in running state but host is offline
retrievedTask1, err := service.GetTaskByID(task1.ID)
require.NoError(t, err)
assert.Equal(t, models.TaskStateRunning, retrievedTask1.State)
retrievedHost1, err := service.GetHostByID(host1.ID)
require.NoError(t, err)
assert.Equal(t, models.HostStateOffline, retrievedHost1.State)
// Simulate task recovery by reassigning to backup host
err = service.AssignTask(task1.ID, host2.ID)
require.NoError(t, err)
err = service.AssignTask(task2.ID, host2.ID)
require.NoError(t, err)
// Complete tasks on backup host
err = service.CompleteTask(task1.ID, "Build completed on backup host")
require.NoError(t, err)
err = service.CompleteTask(task2.ID, "Build completed on backup host")
require.NoError(t, err)
// Verify tasks completed successfully
finalTask1, err := service.GetTaskByID(task1.ID)
require.NoError(t, err)
assert.Equal(t, models.TaskStateCompleted, finalTask1.State)
assert.Equal(t, host2.ID, finalTask1.HostID)
finalTask2, err := service.GetTaskByID(task2.ID)
require.NoError(t, err)
assert.Equal(t, models.TaskStateCompleted, finalTask2.State)
assert.Equal(t, host2.ID, finalTask2.HostID)
})
}
// TestSystemStability tests system stability under load
func TestSystemStability(t *testing.T) {
service := database.NewMockService()
require.NotNil(t, service)
t.Run("High Load Stability", func(t *testing.T) {
const numOperations = 100
// Create hosts
hosts := make([]*models.Host, 5)
for i := 0; i < 5; i++ {
hostReq := models.HostRequest{
Name: fmt.Sprintf("stability-host-%d", i),
Arch: "amd64",
Channel: "stable",
Capacity: 10,
Hostname: fmt.Sprintf("192.168.1.%d", 300+i),
Port: 22,
Capabilities: map[string]interface{}{"build": true},
}
host := models.NewHost(hostReq)
err := service.CreateHost(host)
require.NoError(t, err)
hosts[i] = host
}
// Create and process many tasks
start := time.Now()
for i := 0; i < numOperations; i++ {
taskReq := models.TaskRequest{
Method: "build",
Args: []string{fmt.Sprintf("stability-package-%d", i)},
Priority: i % 3,
Arch: "amd64",
Channel: "stable",
}
task := models.NewTask(taskReq)
err := service.CreateTask(task)
require.NoError(t, err)
// Assign to random host
hostIndex := i % len(hosts)
err = service.AssignTask(task.ID, hosts[hostIndex].ID)
require.NoError(t, err)
// Start and complete immediately
err = service.StartTask(task.ID)
require.NoError(t, err)
err = service.CompleteTask(task.ID, "Stability test completed")
require.NoError(t, err)
}
duration := time.Since(start)
// Performance assertions
assert.Less(t, duration, 5*time.Second, "High load operations should complete within 5 seconds")
avgTime := duration / numOperations
assert.Less(t, avgTime, 50*time.Millisecond, "Average operation time should be under 50ms")
t.Logf("Processed %d operations in %v (avg: %v per operation)",
numOperations, duration, avgTime)
// Verify system integrity
stats, err := service.GetTaskStats()
require.NoError(t, err)
assert.GreaterOrEqual(t, stats.(map[string]int)["total_tasks"], numOperations)
// Verify all hosts are available
for _, host := range hosts {
retrievedHost, err := service.GetHostByID(host.ID)
require.NoError(t, err)
assert.True(t, retrievedHost.IsAvailable())
}
})
}

664
internal/hub/server_test.go Normal file
View file

@ -0,0 +1,664 @@
package hub
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/debian/deb-orchestrator/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestServerHealthEndpoint tests the health check endpoint
func TestServerHealthEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create request
req, err := http.NewRequest("GET", "/health", nil)
require.NoError(t, err)
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.healthHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusOK, rr.Code)
// Check response body
var response map[string]interface{}
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, "ok", response["status"])
}
// TestServerCreateTaskEndpoint tests the task creation endpoint
func TestServerCreateTaskEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create task request
taskReq := models.TaskRequest{
Method: "build",
Args: []string{"package1"},
Priority: 1,
Arch: "amd64",
Channel: "stable",
}
// Marshal request body
body, err := json.Marshal(taskReq)
require.NoError(t, err)
// Create request
req, err := http.NewRequest("POST", "/api/v1/tasks", bytes.NewBuffer(body))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.createTaskHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusCreated, rr.Code)
// Check response body
var response map[string]interface{}
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.NotEmpty(t, response["id"])
}
// TestServerListTasksEndpoint tests the task listing endpoint
func TestServerListTasksEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create test tasks
task1 := models.NewTask(models.TaskRequest{
Method: "build",
Args: []string{"package1"},
Priority: 1,
Arch: "amd64",
Channel: "stable",
})
task1.ID = 1
server.tasks[1] = task1
req, err := http.NewRequest("GET", "/api/v1/tasks", nil)
require.NoError(t, err)
rr := httptest.NewRecorder()
server.listTasksHandler(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
var response []map[string]interface{}
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Len(t, response, 1)
}
// TestServerGetTaskEndpoint tests the individual task retrieval endpoint
func TestServerGetTaskEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create a test task
task := models.NewTask(models.TaskRequest{
Method: "build",
Args: []string{"package1"},
Priority: 1,
Arch: "amd64",
Channel: "stable",
})
task.ID = 1
server.tasks[1] = task
// Create request with task ID in URL
req, err := http.NewRequest("GET", "/api/v1/tasks/1", nil)
require.NoError(t, err)
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.getTaskHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusOK, rr.Code)
// Check response body
var response map[string]interface{}
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, float64(1), response["id"])
assert.Equal(t, "build", response["method"])
}
// TestServerAssignTaskEndpoint tests the task assignment endpoint
func TestServerAssignTaskEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create a test task
task := models.NewTask(models.TaskRequest{
Method: "build",
Args: []string{"package1"},
Priority: 1,
Arch: "amd64",
Channel: "stable",
})
task.ID = 1
server.tasks[1] = task
// Create assignment request
assignmentReq := map[string]interface{}{
"host_id": 1,
}
// Marshal request body
body, err := json.Marshal(assignmentReq)
require.NoError(t, err)
// Create request
req, err := http.NewRequest("POST", "/api/v1/tasks/1/assign", bytes.NewBuffer(body))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.assignTaskHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusOK, rr.Code)
// Verify task was assigned
assert.Equal(t, int64(1), task.HostID)
assert.Equal(t, models.TaskStateAssigned, task.State)
}
// TestServerStartTaskEndpoint tests the task start endpoint
func TestServerStartTaskEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create a test task
task := models.NewTask(models.TaskRequest{
Method: "build",
Args: []string{"package1"},
Priority: 1,
Arch: "amd64",
Channel: "stable",
})
task.ID = 1
server.tasks[1] = task
// Create request
req, err := http.NewRequest("POST", "/api/v1/tasks/1/start", nil)
require.NoError(t, err)
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.startTaskHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusOK, rr.Code)
// Verify task was started
assert.Equal(t, models.TaskStateRunning, task.State)
assert.NotZero(t, task.Started)
}
// TestServerCompleteTaskEndpoint tests the task completion endpoint
func TestServerCompleteTaskEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create a test task
task := models.NewTask(models.TaskRequest{
Method: "build",
Args: []string{"package1"},
Priority: 1,
Arch: "amd64",
Channel: "stable",
})
task.ID = 1
server.tasks[1] = task
// Create completion request
completionReq := map[string]interface{}{
"result": "Build completed successfully",
}
// Marshal request body
body, err := json.Marshal(completionReq)
require.NoError(t, err)
// Create request
req, err := http.NewRequest("POST", "/api/v1/tasks/1/complete", bytes.NewBuffer(body))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.completeTaskHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusOK, rr.Code)
// Verify task was completed
assert.Equal(t, models.TaskStateCompleted, task.State)
assert.Equal(t, "Build completed successfully", task.Result)
assert.NotZero(t, task.Completed)
}
// TestServerFailTaskEndpoint tests the task failure endpoint
func TestServerFailTaskEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create a test task
task := models.NewTask(models.TaskRequest{
Method: "build",
Args: []string{"package1"},
Priority: 1,
Arch: "amd64",
Channel: "stable",
})
task.ID = 1
server.tasks[1] = task
// Create failure request
failureReq := map[string]interface{}{
"error": "Build failed due to dependency issues",
}
// Marshal request body
body, err := json.Marshal(failureReq)
require.NoError(t, err)
// Create request
req, err := http.NewRequest("POST", "/api/v1/tasks/1/fail", bytes.NewBuffer(body))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.failTaskHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusOK, rr.Code)
// Verify task was failed
assert.Equal(t, models.TaskStateFailed, task.State)
assert.Equal(t, "Build failed due to dependency issues", task.Result)
assert.NotZero(t, task.Completed)
}
// TestServerListHostsEndpoint tests the host listing endpoint
func TestServerListHostsEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create some test hosts
host1 := &Host{
ID: 1,
Name: "host1",
Arch: "amd64",
Status: "online",
LastSeen: time.Now(),
}
host2 := &Host{
ID: 2,
Name: "host2",
Arch: "arm64",
Status: "online",
LastSeen: time.Now(),
}
server.hosts[1] = host1
server.hosts[2] = host2
// Create request
req, err := http.NewRequest("GET", "/api/v1/hosts", nil)
require.NoError(t, err)
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.listHostsHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusOK, rr.Code)
// Check response body
var response []map[string]interface{}
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Len(t, response, 2)
}
// TestServerRegisterHostEndpoint tests the host registration endpoint
func TestServerRegisterHostEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create host registration request
hostReq := map[string]interface{}{
"name": "new-host",
"arch": "amd64",
"status": "online",
}
// Marshal request body
body, err := json.Marshal(hostReq)
require.NoError(t, err)
// Create request
req, err := http.NewRequest("POST", "/api/v1/hosts", bytes.NewBuffer(body))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.registerHostHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusCreated, rr.Code)
// Check response body
var response map[string]interface{}
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.NotEmpty(t, response["id"])
assert.Equal(t, "new-host", response["name"])
}
// TestServerHostHeartbeatEndpoint tests the host heartbeat endpoint
func TestServerHostHeartbeatEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create a test host
host := &Host{
ID: 1,
Name: "test-host",
Arch: "amd64",
Status: "online",
LastSeen: time.Now().Add(-time.Hour), // Old timestamp
}
server.hosts[1] = host
// Create request
req, err := http.NewRequest("POST", "/api/v1/hosts/1/heartbeat", nil)
require.NoError(t, err)
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.hostHeartbeatHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusOK, rr.Code)
// Verify host heartbeat was updated
assert.True(t, host.LastSeen.After(time.Now().Add(-time.Minute)))
}
// TestServerCreateBuildEndpoint tests the build creation endpoint
func TestServerCreateBuildEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create build request
buildReq := map[string]interface{}{
"package": "test-package",
"arch": "amd64",
"channel": "stable",
}
// Marshal request body
body, err := json.Marshal(buildReq)
require.NoError(t, err)
// Create request
req, err := http.NewRequest("POST", "/api/v1/builds", bytes.NewBuffer(body))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.createBuildHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusCreated, rr.Code)
// Check response body
var response map[string]interface{}
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.NotEmpty(t, response["id"])
}
// TestServerListBuildsEndpoint tests the build listing endpoint
func TestServerListBuildsEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create request
req, err := http.NewRequest("GET", "/api/v1/builds", nil)
require.NoError(t, err)
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.listBuildsHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusOK, rr.Code)
// Check response body
var response []map[string]interface{}
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.NotNil(t, response)
}
// TestServerGetBuildEndpoint tests the individual build retrieval endpoint
func TestServerGetBuildEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create request
req, err := http.NewRequest("GET", "/api/v1/builds/1", nil)
require.NoError(t, err)
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.getBuildHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusOK, rr.Code)
}
// TestServerRunSchedulerEndpoint tests the scheduler execution endpoint
func TestServerRunSchedulerEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create request
req, err := http.NewRequest("POST", "/api/v1/scheduler/run", nil)
require.NoError(t, err)
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.runSchedulerHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusOK, rr.Code)
// Check response body
var response map[string]interface{}
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, "Scheduler executed", response["message"])
}
// TestServerGetSchedulerStatsEndpoint tests the scheduler stats endpoint
func TestServerGetSchedulerStatsEndpoint(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Create request
req, err := http.NewRequest("GET", "/api/v1/scheduler/stats", nil)
require.NoError(t, err)
// Create response recorder
rr := httptest.NewRecorder()
// Call handler
server.getSchedulerStatsHandler(rr, req)
// Check status code
assert.Equal(t, http.StatusOK, rr.Code)
// Check response body
var response map[string]interface{}
err = json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.NotNil(t, response)
}
// TestServerErrorHandling tests error handling in various scenarios
func TestServerErrorHandling(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
t.Run("Get Non-existent Task", func(t *testing.T) {
// Create request for non-existent task
req, err := http.NewRequest("GET", "/api/v1/tasks/999", nil)
require.NoError(t, err)
rr := httptest.NewRecorder()
server.getTaskHandler(rr, req)
// Should return 404
assert.Equal(t, http.StatusNotFound, rr.Code)
})
t.Run("Assign Non-existent Task", func(t *testing.T) {
// Create assignment request for non-existent task
assignmentReq := map[string]interface{}{
"host_id": 1,
}
body, err := json.Marshal(assignmentReq)
require.NoError(t, err)
req, err := http.NewRequest("POST", "/api/v1/tasks/999/assign", bytes.NewBuffer(body))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
server.assignTaskHandler(rr, req)
// Should return 404
assert.Equal(t, http.StatusNotFound, rr.Code)
})
t.Run("Invalid JSON in Request", func(t *testing.T) {
// Create request with invalid JSON
req, err := http.NewRequest("POST", "/api/v1/tasks", bytes.NewBuffer([]byte("invalid json")))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
server.createTaskHandler(rr, req)
// Should return 400
assert.Equal(t, http.StatusBadRequest, rr.Code)
})
}
// TestServerConcurrency tests concurrent access to the server
func TestServerConcurrency(t *testing.T) {
server := NewServer()
require.NotNil(t, server)
// Test concurrent task creation
const numTasks = 10
errors := make(chan error, numTasks)
for i := 0; i < numTasks; i++ {
go func(id int) {
taskReq := models.TaskRequest{
Method: "build",
Args: []string{fmt.Sprintf("package%d", id)},
Priority: id % 3,
Arch: "amd64",
Channel: "stable",
}
body, err := json.Marshal(taskReq)
if err != nil {
errors <- err
return
}
req, err := http.NewRequest("POST", "/api/v1/tasks", bytes.NewBuffer(body))
if err != nil {
errors <- err
return
}
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
server.createTaskHandler(rr, req)
if rr.Code != http.StatusCreated {
errors <- fmt.Errorf("expected status 201, got %d", rr.Code)
return
}
errors <- nil
}(i)
}
// Collect results
for i := 0; i < numTasks; i++ {
err := <-errors
assert.NoError(t, err)
}
// Verify all tasks were created
assert.Len(t, server.tasks, numTasks)
}