test: unknown and extict races for commands

This commit is contained in:
Ilia Denisov
2026-02-08 14:57:38 +02:00
parent c36857e702
commit 175fb98c3a
17 changed files with 292 additions and 63 deletions
+31 -2
View File
@@ -23,7 +23,10 @@ var (
game.TechShields: 1.3, game.TechShields: 1.3,
game.TechCargo: 1.4, game.TechCargo: 1.4,
}, },
Relations: []game.RaceRelation{{RaceID: Race_1_ID, Relation: game.RelationWar}}, Relations: []game.RaceRelation{
{RaceID: Race_1_ID, Relation: game.RelationWar},
{RaceID: Race_2_ID, Relation: game.RelationWar},
},
} }
Race_1 = game.Race{ Race_1 = game.Race{
ID: Race_1_ID, ID: Race_1_ID,
@@ -36,7 +39,27 @@ var (
game.TechShields: 2.3, game.TechShields: 2.3,
game.TechCargo: 2.4, game.TechCargo: 2.4,
}, },
Relations: []game.RaceRelation{{RaceID: Race_0_ID, Relation: game.RelationPeace}}, Relations: []game.RaceRelation{
{RaceID: Race_0_ID, Relation: game.RelationPeace},
{RaceID: Race_2_ID, Relation: game.RelationPeace},
},
}
Race_Extinct = game.Race{
ID: Race_2_ID,
VoteFor: Race_2_ID,
Name: "Race_Extinct",
Extinct: true,
TTL: 0,
Tech: map[game.Tech]game.Float{
game.TechDrive: 3.1,
game.TechWeapons: 3.2,
game.TechShields: 3.3,
game.TechCargo: 3.4,
},
Relations: []game.RaceRelation{
{RaceID: Race_0_ID, Relation: game.RelationPeace},
{RaceID: Race_1_ID, Relation: game.RelationWar},
},
} }
Race_0_ID = uuid.New() Race_0_ID = uuid.New()
@@ -58,6 +81,8 @@ var (
Race_1_Freighter_idx = 1 Race_1_Freighter_idx = 1
Race_1_Cruiser_idx = 2 Race_1_Cruiser_idx = 2
Race_2_ID = uuid.New()
Uninhabited_Planet_3_num uint = 3 Uninhabited_Planet_3_num uint = 3
Uninhabited_Planet_4_num uint = 4 Uninhabited_Planet_4_num uint = 4
@@ -71,6 +96,9 @@ var (
Shields: 15, Shields: 15,
Cargo: 0, Cargo: 0,
} }
BadEntityName = "Bad(entitty)Name"
UnknownRace = "UnknownRace"
) )
// [ ] Delete this fake test // [ ] Delete this fake test
@@ -92,6 +120,7 @@ func newGame() *game.Game {
Race: []game.Race{ Race: []game.Race{
Race_0, Race_0,
Race_1, Race_1,
Race_Extinct,
}, },
Map: game.Map{ Map: game.Map{
Width: 1000, Width: 1000,
+4 -1
View File
@@ -51,8 +51,11 @@ func TestSendFleet(t *testing.T) {
c.ShipGroup(1).StateInSpace = &game.InSpace{Origin: 2, Range: 1.23} c.ShipGroup(1).StateInSpace = &game.InSpace{Origin: 2, Range: 1.23}
assert.ErrorContains(t, assert.ErrorContains(t,
g.SendFleet("UnknownRace", fleetSending, 2), g.SendFleet(UnknownRace, fleetSending, 2),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.SendFleet(Race_Extinct.Name, fleetSending, 2),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.SendFleet(Race_0.Name, "UnknownFleet", 2), g.SendFleet(Race_0.Name, "UnknownFleet", 2),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
+28 -5
View File
@@ -14,8 +14,11 @@ func TestJoinShipGroupToFleet(t *testing.T) {
c, g := newCache() c, g := newCache()
var groupIndex uint = 1 var groupIndex uint = 1
fleetOne := "R0_Fleet_one"
fleetTwo := "R0_Fleet_two"
assert.ErrorContains(t, assert.ErrorContains(t,
g.JoinShipGroupToFleet(Race_0.Name, " ", groupIndex, 0), g.JoinShipGroupToFleet(Race_0.Name, BadEntityName, groupIndex, 0),
e.GenericErrorText(e.ErrInputEntityTypeNameInvalid)) e.GenericErrorText(e.ErrInputEntityTypeNameInvalid))
assert.ErrorContains(t, assert.ErrorContains(t,
@@ -25,6 +28,13 @@ func TestJoinShipGroupToFleet(t *testing.T) {
// creating ShipGroup // creating ShipGroup
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 5)) assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 5))
assert.ErrorContains(t,
g.JoinShipGroupToFleet(UnknownRace, fleetOne, groupIndex, 0),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.JoinShipGroupToFleet(Race_Extinct.Name, fleetOne, groupIndex, 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.JoinShipGroupToFleet(Race_0.Name, "Unnamed", groupIndex, 6), g.JoinShipGroupToFleet(Race_0.Name, "Unnamed", groupIndex, 6),
e.GenericErrorText(e.ErrJoinFleetGroupNumberNotEnough)) e.GenericErrorText(e.ErrJoinFleetGroupNumberNotEnough))
@@ -32,9 +42,6 @@ func TestJoinShipGroupToFleet(t *testing.T) {
// ensure race has no Fleets // ensure race has no Fleets
assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 0) assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 0)
fleetOne := "R0_Fleet_one"
fleetTwo := "R0_Fleet_two"
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetOne, groupIndex, 0)) assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetOne, groupIndex, 0))
fleets := slices.Collect(c.ListFleets(Race_0_idx)) fleets := slices.Collect(c.ListFleets(Race_0_idx))
groups := slices.Collect(c.RaceShipGroups(Race_0_idx)) groups := slices.Collect(c.RaceShipGroups(Race_0_idx))
@@ -121,14 +128,30 @@ func TestJoinFleets(t *testing.T) {
assert.ErrorContains(t, assert.ErrorContains(t,
g.JoinFleets(Race_0.Name, fleetSourceOne, fleetTargetTwo), g.JoinFleets(Race_0.Name, fleetSourceOne, fleetTargetTwo),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t,
g.JoinFleets(UnknownRace, fleetSourceOne, fleetTargetTwo),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.JoinFleets(Race_Extinct.Name, fleetSourceOne, fleetTargetTwo),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t,
g.JoinShipGroupToFleet(UnknownRace, fleetSourceOne, 1, 0),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.JoinShipGroupToFleet(Race_Extinct.Name, fleetSourceOne, 1, 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetSourceOne, 1, 0)) assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetSourceOne, 1, 0))
assert.ErrorContains(t, assert.ErrorContains(t,
g.JoinFleets(Race_0.Name, fleetSourceOne, fleetTargetTwo), g.JoinFleets(Race_0.Name, fleetSourceOne, fleetTargetTwo),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetTargetTwo, 3, 0)) assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetTargetTwo, 3, 0))
assert.NoError(t, g.JoinFleets(Race_0.Name, fleetSourceOne, fleetTargetTwo)) assert.NoError(t, g.JoinFleets(Race_0.Name, fleetSourceOne, fleetTargetTwo))
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetOnPlanet2, 2, 0)) assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetOnPlanet2, 2, 0))
assert.ErrorContains(t, assert.ErrorContains(t,
g.JoinFleets(Race_0.Name, fleetOnPlanet2, fleetTargetTwo), g.JoinFleets(Race_0.Name, fleetOnPlanet2, fleetTargetTwo),
e.GenericErrorText(e.ErrShipsNotOnSamePlanet)) e.GenericErrorText(e.ErrShipsNotOnSamePlanet))
+4 -2
View File
@@ -43,8 +43,10 @@ func (c *Cache) PlanetProduction(ri int, number int, prod game.ProductionType, s
return e.NewEntityNotOwnedError("planet #%d", number) return e.NewEntityNotOwnedError("planet #%d", number)
} }
var subjectID *uuid.UUID var subjectID *uuid.UUID
if (prod == game.ResearchScience || prod == game.ProductionShip) && subj == "" { if prod == game.ResearchScience || prod == game.ProductionShip {
return e.NewEntityTypeNameValidationError("%s=%q", prod, subj) if _, ok := util.ValidateTypeName(subj); !ok {
return e.NewEntityTypeNameValidationError("%s=%q", prod, subj)
}
} }
if prod == game.ResearchScience { if prod == game.ResearchScience {
+8 -2
View File
@@ -20,8 +20,11 @@ func TestRenamePlanet(t *testing.T) {
assert.Equal(t, "Home_World", c.MustPlanet(R0_Planet_0_num).Name) assert.Equal(t, "Home_World", c.MustPlanet(R0_Planet_0_num).Name)
assert.ErrorContains(t, assert.ErrorContains(t,
g.RenamePlanet("UnknownRace", int(R0_Planet_0_num), "Home_World"), g.RenamePlanet(UnknownRace, int(R0_Planet_0_num), "Home_World"),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.RenamePlanet(Race_Extinct.Name, int(R0_Planet_0_num), "Home_World"),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.RenamePlanet(Race_0.Name, -1, "Home_World"), g.RenamePlanet(Race_0.Name, -1, "Home_World"),
e.GenericErrorText(e.ErrInputPlanetNumber)) e.GenericErrorText(e.ErrInputPlanetNumber))
@@ -97,8 +100,11 @@ func TestPlanetProduction(t *testing.T) {
pn = int(R0_Planet_2_num) pn = int(R0_Planet_2_num)
assert.ErrorContains(t, assert.ErrorContains(t,
g.PlanetProduction("UnknownRace", pn, "DRIVE", ""), g.PlanetProduction(UnknownRace, pn, "DRIVE", ""),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.PlanetProduction(Race_Extinct.Name, pn, "DRIVE", ""),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.PlanetProduction(Race_0.Name, pn, "Hyperdrive", ""), g.PlanetProduction(Race_0.Name, pn, "Hyperdrive", ""),
e.GenericErrorText(e.ErrInputProductionInvalid)) e.GenericErrorText(e.ErrInputProductionInvalid))
+35 -7
View File
@@ -18,8 +18,18 @@ func TestGiveVotes(t *testing.T) {
assert.Equal(t, Race_1_idx, c.Voted(Race_0_idx)) assert.Equal(t, Race_1_idx, c.Voted(Race_0_idx))
assert.Equal(t, Race_1_idx, c.Voted(Race_1_idx)) assert.Equal(t, Race_1_idx, c.Voted(Race_1_idx))
assert.ErrorContains(t, g.GiveVotes("UnknownRace", Race_1.Name), e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t,
assert.ErrorContains(t, g.GiveVotes(Race_0.Name, "UnknownRace"), e.GenericErrorText(e.ErrInputUnknownRace)) g.GiveVotes(UnknownRace, Race_1.Name),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.GiveVotes(Race_0.Name, UnknownRace),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.GiveVotes(Race_0.Name, Race_Extinct.Name),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t,
g.GiveVotes(Race_Extinct.Name, Race_1.Name),
e.GenericErrorText(e.ErrRaceExinct))
} }
func TestRelation(t *testing.T) { func TestRelation(t *testing.T) {
@@ -31,13 +41,31 @@ func TestRelation(t *testing.T) {
assert.Equal(t, game.RelationWar, c.Relation(Race_0_idx, Race_1_idx)) assert.Equal(t, game.RelationWar, c.Relation(Race_0_idx, Race_1_idx))
assert.Equal(t, game.RelationPeace, c.Relation(Race_1_idx, Race_0_idx)) assert.Equal(t, game.RelationPeace, c.Relation(Race_1_idx, Race_0_idx))
c.WipeRace(Race_1_idx)
assert.ErrorContains(t, assert.ErrorContains(t,
g.UpdateRelation(Race_0.Name, Race_1.Name, game.RelationWar), g.UpdateRelation(Race_0.Name, UnknownRace, game.RelationWar),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.UpdateRelation(UnknownRace, Race_0.Name, game.RelationWar),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.UpdateRelation(Race_0.Name, Race_Extinct.Name, game.RelationWar),
e.GenericErrorText(e.ErrRaceExinct)) e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.UpdateRelation(Race_1.Name, Race_0.Name, game.RelationWar), g.UpdateRelation(Race_Extinct.Name, Race_0.Name, game.RelationWar),
e.GenericErrorText(e.ErrRaceExinct)) e.GenericErrorText(e.ErrRaceExinct))
} }
func TestQuitGame(t *testing.T) {
c, g := newCache()
assert.ErrorContains(t,
g.QuitGame(UnknownRace),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.QuitGame(Race_Extinct.Name),
e.GenericErrorText(e.ErrRaceExinct))
assert.NoError(t, g.QuitGame(Race_0.Name))
assert.Equal(t, 3, int(c.Race(Race_0_idx).TTL))
}
-1
View File
@@ -66,7 +66,6 @@ func (c *Cache) RemovePlanetRoute(rt game.RouteType, origin uint) {
} }
} }
// TODO: NOT IN THIS FUNC: remove routes if planet became uninhabited (bombing, quit game, etc)
func (c *Cache) SendRoutedGroups() { func (c *Cache) SendRoutedGroups() {
for pi := range c.g.Map.Planet { for pi := range c.g.Map.Planet {
if len(c.g.Map.Planet[pi].Route) == 0 { if len(c.g.Map.Planet[pi].Route) == 0 {
+8 -2
View File
@@ -44,8 +44,11 @@ func TestSetRoute(t *testing.T) {
assert.Contains(t, c.MustPlanet(0).Route, game.RouteEmpty) assert.Contains(t, c.MustPlanet(0).Route, game.RouteEmpty)
assert.ErrorContains(t, assert.ErrorContains(t,
g.SetRoute("UnknownRace", "COL", 0, 2), g.SetRoute(UnknownRace, "COL", 0, 2),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.SetRoute(Race_Extinct.Name, "COL", 0, 2),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.SetRoute(Race_0.Name, "IND", 0, 2), g.SetRoute(Race_0.Name, "IND", 0, 2),
e.GenericErrorText(e.ErrInputCargoTypeInvalid)) e.GenericErrorText(e.ErrInputCargoTypeInvalid))
@@ -79,8 +82,11 @@ func TestRemoveRoute(t *testing.T) {
assert.NotContains(t, c.MustPlanet(2).Route, game.RouteEmpty) assert.NotContains(t, c.MustPlanet(2).Route, game.RouteEmpty)
assert.ErrorContains(t, assert.ErrorContains(t,
g.RemoveRoute("UnknownRace", "COL", 0), g.RemoveRoute(UnknownRace, "COL", 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.RemoveRoute(Race_Extinct.Name, "COL", 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.RemoveRoute(Race_0.Name, "IND", 0), g.RemoveRoute(Race_0.Name, "IND", 0),
e.GenericErrorText(e.ErrInputCargoTypeInvalid)) e.GenericErrorText(e.ErrInputCargoTypeInvalid))
+9 -7
View File
@@ -11,7 +11,6 @@ import (
) )
func TestCreateScience(t *testing.T) { func TestCreateScience(t *testing.T) {
// TODO: test on dead race
c, g := newCache() c, g := newCache()
first := "Drive_Shields" first := "Drive_Shields"
second := "Hyperdrive" second := "Hyperdrive"
@@ -28,10 +27,13 @@ func TestCreateScience(t *testing.T) {
assert.Equal(t, 0., sc.Cargo.F()) assert.Equal(t, 0., sc.Cargo.F())
assert.ErrorContains(t, assert.ErrorContains(t,
g.CreateScience("UnknownRace", second, 0.4, 0, 0.6, 0), g.CreateScience(UnknownRace, second, 0.4, 0, 0.6, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, assert.ErrorContains(t,
g.CreateScience(Race_0.Name, " ", 0.4, 0, 0.6, 0), g.CreateScience(Race_Extinct.Name, second, 0.4, 0, 0.6, 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t,
g.CreateScience(Race_0.Name, BadEntityName, 0.4, 0, 0.6, 0),
e.GenericErrorText(e.ErrInputEntityTypeNameInvalid)) e.GenericErrorText(e.ErrInputEntityTypeNameInvalid))
assert.ErrorContains(t, assert.ErrorContains(t,
g.CreateScience(Race_0.Name, first, 0.4, 0, 0.6, 0), g.CreateScience(Race_0.Name, first, 0.4, 0, 0.6, 0),
@@ -73,9 +75,6 @@ func TestCreateScience(t *testing.T) {
} }
func TestDeleteScience(t *testing.T) { func TestDeleteScience(t *testing.T) {
// TODO: test on dead race
// TODO: test with existing ship group
// TODO: test with planet production busy with science
c, g := newCache() c, g := newCache()
first := "Drive_Shields" first := "Drive_Shields"
second := "Hyperdrive" second := "Hyperdrive"
@@ -90,8 +89,11 @@ func TestDeleteScience(t *testing.T) {
g.PlanetProduction(Race_0.Name, int(R0_Planet_0_num), "SCIENCE", second) g.PlanetProduction(Race_0.Name, int(R0_Planet_0_num), "SCIENCE", second)
assert.ErrorContains(t, assert.ErrorContains(t,
g.DeleteScience("UnknownRace", second), g.DeleteScience(UnknownRace, second),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.DeleteScience(Race_Extinct.Name, second),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.DeleteScience(Race_0.Name, first), g.DeleteScience(Race_0.Name, first),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
+14 -15
View File
@@ -13,7 +13,7 @@ import (
func (c *Cache) CreateShipType(ri int, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error { func (c *Cache) CreateShipType(ri int, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error {
c.validateRaceIndex(ri) c.validateRaceIndex(ri)
if err := checkShipTypeValues(drive, ammo, weapons, shileds, cargo); err != nil { if err := validateShipTypeValues(drive, ammo, weapons, shileds, cargo); err != nil {
return err return err
} }
n, ok := util.ValidateTypeName(typeName) n, ok := util.ValidateTypeName(typeName)
@@ -37,24 +37,24 @@ func (c *Cache) CreateShipType(ri int, typeName string, drive float64, ammo int,
return nil return nil
} }
func (c *Cache) MergeShipType(ri int, name, targetName string) error { func (c *Cache) MergeShipType(ri int, sourceName, targetName string) error {
c.validateRaceIndex(ri) c.validateRaceIndex(ri)
st, sti, ok := c.ShipClass(ri, name) sourceClass, sti, ok := c.ShipClass(ri, sourceName)
if !ok { if !ok {
return e.NewEntityNotExistsError("source ship type %q", name) return e.NewEntityNotExistsError("source ship type %q", sourceName)
} }
tt, _, ok := c.ShipClass(ri, targetName) targetClass, _, ok := c.ShipClass(ri, targetName)
if !ok { if !ok {
return e.NewEntityNotExistsError("target ship type %q", name) return e.NewEntityNotExistsError("target ship type %q", sourceName)
} }
if st.Name == tt.Name { if sourceClass.Name == targetClass.Name {
return e.NewEntityTypeNameEqualityError("ship type %q", targetName) return e.NewEntityTypeNameEqualityError("ship type %q", targetName)
} }
if !st.Equal(*tt) { if !sourceClass.Equal(*targetClass) {
return e.NewMergeShipTypeNotEqualError() return e.NewMergeShipTypeNotEqualError()
} }
@@ -63,16 +63,16 @@ func (c *Cache) MergeShipType(ri int, name, targetName string) error {
if c.g.Map.Planet[pl].OwnedBy(c.g.Race[ri].ID) && if c.g.Map.Planet[pl].OwnedBy(c.g.Race[ri].ID) &&
c.g.Map.Planet[pl].Production.Type == game.ProductionShip && c.g.Map.Planet[pl].Production.Type == game.ProductionShip &&
c.g.Map.Planet[pl].Production.SubjectID != nil && c.g.Map.Planet[pl].Production.SubjectID != nil &&
*c.g.Map.Planet[pl].Production.SubjectID == st.ID { *c.g.Map.Planet[pl].Production.SubjectID == sourceClass.ID {
c.g.Map.Planet[pl].Production.SubjectID = &tt.ID c.g.Map.Planet[pl].Production.SubjectID = &targetClass.ID
} }
} }
// switch ship groups to the new type // switch ship groups to the new type
for sg := range c.g.ShipGroups { for sg := range c.g.ShipGroups {
if c.g.ShipGroups[sg].OwnerID == c.g.Race[ri].ID && c.g.ShipGroups[sg].TypeID == st.ID { if c.g.ShipGroups[sg].OwnerID == c.g.Race[ri].ID && c.g.ShipGroups[sg].TypeID == sourceClass.ID {
c.g.ShipGroups[sg].TypeID = tt.ID c.g.ShipGroups[sg].TypeID = targetClass.ID
} }
} }
@@ -141,7 +141,6 @@ func (c *Cache) ShipClass(ri int, name string) (*game.ShipType, int, bool) {
} }
func (c *Cache) ShipType(ri int, ID uuid.UUID) (*game.ShipType, bool) { func (c *Cache) ShipType(ri int, ID uuid.UUID) (*game.ShipType, bool) {
// TODO: cache + invalidate
c.validateRaceIndex(ri) c.validateRaceIndex(ri)
for i := range c.g.Race[ri].ShipTypes { for i := range c.g.Race[ri].ShipTypes {
if c.g.Race[ri].ShipTypes[i].ID == ID { if c.g.Race[ri].ShipTypes[i].ID == ID {
@@ -155,10 +154,10 @@ func (c *Cache) MustShipType(ri int, ID uuid.UUID) *game.ShipType {
if v, ok := c.ShipType(ri, ID); ok { if v, ok := c.ShipType(ri, ID); ok {
return v return v
} }
panic(fmt.Sprintf("ship_class not found: race_id=%d id=%v", ri, ID)) panic(fmt.Sprintf("ship class not found: race_idx=%d id=%v", ri, ID))
} }
func checkShipTypeValues(d float64, a int, w, s, c float64) error { func validateShipTypeValues(d float64, a int, w, s, c float64) error {
if !checkShipTypeValueDWSC(d) { if !checkShipTypeValueDWSC(d) {
return e.NewDriveValueError(d) return e.NewDriveValueError(d)
} }
+96
View File
@@ -0,0 +1,96 @@
package controller_test
import (
"slices"
"testing"
e "github.com/iliadenisov/galaxy/internal/error"
"github.com/stretchr/testify/assert"
)
func TestCreateShipClass(t *testing.T) {
c, g := newCache()
assert.NoError(t, g.CreateShipType(Race_0.Name, "Random", 1, 3, 5, 4, 2))
ships := slices.Collect(c.ListShipTypes(Race_0_idx))
assert.Len(t, ships, 4)
st := ships[3]
assert.Equal(t, 1., float64(st.Drive))
assert.Equal(t, 3, int(st.Armament))
assert.Equal(t, 5., float64(st.Weapons))
assert.Equal(t, 4., float64(st.Shields))
assert.Equal(t, 2., float64(st.Cargo))
assert.ErrorContains(t,
g.CreateShipType(Race_0.Name, Race_0_Gunship, 1, 0, 0, 0, 0),
e.GenericErrorText(e.ErrInputEntityTypeNameDuplicate))
assert.ErrorContains(t,
g.CreateShipType(UnknownRace, "Drone", 1, 0, 0, 0, 0),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.CreateShipType(Race_Extinct.Name, "Drone", 1, 0, 0, 0, 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t,
g.CreateShipType(Race_0.Name, BadEntityName, 1, 0, 0, 0, 0),
e.GenericErrorText(e.ErrInputEntityTypeNameInvalid))
}
func TestMergeShipClass(t *testing.T) {
c, g := newCache()
assert.Len(t, c.ShipTypes(Race_0_idx), 3)
assert.NoError(t, g.CreateShipType(Race_0.Name, "Drone", 1, 0, 0, 0, 0))
assert.Len(t, c.ShipTypes(Race_0_idx), 4)
assert.NoError(t, g.CreateShipType(Race_0.Name, "Spy", 1, 0, 0, 0, 0))
assert.Len(t, c.ShipTypes(Race_0_idx), 5)
assert.NoError(t, g.CreateShipType(Race_0.Name, "Surfer", 15, 15, 15, 0, 1))
assert.Len(t, c.ShipTypes(Race_0_idx), 6)
assert.ErrorContains(t,
g.MergeShipType(Race_0.Name, "Sky", "Drone"),
e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t,
g.MergeShipType(Race_0.Name, "Spy", "Elephant"),
e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t,
g.MergeShipType(Race_Extinct.Name, "Spy", "Drone"),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t,
g.MergeShipType(Race_0.Name, "Spy", "Spy"),
e.GenericErrorText(e.ErrInputEntityTypeNameEquality))
assert.NoError(t, g.MergeShipType(Race_0.Name, "Spy", "Drone"))
assert.Len(t, c.ShipTypes(Race_0_idx), 5)
assert.ErrorContains(t,
g.MergeShipType(Race_0.Name, "Drone", "Surfer"),
e.GenericErrorText(e.ErrMergeShipTypeNotEqual))
}
func TestDeleteShipClass(t *testing.T) {
c, g := newCache()
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1))
assert.NoError(t, g.CreateShipType(Race_0.Name, "Drone", 1, 0, 0, 0, 0))
g.PlanetProduction(Race_0.Name, int(R0_Planet_0_num), "SHIP", "Drone")
assert.ErrorContains(t,
g.DeleteShipType(UnknownRace, Race_0_Freighter),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.DeleteShipType(Race_Extinct.Name, Race_0_Freighter),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t,
g.DeleteShipType(Race_0.Name, "Elephant"),
e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t,
g.DeleteShipType(Race_0.Name, "Drone"),
e.GenericErrorText(e.ErrDeleteShipTypePlanetProduction))
assert.NoError(t, g.DeleteShipType(Race_0.Name, Race_0_Freighter))
assert.ErrorContains(t,
g.DeleteShipType(Race_0.Name, Race_0_Gunship),
e.GenericErrorText(e.ErrDeleteShipTypeExistingGroup))
}
+11 -4
View File
@@ -1,6 +1,7 @@
package controller package controller
import ( import (
"cmp"
"fmt" "fmt"
"iter" "iter"
"maps" "maps"
@@ -551,15 +552,21 @@ func (c *Cache) listShipGroups(ri int) iter.Seq[*game.ShipGroup] {
} }
} }
// TODO: order upgradable groups by cost descending, describe in Rules
func (c *Cache) shipGroupsInUpgrade(planetNumber uint) iter.Seq[*game.ShipGroup] { func (c *Cache) shipGroupsInUpgrade(planetNumber uint) iter.Seq[*game.ShipGroup] {
return func(yield func(*game.ShipGroup) bool) { return func(yield func(*game.ShipGroup) bool) {
result := make([]int, 0)
for sg := range c.g.ShipGroups { for sg := range c.g.ShipGroups {
// number checked for further sanity after battles // number checked for further sanity after battles
if c.g.ShipGroups[sg].Number > 0 && c.g.ShipGroups[sg].Destination == planetNumber && c.g.ShipGroups[sg].State() == game.StateUpgrade { if c.g.ShipGroups[sg].Number > 0 && c.g.ShipGroups[sg].Destination == planetNumber && c.g.ShipGroups[sg].State() == game.StateUpgrade {
if !yield(&c.g.ShipGroups[sg]) { result = append(result, sg)
break }
} }
slices.SortFunc(result, func(a, b int) int {
return cmp.Compare(c.g.ShipGroups[b].StateUpgrade.Cost(), c.g.ShipGroups[a].StateUpgrade.Cost())
})
for i := range result {
if !yield(&c.g.ShipGroups[result[i]]) {
return
} }
} }
} }
+4 -1
View File
@@ -23,8 +23,11 @@ func TestSendGroup(t *testing.T) {
assert.NoError(t, c.CreateShips(Race_0_idx, "Fortress", R0_Planet_0_num, 1)) assert.NoError(t, c.CreateShips(Race_0_idx, "Fortress", R0_Planet_0_num, 1))
assert.ErrorContains(t, assert.ErrorContains(t,
g.SendGroup("UnknownRace", 1, 2, 0), g.SendGroup(UnknownRace, 1, 2, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.SendGroup(Race_Extinct.Name, 1, 2, 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.SendGroup(Race_0.Name, 555, 2, 0), g.SendGroup(Race_0.Name, 555, 2, 0),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
+33 -8
View File
@@ -36,7 +36,7 @@ func TestCreateShips(t *testing.T) {
} }
func TestJoinEqualGroups(t *testing.T) { func TestJoinEqualGroups(t *testing.T) {
c, _ := newCache() c, g := newCache()
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 1)) // 1 -> 2 assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 1)) // 1 -> 2
assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Freighter, R1_Planet_1_num, 1)) assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Freighter, R1_Planet_1_num, 1))
@@ -57,7 +57,14 @@ func TestJoinEqualGroups(t *testing.T) {
assert.NoError(t, c.CreateShips(1, Race_1_Freighter, R1_Planet_1_num, 1)) assert.NoError(t, c.CreateShips(1, Race_1_Freighter, R1_Planet_1_num, 1))
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 3) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 3)
c.JoinEqualGroups(Race_0_idx) assert.ErrorContains(t,
g.JoinEqualGroups(UnknownRace),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.JoinEqualGroups(Race_Extinct.Name),
e.GenericErrorText(e.ErrRaceExinct))
assert.NoError(t, g.JoinEqualGroups(Race_0.Name))
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 3) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 3)
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
@@ -104,8 +111,11 @@ func TestBreakGroup(t *testing.T) {
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleet, 1, 0)) assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleet, 1, 0))
assert.ErrorContains(t, assert.ErrorContains(t,
g.BreakGroup("UnknownRace", 1, 0), g.BreakGroup(UnknownRace, 1, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.BreakGroup(Race_Extinct.Name, 1, 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.BreakGroup(Race_0.Name, 555, 0), g.BreakGroup(Race_0.Name, 555, 0),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
@@ -179,11 +189,17 @@ func TestTransferGroup(t *testing.T) {
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1)
assert.ErrorContains(t, assert.ErrorContains(t,
g.TransferGroup("UnknownRace", Race_1.Name, 2, 0), g.TransferGroup(UnknownRace, Race_1.Name, 2, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, assert.ErrorContains(t,
g.TransferGroup(Race_0.Name, "UnknownRace", 2, 0), g.TransferGroup(Race_0.Name, UnknownRace, 2, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.TransferGroup(Race_0.Name, Race_Extinct.Name, 2, 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t,
g.TransferGroup(Race_Extinct.Name, Race_1.Name, 2, 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.TransferGroup(Race_0.Name, Race_0.Name, 2, 0), g.TransferGroup(Race_0.Name, Race_0.Name, 2, 0),
e.GenericErrorText(e.ErrInputSameRace)) e.GenericErrorText(e.ErrInputSameRace))
@@ -273,8 +289,11 @@ func TestLoadCargo(t *testing.T) {
// tests // tests
assert.ErrorContains(t, assert.ErrorContains(t,
g.LoadCargo("UnknownRace", 1, game.CargoMaterial.String(), 0, 0), g.LoadCargo(UnknownRace, 1, game.CargoMaterial.String(), 0, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.LoadCargo(Race_Extinct.Name, 1, game.CargoMaterial.String(), 0, 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.LoadCargo(Race_0.Name, 1, "GOLD", 0, 0), g.LoadCargo(Race_0.Name, 1, "GOLD", 0, 0),
e.GenericErrorText(e.ErrInputCargoTypeInvalid)) e.GenericErrorText(e.ErrInputCargoTypeInvalid))
@@ -401,8 +420,11 @@ func TestUnloadCargo(t *testing.T) {
// tests // tests
assert.ErrorContains(t, assert.ErrorContains(t,
g.UnloadCargo("UnknownRace", 1, 0, 0), g.UnloadCargo(UnknownRace, 1, 0, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.UnloadCargo(Race_Extinct.Name, 1, 0, 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.UnloadCargo(Race_0.Name, 555, 0, 0), g.UnloadCargo(Race_0.Name, 555, 0, 0),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
@@ -495,8 +517,11 @@ func TestDisassembleGroup(t *testing.T) {
// tests // tests
assert.ErrorContains(t, assert.ErrorContains(t,
g.DisassembleGroup("UnknownRace", 1, 0), g.DisassembleGroup(UnknownRace, 1, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.DisassembleGroup(Race_Extinct.Name, 1, 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.DisassembleGroup(Race_0.Name, 555, 0), g.DisassembleGroup(Race_0.Name, 555, 0),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
@@ -125,8 +125,11 @@ func TestUpgradeGroup(t *testing.T) {
c.ShipGroup(2).Destination = R1_Planet_1_num c.ShipGroup(2).Destination = R1_Planet_1_num
assert.ErrorContains(t, assert.ErrorContains(t,
g.UpgradeGroup("UnknownRace", 1, "DRIVE", 0, 0), g.UpgradeGroup(UnknownRace, 1, "DRIVE", 0, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.UpgradeGroup(Race_Extinct.Name, 1, "DRIVE", 0, 0),
e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.UpgradeGroup(Race_0.Name, 555, "DRIVE", 0, 0), g.UpgradeGroup(Race_0.Name, 555, "DRIVE", 0, 0),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
+1 -1
View File
@@ -44,7 +44,7 @@ func TestRenamePlanet(t *testing.T) {
assert.GreaterOrEqual(t, ri, 0) assert.GreaterOrEqual(t, ri, 0)
otherRace := cg.Race[ri].Name otherRace := cg.Race[ri].Name
err = game.RenamePlanet(p, unknownRaceName, number, newName) // TODO: test actual rip race err = game.RenamePlanet(p, unknownRaceName, number, newName)
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace))
err = game.RenamePlanet(p, race, number, "") err = game.RenamePlanet(p, race, number, "")
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityTypeNameInvalid)) assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityTypeNameInvalid))
+2 -4
View File
@@ -18,7 +18,7 @@ func TestCreateShipType(t *testing.T) {
c(t, func(p func(*controller.Param), ctrl func() *controller.Controller) { c(t, func(p func(*controller.Param), ctrl func() *controller.Controller) {
err := game.DeleteShipType(p, race, typeName) err := game.DeleteShipType(p, race, typeName)
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists))
err = game.CreateShipType(p, unknownRaceName, " "+typeName+" ", 1, 0, 0, 0, 0) // TODO: test on dead race err = game.CreateShipType(p, unknownRaceName, " "+typeName+" ", 1, 0, 0, 0, 0)
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace))
err = game.CreateShipType(p, race, " "+typeName+" ", 1, 0, 0, 0, 0) err = game.CreateShipType(p, race, " "+typeName+" ", 1, 0, 0, 0, 0)
assert.NoError(t, err) assert.NoError(t, err)
@@ -30,8 +30,7 @@ func TestCreateShipType(t *testing.T) {
assert.Equal(t, st[0].Shields.F(), 0.) assert.Equal(t, st[0].Shields.F(), 0.)
assert.Equal(t, st[0].Cargo.F(), 0.) assert.Equal(t, st[0].Cargo.F(), 0.)
assert.Equal(t, st[0].Armament, uint(0)) assert.Equal(t, st[0].Armament, uint(0))
// TODO: test with existing ship group err = game.DeleteShipType(p, unknownRaceName, typeName)
err = game.DeleteShipType(p, unknownRaceName, typeName) // TODO: test on dead race
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace))
err = game.DeleteShipType(p, race, typeName) err = game.DeleteShipType(p, race, typeName)
assert.NoError(t, err) assert.NoError(t, err)
@@ -111,6 +110,5 @@ func TestMergeShipType(t *testing.T) {
assert.Len(t, st, 2) assert.Len(t, st, 2)
err = game.MergeShipType(p, race, "Drone", "Cruiser") err = game.MergeShipType(p, race, "Drone", "Cruiser")
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrMergeShipTypeNotEqual)) assert.ErrorContains(t, err, e.GenericErrorText(e.ErrMergeShipTypeNotEqual))
// TODO: test group/production changed
}) })
} }