package oci import ( "reflect" "testing" "time" "github.com/oracle/oci-go-sdk/v65/audit" "github.com/oracle/oci-go-sdk/v65/common" ) func TestToAuditEvent(t *testing.T) { eventTime := time.Date(2026, 7, 6, 10, 30, 0, 0, time.UTC) tests := []struct { name string ev audit.AuditEvent want AuditEvent }{ { name: "全字段齐全", ev: audit.AuditEvent{ EventId: common.String("evt-abc"), Source: common.String("ComputeApi"), EventTime: &common.SDKTime{Time: eventTime}, Data: &audit.Data{ EventName: common.String("TerminateInstance"), ResourceName: common.String("web-1"), CompartmentName: common.String("prod"), Identity: &audit.Identity{ PrincipalName: common.String("api-admin"), IpAddress: common.String("1.2.3.4"), }, Request: &audit.Request{ Action: common.String("DELETE"), Path: common.String("/20160918/instances/ocid1..."), }, Response: &audit.Response{Status: common.String("204")}, }, }, want: AuditEvent{ EventId: "evt-abc", EventTime: &eventTime, EventName: "TerminateInstance", Source: "ComputeApi", ResourceName: "web-1", CompartmentName: "prod", PrincipalName: "api-admin", IPAddress: "1.2.3.4", Status: "204", RequestAction: "DELETE", RequestPath: "/20160918/instances/ocid1...", }, }, { name: "Data 为 nil 时只保留信封字段", ev: audit.AuditEvent{ Source: common.String("VcnApi"), EventTime: &common.SDKTime{Time: eventTime}, }, want: AuditEvent{EventTime: &eventTime, Source: "VcnApi"}, }, { name: "嵌套局部 nil 各自安全跳过", ev: audit.AuditEvent{ Data: &audit.Data{ EventName: common.String("GetInstance"), Identity: nil, Request: &audit.Request{Path: common.String("/instances")}, Response: nil, }, }, want: AuditEvent{EventName: "GetInstance", RequestPath: "/instances"}, }, { name: "空事件全部零值", ev: audit.AuditEvent{}, want: AuditEvent{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := toAuditEvent(tt.ev); !auditEventEqual(got, tt.want) { t.Errorf("toAuditEvent() = %+v, want %+v", got, tt.want) } }) } } // auditEventEqual 比较两个 DTO:EventTime 按值比较,Raw 不参与,其余反射比较。 func auditEventEqual(a, b AuditEvent) bool { if (a.EventTime == nil) != (b.EventTime == nil) { return false } if a.EventTime != nil && !a.EventTime.Equal(*b.EventTime) { return false } a.EventTime, b.EventTime = nil, nil a.Raw, b.Raw = nil, nil return reflect.DeepEqual(a, b) } func TestSortAuditEvents(t *testing.T) { t1 := time.Date(2026, 7, 6, 8, 0, 0, 0, time.UTC) t2 := time.Date(2026, 7, 6, 9, 0, 0, 0, time.UTC) items := []AuditEvent{ {EventName: "old", EventTime: &t1}, {EventName: "no-time", EventTime: nil}, {EventName: "new", EventTime: &t2}, } sortAuditEvents(items) got := []string{items[0].EventName, items[1].EventName, items[2].EventName} want := []string{"new", "old", "no-time"} for i := range want { if got[i] != want[i] { t.Fatalf("sortAuditEvents() order = %v, want %v", got, want) } } } func TestKeepAuditEvent(t *testing.T) { cases := []struct { name string event AuditEvent keep bool }{ {"公网IP的普通事件保留", AuditEvent{EventName: "CreateUser", IPAddress: "203.0.113.7"}, true}, {"SummarizeMetricsData 噪声排除", AuditEvent{EventName: "SummarizeMetricsData", IPAddress: "203.0.113.7"}, false}, {"内网10段发起排除", AuditEvent{EventName: "ListInstances", IPAddress: "10.1.2.3"}, false}, {"内网172.16段发起排除", AuditEvent{EventName: "ListInstances", IPAddress: "172.20.0.1"}, false}, {"172.15不属内网保留", AuditEvent{EventName: "ListInstances", IPAddress: "172.15.0.1"}, true}, {"内网192.168段发起排除", AuditEvent{EventName: "ListInstances", IPAddress: "192.168.1.1"}, false}, {"CGNAT 100.64段排除", AuditEvent{EventName: "ListInstances", IPAddress: "100.100.0.1"}, false}, {"100.128不属CGNAT保留", AuditEvent{EventName: "ListInstances", IPAddress: "100.128.0.1"}, true}, {"IP为空保留(控制面事件)", AuditEvent{EventName: "TerminateInstance", IPAddress: ""}, true}, {"IP不可解析保留", AuditEvent{EventName: "ListInstances", IPAddress: "not-an-ip"}, true}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { if got := keepAuditEvent(tc.event); got != tc.keep { t.Fatalf("keepAuditEvent(%+v) = %v, want %v", tc.event, got, tc.keep) } }) } } func TestAuditCursorAdvance(t *testing.T) { now := time.Date(2026, 7, 10, 12, 0, 0, 0, time.UTC) base := AuditCursor{ Start: now.Add(-24 * time.Hour), End: now, WindowHours: 24, } cases := []struct { name string cur AuditCursor empty bool wantHours int wantDone bool }{ {"有事件重置 24h 窗", AuditCursor{Start: base.Start, End: base.End, WindowHours: 96}, false, 24, false}, {"空窗倍增", base, true, 48, false}, {"倍增封顶 720h", AuditCursor{Start: base.Start, End: base.End, WindowHours: 512}, true, 720, false}, {"窗宽缺省按 24h 起算", AuditCursor{Start: base.Start, End: base.End}, true, 48, false}, {"越过保留期即尽头", AuditCursor{Start: now.AddDate(0, 0, -366), End: now.AddDate(0, 0, -365), WindowHours: 24}, false, 0, true}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { next, done := tc.cur.advance(now, tc.empty) if done != tc.wantDone { t.Fatalf("done = %v, want %v", done, tc.wantDone) } if done { return } if next.WindowHours != tc.wantHours { t.Fatalf("WindowHours = %d, want %d", next.WindowHours, tc.wantHours) } if !next.End.Equal(tc.cur.Start) { t.Fatalf("新窗 End = %v, 应紧邻上窗 Start %v", next.End, tc.cur.Start) } if got := next.End.Sub(next.Start); got != time.Duration(tc.wantHours)*time.Hour { t.Fatalf("窗宽 = %v, want %dh", got, tc.wantHours) } if next.Page != "" { t.Fatalf("新窗应清空窗内游标, got %q", next.Page) } }) } }