test(sitecall-audit): symmetric SourceNode coverage on DbOutbound emitter + clarify DI comments
Two follow-ups from the T13/T14 code review: - M1: Add CachedWrite_StampsSourceNode_OnSubmitTelemetryRow and CachedWrite_NoSourceNodeWired_LeavesSourceNodeNull to DatabaseCachedWriteEmissionTests, mirroring the existing ApiOutbound SourceNode tests in ExternalSystemCachedCallEmissionTests. Site-emitter coverage now symmetric across both cached-call channels. - M2: Clarify the GetService(INodeIdentityProvider) DI comments on the CachedCallTelemetryForwarder and CachedCallLifecycleBridge factories: it's test composition roots that may not register the provider, not central production. Both site and central hosts always register it via SiteServiceRegistration.BindSharedOptions.
This commit is contained in:
@@ -149,10 +149,13 @@ public static class ServiceCollectionExtensions
|
||||
sp.GetRequiredService<ILogger<CachedCallTelemetryForwarder>>(),
|
||||
// SourceNode-stamping (Task 14): the local node identity is
|
||||
// threaded through so RecordEnqueueAsync can stamp the
|
||||
// tracking row's SourceNode column. GetService — central
|
||||
// composition roots may not register the provider, in which
|
||||
// case the forwarder degrades to a null SourceNode rather
|
||||
// than failing the DI resolution.
|
||||
// tracking row's SourceNode column. GetService (not
|
||||
// GetRequiredService) — test composition roots that build a
|
||||
// stripped DI container may not register the provider, in
|
||||
// which case the forwarder degrades to a null SourceNode
|
||||
// rather than failing the DI resolution. Production hosts
|
||||
// (site + central) always register it via
|
||||
// SiteServiceRegistration.BindSharedOptions.
|
||||
sp.GetService<INodeIdentityProvider>()));
|
||||
|
||||
// M3 Bundle F: bridge the store-and-forward retry-loop observer hook
|
||||
@@ -165,9 +168,11 @@ public static class ServiceCollectionExtensions
|
||||
// INodeIdentityProvider singleton can be threaded through — the
|
||||
// bridge stamps SiteCallOperational.SourceNode from
|
||||
// INodeIdentityProvider.NodeName on every cached-call lifecycle row.
|
||||
// GetService (not GetRequiredService) — central composition roots may
|
||||
// not register the provider, in which case the bridge degrades to a
|
||||
// null SourceNode rather than failing the DI resolution.
|
||||
// GetService (not GetRequiredService) — test composition roots that
|
||||
// build a stripped DI container may not register the provider, in
|
||||
// which case the bridge degrades to a null SourceNode rather than
|
||||
// failing the DI resolution. Production hosts (site + central)
|
||||
// always register it via SiteServiceRegistration.BindSharedOptions.
|
||||
services.AddSingleton<CachedCallLifecycleBridge>(sp => new CachedCallLifecycleBridge(
|
||||
sp.GetRequiredService<ICachedCallTelemetryForwarder>(),
|
||||
sp.GetRequiredService<ILogger<CachedCallLifecycleBridge>>(),
|
||||
|
||||
@@ -328,4 +328,68 @@ public class DatabaseCachedWriteEmissionTests
|
||||
It.IsAny<Guid?>(), It.IsAny<string?>(), It.IsAny<Guid?>()),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
// ── SourceNode-stamping (Task 14) ──
|
||||
|
||||
[Fact]
|
||||
public async Task CachedWrite_StampsSourceNode_OnSubmitTelemetryRow()
|
||||
{
|
||||
// Symmetric to ExternalSystemCachedCallEmissionTests's
|
||||
// CachedCall_StampsSourceNode_OnEverySiteCallOperationalRow — locks
|
||||
// the DbOutbound emitter against a future refactor that drops
|
||||
// _sourceNode from the Database.CachedWrite CachedSubmit row.
|
||||
var gateway = new Mock<IDatabaseGateway>();
|
||||
gateway
|
||||
.Setup(g => g.CachedWriteAsync(
|
||||
"myDb", "INSERT INTO t VALUES (1)",
|
||||
It.IsAny<IReadOnlyDictionary<string, object?>?>(),
|
||||
InstanceName,
|
||||
It.IsAny<CancellationToken>(),
|
||||
It.IsAny<TrackedOperationId?>(),
|
||||
It.IsAny<Guid?>(), It.IsAny<string?>(), It.IsAny<Guid?>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
var forwarder = new CapturingForwarder();
|
||||
|
||||
var helper = new ScriptRuntimeContext.DatabaseHelper(
|
||||
gateway.Object,
|
||||
InstanceName,
|
||||
NullLogger.Instance,
|
||||
TestExecutionId,
|
||||
auditWriter: null,
|
||||
siteId: SiteId,
|
||||
sourceScript: SourceScript,
|
||||
cachedForwarder: forwarder,
|
||||
parentExecutionId: null,
|
||||
sourceNode: "node-a");
|
||||
|
||||
await helper.CachedWrite("myDb", "INSERT INTO t VALUES (1)");
|
||||
|
||||
var packet = Assert.Single(forwarder.Telemetry);
|
||||
Assert.Equal("node-a", packet.Operational.SourceNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CachedWrite_NoSourceNodeWired_LeavesSourceNodeNull()
|
||||
{
|
||||
// Default CreateHelper does NOT pass sourceNode — the legacy / test
|
||||
// host path. The operational row carries null SourceNode, leaving
|
||||
// central's SiteCalls.SourceNode NULL.
|
||||
var gateway = new Mock<IDatabaseGateway>();
|
||||
gateway
|
||||
.Setup(g => g.CachedWriteAsync(
|
||||
"myDb", "INSERT INTO t VALUES (1)",
|
||||
It.IsAny<IReadOnlyDictionary<string, object?>?>(),
|
||||
InstanceName,
|
||||
It.IsAny<CancellationToken>(),
|
||||
It.IsAny<TrackedOperationId?>(),
|
||||
It.IsAny<Guid?>(), It.IsAny<string?>(), It.IsAny<Guid?>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
var forwarder = new CapturingForwarder();
|
||||
|
||||
var helper = CreateHelper(gateway.Object, forwarder);
|
||||
await helper.CachedWrite("myDb", "INSERT INTO t VALUES (1)");
|
||||
|
||||
var packet = Assert.Single(forwarder.Telemetry);
|
||||
Assert.Null(packet.Operational.SourceNode);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user