adventure: work the five review findings the last pass left open
The extract pre-check is gone. It read a snapshot up to two minutes behind and still got the last word, so somebody who set out over Matrix during a lagging roster push was told they weren't on an expedition for a run gogobee would happily have ended. Same call abandon and leave already made: let it through and let rejected_not_running be the answer. The siege_join check stays, because whether a boss is camped outside town is town-wide and runs on a day-or-longer clock, but it now reads one column through SiegeIsCamped instead of loading every defender row and the whole history to look at one flag. The war-room history insert is OR REPLACE. boss_id is the primary key and it was never settled whether gogobee means the siege instance or the boss type by it, so a duplicate pair used to fail the transaction carrying the live boss and the muster too and freeze the war room on the last good snapshot. A dropped history row is the smaller failure; the open question is noted in the schema. offersToUndo's guard didn't cover the case its comment claimed. A gogobee too old to push seats sends a valid blob with no party key, which decodes to the same empty slice as a solo run, and a party member got shown the button that throws away everyone's day. That needs a new field, so whoDetail gains party_known and the flag gates the empty-list branch alone; the branch that reads the viewer's own seat is self-evidencing and keeps working against any sender. gogobee's half is written up in adventure_party_known_flag.md. And an empty offer list no longer claims "you're already out there", which Pete can't actually know from a game box too old to push offers at all.
This commit is contained in:
@@ -36,19 +36,19 @@ func TestOffersToUndoReadsTheViewersOwnSeat(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
token, status string
|
||||
haveParty bool
|
||||
partyKnown bool
|
||||
party []partySeat
|
||||
self storage.PlayerDetail
|
||||
abandon, leave, cancel bool
|
||||
}{
|
||||
{
|
||||
name: "leader of a party is offered the abandon",
|
||||
token: "tok-josie", status: "expedition", haveParty: true, party: party,
|
||||
token: "tok-josie", status: "expedition", partyKnown: true, party: party,
|
||||
abandon: true,
|
||||
},
|
||||
{
|
||||
name: "member of a party is offered the exit, never the abandon",
|
||||
token: "tok-cam", status: "expedition", haveParty: true, party: party,
|
||||
token: "tok-cam", status: "expedition", partyKnown: true, party: party,
|
||||
leave: true,
|
||||
},
|
||||
{
|
||||
@@ -56,7 +56,7 @@ func TestOffersToUndoReadsTheViewersOwnSeat(t *testing.T) {
|
||||
// two seats), so an empty list on a live run means "nobody else", not
|
||||
// "we don't know" — and the one body down there is the leader.
|
||||
name: "solo run is offered the abandon",
|
||||
token: "tok-josie", status: "expedition", haveParty: true,
|
||||
token: "tok-josie", status: "expedition", partyKnown: true,
|
||||
abandon: true,
|
||||
},
|
||||
{
|
||||
@@ -64,19 +64,35 @@ func TestOffersToUndoReadsTheViewersOwnSeat(t *testing.T) {
|
||||
// snapshot we do not understand, and the safe answer is to offer
|
||||
// nothing rather than guess which of the two buttons applies.
|
||||
name: "a party with no seat for the viewer offers nothing",
|
||||
token: "tok-nobody", status: "expedition", haveParty: true, party: party,
|
||||
token: "tok-nobody", status: "expedition", partyKnown: true, party: party,
|
||||
},
|
||||
{
|
||||
// The one that came out of running it. An undecodable public sheet
|
||||
// gives the same empty slice as a solo run, and treating the two alike
|
||||
// offered a party MEMBER the abandon — convincingly, with the rest of
|
||||
// the page looking fine. haveParty is what tells them apart.
|
||||
// the page looking fine.
|
||||
name: "a run whose sheet did not decode offers nothing",
|
||||
token: "tok-cam", status: "expedition", haveParty: false,
|
||||
token: "tok-cam", status: "expedition", partyKnown: false,
|
||||
},
|
||||
{
|
||||
// The same empty slice again, this time from a gogobee too old to push
|
||||
// seats at all. It decodes fine, so only the sender's own flag tells it
|
||||
// apart from the solo case two rows up.
|
||||
name: "an empty party from a sender that never pushes seats offers nothing",
|
||||
token: "tok-cam", status: "expedition", partyKnown: false, party: nil,
|
||||
},
|
||||
{
|
||||
// The asymmetry: the seat list is self-evidencing, so it keeps working
|
||||
// against a sender whose capability we cannot confirm. A seat saying
|
||||
// "member" is not a guess, and refusing the exit here would strand
|
||||
// somebody in a party for the length of the rollout.
|
||||
name: "a seated member is offered the exit even without the flag",
|
||||
token: "tok-cam", status: "expedition", partyKnown: false, party: party,
|
||||
leave: true,
|
||||
},
|
||||
{
|
||||
name: "standing in town with nothing open offers nothing",
|
||||
token: "tok-josie", status: "idle", haveParty: true, party: nil,
|
||||
token: "tok-josie", status: "idle", partyKnown: true, party: nil,
|
||||
},
|
||||
{
|
||||
// The case the roster status cannot see: an extracted expedition is
|
||||
@@ -101,7 +117,7 @@ func TestOffersToUndoReadsTheViewersOwnSeat(t *testing.T) {
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
abandon, leave, cancel := offersToUndo(tc.token, tc.status, tc.haveParty, tc.party, tc.self)
|
||||
abandon, leave, cancel := offersToUndo(tc.token, tc.status, tc.partyKnown, tc.party, tc.self)
|
||||
if abandon != tc.abandon || leave != tc.leave || cancel != tc.cancel {
|
||||
t.Fatalf("offers = abandon:%v leave:%v cancel:%v, want abandon:%v leave:%v cancel:%v",
|
||||
abandon, leave, cancel, tc.abandon, tc.leave, tc.cancel)
|
||||
|
||||
Reference in New Issue
Block a user