package main import "testing" // TestNewStartingSystems_layout verifies the 5-entry slice is indexed by // int(RoomRole) and that each entry carries the correct role and cap. func TestNewStartingSystems_layout(t *testing.T) { sys := NewStartingSystems() if len(sys) != 5 { t.Fatalf("expected 5 systems, got %d", len(sys)) } type want struct { role RoomRole maxLevel int } cases := []want{ {RolePilot, 1}, {RoleWeapons, 4}, {RoleShields, 4}, {RoleMedBay, 2}, {RoleEngines, 4}, } for _, w := range cases { idx := int(w.role) s := sys[idx] if s.Role != w.role { t.Errorf("sys[%d].Role = %v, want %v", idx, s.Role, w.role) } if s.MaxLevel != w.maxLevel { t.Errorf("sys[%d].MaxLevel = %d, want %d", idx, s.MaxLevel, w.maxLevel) } if s.PowerLevel != 0 { t.Errorf("sys[%d].PowerLevel = %d, want 0", idx, s.PowerLevel) } } } // TestReactorUsed_zero verifies that fresh systems report zero reactor usage. func TestReactorUsed_zero(t *testing.T) { sys := NewStartingSystems() if got := reactorUsed(sys); got != 0 { t.Fatalf("reactorUsed on fresh systems = %d, want 0", got) } } // TestReactorUsed_sums verifies that reactorUsed returns the sum of all PowerLevels. func TestReactorUsed_sums(t *testing.T) { sys := NewStartingSystems() // Directly set some levels for this test only (bypassing addPower to isolate the sum logic). sys[int(RolePilot)].PowerLevel = 1 sys[int(RoleWeapons)].PowerLevel = 3 sys[int(RoleEngines)].PowerLevel = 2 want := 1 + 3 + 2 if got := reactorUsed(sys); got != want { t.Fatalf("reactorUsed = %d, want %d", got, want) } } // TestAddPower_happy verifies a normal add when below cap and reactor has headroom. func TestAddPower_happy(t *testing.T) { sys := NewStartingSystems() ok := addPower(sys, RoleWeapons, ReactorCap) if !ok { t.Fatal("addPower returned false, expected true") } if sys[int(RoleWeapons)].PowerLevel != 1 { t.Fatalf("PowerLevel = %d, want 1", sys[int(RoleWeapons)].PowerLevel) } } // TestAddPower_systemAtCap verifies that adding beyond MaxLevel is rejected. func TestAddPower_systemAtCap(t *testing.T) { sys := NewStartingSystems() // Pilot cap is 1; add once to hit the cap. if !addPower(sys, RolePilot, ReactorCap) { t.Fatal("first addPower on Pilot should succeed") } if sys[int(RolePilot)].PowerLevel != 1 { t.Fatalf("expected level 1 after first add, got %d", sys[int(RolePilot)].PowerLevel) } // Now at cap — must be rejected. ok := addPower(sys, RolePilot, ReactorCap) if ok { t.Fatal("addPower at cap returned true, expected false") } if sys[int(RolePilot)].PowerLevel != 1 { t.Fatalf("PowerLevel changed after rejected add: got %d", sys[int(RolePilot)].PowerLevel) } } // TestAddPower_reactorFull verifies that adding power when reactor is exhausted is rejected. func TestAddPower_reactorFull(t *testing.T) { sys := NewStartingSystems() // Fill reactor to cap using Weapons (cap 4) and Engines (cap 4). for i := 0; i < 4; i++ { if !addPower(sys, RoleWeapons, ReactorCap) { t.Fatalf("addPower Weapons step %d failed unexpectedly", i) } } for i := 0; i < 4; i++ { if !addPower(sys, RoleEngines, ReactorCap) { t.Fatalf("addPower Engines step %d failed unexpectedly", i) } } if reactorUsed(sys) != ReactorCap { t.Fatalf("expected reactor full (%d), got %d", ReactorCap, reactorUsed(sys)) } // Shields is below cap but reactor is full — must be rejected. ok := addPower(sys, RoleShields, ReactorCap) if ok { t.Fatal("addPower with full reactor returned true, expected false") } if sys[int(RoleShields)].PowerLevel != 0 { t.Fatalf("PowerLevel changed on rejected add: got %d", sys[int(RoleShields)].PowerLevel) } } // TestAddPower_invariantsHold verifies IV3 and IV4 across all outcomes of addPower. func TestAddPower_invariantsHold(t *testing.T) { checkInvariants := func(sys []System) { t.Helper() used := reactorUsed(sys) if used > ReactorCap { t.Errorf("IV3 violated: reactorUsed %d > ReactorCap %d", used, ReactorCap) } for i, s := range sys { if s.PowerLevel < 0 || s.PowerLevel > s.MaxLevel { t.Errorf("IV4 violated: sys[%d].PowerLevel %d not in [0, %d]", i, s.PowerLevel, s.MaxLevel) } } } roles := []RoomRole{RolePilot, RoleWeapons, RoleShields, RoleMedBay, RoleEngines} sys := NewStartingSystems() // Repeatedly try to add power to every system in a round-robin until the // reactor is full, then one more round of attempts (all should fail). for round := 0; round < 10; round++ { for _, r := range roles { addPower(sys, r, ReactorCap) //nolint:errcheck — we only care about invariants checkInvariants(sys) } } } // TestRemovePower_happy verifies that removing from a powered system decrements level. func TestRemovePower_happy(t *testing.T) { sys := NewStartingSystems() if !addPower(sys, RoleShields, ReactorCap) { t.Fatal("addPower Shields failed") } ok := removePower(sys, RoleShields) if !ok { t.Fatal("removePower returned false, expected true") } if sys[int(RoleShields)].PowerLevel != 0 { t.Fatalf("PowerLevel = %d, want 0", sys[int(RoleShields)].PowerLevel) } } // TestRemovePower_zeroLevel verifies that removing from an unpowered system is rejected. func TestRemovePower_zeroLevel(t *testing.T) { sys := NewStartingSystems() ok := removePower(sys, RoleEngines) if ok { t.Fatal("removePower at level 0 returned true, expected false") } if sys[int(RoleEngines)].PowerLevel != 0 { t.Fatalf("PowerLevel changed after rejected remove: got %d", sys[int(RoleEngines)].PowerLevel) } }