notify doctors work, audit work, new product advisory sprints

This commit is contained in:
master
2026-01-13 08:36:29 +02:00
parent b8868a5f13
commit 9ca7cb183e
343 changed files with 24492 additions and 3544 deletions

View File

@@ -0,0 +1,240 @@
// -----------------------------------------------------------------------------
// GreyQueueEntryTests.cs
// Description: Unit tests for Grey Queue entry model.
// -----------------------------------------------------------------------------
using System.Text.Json;
using FluentAssertions;
using StellaOps.Unknowns.Core.Models;
using Xunit;
namespace StellaOps.Unknowns.Core.Tests.Models;
[Trait("Category", "Unit")]
[Trait("Category", "GreyQueue")]
public sealed class GreyQueueEntryTests
{
[Fact]
public void IsPending_WhenStatusPending_ReturnsTrue()
{
// Arrange
var entry = CreateEntry(GreyQueueStatus.Pending);
// Act & Assert
entry.IsPending.Should().BeTrue();
}
[Fact]
public void IsPending_WhenStatusRetrying_ReturnsTrue()
{
// Arrange
var entry = CreateEntry(GreyQueueStatus.Retrying);
// Act & Assert
entry.IsPending.Should().BeTrue();
}
[Theory]
[InlineData(GreyQueueStatus.Processing)]
[InlineData(GreyQueueStatus.Resolved)]
[InlineData(GreyQueueStatus.Failed)]
[InlineData(GreyQueueStatus.Expired)]
[InlineData(GreyQueueStatus.Dismissed)]
public void IsPending_WhenStatusNotPendingOrRetrying_ReturnsFalse(GreyQueueStatus status)
{
// Arrange
var entry = CreateEntry(status);
// Act & Assert
entry.IsPending.Should().BeFalse();
}
[Fact]
public void IsExhausted_WhenAttemptsEqualMax_ReturnsTrue()
{
// Arrange
var entry = CreateEntry(GreyQueueStatus.Pending) with
{
ProcessingAttempts = 10,
MaxAttempts = 10
};
// Act & Assert
entry.IsExhausted.Should().BeTrue();
}
[Fact]
public void IsExhausted_WhenAttemptsExceedMax_ReturnsTrue()
{
// Arrange
var entry = CreateEntry(GreyQueueStatus.Pending) with
{
ProcessingAttempts = 15,
MaxAttempts = 10
};
// Act & Assert
entry.IsExhausted.Should().BeTrue();
}
[Fact]
public void IsExhausted_WhenAttemptsBelowMax_ReturnsFalse()
{
// Arrange
var entry = CreateEntry(GreyQueueStatus.Pending) with
{
ProcessingAttempts = 5,
MaxAttempts = 10
};
// Act & Assert
entry.IsExhausted.Should().BeFalse();
}
[Fact]
public void IsReadyForProcessing_WhenPendingNotExhaustedAndPastTime_ReturnsTrue()
{
// Arrange
var entry = CreateEntry(GreyQueueStatus.Pending) with
{
ProcessingAttempts = 3,
MaxAttempts = 10,
NextProcessingAt = DateTimeOffset.UtcNow.AddHours(-1)
};
// Act & Assert
entry.IsReadyForProcessing.Should().BeTrue();
}
[Fact]
public void IsReadyForProcessing_WhenPendingNotExhaustedAndNullTime_ReturnsTrue()
{
// Arrange
var entry = CreateEntry(GreyQueueStatus.Pending) with
{
ProcessingAttempts = 3,
MaxAttempts = 10,
NextProcessingAt = null
};
// Act & Assert
entry.IsReadyForProcessing.Should().BeTrue();
}
[Fact]
public void IsReadyForProcessing_WhenFutureNextProcessingTime_ReturnsFalse()
{
// Arrange
var entry = CreateEntry(GreyQueueStatus.Pending) with
{
ProcessingAttempts = 3,
MaxAttempts = 10,
NextProcessingAt = DateTimeOffset.UtcNow.AddHours(1)
};
// Act & Assert
entry.IsReadyForProcessing.Should().BeFalse();
}
[Fact]
public void IsReadyForProcessing_WhenExhausted_ReturnsFalse()
{
// Arrange
var entry = CreateEntry(GreyQueueStatus.Pending) with
{
ProcessingAttempts = 10,
MaxAttempts = 10,
NextProcessingAt = DateTimeOffset.UtcNow.AddHours(-1)
};
// Act & Assert
entry.IsReadyForProcessing.Should().BeFalse();
}
[Fact]
public void IsReadyForProcessing_WhenNotPending_ReturnsFalse()
{
// Arrange
var entry = CreateEntry(GreyQueueStatus.Resolved) with
{
ProcessingAttempts = 3,
MaxAttempts = 10,
NextProcessingAt = DateTimeOffset.UtcNow.AddHours(-1)
};
// Act & Assert
entry.IsReadyForProcessing.Should().BeFalse();
}
[Fact]
public void CanSerializeToJson_WithAllFields()
{
// Arrange
var entry = CreateEntry(GreyQueueStatus.Pending) with
{
ReasonDetail = "Insufficient VEX coverage from vendors",
TriggerOnCveUpdate = ["CVE-2025-12345", "CVE-2025-12346"],
TriggerOnPurlMatch = ["pkg:npm/*"]
};
// Act
var json = JsonSerializer.Serialize(entry);
var deserialized = JsonSerializer.Deserialize<GreyQueueEntry>(json);
// Assert
deserialized.Should().NotBeNull();
deserialized!.Id.Should().Be(entry.Id);
deserialized.Reason.Should().Be(entry.Reason);
deserialized.TriggerOnCveUpdate.Should().BeEquivalentTo(entry.TriggerOnCveUpdate);
}
[Theory]
[InlineData(GreyQueueReason.InsufficientVex)]
[InlineData(GreyQueueReason.ConflictingVex)]
[InlineData(GreyQueueReason.MissingReachability)]
[InlineData(GreyQueueReason.AmbiguousIdentity)]
[InlineData(GreyQueueReason.FeedNotAvailable)]
[InlineData(GreyQueueReason.ToolUnsupported)]
[InlineData(GreyQueueReason.BinaryAnalysisInconclusive)]
[InlineData(GreyQueueReason.BackportUncertain)]
[InlineData(GreyQueueReason.MultipleInterpretations)]
[InlineData(GreyQueueReason.AwaitingUpstreamAdvisory)]
public void AllGreyQueueReasons_AreValid(GreyQueueReason reason)
{
// Arrange & Act
var entry = CreateEntry(GreyQueueStatus.Pending) with { Reason = reason };
// Assert
entry.Reason.Should().Be(reason);
}
[Theory]
[InlineData(GreyQueueResolution.FeedUpdate)]
[InlineData(GreyQueueResolution.ToolUpdate)]
[InlineData(GreyQueueResolution.VexReceived)]
[InlineData(GreyQueueResolution.ManualResolution)]
[InlineData(GreyQueueResolution.Superseded)]
[InlineData(GreyQueueResolution.NotApplicable)]
[InlineData(GreyQueueResolution.Expired)]
public void AllGreyQueueResolutions_AreValid(GreyQueueResolution resolution)
{
// Arrange & Act
var entry = CreateEntry(GreyQueueStatus.Resolved) with { Resolution = resolution };
// Assert
entry.Resolution.Should().Be(resolution);
}
private static GreyQueueEntry CreateEntry(GreyQueueStatus status) => new()
{
Id = Guid.NewGuid(),
TenantId = "test-tenant",
UnknownId = Guid.NewGuid(),
Fingerprint = "sha256:abc123",
Status = status,
Priority = 100,
Reason = GreyQueueReason.InsufficientVex,
CreatedAt = DateTimeOffset.UtcNow,
CreatedBy = "test-user"
};
}