fetch_ml/tests/unit/scheduler/port_allocator_test.go
Jeremie Fraeys 43e6446587
feat(scheduler): implement multi-tenant job scheduler with gang scheduling
Add new scheduler component for distributed ML workload orchestration:
- Hub-based coordination for multi-worker clusters
- Pacing controller for rate limiting job submissions
- Priority queue with preemption support
- Port allocator for dynamic service discovery
- Protocol handlers for worker-scheduler communication
- Service manager with OS-specific implementations
- Connection management and state persistence
- Template system for service deployment

Includes comprehensive test suite:
- Unit tests for all core components
- Integration tests for distributed scenarios
- Benchmark tests for performance validation
- Mock fixtures for isolated testing

Refs: scheduler-architecture.md
2026-02-26 12:03:23 -05:00

91 lines
2.2 KiB
Go

package scheduler_test
import (
"testing"
"github.com/jfraeys/fetch_ml/internal/scheduler"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPortAllocator_BasicAllocation(t *testing.T) {
pa := scheduler.NewPortAllocator(10000, 10010)
// Allocate first port
port1, err := pa.Allocate("service-1")
require.NoError(t, err)
assert.Equal(t, 10000, port1)
// Allocate second port
port2, err := pa.Allocate("service-2")
require.NoError(t, err)
assert.Equal(t, 10001, port2)
}
func TestPortAllocator_Exhaustion(t *testing.T) {
pa := scheduler.NewPortAllocator(10000, 10002) // Only 3 ports available
// Allocate all ports
port1, _ := pa.Allocate("service-1")
assert.Equal(t, 10000, port1)
port2, _ := pa.Allocate("service-2")
assert.Equal(t, 10001, port2)
port3, _ := pa.Allocate("service-3")
assert.Equal(t, 10002, port3)
// Should fail - no ports left
_, err := pa.Allocate("service-4")
assert.Error(t, err)
}
func TestPortAllocator_Release(t *testing.T) {
pa := scheduler.NewPortAllocator(10000, 10005)
// Allocate and release
port, _ := pa.Allocate("service-1")
assert.Equal(t, 10000, port)
pa.Release(port)
// Should be able to allocate again
port2, err := pa.Allocate("service-2")
require.NoError(t, err)
assert.Equal(t, 10000, port2) // Reuses released port
}
func TestPortAllocator_DuplicateServiceID(t *testing.T) {
pa := scheduler.NewPortAllocator(10000, 10010)
// Allocate for service
port1, err := pa.Allocate("service-1")
require.NoError(t, err)
// Allocate again with same service ID - gets new port (current behavior)
port2, err := pa.Allocate("service-1")
require.NoError(t, err)
assert.NotEqual(t, port1, port2) // Each call returns new port
}
func TestPortAllocator_ConcurrentAccess(t *testing.T) {
pa := scheduler.NewPortAllocator(10000, 10100) // 101 ports
done := make(chan bool, 10)
// Concurrent allocations
for i := 0; i < 10; i++ {
go func(id int) {
for j := 0; j < 10; j++ {
pa.Allocate("service-")
}
done <- true
}(i)
}
for i := 0; i < 10; i++ {
<-done
}
// All 100 ports should be allocated
// (10 goroutines * 10 allocations, but only 100 unique service IDs)
}