feat(mail): keep mail a player deletes, marked deleted, for staff

Deleting mail in game now sets mail.deleted_at (migrations mysql 95,
sqlite 78) instead of removing the row. Every read for the game skips
deleted mail: the mailbox, a single mail (claim, read, delete), the
unread count, the economy scan of waiting attachments and the UGC
lookup of mailed models. Deleting a character still removes its mail.

Check: delete a mail in game; it leaves the mailbox, the unread count
drops, and the row is still in the mail table with deleted_at set.
Parity test: ParitySeeded.Mail (DeleteMail).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 00:35:46 -05:00
parent e42fd45c1f
commit acb0e03a74
9 changed files with 37 additions and 13 deletions

View File

@@ -919,6 +919,19 @@ TEST_F(ParitySeeded, Mail) {
return rows;
});
Both("GetDashboardSnapshot", [](GameDatabase& db) { return db.GetDashboardSnapshot(); });
// A player deleting mail keeps the row for staff; the game's reads skip it
Both("DeleteMail", [](GameDatabase& db) {
db.DeleteMail(2);
db.DeleteMail(1); // still has its attachment: the game refuses this, but the database hides it all the same
json out{ db.GetMail(1), db.GetMail(2), db.GetMailForPlayer(CHAR_BOB, 20), db.GetUnreadMailCount(CHAR_BOB) };
json rows = json::array();
db.ForEachMailAttachment([&](const IEconomyLedger::MailAttachment& attachment) { rows.push_back(attachment); });
out.push_back(rows);
EXPECT_EQ(out[2], json::array());
EXPECT_EQ(out[3], 0);
EXPECT_EQ(rows, json::array());
return out;
});
}
TEST_F(ParitySeeded, Friends) {