Files
kdletters 55471e15de 补齐后台兑换码有效期与独立滚动
为兑换码增加可选生效和截止时间,并按左闭右开区间校验
同步 SpacetimeDB 迁移、生成绑定、共享契约和后台管理界面
让后台侧边栏与主内容区域独立滚动
修正兑换业务拒绝的 HTTP 状态映射并补充回归测试和文档
2026-07-13 17:26:47 +08:00

90 lines
3.1 KiB
Rust

use module_runtime::{
RuntimeProfileFieldError, RuntimeProfileRedeemCodeMode, RuntimeProfileRedeemCodeSnapshot,
build_runtime_profile_redeem_code_admin_upsert_input, build_runtime_profile_redeem_code_record,
validate_runtime_profile_redeem_code_usage,
validate_runtime_profile_redeem_code_validity_window,
};
#[test]
fn redeem_code_validity_window_allows_open_ended_and_requires_strict_order() {
assert!(validate_runtime_profile_redeem_code_validity_window(None, None).is_ok());
assert!(validate_runtime_profile_redeem_code_validity_window(Some(10), None).is_ok());
assert!(validate_runtime_profile_redeem_code_validity_window(None, Some(10)).is_ok());
assert_eq!(
validate_runtime_profile_redeem_code_validity_window(Some(10), Some(10)),
Err(RuntimeProfileFieldError::InvalidRedeemCodeValidityWindow)
);
assert_eq!(
validate_runtime_profile_redeem_code_validity_window(Some(20), Some(10)),
Err(RuntimeProfileFieldError::InvalidRedeemCodeValidityWindow)
);
}
#[test]
fn redeem_code_admin_upsert_carries_optional_validity_window() {
let input = build_runtime_profile_redeem_code_admin_upsert_input(
"admin-1".to_string(),
"GIFT".to_string(),
RuntimeProfileRedeemCodeMode::Public,
30,
1,
true,
Vec::new(),
Vec::new(),
Some(10),
Some(20),
5,
)
.expect("valid window should pass");
assert_eq!(input.starts_at_micros, Some(10));
assert_eq!(input.expires_at_micros, Some(20));
}
#[test]
fn redeem_code_usage_uses_inclusive_start_and_exclusive_expire_boundary() {
let code = redeem_code_snapshot(Some(20), Some(30));
assert_eq!(
validate_runtime_profile_redeem_code_usage(&code, "user-1", 0, 19),
Err(RuntimeProfileFieldError::RedeemCodeNotStarted)
);
validate_runtime_profile_redeem_code_usage(&code, "user-1", 0, 20)
.expect("start boundary should be valid");
validate_runtime_profile_redeem_code_usage(&code, "user-1", 0, 29)
.expect("time before expire boundary should be valid");
assert_eq!(
validate_runtime_profile_redeem_code_usage(&code, "user-1", 0, 30),
Err(RuntimeProfileFieldError::RedeemCodeExpired)
);
}
#[test]
fn redeem_code_record_formats_optional_validity_window() {
let record =
build_runtime_profile_redeem_code_record(redeem_code_snapshot(Some(0), Some(1_000_000)));
assert_eq!(record.starts_at.as_deref(), Some("1970-01-01T00:00:00Z"));
assert_eq!(record.expires_at.as_deref(), Some("1970-01-01T00:00:01Z"));
}
fn redeem_code_snapshot(
starts_at_micros: Option<i64>,
expires_at_micros: Option<i64>,
) -> RuntimeProfileRedeemCodeSnapshot {
RuntimeProfileRedeemCodeSnapshot {
code: "GIFT".to_string(),
mode: RuntimeProfileRedeemCodeMode::Public,
reward_points: 30,
max_uses: 1,
global_used_count: 0,
enabled: true,
allowed_user_ids: Vec::new(),
created_by: "admin".to_string(),
created_at_micros: 1,
updated_at_micros: 1,
starts_at_micros,
expires_at_micros,
}
}