package audit import ( "context" "encoding/json" "testing" "gorm.io/gorm" accessauditapp "github.com/break/junhong_cmp_fiber/internal/application/accessaudit" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/pkg/auditfailure" "github.com/break/junhong_cmp_fiber/pkg/constants" ) func TestAppendFailureDoesNotReturnToBusiness(t *testing.T) { writer := NewWriter(nil, nil) input := AppendInput{ActionCode: "missing_action"} before := auditfailure.SecondaryWriteFailureCount() if err := writer.Append(context.Background(), nil, input); err != nil { t.Fatalf("Append 返回审计失败: %v", err) } if err := writer.WriteAccessChange(context.Background(), nil, accessauditapp.ChangeAudit{ ActionCode: constants.AuditActionPersonalCustomerAssetBound, OperatorID: 1, }); err != nil { t.Fatalf("资源构造失败返回业务: %v", err) } if got := auditfailure.SecondaryWriteFailureCount(); got != before+2 { t.Fatalf("二次失败记录次数 = %d, want %d", got, before+2) } if _, err := writer.AppendAndGet(context.Background(), nil, input); err == nil { t.Fatal("AppendAndGet 未保留错误语义") } } func TestPersonalCustomerAssetBoundProjectsOnlyPersonalResources(t *testing.T) { action, ok := NewRegistry().Action(constants.AuditActionPersonalCustomerAssetBound) if !ok { t.Fatal("未注册个人客户资产绑定审计动作") } resources, err := accessResources(accessauditapp.ChangeAudit{ ActionCode: constants.AuditActionPersonalCustomerAssetBound, PersonalCustomer: &model.PersonalCustomer{Model: gorm.Model{ID: 1}, Nickname: "客户"}, PersonalDevices: []accessauditapp.PersonalCustomerDeviceChange{{ Binding: &model.PersonalCustomerDevice{Model: gorm.Model{ID: 2}, CustomerID: 1, VirtualNo: "DEVICE-1"}, }}, PersonalICCIDs: []accessauditapp.PersonalCustomerICCIDChange{{ Binding: &model.PersonalCustomerICCID{Model: gorm.Model{ID: 3}, CustomerID: 1, ICCID: "ICCID-1"}, }}, SubjectVisibility: constants.AuditSubjectDetail, SubjectSummary: "绑定个人客户资产", SubjectData: map[string]any{"asset_type": constants.AuditResourceIotCard, "asset_id": uint(9)}, }, action.PrimaryResource) if err != nil { t.Fatalf("构造绑定审计资源失败: %v", err) } projected, err := NewWriter(nil, nil).buildResources(resources, action) if err != nil { t.Fatalf("构造绑定审计投影失败: %v", err) } want := map[string]bool{ constants.AuditResourcePersonalCustomer: true, constants.AuditResourcePersonalCustomerDevice: true, constants.AuditResourcePersonalCustomerICCID: true, } for _, resource := range projected { if resource.ResourceType == constants.AuditResourceIotCard || resource.ResourceType == constants.AuditResourceDevice { t.Fatalf("绑定审计投影包含内部资源: %s", resource.ResourceType) } delete(want, resource.ResourceType) if resource.ResourceType == constants.AuditResourcePersonalCustomer { var subjectData map[string]any if err := json.Unmarshal(resource.SubjectData, &subjectData); err != nil || resource.SubjectVisibility != constants.AuditSubjectDetail || subjectData["asset_type"] != constants.AuditResourceIotCard || subjectData["asset_id"] != float64(9) { t.Fatalf("主个人客户主体投影不完整: %#v", resource) } } } for resourceType := range want { t.Fatalf("绑定审计投影缺少合法资源: %s", resourceType) } }