@@ -3382,6 +3382,116 @@ func TestNewGameSessionFactoryItemMovePartialMergeKeepsSourceQuickslotStable(t *
33823382 }
33833383}
33843384
3385+ func TestNewGameSessionFactoryItemUseToItemRejectsLockedSourceOrTargetWithoutMutation (t * testing.T ) {
3386+ cases := []struct {
3387+ name string
3388+ inventory []inventory.ItemInstance
3389+ }{
3390+ {
3391+ name : "locked source" ,
3392+ inventory : []inventory.ItemInstance {
3393+ {ID : 11 , Vnum : 27001 , Count : 3 , Slot : 5 , Locked : true },
3394+ {ID : 12 , Vnum : 27001 , Count : 2 , Slot : 8 },
3395+ },
3396+ },
3397+ {
3398+ name : "locked target" ,
3399+ inventory : []inventory.ItemInstance {
3400+ {ID : 11 , Vnum : 27001 , Count : 3 , Slot : 5 },
3401+ {ID : 12 , Vnum : 27001 , Count : 2 , Slot : 8 , Locked : true },
3402+ },
3403+ },
3404+ }
3405+ for _ , tc := range cases {
3406+ t .Run (tc .name , func (t * testing.T ) {
3407+ store := loginticket .NewFileStore (t .TempDir ())
3408+ accounts := accountstore .NewFileStore (t .TempDir ())
3409+ characters := stubCharacters ()
3410+ characters [1 ].Inventory = append ([]inventory.ItemInstance (nil ), tc .inventory ... )
3411+ characters [1 ].Quickslots = []loginticket.Quickslot {
3412+ {Position : 2 , Type : quickslotproto .TypeItem , Slot : 5 },
3413+ {Position : 3 , Type : quickslotproto .TypeItem , Slot : 8 },
3414+ }
3415+ if err := accounts .Save (accountstore.Account {Login : StubLogin , Empire : 2 , Characters : cloneCharacters (characters )}); err != nil {
3416+ t .Fatalf ("save account: %v" , err )
3417+ }
3418+ if err := store .Issue (loginticket.Ticket {Login : StubLogin , LoginKey : 0x01020304 , Empire : 2 , Characters : cloneCharacters (characters )}); err != nil {
3419+ t .Fatalf ("issue login ticket: %v" , err )
3420+ }
3421+ flow := newStartedGameFlow (t , store , accounts )
3422+
3423+ mergeOut , err := flow .HandleClientFrame (decodeSingleFrame (t , itemproto .EncodeClientUseToItem (itemproto.ClientUseToItemPacket {
3424+ Source : itemproto .InventoryPosition (5 ),
3425+ Target : itemproto .InventoryPosition (8 ),
3426+ })))
3427+ if err != nil {
3428+ t .Fatalf ("unexpected locked %s use-to-item error: %v" , tc .name , err )
3429+ }
3430+ if len (mergeOut ) != 0 {
3431+ t .Fatalf ("expected locked %s use-to-item to fail closed with no frames, got %d" , tc .name , len (mergeOut ))
3432+ }
3433+ account , err := accounts .Load (StubLogin )
3434+ if err != nil {
3435+ t .Fatalf ("load persisted account: %v" , err )
3436+ }
3437+ if ! reflect .DeepEqual (account .Characters [1 ].Inventory , characters [1 ].Inventory ) {
3438+ t .Fatalf ("unexpected persisted inventory after locked %s use-to-item attempt: %#v" , tc .name , account .Characters [1 ].Inventory )
3439+ }
3440+ if ! reflect .DeepEqual (account .Characters [1 ].Quickslots , characters [1 ].Quickslots ) {
3441+ t .Fatalf ("unexpected persisted quickslots after locked %s use-to-item attempt: %#v" , tc .name , account .Characters [1 ].Quickslots )
3442+ }
3443+ })
3444+ }
3445+ }
3446+
3447+ func TestNewGameSessionFactoryItemUseToItemRejectsNonStackableTemplateWithoutMutation (t * testing.T ) {
3448+ store := loginticket .NewFileStore (t .TempDir ())
3449+ accounts := accountstore .NewFileStore (t .TempDir ())
3450+ characters := stubCharacters ()
3451+ characters [1 ].Inventory = []inventory.ItemInstance {
3452+ {ID : 11 , Vnum : 27001 , Count : 1 , Slot : 5 },
3453+ {ID : 12 , Vnum : 27001 , Count : 1 , Slot : 8 },
3454+ }
3455+ characters [1 ].Quickslots = []loginticket.Quickslot {
3456+ {Position : 2 , Type : quickslotproto .TypeItem , Slot : 5 },
3457+ {Position : 3 , Type : quickslotproto .TypeItem , Slot : 8 },
3458+ }
3459+ if err := accounts .Save (accountstore.Account {Login : StubLogin , Empire : 2 , Characters : cloneCharacters (characters )}); err != nil {
3460+ t .Fatalf ("save account: %v" , err )
3461+ }
3462+ if err := store .Issue (loginticket.Ticket {Login : StubLogin , LoginKey : 0x01020304 , Empire : 2 , Characters : cloneCharacters (characters )}); err != nil {
3463+ t .Fatalf ("issue login ticket: %v" , err )
3464+ }
3465+ itemTemplates := staticItemTemplateStore {snapshot : itemcatalog.Snapshot {Templates : []itemcatalog.Template {{
3466+ Vnum : 27001 ,
3467+ Name : "Non-Stackable Potion-Like Item" ,
3468+ Stackable : false ,
3469+ MaxCount : 1 ,
3470+ }}}}
3471+ flow := newStartedGameFlowWithItemStore (t , store , accounts , itemTemplates )
3472+
3473+ mergeOut , err := flow .HandleClientFrame (decodeSingleFrame (t , itemproto .EncodeClientUseToItem (itemproto.ClientUseToItemPacket {
3474+ Source : itemproto .InventoryPosition (5 ),
3475+ Target : itemproto .InventoryPosition (8 ),
3476+ })))
3477+ if err != nil {
3478+ t .Fatalf ("unexpected non-stackable use-to-item error: %v" , err )
3479+ }
3480+ if len (mergeOut ) != 0 {
3481+ t .Fatalf ("expected non-stackable use-to-item to fail closed with no frames, got %d" , len (mergeOut ))
3482+ }
3483+ account , err := accounts .Load (StubLogin )
3484+ if err != nil {
3485+ t .Fatalf ("load persisted account: %v" , err )
3486+ }
3487+ if ! reflect .DeepEqual (account .Characters [1 ].Inventory , characters [1 ].Inventory ) {
3488+ t .Fatalf ("unexpected persisted inventory after non-stackable use-to-item attempt: %#v" , account .Characters [1 ].Inventory )
3489+ }
3490+ if ! reflect .DeepEqual (account .Characters [1 ].Quickslots , characters [1 ].Quickslots ) {
3491+ t .Fatalf ("unexpected persisted quickslots after non-stackable use-to-item attempt: %#v" , account .Characters [1 ].Quickslots )
3492+ }
3493+ }
3494+
33853495func TestNewGameSessionFactoryItemUseToItemRejectsAuthoredSexAntiFlagWithoutMutation (t * testing.T ) {
33863496 store := loginticket .NewFileStore (t .TempDir ())
33873497 accounts := accountstore .NewFileStore (t .TempDir ())
0 commit comments