发布 0.2.0:模型池自愈、探测修正、任务异步触发与删除加固
This commit is contained in:
@@ -108,7 +108,10 @@ func tenantTaskCases() []tenantTaskCase {
|
||||
{name: "测活多租户", task: taskOf(model.TaskTypeHealthCheck, `{"ociConfigIds":[1,2]}`), wantOK: true, wantPayload: `{"ociConfigIds":[2]}`},
|
||||
{name: "成本去重命中", task: taskOf(model.TaskTypeCost, `{"ociConfigIds":[1,1,2]}`), wantOK: true, wantPayload: `{"ociConfigIds":[2]}`},
|
||||
{name: "成本未命中", task: taskOf(model.TaskTypeCost, `{"ociConfigIds":[2]}`)},
|
||||
{name: "非法 JSON", task: taskOf(model.TaskTypeCost, `{`), wantErr: true},
|
||||
// 坏 payload 记警告跳过(保留原任务),不再 fail-closed 阻断整个租户删除
|
||||
{name: "非法 JSON 跳过", task: taskOf(model.TaskTypeCost, `{`)},
|
||||
{name: "抢机坏 payload 跳过", task: taskOf(model.TaskTypeSnatch, `not-json`)},
|
||||
{name: "抢机空 payload 跳过", task: taskOf(model.TaskTypeSnatch, ``)},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,3 +610,67 @@ func assertCount(t *testing.T, db *gorm.DB, value any, query string, args []any,
|
||||
t.Errorf("count %T = %d, want %d", value, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteTenantSkipsCorruptTaskPayload(t *testing.T) {
|
||||
configs, _, db := newTenantDeleteEnv(t, &fakeClient{})
|
||||
target, _ := seedDeleteTenants(t, db)
|
||||
corrupt := model.Task{Name: "corrupt-snatch", Type: model.TaskTypeSnatch, Payload: `{broken`}
|
||||
mustCreate(t, db, &corrupt)
|
||||
mustCreate(t, db, &model.TaskLog{TaskID: corrupt.ID, Message: "keep"})
|
||||
|
||||
if err := configs.Delete(context.Background(), target.ID); err != nil {
|
||||
t.Fatalf("坏 payload 不应阻断租户删除: %v", err)
|
||||
}
|
||||
var kept model.Task
|
||||
if err := db.First(&kept, corrupt.ID).Error; err != nil || kept.Payload != `{broken` {
|
||||
t.Errorf("坏任务应原样保留: %+v, %v", kept, err)
|
||||
}
|
||||
assertCount(t, db, &model.TaskLog{}, "task_id = ?", []any{corrupt.ID}, 1)
|
||||
assertCount(t, db, &model.OciConfig{}, "id = ?", []any{target.ID}, 0)
|
||||
}
|
||||
|
||||
func TestDeleteTenantEventsAffectedRules(t *testing.T) {
|
||||
_, _, db := newTenantDeleteEnv(t, &fakeClient{})
|
||||
target, other := seedDeleteTenants(t, db)
|
||||
seedTenantEvents(t, db, target.ID, other.ID)
|
||||
|
||||
// 规则 ID 须在删除前取:target-rule 会随租户一并删除
|
||||
var targetRule, globalRule model.AlertRule
|
||||
db.Where("name = ?", "target-rule").First(&targetRule)
|
||||
db.Where("name = ?", "global-rule").First(&globalRule)
|
||||
|
||||
result := &tenantDeleteResult{}
|
||||
err := db.Transaction(func(tx *gorm.DB) error {
|
||||
return deleteTenantEvents(tx, target.ID, result)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("deleteTenantEvents: %v", err)
|
||||
}
|
||||
// target-rule 属本租户,global-rule 的命中引用了本租户事件:都应回收冷却
|
||||
got := map[uint]bool{}
|
||||
for _, id := range result.alertRuleIDs {
|
||||
got[id] = true
|
||||
}
|
||||
if len(got) != 2 || !got[targetRule.ID] || !got[globalRule.ID] {
|
||||
t.Errorf("alertRuleIDs = %v, want 含 target-rule(%d) 与 global-rule(%d)", result.alertRuleIDs, targetRule.ID, globalRule.ID)
|
||||
}
|
||||
// 仅保留 global-rule × other-event 一条命中
|
||||
assertCount(t, db, &model.AlertRuleHit{}, "", nil, 1)
|
||||
}
|
||||
|
||||
func TestDeleteTenantManyEventsNoVarLimit(t *testing.T) {
|
||||
// 回归:事件数超 SQLite 绑定变量上限(32766)时删除仍成功(旧实现 IN 展开必失败)
|
||||
configs, _, db := newTenantDeleteEnv(t, &fakeClient{})
|
||||
target, _ := seedDeleteTenants(t, db)
|
||||
events := make([]model.LogEvent, 0, 33000)
|
||||
for i := 0; i < 33000; i++ {
|
||||
events = append(events, model.LogEvent{OciConfigID: target.ID, MessageID: fmt.Sprintf("bulk-%d", i)})
|
||||
}
|
||||
if err := db.CreateInBatches(&events, 500).Error; err != nil {
|
||||
t.Fatalf("seed events: %v", err)
|
||||
}
|
||||
if err := configs.Delete(context.Background(), target.ID); err != nil {
|
||||
t.Fatalf("数万事件时删除不应受绑定变量上限影响: %v", err)
|
||||
}
|
||||
assertCount(t, db, &model.LogEvent{}, "oci_config_id = ?", []any{target.ID}, 0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user