feat(transport): export site/instance selection end-to-end via CLI + ManagementActor (M8 B4)
This commit is contained in:
@@ -1386,6 +1386,15 @@ public class ManagementActorTests : TestKit, IDisposable
|
||||
.Returns(new List<Template>());
|
||||
_templateRepo.GetAllSharedScriptsAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(new List<Commons.Entities.Scripts.SharedScript>());
|
||||
// M8 (B4): export now also fans out to instances (template repo) and sites
|
||||
// (site repo) for the site/instance-scoped selection.
|
||||
_templateRepo.GetAllInstancesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(new List<Instance>());
|
||||
|
||||
var siteRepo = Substitute.For<ISiteRepository>();
|
||||
siteRepo.GetAllSitesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(new List<Commons.Entities.Sites.Site>());
|
||||
_services.AddScoped(_ => siteRepo);
|
||||
|
||||
var externalRepo = Substitute.For<IExternalSystemRepository>();
|
||||
externalRepo.GetAllExternalSystemsAsync(Arg.Any<CancellationToken>())
|
||||
@@ -1494,6 +1503,95 @@ public class ManagementActorTests : TestKit, IDisposable
|
||||
Assert.Contains("DoesNotExist", response.Error);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExportBundleCommand_WithSiteAndInstanceNames_ResolvesToIds()
|
||||
{
|
||||
// M8 (B4): --sites accepts SiteIdentifier (preferred) or Name; --instances
|
||||
// accepts UniqueName. The handler resolves both to surrogate ids and passes
|
||||
// them on the ExportSelection.
|
||||
var (exporter, _) = AddBundleSubstitutes();
|
||||
|
||||
var siteRepo = (ISiteRepository)_services.BuildServiceProvider()
|
||||
.GetRequiredService<ISiteRepository>();
|
||||
siteRepo.GetAllSitesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(new List<Commons.Entities.Sites.Site>
|
||||
{
|
||||
new("North Plant", "NORTH-01") { Id = 11 },
|
||||
new("East Plant", "EAST-02") { Id = 22 },
|
||||
});
|
||||
_templateRepo.GetAllInstancesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(new List<Instance>
|
||||
{
|
||||
new("NORTH-01.Pump1") { Id = 101, SiteId = 11, TemplateId = 1 },
|
||||
new("NORTH-01.Pump2") { Id = 102, SiteId = 11, TemplateId = 1 },
|
||||
});
|
||||
|
||||
Commons.Types.Transport.ExportSelection? captured = null;
|
||||
exporter.ExportAsync(
|
||||
Arg.Do<Commons.Types.Transport.ExportSelection>(s => captured = s),
|
||||
Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string?>(),
|
||||
Arg.Any<CancellationToken>())
|
||||
.Returns(_ => Task.FromResult<Stream>(new MemoryStream(new byte[] { 1, 2, 3 })));
|
||||
|
||||
var cmd = new ExportBundleCommand(
|
||||
All: false,
|
||||
TemplateNames: null, SharedScriptNames: null,
|
||||
ExternalSystemNames: null, DatabaseConnectionNames: null,
|
||||
NotificationListNames: null, SmtpConfigurationNames: null,
|
||||
ApiMethodNames: null,
|
||||
IncludeDependencies: false, Passphrase: null,
|
||||
SourceEnvironment: "test-env",
|
||||
// mix the SiteIdentifier and the friendly Name to prove the dual-key match.
|
||||
SiteNames: new[] { "NORTH-01", "East Plant" },
|
||||
InstanceNames: new[] { "NORTH-01.Pump1" });
|
||||
|
||||
var actor = CreateActor();
|
||||
actor.Tell(Envelope(cmd, "Designer"));
|
||||
|
||||
ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(5));
|
||||
Assert.NotNull(captured);
|
||||
Assert.Equal(new[] { 11, 22 }, captured!.SiteIds.OrderBy(x => x));
|
||||
Assert.Equal(new[] { 101 }, captured.InstanceIds);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExportBundleCommand_WithAll_IncludesEverySiteAndInstance()
|
||||
{
|
||||
// M8 (B4): All=true ignores per-type name lists and includes every site +
|
||||
// instance, mirroring how All already includes every template/etc.
|
||||
var (exporter, _) = AddBundleSubstitutes();
|
||||
|
||||
var siteRepo = (ISiteRepository)_services.BuildServiceProvider()
|
||||
.GetRequiredService<ISiteRepository>();
|
||||
siteRepo.GetAllSitesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(new List<Commons.Entities.Sites.Site>
|
||||
{
|
||||
new("North Plant", "NORTH-01") { Id = 11 },
|
||||
new("East Plant", "EAST-02") { Id = 22 },
|
||||
});
|
||||
_templateRepo.GetAllInstancesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(new List<Instance>
|
||||
{
|
||||
new("NORTH-01.Pump1") { Id = 101, SiteId = 11, TemplateId = 1 },
|
||||
new("EAST-02.Pump9") { Id = 909, SiteId = 22, TemplateId = 1 },
|
||||
});
|
||||
|
||||
Commons.Types.Transport.ExportSelection? captured = null;
|
||||
exporter.ExportAsync(
|
||||
Arg.Do<Commons.Types.Transport.ExportSelection>(s => captured = s),
|
||||
Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string?>(),
|
||||
Arg.Any<CancellationToken>())
|
||||
.Returns(_ => Task.FromResult<Stream>(new MemoryStream(new byte[] { 1 })));
|
||||
|
||||
var actor = CreateActor();
|
||||
actor.Tell(Envelope(AllExportCommand(), "Designer"));
|
||||
|
||||
ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(5));
|
||||
Assert.NotNull(captured);
|
||||
Assert.Equal(new[] { 11, 22 }, captured!.SiteIds.OrderBy(x => x));
|
||||
Assert.Equal(new[] { 101, 909 }, captured.InstanceIds.OrderBy(x => x));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ImportBundleCommand_WithBlockerRow_AbortsBeforeApply()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user