package vmware import ( "testing" "time" "github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources" ) func TestProviderRecords_ProjectCanonicalVMwareResources(t *testing.T) { previous := IsFeatureEnabled() SetFeatureEnabled(true) t.Cleanup(func() { SetFeatureEnabled(previous) }) collectedAt := time.Date(2026, time.March, 30, 18, 15, 0, 0, time.UTC) provider := NewProvider(InventorySnapshot{ ConnectionID: "vc-1", ConnectionName: "Lab VC", VCenterHost: "vc.lab.local", CollectedAt: collectedAt, Hosts: []InventoryHost{{ Host: "host-101", Name: "esxi-01.lab.local", ConnectionState: "CONNECTED", PowerState: "POWERED_ON", HostUUID: "uuid-host-1", DatacenterID: "datacenter-1", DatacenterName: "DC1", ComputeResourceID: "domain-c101", ComputeResourceName: "Prod Compute", ClusterID: "domain-c101", ClusterName: "Prod Compute", ClusterHAEnabled: boolPtr(true), ClusterDRSEnabled: boolPtr(false), FolderID: "group-h4", FolderName: "Prod Hosts", DatastoreIDs: []string{"datastore-11"}, DatastoreNames: []string{"nvme-primary"}, OverallStatus: "yellow", Metrics: &InventoryMetrics{ CPUPercent: float64Ptr(21.4), MemoryPercent: float64Ptr(63.2), MemoryUsedBytes: int64Ptr(27144105984), MemoryTotalBytes: int64Ptr(42949672960), NetInBytesPerSecond: float64Ptr(1024), NetOutBytesPerSecond: float64Ptr(2048), DiskReadBytesPerSecond: float64Ptr(4096), DiskWriteBytesPerSecond: float64Ptr(8192), }, TriggeredAlarms: []InventoryAlarm{{ Alarm: "alarm-11", Name: "Host connection degraded", OverallStatus: "yellow", TriggeredAt: collectedAt.Add(-2 * time.Minute), }}, RecentTasks: []InventoryTask{{ Task: "task-11", Name: "Reconnect host", State: "running", StartedAt: collectedAt.Add(-1 * time.Minute), }}, }}, VMs: []InventoryVM{{ VM: "vm-201", Name: "app-01", PowerState: "POWERED_ON", CPUCount: 4, MemorySizeMiB: 8192, DatacenterID: "datacenter-1", DatacenterName: "DC1", ComputeResourceID: "domain-c101", ComputeResourceName: "Prod Compute", ClusterID: "domain-c101", ClusterName: "Prod Compute", ClusterHAEnabled: boolPtr(true), ClusterDRSEnabled: boolPtr(false), FolderID: "group-v7", FolderName: "Production VMs", ResourcePoolID: "resgroup-22", ResourcePoolName: "Tier 1", RuntimeHostID: "host-101", RuntimeHostName: "esxi-01.lab.local", DatastoreIDs: []string{"datastore-11"}, DatastoreNames: []string{"nvme-primary"}, InstanceUUID: "vm-instance-201", BIOSUUID: "vm-bios-201", GuestOSFamily: "LINUX", GuestHostname: "app-01.internal", GuestIPAddresses: []string{"10.0.0.21"}, OverallStatus: "green", Metrics: &InventoryMetrics{ CPUPercent: float64Ptr(38.1), MemoryPercent: float64Ptr(57.5), MemoryUsedBytes: int64Ptr(5033164800), MemoryTotalBytes: int64Ptr(8589934592), NetInBytesPerSecond: float64Ptr(512), NetOutBytesPerSecond: float64Ptr(768), DiskReadBytesPerSecond: float64Ptr(1536), DiskWriteBytesPerSecond: float64Ptr(2048), }, TriggeredAlarms: []InventoryAlarm{{ Alarm: "alarm-21", Name: "VM replication fault", OverallStatus: "red", TriggeredAt: collectedAt.Add(-3 * time.Minute), }}, RecentTasks: []InventoryTask{{ Task: "task-21", Name: "Create snapshot", State: "success", StartedAt: collectedAt.Add(-5 * time.Minute), }}, SnapshotCount: 2, CurrentSnapshotID: "snapshot-202", SnapshotTree: []InventoryVMSnapshot{{ Snapshot: "snapshot-201", Name: "pre-upgrade", Description: "Before application upgrade", ID: 101, CreatedAt: timePtr(collectedAt.Add(-48 * time.Hour)), State: "poweredOn", Quiesced: true, Children: []InventoryVMSnapshot{{ Snapshot: "snapshot-202", Name: "post-migration-checkpoint", ID: 102, CreatedAt: timePtr(collectedAt.Add(-24 * time.Hour)), State: "poweredOn", Current: true, }}, }}, NetworkAdapters: []InventoryVMNetworkAdapter{{ NIC: "4000", Label: "Network adapter 1", Type: "VMXNET3", MACType: "GENERATED", MACAddress: "00:50:56:aa:bb:cc", PCISlotNumber: int64Ptr(160), BackingType: "STANDARD_PORTGROUP", NetworkID: "network-101", NetworkName: "VM Network", DistributedSwitchUUID: "50 2b d1", State: "CONNECTED", StartConnected: true, AllowGuestControl: true, WakeOnLANEnabled: true, }}, VirtualDisks: []InventoryVMVirtualDisk{{ Disk: "2000", Label: "Hard disk 1", Type: "SCSI", SCSIBus: int64Ptr(0), SCSIUnit: int64Ptr(1), BackingType: "VMDK_FILE", VMDKFile: "[nvme-primary] app-01/app-01.vmdk", DatastoreName: "nvme-primary", CapacityBytes: int64Ptr(107374182400), }}, Tools: &InventoryVMTools{ AutoUpdateSupported: boolPtr(true), InstallAttemptCount: int64Ptr(1), VersionNumber: int64Ptr(12352), Version: "12.4.0", UpgradePolicy: "MANUAL", VersionStatus: "CURRENT", InstallType: "OPEN_VM_TOOLS", RunState: "RUNNING", GuestRebootRequested: boolPtr(true), GuestRebootComponents: []string{"drivers"}, GuestRebootRequestTime: "2026-03-30T18:20:00Z", }, Hardware: &InventoryVMHardware{ GuestOS: "UBUNTU_64", InstantCloneFrozen: boolPtr(false), Version: "VMX_20", UpgradePolicy: "AFTER_CLEAN_SHUTDOWN", UpgradeVersion: "VMX_21", UpgradeStatus: "PENDING", BootType: "EFI", EFILegacyBoot: boolPtr(false), BootNetworkProtocol: "IPV4", BootDelayMilliseconds: int64Ptr(5000), BootRetry: boolPtr(true), BootRetryDelayMilliseconds: int64Ptr(10000), EnterSetupMode: boolPtr(false), BootDevices: []InventoryVMBootDevice{ {Type: "DISK", Disks: []string{"2000"}}, {Type: "ETHERNET", NIC: "4000"}, }, CPUCoresPerSocket: int64Ptr(2), CPUHotAddEnabled: boolPtr(true), CPUHotRemoveEnabled: boolPtr(false), MemoryHotAddEnabled: boolPtr(true), MemoryHotAddIncrementMiB: int64Ptr(256), MemoryHotAddLimitMiB: int64Ptr(16384), }, }}, Datastores: []InventoryDatastore{{ Datastore: "datastore-11", Name: "nvme-primary", Type: "VMFS", FreeSpace: 40, Capacity: 100, DatacenterID: "datacenter-1", DatacenterName: "DC1", FolderID: "group-s4", FolderName: "Shared Datastores", HostIDs: []string{"host-101"}, HostNames: []string{"esxi-01.lab.local"}, VMIDs: []string{"vm-201"}, VMNames: []string{"app-01"}, Accessible: boolPtr(true), MultipleHostAccess: boolPtr(true), MaintenanceMode: "normal", URL: "ds:///vmfs/volumes/datastore-11/", OverallStatus: "yellow", RecentTasks: []InventoryTask{{ Task: "task-31", Name: "Refresh datastore", State: "queued", StartedAt: collectedAt.Add(-4 * time.Minute), }}, }}, Networks: []InventoryNetwork{{ Network: "network-101", Name: "VM Network", Type: "STANDARD_PORTGROUP", DatacenterID: "datacenter-1", DatacenterName: "DC1", FolderID: "group-n4", FolderName: "Networks", HostIDs: []string{"host-101"}, HostNames: []string{"esxi-01.lab.local"}, VMIDs: []string{"vm-201"}, VMNames: []string{"app-01"}, OverallStatus: "yellow", TriggeredAlarms: []InventoryAlarm{{ Alarm: "alarm-41", Name: "Network uplink redundancy", OverallStatus: "yellow", TriggeredAt: collectedAt.Add(-6 * time.Minute), }}, }}, }) records := provider.Records() if len(records) != 4 { t.Fatalf("expected 4 VMware records, got %d", len(records)) } hostRecord := records[0] if hostRecord.SourceID != "vc-1:host:host-101" { t.Fatalf("host source id = %q, want vc-1:host:host-101", hostRecord.SourceID) } if hostRecord.Resource.Type != unifiedresources.ResourceTypeAgent { t.Fatalf("host resource type = %q, want %q", hostRecord.Resource.Type, unifiedresources.ResourceTypeAgent) } if hostRecord.Resource.Status != unifiedresources.StatusWarning { t.Fatalf("host status = %q, want %q", hostRecord.Resource.Status, unifiedresources.StatusWarning) } if hostRecord.Resource.VMware == nil || hostRecord.Resource.VMware.ManagedObjectID != "host-101" { t.Fatalf("expected VMware host metadata, got %+v", hostRecord.Resource.VMware) } if got := hostRecord.Resource.VMware.ActiveAlarmCount; got != 1 { t.Fatalf("host active alarm count = %d, want 1", got) } if got := hostRecord.Resource.VMware.RecentTaskSummary; got != "Reconnect host (running)" { t.Fatalf("host recent task summary = %q, want %q", got, "Reconnect host (running)") } if got := hostRecord.Resource.VMware.ClusterName; got != "Prod Compute" { t.Fatalf("host cluster name = %q, want Prod Compute", got) } if got := hostRecord.Resource.VMware.ClusterHAEnabled; got == nil || !*got { t.Fatalf("host cluster HA enabled = %+v, want true", got) } if got := hostRecord.Resource.VMware.ClusterDRSEnabled; got == nil || *got { t.Fatalf("host cluster DRS enabled = %+v, want false", got) } if got := hostRecord.Resource.VMware.DatastoreNames; len(got) != 1 || got[0] != "nvme-primary" { t.Fatalf("host datastore names = %#v, want [nvme-primary]", got) } if hostRecord.Resource.Metrics == nil || hostRecord.Resource.Metrics.CPU == nil || hostRecord.Resource.Metrics.Memory == nil { t.Fatalf("expected VMware host metrics, got %+v", hostRecord.Resource.Metrics) } if got := hostRecord.Resource.Metrics.CPU.Percent; got != 21.4 { t.Fatalf("host cpu percent = %v, want 21.4", got) } if got := hostRecord.Resource.Metrics.NetOut.Value; got != 2048 { t.Fatalf("host net out = %v, want 2048", got) } if len(hostRecord.Resource.Incidents) != 1 || hostRecord.Resource.Incidents[0].Code != "vmware_alarm_state" { t.Fatalf("expected host VMware incident projection, got %+v", hostRecord.Resource.Incidents) } if hostRecord.Identity.DMIUUID != "uuid-host-1" { t.Fatalf("host identity DMI UUID = %q, want uuid-host-1", hostRecord.Identity.DMIUUID) } if got := hostRecord.Identity.ClusterName; got != "Prod Compute" { t.Fatalf("host cluster name = %q, want Prod Compute", got) } if hostRecord.Resource.LastSeen != collectedAt { t.Fatalf("host last seen = %v, want %v", hostRecord.Resource.LastSeen, collectedAt) } vmRecord := records[1] if vmRecord.SourceID != "vc-1:vm:vm-201" { t.Fatalf("vm source id = %q, want vc-1:vm:vm-201", vmRecord.SourceID) } if vmRecord.Resource.Type != unifiedresources.ResourceTypeVM { t.Fatalf("vm resource type = %q, want %q", vmRecord.Resource.Type, unifiedresources.ResourceTypeVM) } if vmRecord.Resource.VMware == nil || vmRecord.Resource.VMware.CPUCount != 4 { t.Fatalf("expected VMware VM metadata with cpu count, got %+v", vmRecord.Resource.VMware) } if vmRecord.Resource.Status != unifiedresources.StatusWarning { t.Fatalf("vm status = %q, want %q", vmRecord.Resource.Status, unifiedresources.StatusWarning) } if got := vmRecord.Resource.VMware.ActiveAlarmSummary; got != "VM replication fault (red)" { t.Fatalf("vm active alarm summary = %q, want %q", got, "VM replication fault (red)") } if got := vmRecord.Resource.VMware.ClusterHAEnabled; got == nil || !*got { t.Fatalf("vm cluster HA enabled = %+v, want true", got) } if got := vmRecord.Resource.VMware.ClusterDRSEnabled; got == nil || *got { t.Fatalf("vm cluster DRS enabled = %+v, want false", got) } if got := vmRecord.Resource.VMware.SnapshotCount; got != 2 { t.Fatalf("vm snapshot count = %d, want 2", got) } if got := vmRecord.Resource.VMware.CurrentSnapshotID; got != "snapshot-202" { t.Fatalf("vm current snapshot id = %q, want snapshot-202", got) } if got := vmRecord.Resource.VMware.SnapshotTree; len(got) != 1 || got[0].Name != "pre-upgrade" || len(got[0].Children) != 1 || !got[0].Children[0].Current { t.Fatalf("vm snapshot tree projection = %+v, want root pre-upgrade with current child", got) } if got := vmRecord.Resource.VMware.NetworkAdapters; len(got) != 1 || got[0].NetworkName != "VM Network" || got[0].MACAddress != "00:50:56:aa:bb:cc" { t.Fatalf("vm network adapter projection = %+v, want VM Network adapter with MAC", got) } if got := vmRecord.Resource.VMware.NetworkAdapters[0].PCISlotNumber; got == nil || *got != 160 { t.Fatalf("vm network adapter pci slot = %+v, want 160", got) } if got := vmRecord.Resource.VMware.VirtualDisks; len(got) != 1 || got[0].Label != "Hard disk 1" || got[0].VMDKFile != "[nvme-primary] app-01/app-01.vmdk" { t.Fatalf("vm virtual disk projection = %+v, want hard disk backing on nvme-primary", got) } if got := vmRecord.Resource.VMware.VirtualDisks[0].SCSIUnit; got == nil || *got != 1 { t.Fatalf("vm virtual disk scsi unit = %+v, want 1", got) } if got := vmRecord.Resource.VMware.VirtualDisks[0].CapacityBytes; got == nil || *got != 107374182400 { t.Fatalf("vm virtual disk capacity = %+v, want 107374182400", got) } if got := vmRecord.Resource.VMware.Tools; got == nil || got.RunState != "RUNNING" || got.VersionStatus != "CURRENT" || got.Version != "12.4.0" { t.Fatalf("vm tools projection = %+v, want running/current 12.4.0", got) } if got := vmRecord.Resource.VMware.Tools.GuestRebootRequested; got == nil || !*got { t.Fatalf("vm tools guest reboot requested = %+v, want true", got) } if got := vmRecord.Resource.VMware.Tools.GuestRebootComponents; len(got) != 1 || got[0] != "drivers" { t.Fatalf("vm tools reboot components = %+v, want [drivers]", got) } if got := vmRecord.Resource.VMware.Hardware; got == nil || got.Version != "VMX_20" || got.UpgradeStatus != "PENDING" { t.Fatalf("vm hardware projection = %+v, want VMX_20 pending", got) } if got := vmRecord.Resource.VMware.Hardware.BootDevices; len(got) != 2 || got[0].Disks[0] != "2000" || got[1].NIC != "4000" { t.Fatalf("vm hardware boot devices = %+v, want disk and ethernet entries", got) } if got := vmRecord.Resource.VMware.Hardware.MemoryHotAddLimitMiB; got == nil || *got != 16384 { t.Fatalf("vm hardware memory hot-add limit = %+v, want 16384", got) } if got := vmRecord.Resource.ParentName; got != "esxi-01.lab.local" { t.Fatalf("vm parent name = %q, want esxi-01.lab.local", got) } if got := vmRecord.ParentSourceID; got != "vc-1:host:host-101" { t.Fatalf("vm parent source id = %q, want vc-1:host:host-101", got) } if got := vmRecord.Resource.VMware.RuntimeHostName; got != "esxi-01.lab.local" { t.Fatalf("vm runtime host name = %q, want esxi-01.lab.local", got) } if got := vmRecord.Resource.VMware.InstanceUUID; got != "vm-instance-201" { t.Fatalf("vm instance uuid = %q, want vm-instance-201", got) } if got := vmRecord.Identity.MachineID; got != "vm-instance-201" { t.Fatalf("vm machine id = %q, want vm-instance-201", got) } if got := vmRecord.Identity.IPAddresses; len(got) != 1 || got[0] != "10.0.0.21" { t.Fatalf("vm identity IPs = %#v, want [10.0.0.21]", got) } if got := vmRecord.Identity.MACAddresses; len(got) != 1 || got[0] != "00:50:56:aa:bb:cc" { t.Fatalf("vm identity MACs = %#v, want [00:50:56:aa:bb:cc]", got) } if vmRecord.Resource.Metrics == nil || vmRecord.Resource.Metrics.CPU == nil || vmRecord.Resource.Metrics.Memory == nil { t.Fatalf("expected VMware VM metrics, got %+v", vmRecord.Resource.Metrics) } if got := vmRecord.Resource.Metrics.Memory.Percent; got != 57.5 { t.Fatalf("vm memory percent = %v, want 57.5", got) } if got := vmRecord.Resource.Metrics.DiskWrite.Value; got != 2048 { t.Fatalf("vm disk write = %v, want 2048", got) } if len(vmRecord.Resource.Incidents) != 1 || vmRecord.Resource.Incidents[0].Code != "vmware_alarm_state" { t.Fatalf("expected VMware VM incident projection, got %+v", vmRecord.Resource.Incidents) } datastoreRecord := records[2] if datastoreRecord.SourceID != "vc-1:datastore:datastore-11" { t.Fatalf("datastore source id = %q, want vc-1:datastore:datastore-11", datastoreRecord.SourceID) } if datastoreRecord.Resource.Type != unifiedresources.ResourceTypeStorage { t.Fatalf("datastore resource type = %q, want %q", datastoreRecord.Resource.Type, unifiedresources.ResourceTypeStorage) } if datastoreRecord.Resource.Storage == nil || datastoreRecord.Resource.Storage.Platform != "vmware-vsphere" { t.Fatalf("expected canonical VMware storage metadata, got %+v", datastoreRecord.Resource.Storage) } if datastoreRecord.Resource.Status != unifiedresources.StatusWarning { t.Fatalf("datastore status = %q, want %q", datastoreRecord.Resource.Status, unifiedresources.StatusWarning) } if datastoreRecord.Resource.Metrics == nil || datastoreRecord.Resource.Metrics.Disk == nil { t.Fatal("expected datastore disk metrics to be populated") } if got := datastoreRecord.Resource.Metrics.Disk.Percent; got != 60 { t.Fatalf("datastore disk usage percent = %v, want 60", got) } if len(datastoreRecord.Resource.Incidents) != 1 || datastoreRecord.Resource.Incidents[0].Code != "vmware_health_state" { t.Fatalf("expected VMware datastore health incident, got %+v", datastoreRecord.Resource.Incidents) } if got := datastoreRecord.Resource.VMware.RecentTaskSummary; got != "Refresh datastore (queued)" { t.Fatalf("datastore recent task summary = %q, want %q", got, "Refresh datastore (queued)") } if !datastoreRecord.Resource.Storage.Shared { t.Fatal("expected datastore storage meta to mark the datastore as shared") } if got := datastoreRecord.Resource.Storage.Nodes; len(got) != 1 || got[0] != "esxi-01.lab.local" { t.Fatalf("datastore storage nodes = %#v, want [esxi-01.lab.local]", got) } if got := datastoreRecord.Resource.Storage.ConsumerCount; got != 1 { t.Fatalf("datastore consumer count = %d, want 1", got) } if got := datastoreRecord.Resource.Storage.TopConsumers; len(got) != 1 || got[0].Name != "app-01" { t.Fatalf("datastore top consumers = %#v, want app-01", got) } if datastoreRecord.Resource.VMware.DatastoreAccessible == nil || !*datastoreRecord.Resource.VMware.DatastoreAccessible { t.Fatalf("datastore accessible = %#v, want true", datastoreRecord.Resource.VMware.DatastoreAccessible) } networkRecord := records[3] if networkRecord.SourceID != "vc-1:network:network-101" { t.Fatalf("network source id = %q, want vc-1:network:network-101", networkRecord.SourceID) } if networkRecord.Resource.Type != unifiedresources.ResourceTypeNetwork { t.Fatalf("network resource type = %q, want %q", networkRecord.Resource.Type, unifiedresources.ResourceTypeNetwork) } if networkRecord.Resource.Status != unifiedresources.StatusWarning { t.Fatalf("network status = %q, want %q", networkRecord.Resource.Status, unifiedresources.StatusWarning) } if networkRecord.Resource.VMware == nil || networkRecord.Resource.VMware.NetworkType != "STANDARD_PORTGROUP" { t.Fatalf("expected VMware network metadata, got %+v", networkRecord.Resource.VMware) } if got := networkRecord.Resource.VMware.NetworkHostNames; len(got) != 1 || got[0] != "esxi-01.lab.local" { t.Fatalf("network host names = %#v, want [esxi-01.lab.local]", got) } if got := networkRecord.Resource.VMware.NetworkVMNames; len(got) != 1 || got[0] != "app-01" { t.Fatalf("network VM names = %#v, want [app-01]", got) } if len(networkRecord.Resource.Incidents) != 1 || networkRecord.Resource.Incidents[0].Code != "vmware_alarm_state" { t.Fatalf("expected VMware network incident projection, got %+v", networkRecord.Resource.Incidents) } } func TestProviderActivityChanges_ProjectCanonicalTimelineEntries(t *testing.T) { previous := IsFeatureEnabled() SetFeatureEnabled(true) t.Cleanup(func() { SetFeatureEnabled(previous) }) collectedAt := time.Date(2026, time.March, 30, 18, 20, 0, 0, time.UTC) provider := NewProvider(InventorySnapshot{ ConnectionID: "vc-1", ConnectionName: "Lab VC", VCenterHost: "vc.lab.local", CollectedAt: collectedAt, Hosts: []InventoryHost{{ Host: "host-101", Name: "esxi-01.lab.local", RecentTasks: []InventoryTask{{ Task: "task-11", Name: "Reconnect host", State: "running", StartedAt: collectedAt.Add(-1 * time.Minute), }}, }}, VMs: []InventoryVM{{ VM: "vm-201", Name: "app-01", RecentEvents: []InventoryEvent{{ Event: "event-201", Type: "VmMessageEvent", Message: "Snapshot completed successfully", User: "vpxuser", CreatedAt: collectedAt.Add(-2 * time.Minute), }}, }}, Datastores: []InventoryDatastore{{ Datastore: "datastore-11", Name: "nvme-primary", RecentEvents: []InventoryEvent{{ Event: "event-301", Type: "DatastoreRenamedEvent", Message: "Datastore metadata refreshed", User: "storage-admin", CreatedAt: collectedAt.Add(-3 * time.Minute), }}, }}, Networks: []InventoryNetwork{{ Network: "network-101", Name: "VM Network", RecentEvents: []InventoryEvent{{ Event: "event-401", Type: "NetworkEvent", Message: "Network metadata refreshed", User: "network-admin", CreatedAt: collectedAt.Add(-4 * time.Minute), }}, }}, }) changes := provider.ActivityChanges() if len(changes) != 4 { t.Fatalf("expected 4 VMware activity changes, got %d", len(changes)) } if changes[0].Kind != unifiedresources.ChangeActivity { t.Fatalf("latest change kind = %q, want %q", changes[0].Kind, unifiedresources.ChangeActivity) } if changes[0].SourceType != unifiedresources.SourcePlatformEvent { t.Fatalf("latest change source type = %q, want %q", changes[0].SourceType, unifiedresources.SourcePlatformEvent) } if changes[0].SourceAdapter != unifiedresources.AdapterVMware { t.Fatalf("latest change source adapter = %q, want %q", changes[0].SourceAdapter, unifiedresources.AdapterVMware) } if changes[0].ResourceID != "vc-1:host:host-101" { t.Fatalf("latest change resource ID = %q, want vc-1:host:host-101", changes[0].ResourceID) } if changes[0].OccurredAt == nil || !changes[0].OccurredAt.Equal(collectedAt.Add(-1*time.Minute)) { t.Fatalf("latest change occurred_at = %v, want %v", changes[0].OccurredAt, collectedAt.Add(-1*time.Minute)) } if got := changes[0].Metadata[unifiedresources.MetadataActivityType]; got != "vmware_task" { t.Fatalf("latest change activity_type = %#v, want vmware_task", got) } if got := changes[1].Metadata["vmwareEventType"]; got != "VmMessageEvent" { t.Fatalf("vm change event type = %#v, want VmMessageEvent", got) } if got := changes[2].Metadata["vmwareEventUser"]; got != "storage-admin" { t.Fatalf("datastore change user = %#v, want storage-admin", got) } if got := changes[3].Metadata["vmwareEntityType"]; got != "network" { t.Fatalf("network change entity type = %#v, want network", got) } } func boolPtr(value bool) *bool { return &value } func timePtr(value time.Time) *time.Time { return &value }