test(backend): add process manager and steamcmd tests with stub scripts
- server_process_test.go: 16 tests covering splitArgs, hasArgPrefix, buildAutoArgs (config/port/profiles), buildArgs, resolveModPath (name/ ID/empty), buildModPath, WriteUserconfigFiles, Start/Stop lifecycle, percent-command substitution - steamcmd_test.go: 9 tests covering CheckWorkshopMod (directory/file/ empty), SteamCmdManager state machine, DownloadMods/UpdateGame - testdata/: stub arma3server_x64 and steamcmd scripts for safe process testing without real binaries
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCheckWorkshopMod(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
// Existing workshop mod
|
||||
modDir := filepath.Join(dir, "steamapps", "workshop", "content", "107410", "123456")
|
||||
os.MkdirAll(modDir, 0755)
|
||||
|
||||
if !CheckWorkshopMod(dir, "123456") {
|
||||
t.Error("CheckWorkshopMod should return true for existing mod")
|
||||
}
|
||||
|
||||
// Non-existing mod
|
||||
if CheckWorkshopMod(dir, "999999") {
|
||||
t.Error("CheckWorkshopMod should return false for non-existing mod")
|
||||
}
|
||||
|
||||
// Empty mod ID
|
||||
if CheckWorkshopMod(dir, "") {
|
||||
t.Error("CheckWorkshopMod should return false for empty mod ID")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckWorkshopMod_IsFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
// Create a file instead of directory at the expected path
|
||||
workshopDir := filepath.Join(dir, "steamapps", "workshop", "content", "107410")
|
||||
os.MkdirAll(workshopDir, 0755)
|
||||
os.WriteFile(filepath.Join(workshopDir, "111111"), []byte("not a dir"), 0644)
|
||||
|
||||
if CheckWorkshopMod(dir, "111111") {
|
||||
t.Error("CheckWorkshopMod should return false for file (not directory)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSteamCmdManager_IsRunning(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
streamer := NewLogStreamer()
|
||||
|
||||
sm := NewSteamCmdManager(dir, streamer)
|
||||
|
||||
if sm.IsRunning() {
|
||||
t.Error("IsRunning() should be false initially")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSteamCmdManager_DoubleStart(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
streamer := NewLogStreamer()
|
||||
|
||||
sm := NewSteamCmdManager(dir, streamer)
|
||||
|
||||
// First DownloadMods sets running=true. If the binary exists, the goroutine
|
||||
// handles cleanup. If it doesn't, running is reset synchronously.
|
||||
sm.DownloadMods([]string{"123456"})
|
||||
|
||||
// Second call while running should get "already running"
|
||||
err2 := sm.DownloadMods([]string{"789012"})
|
||||
if err2 == nil || err2.Error() != "steamcmd already running" {
|
||||
t.Logf("second DownloadMods: %v (may vary based on timing)", err2)
|
||||
}
|
||||
|
||||
waitForNotRunning(t, sm, 30*time.Second)
|
||||
}
|
||||
|
||||
func waitForNotRunning(t *testing.T, sm *SteamCmdManager, timeout time.Duration) {
|
||||
t.Helper()
|
||||
deadline := time.Now().Add(timeout)
|
||||
for time.Now().Before(deadline) {
|
||||
if !sm.IsRunning() {
|
||||
return
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
t.Errorf("IsRunning() still true after %v", timeout)
|
||||
}
|
||||
|
||||
func TestSteamCmdManager_StopResetsFlag(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
streamer := NewLogStreamer()
|
||||
|
||||
sm := NewSteamCmdManager(dir, streamer)
|
||||
|
||||
// After run completes (success or failure), running flag should be reset
|
||||
sm.DownloadMods([]string{"123"})
|
||||
waitForNotRunning(t, sm, 30*time.Second)
|
||||
}
|
||||
|
||||
func TestSteamCmdManager_UpdateGameArgs(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
streamer := NewLogStreamer()
|
||||
|
||||
sm := NewSteamCmdManager(dir, streamer)
|
||||
|
||||
err := sm.UpdateGame("stable", "anonymous")
|
||||
if err != nil {
|
||||
t.Logf("UpdateGame returned (expected): %v", err)
|
||||
}
|
||||
|
||||
waitForNotRunning(t, sm, 30*time.Second)
|
||||
}
|
||||
|
||||
func TestSteamCmdManager_UpdateGameBeta(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
streamer := NewLogStreamer()
|
||||
|
||||
sm := NewSteamCmdManager(dir, streamer)
|
||||
|
||||
// Test with a beta branch
|
||||
err := sm.UpdateGame("creatordlc", "anonymous")
|
||||
if err != nil {
|
||||
t.Logf("UpdateGame(creatordlc) returned (expected): %v", err)
|
||||
}
|
||||
|
||||
waitForNotRunning(t, sm, 30*time.Second)
|
||||
}
|
||||
|
||||
func TestSteamCmdManager_DownloadModEmpty(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
streamer := NewLogStreamer()
|
||||
|
||||
sm := NewSteamCmdManager(dir, streamer)
|
||||
|
||||
err := sm.DownloadMods([]string{})
|
||||
if err == nil {
|
||||
t.Error("DownloadMods with empty list should return error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSteamCmdManager_DownloadModSingle(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
streamer := NewLogStreamer()
|
||||
|
||||
sm := NewSteamCmdManager(dir, streamer)
|
||||
|
||||
err := sm.DownloadMod("123456")
|
||||
// steamcmd may or may not exist; either way the call should complete
|
||||
if err != nil {
|
||||
t.Logf("DownloadMod returned (expected): %v", err)
|
||||
}
|
||||
|
||||
waitForNotRunning(t, sm, 30*time.Second)
|
||||
}
|
||||
|
||||
func TestNewSteamCmdManager(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
streamer := NewLogStreamer()
|
||||
|
||||
sm := NewSteamCmdManager(dir, streamer)
|
||||
|
||||
if sm.serverfileDir != dir {
|
||||
t.Errorf("serverfileDir = %q, want %q", sm.serverfileDir, dir)
|
||||
}
|
||||
if sm.streamer != streamer {
|
||||
t.Error("streamer should be set")
|
||||
}
|
||||
if sm.IsRunning() {
|
||||
t.Error("new manager should not be running")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user