fix(market): relisting an expired auction actually relists it

The client's relist arrives as a fresh ISStart (`POST /auctionhouse`) for an item
that ALREADY has a listing row, so `create_listing` hit a primary-key conflict. The
handler treated `Err(Conflict)` as success: it logged `listed=true`, handed the
client its trade id, and persisted nothing. The stale row kept its old `created_at`,
so the card stayed expired and the relist appeared to do nothing -- observed live,
with the client's price-limits fetch and the ISStart POST both in the log.

The PK conflict IS the relist path. `relist_listing` now resets `created_at` to now
and takes the new prices and duration, so the auction actually returns to the market
with a fresh countdown.

Refuses to revive a `sold` or `reserved` row: re-opening a sold auction would sell
the same card twice. `cancelled` rows ARE relistable (the card is back in the pile).
Missing rows report NotFound rather than silently succeeding. The failure paths still
ack so the screen cannot wedge, but they now say `relisted=false reason=...` in the
log instead of claiming success.

Three store tests: the clock/price reset, the sold+reserved revival guard (plus the
cancelled-is-relistable case), and NotFound.

336 tests pass, 0 failed, clippy clean.
This commit is contained in:
funman300
2026-08-17 19:17:35 +00:00
parent dcbef721f2
commit b1d7ed2570
2 changed files with 169 additions and 1 deletions
+36 -1
View File
@@ -302,13 +302,48 @@ pub async fn handle_market_list(
)
.await
{
Ok(_) | Err(MarketError::Conflict) => {
Ok(_) => {
eprintln!(
"utas-host owner=RUST route=market-list POST item_id={} listed=true trade_id={trade_id}",
r.item_id
);
ok_json(&json!({ "id": trade_id }))
}
// A row for this item already exists, so this POST is a RELIST.
// FIFA 17 relists by re-sending ISStart, so the PK conflict is the
// normal relist path — NOT an error, and NOT a success to swallow.
// Acking it without resetting the clock is why relisting an expired
// card appeared to do nothing: the client got its id back while the
// stale row stayed expired.
Err(MarketError::Conflict) => {
match store
.relist_listing(
&listing_id,
r.start,
r.buy_now,
r.duration,
r.item_json.as_deref(),
)
.await
{
Ok(_) => {
eprintln!(
"utas-host owner=RUST route=market-list POST item_id={} relisted=true trade_id={trade_id}",
r.item_id
);
ok_json(&json!({ "id": trade_id }))
}
// Sold or in-flight: never revive it. Ack so the screen does
// not wedge, but say so plainly in the log.
Err(e) => {
eprintln!(
"utas-host owner=RUST route=market-list POST item_id={} relisted=false reason={e:?} trade_id={trade_id}",
r.item_id
);
ok_json(&json!({ "id": trade_id }))
}
}
}
Err(_) => json_body(503, &json!({ "error": "market_store" })),
}
}