feat: 完成源晶权限与经验系统并优化 me/admin 交互
这个提交包含在:
115
backend/tests/season_service_test.cc
普通文件
115
backend/tests/season_service_test.cc
普通文件
@@ -0,0 +1,115 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "csp/db/sqlite_db.h"
|
||||
#include "csp/services/auth_service.h"
|
||||
#include "csp/services/contest_service.h"
|
||||
#include "csp/services/season_service.h"
|
||||
#include "csp/services/user_service.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
TEST_CASE("season reward claim is idempotent and writes loot log") {
|
||||
auto db = csp::db::SqliteDb::OpenMemory();
|
||||
csp::db::ApplyMigrations(db);
|
||||
csp::db::SeedDemoData(db);
|
||||
|
||||
csp::services::AuthService auth(db);
|
||||
const auto login = auth.Register("season_user_1", "password123");
|
||||
|
||||
csp::services::SeasonService seasons(db);
|
||||
const auto season = seasons.GetCurrentSeason();
|
||||
REQUIRE(season.has_value());
|
||||
const auto tracks = seasons.ListRewardTracks(season->id);
|
||||
REQUIRE_FALSE(tracks.empty());
|
||||
|
||||
const auto target_track = tracks.back();
|
||||
db.Exec("UPDATE users SET rating=200 WHERE id=" + std::to_string(login.user_id));
|
||||
|
||||
const auto before_progress =
|
||||
seasons.GetOrSyncUserProgress(season->id, login.user_id);
|
||||
REQUIRE(before_progress.xp >= target_track.required_xp);
|
||||
|
||||
const auto first_claim = seasons.ClaimReward(
|
||||
season->id, login.user_id, target_track.tier_no, target_track.reward_type);
|
||||
REQUIRE(first_claim.claimed);
|
||||
REQUIRE(first_claim.claim.has_value());
|
||||
REQUIRE(first_claim.rating_after >= 200 + target_track.reward_value);
|
||||
|
||||
const auto second_claim = seasons.ClaimReward(
|
||||
season->id, login.user_id, target_track.tier_no, target_track.reward_type);
|
||||
REQUIRE_FALSE(second_claim.claimed);
|
||||
REQUIRE(second_claim.claim.has_value());
|
||||
REQUIRE(second_claim.rating_after == first_claim.rating_after);
|
||||
|
||||
const auto loot = seasons.ListLootDropsByUser(login.user_id, 20);
|
||||
REQUIRE_FALSE(loot.empty());
|
||||
REQUIRE(loot.front().source_type == "season");
|
||||
REQUIRE(loot.front().source_id == season->id);
|
||||
|
||||
csp::services::UserService users(db);
|
||||
const auto user = users.GetById(login.user_id);
|
||||
REQUIRE(user.has_value());
|
||||
REQUIRE(user->rating == first_claim.rating_after);
|
||||
}
|
||||
|
||||
TEST_CASE("contest modifiers create update and filtered list") {
|
||||
auto db = csp::db::SqliteDb::OpenMemory();
|
||||
csp::db::ApplyMigrations(db);
|
||||
csp::db::SeedDemoData(db);
|
||||
|
||||
csp::services::ContestService contests(db);
|
||||
const auto contest_list = contests.ListContests();
|
||||
REQUIRE_FALSE(contest_list.empty());
|
||||
const int64_t contest_id = contest_list.front().id;
|
||||
|
||||
csp::services::SeasonService seasons(db);
|
||||
csp::services::ContestModifierWrite create;
|
||||
create.code = "no_recursion";
|
||||
create.title = "禁用递归";
|
||||
create.description = "仅允许循环写法。";
|
||||
create.rule_json = R"({"forbid":["recursion"]})";
|
||||
create.is_active = true;
|
||||
const auto created = seasons.CreateContestModifier(contest_id, create);
|
||||
REQUIRE(created.id > 0);
|
||||
REQUIRE(created.contest_id == contest_id);
|
||||
REQUIRE(created.is_active);
|
||||
|
||||
const auto active_list = seasons.ListContestModifiers(contest_id, false);
|
||||
bool found_created = false;
|
||||
for (const auto& one : active_list) {
|
||||
if (one.id == created.id) {
|
||||
found_created = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
REQUIRE(found_created);
|
||||
|
||||
csp::services::ContestModifierPatch patch;
|
||||
patch.title = "禁用递归(更新)";
|
||||
patch.is_active = false;
|
||||
const auto updated =
|
||||
seasons.UpdateContestModifier(contest_id, created.id, patch);
|
||||
REQUIRE(updated.title == "禁用递归(更新)");
|
||||
REQUIRE_FALSE(updated.is_active);
|
||||
|
||||
const auto active_after = seasons.ListContestModifiers(contest_id, false);
|
||||
bool still_active = false;
|
||||
for (const auto& one : active_after) {
|
||||
if (one.id == created.id) {
|
||||
still_active = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
REQUIRE_FALSE(still_active);
|
||||
|
||||
const auto all_after = seasons.ListContestModifiers(contest_id, true);
|
||||
bool found_updated = false;
|
||||
for (const auto& one : all_after) {
|
||||
if (one.id == created.id && one.title == "禁用递归(更新)" &&
|
||||
!one.is_active) {
|
||||
found_updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
REQUIRE(found_updated);
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户