mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 20:53:06 +00:00
c3ee0cad55
Adds the async fan-out producer path from Plan §2.8 / §M5.d so primary Signals + DecisionTransitions rows can be projected onto their index tables without blocking the hot ingest loop. Components: - Constants.QueueNames.TelemetryIndexReconcile (azure-queue-name-safe, all lowercase + hyphens). - IndexReconcileEnvelope (Shared DTO, flat shape with nullable fields per SourceKind="Signal"/"DecisionTransition"). Avoids polymorphic Newtonsoft deserialization — see M5.c.3 finding. - IndexReconcileEnvelopeFactory pure static helper (FromSignal / FromDecisionTransition / BuildBatch) — state-less projection. - IIndexReconcileProducer (Shared interface) contract: no-op when the feature flag is off; never rethrows underlying-queue failures. - AzureQueueIndexReconcileProducer (Functions) — Base64-encoded JSON messages, lazy CreateIfNotExistsAsync guarded by Interlocked flag, Managed-Identity-or-connection-string construction mirroring BlobStorageService / TableStorageService. - IngestTelemetryFunction ctor gains IIndexReconcileProducer; after primary Signals + DecisionTransitions commit it builds envelopes and enqueues them. Event path is untouched (events don't feed the index tables per §2.8). - Program.cs registers the producer as a singleton. Feature gate (AdminConfiguration.EnableIndexDualWrite, default false): - New bool field in AdminConfiguration. - TableConfigRepository Store + Map extension (per feedback_table_storage_serialization). - Producer checks config each call via AdminConfigurationService's 5-min memory cache. Flag off → primary behaviour is bit-exact. Fault tolerance: queue-side exceptions (network, throttling, missing queue before first CreateIfNotExists) are logged per envelope and swallowed. The primary row is the source of truth; the 2h reconcile timer (M5.d.4) is the safety-net recovery path. Tests (+11 in Functions): - 5 IndexReconcileEnvelopeFactoryTests: Signal/DecisionTransition projection, dead-end preservation, batch ordering, null/empty. - 3 IndexReconcileEnvelopeSerializationTests: Newtonsoft round-trip (Signal, full DecisionTransition, minimal-envelope tolerance). - 3 AdminConfigurationEnableIndexDualWriteTests: default-false on new config + CreateDefault, persists when set. Deferred to later sub-milestones: - M5.d.3: queue-triggered consumer that maps envelopes to 0–3 index rows using M5.d.1 TableIndexRepository. - M5.d.4: 2h reconcile timer + release-gate dual-write verification tests, then flip EnableIndexDualWrite=true in prod admin config. Full Functions suite: 672 green (prev 661 + 11 new). Full solution build green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
35 lines
1.2 KiB
C#
35 lines
1.2 KiB
C#
using AutopilotMonitor.Shared.Models;
|
|
|
|
namespace AutopilotMonitor.Functions.Tests;
|
|
|
|
/// <summary>
|
|
/// Default-value + round-trip tests for <see cref="AdminConfiguration.EnableIndexDualWrite"/>
|
|
/// (Plan §M5.d). The flag gates the index-table dual-write rollout — defaulting to false
|
|
/// keeps pre-M5.d behaviour bit-exact and lets operators flip it on deliberately.
|
|
/// </summary>
|
|
public class AdminConfigurationEnableIndexDualWriteTests
|
|
{
|
|
[Fact]
|
|
public void EnableIndexDualWrite_defaults_to_false_on_new_config()
|
|
{
|
|
var cfg = new AdminConfiguration();
|
|
Assert.False(cfg.EnableIndexDualWrite);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnableIndexDualWrite_defaults_to_false_on_CreateDefault()
|
|
{
|
|
// CreateDefault() is called when no admin-config row exists yet — if it somehow
|
|
// enabled dual-write it would break the "default-off" contract immediately.
|
|
var cfg = AdminConfiguration.CreateDefault();
|
|
Assert.False(cfg.EnableIndexDualWrite);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnableIndexDualWrite_true_persists_on_the_config()
|
|
{
|
|
var cfg = new AdminConfiguration { EnableIndexDualWrite = true };
|
|
Assert.True(cfg.EnableIndexDualWrite);
|
|
}
|
|
}
|