server: infer archive collected_at from source events
This commit is contained in:
@@ -656,6 +656,43 @@ func TestConvertToReanimator_DeduplicatesAllSections(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConvertToReanimator_StatusFallbackUsesCollectedAt(t *testing.T) {
|
||||||
|
collectedAt := time.Date(2026, 2, 10, 15, 30, 0, 0, time.UTC)
|
||||||
|
input := &models.AnalysisResult{
|
||||||
|
Filename: "status-fallback.json",
|
||||||
|
CollectedAt: collectedAt,
|
||||||
|
Hardware: &models.HardwareConfig{
|
||||||
|
BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"},
|
||||||
|
Storage: []models.Storage{
|
||||||
|
{
|
||||||
|
Slot: "U.2-1",
|
||||||
|
Model: "PM9A3",
|
||||||
|
SerialNumber: "SSD-001",
|
||||||
|
Present: true,
|
||||||
|
Status: "OK",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := ConvertToReanimator(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ConvertToReanimator() failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(out.Hardware.Storage) != 1 {
|
||||||
|
t.Fatalf("expected 1 storage entry, got %d", len(out.Hardware.Storage))
|
||||||
|
}
|
||||||
|
|
||||||
|
wantTs := collectedAt.UTC().Format(time.RFC3339)
|
||||||
|
got := out.Hardware.Storage[0]
|
||||||
|
if got.StatusCheckedAt != wantTs {
|
||||||
|
t.Fatalf("expected status_checked_at=%q, got %q", wantTs, got.StatusCheckedAt)
|
||||||
|
}
|
||||||
|
if got.StatusAtCollect == nil || got.StatusAtCollect.At != wantTs {
|
||||||
|
t.Fatalf("expected status_at_collection.at=%q, got %#v", wantTs, got.StatusAtCollect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestConvertToReanimator_FirmwareExcludesDeviceBoundEntries(t *testing.T) {
|
func TestConvertToReanimator_FirmwareExcludesDeviceBoundEntries(t *testing.T) {
|
||||||
input := &models.AnalysisResult{
|
input := &models.AnalysisResult{
|
||||||
Filename: "fw-filter-test.json",
|
Filename: "fw-filter-test.json",
|
||||||
|
|||||||
@@ -1589,7 +1589,29 @@ func applyArchiveSourceMetadata(result *models.AnalysisResult) {
|
|||||||
result.SourceType = models.SourceTypeArchive
|
result.SourceType = models.SourceTypeArchive
|
||||||
result.Protocol = ""
|
result.Protocol = ""
|
||||||
result.TargetHost = ""
|
result.TargetHost = ""
|
||||||
result.CollectedAt = time.Now().UTC()
|
if result.CollectedAt.IsZero() {
|
||||||
|
result.CollectedAt = inferArchiveCollectedAt(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func inferArchiveCollectedAt(result *models.AnalysisResult) time.Time {
|
||||||
|
if result == nil {
|
||||||
|
return time.Now().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
var latest time.Time
|
||||||
|
for _, event := range result.Events {
|
||||||
|
if event.Timestamp.IsZero() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if latest.IsZero() || event.Timestamp.After(latest) {
|
||||||
|
latest = event.Timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !latest.IsZero() {
|
||||||
|
return latest.UTC()
|
||||||
|
}
|
||||||
|
return time.Now().UTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyCollectSourceMetadata(result *models.AnalysisResult, req CollectRequest) {
|
func applyCollectSourceMetadata(result *models.AnalysisResult, req CollectRequest) {
|
||||||
|
|||||||
@@ -30,6 +30,36 @@ func TestApplyArchiveSourceMetadata(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApplyArchiveSourceMetadata_PreservesExistingCollectedAt(t *testing.T) {
|
||||||
|
expected := time.Date(2026, 2, 10, 15, 30, 0, 0, time.UTC)
|
||||||
|
result := &models.AnalysisResult{
|
||||||
|
CollectedAt: expected,
|
||||||
|
}
|
||||||
|
|
||||||
|
applyArchiveSourceMetadata(result)
|
||||||
|
|
||||||
|
if !result.CollectedAt.Equal(expected) {
|
||||||
|
t.Fatalf("expected collected_at to be preserved: got %s want %s", result.CollectedAt, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApplyArchiveSourceMetadata_InferCollectedAtFromEvents(t *testing.T) {
|
||||||
|
oldTs := time.Date(2026, 2, 10, 13, 0, 0, 0, time.UTC)
|
||||||
|
newTs := time.Date(2026, 2, 10, 15, 30, 0, 0, time.UTC)
|
||||||
|
result := &models.AnalysisResult{
|
||||||
|
Events: []models.Event{
|
||||||
|
{Timestamp: oldTs},
|
||||||
|
{Timestamp: newTs},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
applyArchiveSourceMetadata(result)
|
||||||
|
|
||||||
|
if !result.CollectedAt.Equal(newTs) {
|
||||||
|
t.Fatalf("expected collected_at from latest event: got %s want %s", result.CollectedAt, newTs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestApplyCollectSourceMetadata(t *testing.T) {
|
func TestApplyCollectSourceMetadata(t *testing.T) {
|
||||||
req := CollectRequest{
|
req := CollectRequest{
|
||||||
Host: "bmc-api.local",
|
Host: "bmc-api.local",
|
||||||
|
|||||||
Reference in New Issue
Block a user