package game // Registration regression: a pending new-account session whose typed name // was created in the meantime (by another concurrent registration) must be // refused at confirm time, never overwrite the existing account's password // or character list. import ( "testing" "thehouseoficarus/internal/net" "thehouseoficarus/internal/player" ) func TestPendingRegistrationCannotOverwriteExistingAccount(t *testing.T) { h := newHarness(t) h.seedAccount("alice", "pw", false) p := h.seedCharacter("alice", "alice") a := &net.Session{Conn: newChanConn(), State: net.StateAccountName} b := &net.Session{Conn: newChanConn(), State: net.StateAccountName} h.g.RunOnOwner(func() { // Both sessions type the same name while it does not exist yet; // both reach the password-confirmation state. h.g.handleAccountName(a, "wait") h.g.handleAccountName(b, "wait") h.g.handleNewPass(a, "firstpassword") h.g.handleNewPass(b, "otherpassword") // Session a wins the creation race... h.g.handleNewAccount(a, "firstpassword") // ...and a character is attached to the account before b confirms. acc, err := h.g.AccountStore.LoadAccount("wait") if err != nil { t.Error(err) return } acc.Characters = []string{p.Name} if err := h.g.AccountStore.SaveAccount(acc); err != nil { t.Error(err) return } // Session b confirms second: this must not clobber the account. h.g.handleNewAccount(b, "otherpassword") }) acc, err := h.g.AccountStore.LoadAccount("wait") if err != nil { t.Fatal(err) } if !player.CheckPassword("firstpassword", acc.PasswordHash) { t.Errorf("winner's password was overwritten by the losing registration") } if len(acc.Characters) != 1 { t.Errorf("character list destroyed by the losing registration: %v", acc.Characters) } }