Files
osmedeus/internal/executor/run_registry_test.go
T
j3ssie f5840272c5 feat: add run cancellation, event enhancements, and performance optimizations
Major features:
- Add run registry for tracking active runs with PID management
- Add API-based run cancellation with process termination
- Add event trigger input vars syntax for multi-variable extraction
- Add filter_functions with utility function support in triggers
- Add event envelope injection for full event context in workflows
- Add write coordinator for batched database operations

API improvements:
- Add logout endpoint and diffs endpoints for assets/vulnerabilities
- Add step-results listing endpoint
- Update schedule model with target, workspace, params fields
- Change run_id to run_uuid across API responses

Performance:
- Add compiled JS program caching for 60-80% faster loop conditions
- Add parallel shard rendering for 20-40% faster workflow startup
- Add memory-mapped I/O for large file line counting
- Add efficient output buffer combining in runners
- Add mtime-based cache invalidation for workflow loader

Other changes:
- Rename trigger field from trigger to triggers in workflow YAML
- Disable pongo2 HTML autoescape for shell command templates
- Update JWT expiration default to 1440 minutes (1 day)
- Change CORS default to reflect-origin for credentials support
- Add source_type field to events (run, eval, api)
- Skip copying core Unix tools to external-binaries
2026-01-24 01:11:33 +08:00

232 lines
4.8 KiB
Go

package executor
import (
"context"
"sync"
"testing"
"time"
)
func TestRunRegistryRegisterAndGet(t *testing.T) {
registry := &RunRegistry{
runs: make(map[string]*ActiveRun),
}
_, cancel := context.WithCancel(context.Background())
defer cancel()
runUUID := "test-run-123"
activeRun := registry.Register(runUUID, cancel)
if activeRun == nil {
t.Fatal("Register returned nil")
}
if activeRun.RunUUID != runUUID {
t.Errorf("Expected RunUUID %s, got %s", runUUID, activeRun.RunUUID)
}
// Test Get
retrieved := registry.Get(runUUID)
if retrieved != activeRun {
t.Error("Get returned different instance")
}
// Test Get for non-existent run
nonExistent := registry.Get("non-existent")
if nonExistent != nil {
t.Error("Get should return nil for non-existent run")
}
}
func TestRunRegistryUnregister(t *testing.T) {
registry := &RunRegistry{
runs: make(map[string]*ActiveRun),
}
_, cancel := context.WithCancel(context.Background())
defer cancel()
runUUID := "test-run-456"
registry.Register(runUUID, cancel)
// Verify registered
if registry.Get(runUUID) == nil {
t.Fatal("Run should be registered")
}
// Unregister
registry.Unregister(runUUID)
// Verify unregistered
if registry.Get(runUUID) != nil {
t.Error("Run should be unregistered")
}
}
func TestRunRegistryCancel(t *testing.T) {
registry := &RunRegistry{
runs: make(map[string]*ActiveRun),
}
ctx, cancel := context.WithCancel(context.Background())
runUUID := "test-run-789"
registry.Register(runUUID, cancel)
// Cancel should call the cancel function
_, err := registry.Cancel(runUUID)
if err != nil {
t.Errorf("Cancel returned error: %v", err)
}
// Context should be cancelled
select {
case <-ctx.Done():
// Expected
default:
t.Error("Context should be cancelled")
}
// Cancel non-existent run
_, err = registry.Cancel("non-existent")
if err == nil {
t.Error("Cancel should return error for non-existent run")
}
}
func TestRunRegistryPIDTracking(t *testing.T) {
registry := &RunRegistry{
runs: make(map[string]*ActiveRun),
}
_, cancel := context.WithCancel(context.Background())
defer cancel()
runUUID := "test-run-pid"
registry.Register(runUUID, cancel)
// Add PIDs
registry.AddPID(runUUID, 1234)
registry.AddPID(runUUID, 5678)
// Verify PIDs are tracked
activeRun := registry.Get(runUUID)
count := 0
activeRun.PIDs.Range(func(_, _ any) bool {
count++
return true
})
if count != 2 {
t.Errorf("Expected 2 PIDs, got %d", count)
}
// Remove a PID
registry.RemovePID(runUUID, 1234)
// Verify PID removed
count = 0
activeRun.PIDs.Range(func(_, _ any) bool {
count++
return true
})
if count != 1 {
t.Errorf("Expected 1 PID after removal, got %d", count)
}
}
func TestRunRegistryKillAllPIDs(t *testing.T) {
// Create a fresh ActiveRun (not using the registry to avoid syscall issues)
activeRun := &ActiveRun{
RunUUID: "test-kill",
PIDs: &sync.Map{},
StartedAt: time.Now(),
}
// Add fake PIDs (these won't exist as real processes)
activeRun.AddPID(99999)
activeRun.AddPID(99998)
// KillAllPIDs will try to kill these (and fail silently)
killed := activeRun.KillAllPIDs()
// Should report the PIDs it attempted to kill
if len(killed) != 2 {
t.Errorf("Expected 2 killed PIDs, got %d", len(killed))
}
// PIDs should be cleared
count := 0
activeRun.PIDs.Range(func(_, _ any) bool {
count++
return true
})
if count != 0 {
t.Errorf("Expected 0 PIDs after kill, got %d", count)
}
}
func TestRunRegistryConcurrency(t *testing.T) {
registry := &RunRegistry{
runs: make(map[string]*ActiveRun),
}
var wg sync.WaitGroup
// Concurrent registrations
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
_, cancel := context.WithCancel(context.Background())
defer cancel()
runUUID := "concurrent-run-" + string(rune('a'+i%26))
registry.Register(runUUID, cancel)
registry.AddPID(runUUID, i+1000)
registry.RemovePID(runUUID, i+1000)
registry.Get(runUUID)
registry.Unregister(runUUID)
}(i)
}
wg.Wait()
// Should have no leftover runs
if registry.Count() != 0 {
t.Errorf("Expected 0 runs, got %d", registry.Count())
}
}
func TestRunRegistryListActive(t *testing.T) {
registry := &RunRegistry{
runs: make(map[string]*ActiveRun),
}
_, cancel := context.WithCancel(context.Background())
defer cancel()
registry.Register("run-1", cancel)
registry.Register("run-2", cancel)
registry.Register("run-3", cancel)
active := registry.ListActive()
if len(active) != 3 {
t.Errorf("Expected 3 active runs, got %d", len(active))
}
if registry.Count() != 3 {
t.Errorf("Expected count 3, got %d", registry.Count())
}
}
func TestGetRunRegistrySingleton(t *testing.T) {
// Get the singleton twice
reg1 := GetRunRegistry()
reg2 := GetRunRegistry()
// Should be the same instance
if reg1 != reg2 {
t.Error("GetRunRegistry should return the same singleton instance")
}
}