package storage import ( "errors" "testing" "time" ) func TestEquipOrderLifecycle(t *testing.T) { setupTestDB(t) o, err := InsertEquipOrder("sub-1", "josie", "Josie", 42, "Cloak of Elvenkind", "cloak", EquipActionEquip) if err != nil { t.Fatal(err) } if o.Status != EquipPending { t.Fatalf("fresh order status = %q, want pending", o.Status) } if o.ItemID != 42 || o.Slot != "cloak" || o.Action != EquipActionEquip { t.Fatalf("order fields lost through insert: %+v", o) } pending, err := PendingEquipOrders(10) if err != nil { t.Fatal(err) } if len(pending) != 1 || pending[0].GUID != o.GUID { t.Fatalf("pending = %+v, want the one order we just placed", pending) } got, err := ResolveEquipOrder(o.GUID, EquipApplied, "worn and bonded") if err != nil { t.Fatal(err) } if got.Status != EquipApplied || got.Detail != "worn and bonded" { t.Fatalf("resolved order = %+v, want applied with detail", got) } if pending, _ := PendingEquipOrders(10); len(pending) != 0 { t.Fatalf("applied order still pending: %+v", pending) } } // TestEquipResolveIsIdempotent: gogobee's poll loop retries, so a verdict can // arrive twice — the second must not overwrite the first. This is the whole // reason Pete can copy mischief's mechanic even though the game action underneath // is not itself idempotent (gogobee guards that separately, on the order guid). func TestEquipResolveIsIdempotent(t *testing.T) { setupTestDB(t) o, err := InsertEquipOrder("sub-1", "josie", "Josie", 7, "Ring of Protection", "ring_1", EquipActionEquip) if err != nil { t.Fatal(err) } if _, err := ResolveEquipOrder(o.GUID, EquipApplied, "first"); err != nil { t.Fatal(err) } got, err := ResolveEquipOrder(o.GUID, EquipRejectedNotOwned, "second") if err != nil { t.Fatalf("re-resolve errored: %v", err) } if got.Status != EquipApplied || got.Detail != "first" { t.Fatalf("idempotency broken: order became %+v", got) } } func TestEquipResolveUnknownAndBadVerdict(t *testing.T) { setupTestDB(t) if _, err := ResolveEquipOrder("nope", EquipApplied, ""); !errors.Is(err, ErrNoSuchEquipOrder) { t.Fatalf("unknown guid err = %v, want ErrNoSuchEquipOrder", err) } o, _ := InsertEquipOrder("sub-1", "josie", "Josie", 1, "Boots", "feet", EquipActionEquip) if _, err := ResolveEquipOrder(o.GUID, "exploded", ""); err == nil { t.Error("a bogus verdict status was accepted") } if got, _ := EquipOrderByGUID(o.GUID); got.Status != EquipPending { t.Fatalf("order moved off pending on a bad verdict: %q", got.Status) } } // TestEquipInsertRejectsBadAction: the action is part of the contract, so a value // that isn't equip/unequip must not reach the table. func TestEquipInsertRejectsBadAction(t *testing.T) { setupTestDB(t) if _, err := InsertEquipOrder("sub-1", "josie", "Josie", 1, "Thing", "cloak", "wield"); err == nil { t.Fatal("a bogus action was accepted") } } // TestEquipUnequipCarriesSlotNotItem: an unequip has no live inventory row to name, // so it rides on the slot alone — item_id 0 is expected, not a bug. func TestEquipUnequipCarriesSlotNotItem(t *testing.T) { setupTestDB(t) o, err := InsertEquipOrder("sub-1", "josie", "Josie", 0, "Cloak of Elvenkind", "cloak", EquipActionUnequip) if err != nil { t.Fatal(err) } got, _ := EquipOrderByGUID(o.GUID) if got.Action != EquipActionUnequip || got.Slot != "cloak" || got.ItemID != 0 { t.Fatalf("unequip order = %+v, want slot-keyed with no item id", got) } } func TestEquipOrdersByOwnerAndCount(t *testing.T) { setupTestDB(t) for i := 0; i < 3; i++ { if _, err := InsertEquipOrder("sub-A", "alice", "Alice", int64(i+1), "Item", "cloak", EquipActionEquip); err != nil { t.Fatal(err) } } if _, err := InsertEquipOrder("sub-B", "bob", "Bob", 9, "Item", "cloak", EquipActionEquip); err != nil { t.Fatal(err) } mine, err := EquipOrdersByOwner("sub-A", 20) if err != nil { t.Fatal(err) } if len(mine) != 3 { t.Fatalf("alice sees %d orders, want 3 (and none of bob's)", len(mine)) } n, err := CountEquipOrdersSince("sub-A", time.Now().Add(-time.Hour).Unix()) if err != nil { t.Fatal(err) } if n != 3 { t.Fatalf("count since an hour ago = %d, want 3", n) } if n, _ := CountEquipOrdersSince("sub-A", time.Now().Add(time.Hour).Unix()); n != 0 { t.Fatalf("count since the future = %d, want 0", n) } }