Adds a data-driven event system (#47 schema, #48 activation, #49 TOTW): - EventDefinition JSON schema (event_type, effects, date range, is_manual) - data/events/totw_week1.json — TOTW event active by date range (2024–2030), injects 5 TOTW cards into the market as [EVENT] listings - data/events/seasonal_events.json — Spring Festival and Icon Weekend, manual-activation-only example events - migrations/0004_events.sql — events override table (NULL/1/0 per event) - services/event — load_event_definitions, compute_is_active (date range + manual override), get_active_events, get_all_with_status, activate/deactivate - routes/events — GET /events, GET /events/:id, POST .../activate, .../deactivate - services/market::refresh_npc_listings now accepts event_defs, injects bonus market cards from active events at 2× premium price - 5 new integration tests (events list, single, activate/deactivate cycle, 404 on unknown, market injection verification); 19/19 passing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -382,3 +382,103 @@ async fn test_win_streak_tracking() {
|
||||
assert_eq!(stats["win_streak"], 3);
|
||||
assert_eq!(stats["best_win_streak"], 3);
|
||||
}
|
||||
|
||||
// ── Phase 5: Events ──────────────────────────────────────────────────────────
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_events_list() {
|
||||
let app = build_test_app().await;
|
||||
let (status, json) = json_get(&app, "/events").await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
assert!(json["events"].is_array());
|
||||
// TOTW Week 1 is date-range active (2024–2030)
|
||||
let active = json["active_count"].as_u64().unwrap_or(0);
|
||||
assert!(active >= 1, "expected at least 1 active event (totw_week1)");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_event_get_single() {
|
||||
let app = build_test_app().await;
|
||||
let (status, json) = json_get(&app, "/events/totw_week1").await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
assert_eq!(json["event"]["id"], "totw_week1");
|
||||
assert_eq!(json["is_active"], true);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_event_activate_deactivate() {
|
||||
let app = build_test_app().await;
|
||||
|
||||
// spring_festival is manual-only — should start inactive
|
||||
let (s, json) = json_get(&app, "/events/spring_festival").await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
assert_eq!(json["is_active"], false);
|
||||
|
||||
// Activate it
|
||||
let (s, json) = json_post(
|
||||
&app,
|
||||
"/events/spring_festival/activate",
|
||||
serde_json::json!({}),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
assert_eq!(json["is_active"], true);
|
||||
|
||||
// Confirm it's now active
|
||||
let (s, json) = json_get(&app, "/events/spring_festival").await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
assert_eq!(json["is_active"], true);
|
||||
|
||||
// Deactivate it
|
||||
let (s, json) = json_post(
|
||||
&app,
|
||||
"/events/spring_festival/deactivate",
|
||||
serde_json::json!({}),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
assert_eq!(json["is_active"], false);
|
||||
|
||||
// Confirm inactive again
|
||||
let (s, json) = json_get(&app, "/events/spring_festival").await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
assert_eq!(json["is_active"], false);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_event_unknown_returns_404() {
|
||||
let app = build_test_app().await;
|
||||
let (status, _) = json_get(&app, "/events/no_such_event").await;
|
||||
assert_eq!(status, StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_event_market_refresh_injects_totw_cards() {
|
||||
let app = build_test_app().await;
|
||||
auth(&app, "EventMarketPlayer").await;
|
||||
|
||||
// Trigger a market refresh (TOTW event is active by date range)
|
||||
let (s, json) = json_post(&app, "/market/refresh", serde_json::json!({})).await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
// totw_week1 has 5 bonus cards + up to 20 NPC listings
|
||||
let count = json["listings_generated"].as_u64().unwrap_or(0);
|
||||
assert!(
|
||||
count >= 5,
|
||||
"expected at least 5 listings (event bonus cards)"
|
||||
);
|
||||
|
||||
// Check market contains at least one [EVENT] listing
|
||||
let (s, market) = json_get(&app, "/market").await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
let listings = market["listings"].as_array().expect("listings array");
|
||||
let event_listing = listings.iter().any(|l| {
|
||||
l["seller_name"]
|
||||
.as_str()
|
||||
.map(|n| n.starts_with("[EVENT]"))
|
||||
.unwrap_or(false)
|
||||
});
|
||||
assert!(
|
||||
event_listing,
|
||||
"expected at least one [EVENT] market listing"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user