Files
AutopilotMonitor/src/Backend/AutopilotMonitor.Functions.Tests/VulnerabilityCorrelateEnvelopeSerializationTests.cs
T
Oliver Kieselbach 86fdc5aca9 backend: stateless queue consumers become [QueueTrigger] functions; webhooks go through a queue; poll loop receives one message at a time
Audit 2026-09-05 F01/F02. Analyze, VulnerabilityCorrelate, TenantAutoApprove,
IndexReconcile and ImeMsiArchive are now host-managed queue functions under
Functions/Queue bound to the DataStorage connection (identity-based, resolved
by the host): the Flex scale controller wakes an instance for a message, the
host renews the lease and owns the retry/poison ladder. The four cascade
workers with lease/CAS state stay self-polling BackgroundServices; their base
now receives one message per poll so waiting messages can no longer expire
under another worker. Terminal enrollment alerts and hardware-rejection alerts
are enqueued on notification-dispatch (channel ids only, resolved at send
time) instead of fire-and-forget sends the host could drop on scale-in.
Requires the app setting DataStorage__queueServiceUri before deploy.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 02:52:37 +02:00

95 lines
3.5 KiB
C#

using AutopilotMonitor.Shared.Models;
using Newtonsoft.Json;
namespace AutopilotMonitor.Functions.Tests;
/// <summary>
/// JSON round-trip tests for <see cref="VulnerabilityCorrelateEnvelope"/>. The producer
/// (<c>AzureQueueVulnerabilityCorrelateProducer</c>) serializes with Newtonsoft and the
/// queue worker (<c>VulnerabilityCorrelateQueueFunction</c>) deserializes the same shape —
/// pinning the contract here keeps the queue from silently breaking when fields are added.
/// </summary>
public class VulnerabilityCorrelateEnvelopeSerializationTests
{
private const string TenantId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
private const string SessionId = "b2c3d4e5-f6a7-8901-bcde-f12345678901";
private static readonly DateTime EnqueuedAt =
new(2026, 4, 30, 9, 48, 58, DateTimeKind.Utc);
[Theory]
[InlineData(null)]
[InlineData(1)]
[InlineData(2)]
public void Envelope_round_trips_through_newtonsoft_for_all_white_glove_parts(int? whiteGlovePart)
{
var original = new VulnerabilityCorrelateEnvelope
{
EnvelopeVersion = "1",
TenantId = TenantId,
SessionId = SessionId,
WhiteGlovePart = whiteGlovePart,
Reason = "shutdown_inventory",
EnqueuedAt = EnqueuedAt,
};
var json = JsonConvert.SerializeObject(original);
var restored = JsonConvert.DeserializeObject<VulnerabilityCorrelateEnvelope>(json);
Assert.NotNull(restored);
Assert.Equal("1", restored!.EnvelopeVersion);
Assert.Equal(TenantId, restored.TenantId);
Assert.Equal(SessionId, restored.SessionId);
Assert.Equal(whiteGlovePart, restored.WhiteGlovePart);
Assert.Equal("shutdown_inventory", restored.Reason);
Assert.Equal(EnqueuedAt, restored.EnqueuedAt.ToUniversalTime());
}
[Fact]
public void Envelope_default_envelope_version_is_one()
{
var envelope = new VulnerabilityCorrelateEnvelope();
Assert.Equal("1", envelope.EnvelopeVersion);
}
[Fact]
public void Envelope_default_strings_are_empty_not_null()
{
var envelope = new VulnerabilityCorrelateEnvelope();
Assert.Equal(string.Empty, envelope.TenantId);
Assert.Equal(string.Empty, envelope.SessionId);
Assert.Equal(string.Empty, envelope.Reason);
}
[Fact]
public void Envelope_default_white_glove_part_is_null()
{
var envelope = new VulnerabilityCorrelateEnvelope();
Assert.Null(envelope.WhiteGlovePart);
}
[Fact]
public void Envelope_serializes_all_fields_on_wire()
{
// Pin the JSON wire-format so a property rename can't slip through unnoticed.
var envelope = new VulnerabilityCorrelateEnvelope
{
TenantId = TenantId,
SessionId = SessionId,
WhiteGlovePart = 1,
Reason = "whiteglove_part1_inventory",
EnqueuedAt = EnqueuedAt,
};
var json = JsonConvert.SerializeObject(envelope);
Assert.Contains("\"TenantId\"", json);
Assert.Contains("\"SessionId\"", json);
Assert.Contains("\"WhiteGlovePart\"", json);
Assert.Contains("\"Reason\"", json);
Assert.Contains("\"EnqueuedAt\"", json);
Assert.Contains("\"EnvelopeVersion\"", json);
Assert.Contains("whiteglove_part1_inventory", json);
}
}