Skip to main content
Glama
restart_integration_test.go6.65 kB
package tui import ( "os" "testing" "time" "github.com/standardbeagle/brummer/internal/process" "github.com/standardbeagle/brummer/pkg/events" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestRestartProcessIntegration(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Create a package.json with a test script that fails on restart packageJSON := `{ "name": "test-app", "scripts": { "server": "echo 'Server starting' && sleep 1 && echo 'Server running' && sleep 10" } }` err := writeFile(tempDir+"/package.json", packageJSON) require.NoError(t, err) // Create process manager eventBus := events.NewEventBus() processMgr, err := process.NewManager(tempDir, eventBus, true) // true = has package.json require.NoError(t, err) // Start the initial process proc, err := processMgr.StartScript("server") require.NoError(t, err) require.NotNil(t, proc) // Wait for process to be running time.Sleep(100 * time.Millisecond) // Verify initial process is running allProcs := processMgr.GetAllProcesses() require.Len(t, allProcs, 1) assert.Equal(t, process.StatusRunning, allProcs[0].Status) initialPID := allProcs[0].ID t.Logf("Initial process: %s (PID: %s, Status: %s)", allProcs[0].Name, allProcs[0].ID, allProcs[0].Status) // Now simulate the restart operation that happens when 'r' is pressed // This mimics handleRestartProcess function timeout := 5 * time.Second // Step 1: Stop the process and wait err = processMgr.StopProcessAndWait(proc.ID, timeout) require.NoError(t, err, "StopProcessAndWait should succeed") // Step 2: Start the script again newProc, err := processMgr.StartScript("server") if err != nil { t.Logf("StartScript failed: %v", err) } // Wait a moment for processes to settle time.Sleep(200 * time.Millisecond) // Check the final state finalProcs := processMgr.GetAllProcesses() t.Logf("Final process count: %d", len(finalProcs)) for i, p := range finalProcs { t.Logf("Process %d: %s (PID: %s, Status: %s)", i+1, p.Name, p.ID, p.Status) } // The bug would manifest as: // 1. Original process showing as failed // 2. New process also showing as failed // 3. Total of 2 processes instead of 1 // What we expect: // - Either 1 successful process (old one cleaned up) // - Or 1 failed + 1 successful (if cleanup doesn't happen) // // What the bug shows: // - 2 failed processes // Count processes by status var runningCount, failedCount, stoppedCount, successCount int for _, p := range finalProcs { switch p.Status { case process.StatusRunning: runningCount++ case process.StatusFailed: failedCount++ case process.StatusStopped: stoppedCount++ case process.StatusSuccess: successCount++ } } t.Logf("Status counts - Running: %d, Failed: %d, Stopped: %d, Success: %d", runningCount, failedCount, stoppedCount, successCount) // The restart should result in exactly one process being in a good state // Either the new process should be running, or if there was an error, // we should have a clear reason why if err != nil { // If StartScript failed, we should have 1 stopped/failed process (the original) assert.LessOrEqual(t, len(finalProcs), 2, "Should not have more than 2 processes after failed restart") // The original process should not be running for _, p := range finalProcs { if p.ID == initialPID { assert.NotEqual(t, process.StatusRunning, p.Status, "Original process should not be running after restart") } } } else { // If StartScript succeeded, we should have a running process require.NotNil(t, newProc, "New process should be created") assert.Equal(t, process.StatusRunning, newProc.Status, "New process should be running") // The key assertion: we should not have 2 failed processes assert.Less(t, failedCount, 2, "Should not have 2 failed processes after restart") // Ideally, we should have exactly 1 running process assert.Equal(t, 1, runningCount, "Should have exactly 1 running process after successful restart") } } func writeFile(path, content string) error { file, err := os.Create(path) if err != nil { return err } defer file.Close() _, err = file.WriteString(content) return err } func TestRestartProcessWithPortConflict(t *testing.T) { // This test simulates a scenario where restart might fail due to port conflicts // which could be similar to what happens with the python server tempDir := t.TempDir() // Create a package.json with a script that uses a specific port packageJSON := `{ "name": "test-app", "scripts": { "server": "python3 -m http.server 3001" } }` err := writeFile(tempDir+"/package.json", packageJSON) require.NoError(t, err) // Create process manager eventBus := events.NewEventBus() processMgr, err := process.NewManager(tempDir, eventBus, true) require.NoError(t, err) // Start the initial process proc, err := processMgr.StartScript("server") require.NoError(t, err) require.NotNil(t, proc) // Wait for process to be running time.Sleep(200 * time.Millisecond) // Verify initial process is running allProcs := processMgr.GetAllProcesses() require.Len(t, allProcs, 1) t.Logf("Initial process: %s (PID: %s, Status: %s)", allProcs[0].Name, allProcs[0].ID, allProcs[0].Status) // Simulate restart with short timeout (might cause issues) timeout := 1 * time.Second // Shorter timeout // Step 1: Stop the process and wait err = processMgr.StopProcessAndWait(proc.ID, timeout) if err != nil { t.Logf("StopProcessAndWait failed: %v", err) } // Step 2: Immediately try to start the script again (potential conflict) _, err = processMgr.StartScript("server") if err != nil { t.Logf("StartScript failed: %v", err) } // Wait a moment for processes to settle time.Sleep(500 * time.Millisecond) // Check the final state finalProcs := processMgr.GetAllProcesses() t.Logf("Final process count: %d", len(finalProcs)) for i, p := range finalProcs { t.Logf("Process %d: %s (PID: %s, Status: %s)", i+1, p.Name, p.ID, p.Status) } // Count failed processes - this is where the bug manifests var failedCount int for _, p := range finalProcs { if p.GetStatus() == process.StatusFailed { failedCount++ } } // The bug: restart creates 2 failed processes instead of 1 working process if failedCount >= 2 { t.Errorf("BUG REPRODUCED: Found %d failed processes after restart (should be 0 or 1)", failedCount) // Additional debugging for _, p := range finalProcs { if p.GetStatus() == process.StatusFailed { t.Logf("Failed process: %s (PID: %s)", p.Name, p.ID) } } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/standardbeagle/brummer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server