using Akka.Actor; using Akka.TestKit.Xunit2; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using ScadaLink.Commons.Messages.Deployment; namespace ScadaLink.Communication.Tests; /// /// WP-2: Tests for CommunicationService initialization and state. /// public class CommunicationServiceTests : TestKit { [Fact] public async Task BeforeInitialization_ThrowsOnUsage() { var options = Options.Create(new CommunicationOptions()); var logger = NullLogger.Instance; var service = new CommunicationService(options, logger); // CommunicationService requires SetCommunicationActor before use await Assert.ThrowsAsync(() => service.DeployInstanceAsync("site1", new DeployInstanceCommand( "dep1", "inst1", "hash1", "{}", "admin", DateTimeOffset.UtcNow))); } [Fact] public void UnsubscribeDebugView_IsTellNotAsk() { // Verify the method signature is void (fire-and-forget Tell pattern) var method = typeof(CommunicationService).GetMethod("UnsubscribeDebugView"); Assert.NotNull(method); Assert.Equal(typeof(void), method!.ReturnType); } // ── DeploymentManager-006: query-the-site-before-redeploy ── [Fact] public async Task QueryDeploymentStateAsync_BeforeInitialization_Throws() { var service = new CommunicationService( Options.Create(new CommunicationOptions()), NullLogger.Instance); await Assert.ThrowsAsync(() => service.QueryDeploymentStateAsync("site1", new DeploymentStateQueryRequest("corr-1", "inst1", DateTimeOffset.UtcNow))); } [Fact] public async Task QueryDeploymentStateAsync_SendsEnvelopeAndReturnsResponse() { // The query must be dispatched as a SiteEnvelope over the existing // command/control transport, exactly like other site-directed commands, // and the typed response returned to the caller. var service = new CommunicationService( Options.Create(new CommunicationOptions()), NullLogger.Instance); // A probe stands in for CentralCommunicationActor: it asserts the // envelope shape and replies with a typed response. var commActor = Sys.ActorOf(Props.Create(() => new EchoStateQueryActor())); service.SetCommunicationActor(commActor); var request = new DeploymentStateQueryRequest("corr-9", "QueriedInst", DateTimeOffset.UtcNow); var response = await service.QueryDeploymentStateAsync("site-a", request); Assert.Equal("corr-9", response.CorrelationId); Assert.Equal("QueriedInst", response.InstanceUniqueName); Assert.True(response.IsDeployed); Assert.Equal("sha256:applied", response.AppliedRevisionHash); } /// /// Stand-in for CentralCommunicationActor: verifies the message is wrapped /// in a SiteEnvelope targeting the requested site and replies with a typed /// DeploymentStateQueryResponse. /// private class EchoStateQueryActor : ReceiveActor { public EchoStateQueryActor() { Receive(env => { if (env is { SiteId: "site-a", Message: DeploymentStateQueryRequest req }) { Sender.Tell(new DeploymentStateQueryResponse( req.CorrelationId, req.InstanceUniqueName, true, "dep-applied", "sha256:applied", DateTimeOffset.UtcNow)); } }); } } }