using System.Text; using Shouldly; using ZB.MOM.NatsNet.Server; using ZB.MOM.NatsNet.Server.Auth; using ZB.MOM.NatsNet.Server.Internal; using ZB.MOM.NatsNet.Server.Internal.DataStructures; namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; public sealed class AccountTests { [Fact] // T:80 public void AccountMultipleServiceImportsWithSameSubjectFromDifferentAccounts_ShouldSucceed() { var importer = Account.NewAccount("CLIENTS"); var svcE = Account.NewAccount("SVC-E"); var svcW = Account.NewAccount("SVC-W"); importer.Imports.Services = new Dictionary> { ["SvcReq.>"] = [ new ServiceImportEntry { Account = svcE, From = "SvcReq.>", To = "SvcReq.>" }, new ServiceImportEntry { Account = svcW, From = "SvcReq.>", To = "SvcReq.>" }, ], }; var copied = Account.NewAccount("CLIENTS"); importer.ShallowCopy(copied); copied.Imports.Services.ShouldNotBeNull(); copied.Imports.Services!.ShouldContainKey("SvcReq.>"); copied.Imports.Services["SvcReq.>"].Count.ShouldBe(2); var accounts = copied.Imports.Services["SvcReq.>"] .Select(static si => si.Account?.Name) .OrderBy(static n => n) .ToArray(); accounts.ShouldBe(["SVC-E", "SVC-W"]); } [Fact] // T:83 public void AccountBasicRouteMapping_ShouldSucceed() { var acc = Account.NewAccount("global"); acc.AddMapping("foo", "bar").ShouldBeNull(); var (dest, mapped) = acc.SelectMappedSubject("foo"); mapped.ShouldBeTrue(); dest.ShouldBe("bar"); acc.RemoveMapping("foo").ShouldBeTrue(); var (destAfterRemove, mappedAfterRemove) = acc.SelectMappedSubject("foo"); mappedAfterRemove.ShouldBeFalse(); destAfterRemove.ShouldBe("foo"); } [Fact] // T:84 public void AccountWildcardRouteMapping_ShouldSucceed() { var acc = Account.NewAccount("global"); acc.AddMapping("foo.*.*", "bar.$2.$1").ShouldBeNull(); acc.AddMapping("bar.*.>", "baz.$1.>").ShouldBeNull(); var (mappedDest, mapped) = acc.SelectMappedSubject("foo.1.2"); mapped.ShouldBeTrue(); mappedDest.ShouldBe("bar.2.1"); var (remappedDest, remapped) = acc.SelectMappedSubject("bar.2.1"); remapped.ShouldBeTrue(); remappedDest.ShouldBe("baz.2.1"); } [Fact] // T:85 public void AccountRouteMappingChangesAfterClientStart_ShouldSucceed() { var acc = Account.NewAccount("global"); var (beforeDest, beforeMapped) = acc.SelectMappedSubject("foo"); beforeMapped.ShouldBeFalse(); beforeDest.ShouldBe("foo"); acc.AddMapping("foo", "bar").ShouldBeNull(); var (afterAddDest, afterAddMapped) = acc.SelectMappedSubject("foo"); afterAddMapped.ShouldBeTrue(); afterAddDest.ShouldBe("bar"); acc.RemoveMapping("foo").ShouldBeTrue(); var (afterRemoveDest, afterRemoveMapped) = acc.SelectMappedSubject("foo"); afterRemoveMapped.ShouldBeFalse(); afterRemoveDest.ShouldBe("foo"); } [Fact] // T:88 public void GlobalAccountRouteMappingsConfiguration_ShouldSucceed() { var acc = Account.NewAccount("global"); acc.AddMapping("foo", "bar").ShouldBeNull(); acc.AddWeightedMappings( "foo.*", MapDest.New("bar.v1.$1", 40), MapDest.New("baz.v2.$1", 20)).ShouldBeNull(); acc.AddMapping("bar.*.*", "RAB.$2.$1").ShouldBeNull(); var (simpleDest, simpleMapped) = acc.SelectMappedSubject("foo"); simpleMapped.ShouldBeTrue(); simpleDest.ShouldBe("bar"); var (crossDest, crossMapped) = acc.SelectMappedSubject("bar.11.22"); crossMapped.ShouldBeTrue(); crossDest.ShouldBe("RAB.22.11"); var counts = new Dictionary(StringComparer.Ordinal); for (var i = 0; i < 400; i++) { var (dest, mapped) = acc.SelectMappedSubject("foo.22"); mapped.ShouldBeTrue(); counts.TryGetValue(dest, out var current); counts[dest] = current + 1; } counts.ShouldContainKey("bar.v1.22"); counts.ShouldContainKey("baz.v2.22"); counts.ShouldContainKey("foo.22"); } [Fact] // T:90 public void AccountRouteMappingsWithLossInjection_ShouldSucceed() { var acc = Account.NewAccount("global"); acc.AddWeightedMappings("foo", MapDest.New("foo", 80)).ShouldBeNull(); acc.AddWeightedMappings("bar", MapDest.New("bar", 0)).ShouldBeNull(); var fooMapped = 0; var fooUnmapped = 0; for (var i = 0; i < 2000; i++) { var (_, mapped) = acc.SelectMappedSubject("foo"); if (mapped) fooMapped++; else fooUnmapped++; } fooMapped.ShouldBeGreaterThan(0); fooUnmapped.ShouldBeGreaterThan(0); for (var i = 0; i < 200; i++) { var (dest, mapped) = acc.SelectMappedSubject("bar"); mapped.ShouldBeFalse(); dest.ShouldBe("bar"); } } [Fact] // T:91 public void AccountRouteMappingsWithOriginClusterFilter_ShouldSucceed() { var acc = Account.NewAccount("global"); acc.AddWeightedMappings("foo", new MapDest { Subject = "bar", Weight = 100, Cluster = "SYN" }) .ShouldBeNull(); var (dest, mapped) = acc.SelectMappedSubject("foo"); mapped.ShouldBeTrue(); dest.ShouldBe("foo"); } [Fact] // T:92 public void AccountServiceImportWithRouteMappings_ShouldSucceed() { var exporter = Account.NewAccount("foo"); var importer = Account.NewAccount("bar"); exporter.AddMapping("request", "request.v2").ShouldBeNull(); importer.Imports.Services = new Dictionary> { ["request"] = [new ServiceImportEntry { Account = exporter, From = "request", To = "request" }], }; var (mappedSubject, mapped) = exporter.SelectMappedSubject("request"); mapped.ShouldBeTrue(); mappedSubject.ShouldBe("request.v2"); importer.Imports.Services.ShouldContainKey("request"); importer.Imports.Services["request"].Count.ShouldBe(1); importer.Imports.Services["request"][0].To.ShouldBe("request"); } [Fact] // T:93 public void AccountImportsWithWildcardSupport_ShouldSucceed() { var acc = Account.NewAccount("bar"); acc.AddMapping("request.*", "my.request.$1").ShouldBeNull(); acc.AddMapping("events.*", "foo.events.$1").ShouldBeNull(); acc.AddMapping("info.*.*.>", "foo.info.$2.$1.>").ShouldBeNull(); acc.SelectMappedSubject("request.22").ShouldBe(("my.request.22", true)); acc.SelectMappedSubject("events.22").ShouldBe(("foo.events.22", true)); acc.SelectMappedSubject("info.11.22.bar").ShouldBe(("foo.info.22.11.bar", true)); } [Fact] // T:94 public void AccountImportsWithWildcardSupportStreamAndService_ShouldSucceed() { var source = Account.NewAccount("foo"); var target = Account.NewAccount("bar"); target.Imports.Services = new Dictionary> { ["request.*"] = [new ServiceImportEntry { Account = source, From = "request.*", To = "my.request.$1", Transform = RequireTransform("request.*", "my.request.$1"), }], }; target.Imports.Streams = [ new StreamImportEntry { Account = source, From = "events.*", To = "foo.events.$1", Transform = RequireTransform("events.*", "foo.events.$1"), }, ]; target.Imports.Services["request.*"].Single().Transform!.TransformSubject("request.22") .ShouldBe("my.request.22"); target.Imports.Streams.Single().Transform!.TransformSubject("events.22") .ShouldBe("foo.events.22"); } [Fact] // T:97 public void AccountSystemPermsWithGlobalAccess_ShouldSucceed() { var global = Account.NewAccount("$G"); var system = Account.NewAccount("$SYS"); system.Exports.Services = new Dictionary { ["$SYS.REQ.>"] = new ServiceExportEntry { Account = system }, }; global.IsExportService("$SYS.REQ.INFO").ShouldBeFalse(); system.IsExportService("$SYS.REQ.INFO").ShouldBeTrue(); system.CheckServiceExportApproved(global, "$SYS.REQ.INFO", null).ShouldBeTrue(); } [Fact] // T:81 public void MultipleStreamImportsWithSameSubjectDifferentPrefix_ShouldSucceed() { var fooAccount = Account.NewAccount("foo"); var barAccount = Account.NewAccount("bar"); var importAccount = Account.NewAccount("import"); fooAccount.AddStreamExport("test", null).ShouldBeNull(); barAccount.AddStreamExport("test", null).ShouldBeNull(); importAccount.AddStreamImport(fooAccount, "test", "foo").ShouldBeNull(); importAccount.AddStreamImport(barAccount, "test", "bar").ShouldBeNull(); importAccount.Imports.Streams.ShouldNotBeNull(); importAccount.Imports.Streams!.Count.ShouldBe(2); importAccount.Imports.Streams.Select(si => si.To).OrderBy(static t => t).ToArray() .ShouldBe(["bar.test", "foo.test"]); } [Fact] // T:82 public void MultipleStreamImportsWithSameSubject_ShouldSucceed() { var fooAccount = Account.NewAccount("foo"); var barAccount = Account.NewAccount("bar"); var importAccount = Account.NewAccount("import"); fooAccount.AddStreamExport("test", null).ShouldBeNull(); barAccount.AddStreamExport("test", null).ShouldBeNull(); importAccount.AddStreamImport(fooAccount, "test", string.Empty).ShouldBeNull(); importAccount.AddStreamImport(fooAccount, "test", string.Empty).ShouldBe(ServerErrors.ErrStreamImportDuplicate); importAccount.AddStreamImport(barAccount, "test", string.Empty).ShouldBeNull(); importAccount.Imports.Streams.ShouldNotBeNull(); importAccount.Imports.Streams!.Count.ShouldBe(2); importAccount.Imports.Streams.Select(si => si.Account?.Name).OrderBy(static n => n).ToArray() .ShouldBe(["bar", "foo"]); } [Fact] // T:95 public void BenchmarkNewRouteReply() { var globalAccount = Account.NewAccount("$G"); var first = globalAccount.NewServiceReply(tracking: false); var second = globalAccount.NewServiceReply(tracking: false); var tracked = globalAccount.NewServiceReply(tracking: true); first.Length.ShouldBeGreaterThan(20); second.Length.ShouldBeGreaterThan(20); first.SequenceEqual(second).ShouldBeFalse(); var trackedText = Encoding.ASCII.GetString(tracked); trackedText.EndsWith(".T", StringComparison.Ordinal).ShouldBeTrue(); } [Fact] // T:98 public void ImportSubscriptionPartialOverlapWithPrefix_ShouldSucceed() { var transform = RequireTransform(">", "myprefix.>"); var mapped = transform.TransformSubject("test"); mapped.ShouldBe("myprefix.test"); foreach (var filter in new[] { ">", "myprefix.*", "myprefix.>", "myprefix.test", "*.>", "*.*", "*.test" }) SubscriptionIndex.SubjectIsSubsetMatch(mapped, filter).ShouldBeTrue(); } [Fact] // T:99 public void ImportSubscriptionPartialOverlapWithTransform_ShouldSucceed() { var transform = RequireTransform("*.*.>", "myprefix.$2.$1.>"); var mapped = transform.TransformSubject("1.2.test"); mapped.ShouldBe("myprefix.2.1.test"); foreach (var filter in new[] { ">", "*.*.*.>", "*.2.*.>", "*.*.1.>", "*.2.1.>", "*.*.*.*", "*.2.1.*", "*.*.*.test", "*.*.1.test", "*.2.*.test", "*.2.1.test", "myprefix.*.*.*", "myprefix.>", "myprefix.*.>", "myprefix.*.*.>", "myprefix.2.>", "myprefix.2.1.>", "myprefix.*.1.>", "myprefix.2.*.>", "myprefix.2.1.*", "myprefix.*.*.test", "myprefix.2.1.test", }) { SubscriptionIndex.SubjectIsSubsetMatch(mapped, filter).ShouldBeTrue(); } } [Fact] // T:104 public void AccountUserSubPermsWithQueueGroups_ShouldSucceed() { var c = new ClientConnection(ClientKind.Client); c.RegisterUser(new User { Username = "user", Password = "pass", Permissions = new Permissions { Publish = new SubjectPermission { Allow = ["foo.restricted"] }, Subscribe = new SubjectPermission { Allow = ["foo.>"], Deny = ["foo.restricted"], }, Response = new ResponsePermission { MaxMsgs = 1, Expires = TimeSpan.Zero }, }, }); c.Perms.ShouldNotBeNull(); c.Perms!.Sub.Allow.ShouldNotBeNull(); c.Perms.Sub.Deny.ShouldNotBeNull(); c.Perms.Sub.Allow!.Match("foo.restricted").PSubs.Count.ShouldBeGreaterThan(0); c.Perms.Sub.Deny!.Match("foo.restricted").PSubs.Count.ShouldBeGreaterThan(0); var (_, queue) = ClientConnection.SplitSubjectQueue("foo.> qg"); queue.ShouldNotBeNull(); Encoding.ASCII.GetString(queue!).ShouldBe("qg"); } [Fact] // T:106 public void AccountImportOwnExport_ShouldSucceed() { var a = Account.NewAccount("A"); a.Exports.Services = new Dictionary { ["echo"] = new ServiceExportEntry { Account = a, Latency = new InternalServiceLatency { Subject = "latency.echo", Sampling = 100 }, }, }; a.Imports.Services = new Dictionary> { ["echo"] = [new ServiceImportEntry { Account = a, From = "echo", To = "echo" }], }; a.IsExportService("echo").ShouldBeTrue(); a.CheckServiceExportApproved(a, "echo", null).ShouldBeTrue(); a.Imports.Services["echo"].Count.ShouldBe(1); } [Fact] // T:107 public void AccountImportDuplicateResponseDeliveryWithLeafnodes_ShouldSucceed() { var exporter = Account.NewAccount("A"); var importer = Account.NewAccount("B"); exporter.Exports.Services = new Dictionary { ["foo"] = new ServiceExportEntry { Account = exporter, ResponseType = ServiceRespType.Streamed }, }; importer.Imports.Services = new Dictionary> { ["foo"] = [new ServiceImportEntry { Account = exporter, From = "foo", To = "foo", ResponseType = ServiceRespType.Streamed, }], }; importer.Imports.Services["foo"].Count.ShouldBe(1); importer.Imports.Services["foo"][0].DidDeliver.ShouldBeFalse(); exporter.CheckServiceExportApproved(importer, "foo", null).ShouldBeTrue(); } [Fact] // T:109 public void AccountServiceAndStreamExportDoubleDelivery_ShouldSucceed() { var tenant = Account.NewAccount("tenant1"); tenant.Exports.Streams = new Dictionary { ["DW.>"] = new StreamExport(), }; tenant.Exports.Services = new Dictionary { ["DW.>"] = new ServiceExportEntry { Account = tenant }, }; tenant.CheckStreamExportApproved(tenant, "DW.test.123", null).ShouldBeTrue(); tenant.CheckServiceExportApproved(tenant, "DW.test.123", null).ShouldBeTrue(); tenant.IsExportService("DW.test.123").ShouldBeTrue(); } [Fact] // T:110 public void AccountServiceImportNoResponders_ShouldSucceed() { var exporter = Account.NewAccount("accExp"); var importer = Account.NewAccount("accImp"); importer.Imports.Services = new Dictionary> { ["foo"] = [new ServiceImportEntry { Account = exporter, From = "foo", To = "foo" }], }; importer.Imports.Services["foo"].Count.ShouldBe(1); exporter.CheckServiceExportApproved(importer, "foo", null).ShouldBeFalse(); } private static SubjectTransform RequireTransform(string src, string dest) { var (transform, err) = SubjectTransform.New(src, dest); err.ShouldBeNull(); transform.ShouldNotBeNull(); return transform!; } }