fix: 应用 xhigh 代码评审的跨端修复
来自 xhigh code review 的正确性/健壮性修复,覆盖全部五端: - server:鉴权 fail-closed、计量交叉校验与配额扣穿处理、WS 网关并发与关闭顺序、 billing 行锁、redis Lua 过期与设备槽刷新、config 解析 - desktop:会话 epoch 防串话、WS 重连与 401 处理、api 客户端复用、统一 usePoll 轮询 - android:握手时序、请求头封装、账户状态派生、按需重组 - ios:finalize 宽限、串行采集、错误文案服务端优先、删除死代码 CommitController Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -35,21 +35,25 @@ func TestAllowSessionWindow(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllowAudioSecondsWindow(t *testing.T) {
|
||||
// TestAudioWindowExhausted 复用生产路径(AudioWindowExhausted 只查 + RecordAudioSeconds 记账)。
|
||||
func TestAudioWindowExhausted(t *testing.T) {
|
||||
r := rdb(t)
|
||||
ctx := context.Background()
|
||||
now := time.Now()
|
||||
// 1700s 放行
|
||||
if ok, _ := AllowAudioSeconds(ctx, r, "dev1", 1700, now); !ok {
|
||||
t.Fatal("1700s should pass")
|
||||
// 初始未满
|
||||
if full, _ := AudioWindowExhausted(ctx, r, "dev1", now); full {
|
||||
t.Fatal("fresh window should not be exhausted")
|
||||
}
|
||||
// 再 100s(累计 1800)放行
|
||||
if ok, _ := AllowAudioSeconds(ctx, r, "dev1", 100, now.Add(time.Second)); !ok {
|
||||
t.Fatal("cumulative 1800s should pass")
|
||||
// 记满后窗口耗尽(AudioWindowExhausted 只查不记,val=0,sum>limit 才算满)
|
||||
if err := RecordAudioSeconds(ctx, r, "dev1", 1801, now); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 再 1s 超限拒绝
|
||||
if ok, _ := AllowAudioSeconds(ctx, r, "dev1", 1, now.Add(2*time.Second)); ok {
|
||||
t.Fatal("1801s should be rejected")
|
||||
if full, _ := AudioWindowExhausted(ctx, r, "dev1", now.Add(time.Second)); !full {
|
||||
t.Fatal("window should be exhausted after exceeding 1800s")
|
||||
}
|
||||
// 其他设备不受影响
|
||||
if full, _ := AudioWindowExhausted(ctx, r, "dev2", now); full {
|
||||
t.Fatal("other device window should not be exhausted")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,3 +82,24 @@ func TestDeviceSlot(t *testing.T) {
|
||||
t.Fatal("acquire after release should pass")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRefreshDeviceSlot 仅持有者能续期;非持有者续期无效(17D)。
|
||||
func TestRefreshDeviceSlot(t *testing.T) {
|
||||
r := rdb(t)
|
||||
ctx := context.Background()
|
||||
if ok, err := AcquireDeviceSlot(ctx, r, "dev1", "s1"); err != nil || !ok {
|
||||
t.Fatalf("acquire should pass: %v", err)
|
||||
}
|
||||
// 持有者续期成功
|
||||
if ok, err := RefreshDeviceSlot(ctx, r, "dev1", "s1"); err != nil || !ok {
|
||||
t.Fatalf("holder refresh should succeed: ok=%v err=%v", ok, err)
|
||||
}
|
||||
// 非持有者续期失败
|
||||
if ok, _ := RefreshDeviceSlot(ctx, r, "dev1", "s2"); ok {
|
||||
t.Fatal("non-holder refresh should fail")
|
||||
}
|
||||
// 续期未释放槽位:他人仍抢不到
|
||||
if ok, _ := AcquireDeviceSlot(ctx, r, "dev1", "s2"); ok {
|
||||
t.Fatal("slot should still be held after refresh")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user