test: cover WatchDeployEvents scoped-count re-projection
This commit is contained in:
+81
-7
@@ -63,18 +63,78 @@ public sealed class GalaxyRepositoryGrpcServiceScopeTests
|
|||||||
Assert.Equal(0, scoped.TotalChildCount);
|
Assert.Equal(0, scoped.TotalChildCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When the scope provider returns a non-empty glob, the deploy event's
|
||||||
|
/// object/attribute counts are re-projected against the scoped subtree and override
|
||||||
|
/// the raw counts the notifier published.
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task WatchDeployEvents_ScopedProvider_EmitsFilteredCounts()
|
||||||
|
{
|
||||||
|
GalaxyHierarchyCacheEntry entry = BuildSampleEntry();
|
||||||
|
|
||||||
|
// Sanity: the full hierarchy projects to six objects / four attributes.
|
||||||
|
GalaxyHierarchyQueryResult full = GalaxyHierarchyProjector.Project(
|
||||||
|
entry,
|
||||||
|
new DiscoverHierarchyRequest());
|
||||||
|
Assert.Equal(6, full.TotalObjectCount);
|
||||||
|
Assert.Equal(4, full.Objects.Sum(obj => obj.Attributes.Count));
|
||||||
|
|
||||||
|
// The glob selects only LineA's two leaf objects (Pump01, Valve01), each with one
|
||||||
|
// attribute. That scoped projection (2 objects / 2 attributes) is a non-empty subset
|
||||||
|
// distinct from both the full count and the raw notifier values below.
|
||||||
|
GalaxyHierarchyQueryResult scopedProjection = GalaxyHierarchyProjector.Project(
|
||||||
|
entry,
|
||||||
|
new DiscoverHierarchyRequest(),
|
||||||
|
browseSubtreeGlobs: ["PlantArea/LineA/*"]);
|
||||||
|
Assert.Equal(2, scopedProjection.TotalObjectCount);
|
||||||
|
Assert.Equal(2, scopedProjection.Objects.Sum(obj => obj.Attributes.Count));
|
||||||
|
|
||||||
|
// Publish a deploy event whose RAW counts differ from both full and scoped, so an
|
||||||
|
// assertion on the scoped values proves the override actually happened.
|
||||||
|
RecordingDeployNotifier notifier = new();
|
||||||
|
notifier.Publish(new GalaxyDeployEventInfo(
|
||||||
|
Sequence: 42,
|
||||||
|
ObservedAt: new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero),
|
||||||
|
TimeOfLastDeploy: new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero),
|
||||||
|
ObjectCount: 999,
|
||||||
|
AttributeCount: 888));
|
||||||
|
|
||||||
|
GalaxyRepositoryGrpcService service = CreateService(
|
||||||
|
entry,
|
||||||
|
new FakeBrowseScopeProvider(subtrees: ["PlantArea/LineA/*"]),
|
||||||
|
notifier);
|
||||||
|
|
||||||
|
// RecordingDeployNotifier yields the latest event then completes, so the stream
|
||||||
|
// ends after the single event without needing cancellation.
|
||||||
|
CapturingStreamWriter responseStream = new();
|
||||||
|
await service.WatchDeployEvents(
|
||||||
|
new WatchDeployEventsRequest(),
|
||||||
|
responseStream,
|
||||||
|
new TestServerCallContext());
|
||||||
|
|
||||||
|
DeployEvent emitted = Assert.Single(responseStream.Written);
|
||||||
|
Assert.Equal(scopedProjection.TotalObjectCount, emitted.ObjectCount);
|
||||||
|
Assert.Equal(2, emitted.AttributeCount);
|
||||||
|
// The raw notifier values were overridden by the scoped re-projection.
|
||||||
|
Assert.NotEqual(999, emitted.ObjectCount);
|
||||||
|
Assert.NotEqual(888, emitted.AttributeCount);
|
||||||
|
}
|
||||||
|
|
||||||
private static GalaxyRepositoryGrpcService CreateService(
|
private static GalaxyRepositoryGrpcService CreateService(
|
||||||
GalaxyHierarchyCacheEntry entry,
|
GalaxyHierarchyCacheEntry entry,
|
||||||
IGalaxyBrowseScopeProvider scope)
|
IGalaxyBrowseScopeProvider scope,
|
||||||
|
IGalaxyDeployNotifier? notifier = null)
|
||||||
{
|
{
|
||||||
GalaxyRepositoryOptions options = new()
|
// No test here calls TestConnection, so a fake repository (no real SQL) is enough
|
||||||
{
|
// and removes any latent localhost-connection risk.
|
||||||
ConnectionString = "Server=localhost;Database=ZB;Integrated Security=True;Encrypt=False;",
|
|
||||||
};
|
|
||||||
return new GalaxyRepositoryGrpcService(
|
return new GalaxyRepositoryGrpcService(
|
||||||
new GalaxyRepository(options),
|
new FakeGalaxyRepository(
|
||||||
|
Array.Empty<GalaxyHierarchyRow>(),
|
||||||
|
Array.Empty<GalaxyAttributeRow>(),
|
||||||
|
deployTime: null),
|
||||||
new StubGalaxyHierarchyCache(entry),
|
new StubGalaxyHierarchyCache(entry),
|
||||||
new RecordingDeployNotifier(),
|
notifier ?? new RecordingDeployNotifier(),
|
||||||
scope);
|
scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +218,20 @@ public sealed class GalaxyRepositoryGrpcServiceScopeTests
|
|||||||
public Task WaitForFirstLoadAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
public Task WaitForFirstLoadAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Records every <see cref="DeployEvent"/> the service streams.</summary>
|
||||||
|
private sealed class CapturingStreamWriter : IServerStreamWriter<DeployEvent>
|
||||||
|
{
|
||||||
|
public List<DeployEvent> Written { get; } = [];
|
||||||
|
|
||||||
|
public WriteOptions? WriteOptions { get; set; }
|
||||||
|
|
||||||
|
public Task WriteAsync(DeployEvent message)
|
||||||
|
{
|
||||||
|
Written.Add(message);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Minimal in-memory <see cref="ServerCallContext"/> for direct service unit tests.</summary>
|
/// <summary>Minimal in-memory <see cref="ServerCallContext"/> for direct service unit tests.</summary>
|
||||||
private sealed class TestServerCallContext : ServerCallContext
|
private sealed class TestServerCallContext : ServerCallContext
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user