first commit
This commit is contained in:
commit
1751544440
28 changed files with 6994 additions and 0 deletions
45
internal/database/service_benchmark_test.go
Normal file
45
internal/database/service_benchmark_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
132
internal/database/service_integration_test.go
Normal file
132
internal/database/service_integration_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
234
internal/e2e/workflow_test.go
Normal file
234
internal/e2e/workflow_test.go
Normal 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
664
internal/hub/server_test.go
Normal 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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue