feat: add stream import cycle detection (Gap 9.3)
Add StreamImportFormsCycle DFS method to Account plus GetStreamImportSources and HasStreamImportFrom helpers. Add GetStreamImportSourceAccounts to ImportMap. 10 tests cover direct, indirect, self, diamond, and empty import scenarios.
This commit is contained in:
169
tests/NATS.Server.Tests/Auth/WildcardExportTests.cs
Normal file
169
tests/NATS.Server.Tests/Auth/WildcardExportTests.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
// Tests for wildcard service export matching on Account.
|
||||
// Go reference: accounts_test.go — getWildcardServiceExport, getServiceExport (accounts.go line 2849).
|
||||
|
||||
using NATS.Server.Auth;
|
||||
using NATS.Server.Imports;
|
||||
|
||||
namespace NATS.Server.Tests.Auth;
|
||||
|
||||
public class WildcardExportTests
|
||||
{
|
||||
private static Account CreateAccount(string name = "TestAccount") => new(name);
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// GetWildcardServiceExport
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void GetWildcardServiceExport_ExactMatch_ReturnsExport()
|
||||
{
|
||||
// Go ref: accounts.go getWildcardServiceExport — exact key in exports.services map
|
||||
var acct = CreateAccount();
|
||||
acct.AddServiceExport("orders.create", ServiceResponseType.Singleton, null);
|
||||
|
||||
var result = acct.GetWildcardServiceExport("orders.create");
|
||||
|
||||
result.ShouldNotBeNull();
|
||||
result.Subject.ShouldBe("orders.create");
|
||||
result.ResponseType.ShouldBe(ServiceResponseType.Singleton);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetWildcardServiceExport_StarWildcard_ReturnsExport()
|
||||
{
|
||||
// Go ref: accounts.go getWildcardServiceExport — isSubsetMatch with '*' wildcard
|
||||
var acct = CreateAccount();
|
||||
acct.AddServiceExport("orders.*", ServiceResponseType.Streamed, null);
|
||||
|
||||
var result = acct.GetWildcardServiceExport("orders.create");
|
||||
|
||||
result.ShouldNotBeNull();
|
||||
result.Subject.ShouldBe("orders.*");
|
||||
result.ResponseType.ShouldBe(ServiceResponseType.Streamed);
|
||||
result.IsWildcard.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetWildcardServiceExport_GtWildcard_ReturnsExport()
|
||||
{
|
||||
// Go ref: accounts.go getWildcardServiceExport — isSubsetMatch with '>' wildcard
|
||||
var acct = CreateAccount();
|
||||
acct.AddServiceExport("orders.>", ServiceResponseType.Chunked, null);
|
||||
|
||||
var result = acct.GetWildcardServiceExport("orders.create.new");
|
||||
|
||||
result.ShouldNotBeNull();
|
||||
result.Subject.ShouldBe("orders.>");
|
||||
result.ResponseType.ShouldBe(ServiceResponseType.Chunked);
|
||||
result.IsWildcard.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetWildcardServiceExport_NoMatch_ReturnsNull()
|
||||
{
|
||||
// Go ref: accounts.go getWildcardServiceExport — returns nil when no pattern matches
|
||||
var acct = CreateAccount();
|
||||
acct.AddServiceExport("payments.*", ServiceResponseType.Singleton, null);
|
||||
|
||||
var result = acct.GetWildcardServiceExport("orders.create");
|
||||
|
||||
result.ShouldBeNull();
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// GetAllServiceExports
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void GetAllServiceExports_ReturnsAll()
|
||||
{
|
||||
// Go ref: accounts.go — exports.services map contains all registered exports
|
||||
var acct = CreateAccount();
|
||||
acct.AddServiceExport("svc.a", ServiceResponseType.Singleton, null);
|
||||
acct.AddServiceExport("svc.b.*", ServiceResponseType.Streamed, null);
|
||||
acct.AddServiceExport("svc.>", ServiceResponseType.Chunked, null);
|
||||
|
||||
var all = acct.GetAllServiceExports();
|
||||
|
||||
all.Count.ShouldBe(3);
|
||||
all.Select(e => e.Subject).ShouldContain("svc.a");
|
||||
all.Select(e => e.Subject).ShouldContain("svc.b.*");
|
||||
all.Select(e => e.Subject).ShouldContain("svc.>");
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// GetExactServiceExport
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void GetExactServiceExport_Found()
|
||||
{
|
||||
// Go ref: accounts.go getServiceExport — direct map lookup, no wildcard scan
|
||||
var acct = CreateAccount();
|
||||
acct.AddServiceExport("orders.create", ServiceResponseType.Singleton, null);
|
||||
|
||||
var result = acct.GetExactServiceExport("orders.create");
|
||||
|
||||
result.ShouldNotBeNull();
|
||||
result.Subject.ShouldBe("orders.create");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExactServiceExport_NotFound_ReturnsNull()
|
||||
{
|
||||
// Go ref: accounts.go getServiceExport — map lookup misses wildcard patterns
|
||||
var acct = CreateAccount();
|
||||
acct.AddServiceExport("orders.*", ServiceResponseType.Singleton, null);
|
||||
|
||||
// "orders.create" is not an exact key in the map — only "orders.*" is
|
||||
var result = acct.GetExactServiceExport("orders.create");
|
||||
|
||||
result.ShouldBeNull();
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// HasServiceExport
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void HasServiceExport_ExactMatch_ReturnsTrue()
|
||||
{
|
||||
// Go ref: accounts.go — exact subject registered as an export
|
||||
var acct = CreateAccount();
|
||||
acct.AddServiceExport("orders.create", ServiceResponseType.Singleton, null);
|
||||
|
||||
acct.HasServiceExport("orders.create").ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasServiceExport_WildcardMatch_ReturnsTrue()
|
||||
{
|
||||
// Go ref: accounts.go — wildcard pattern covers the queried literal subject
|
||||
var acct = CreateAccount();
|
||||
acct.AddServiceExport("orders.>", ServiceResponseType.Singleton, null);
|
||||
|
||||
acct.HasServiceExport("orders.create.urgent").ShouldBeTrue();
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// IsWildcard flag
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Theory]
|
||||
[InlineData("orders.*", true)]
|
||||
[InlineData("orders.>", true)]
|
||||
[InlineData("orders.*.create", true)]
|
||||
[InlineData("orders.create", false)]
|
||||
[InlineData("svc", false)]
|
||||
public void IsWildcard_DetectsWildcardSubjects(string subject, bool expectedWildcard)
|
||||
{
|
||||
// Go ref: accounts.go — wildcard subjects contain '*' or '>'
|
||||
var acct = CreateAccount();
|
||||
acct.AddServiceExport(subject, ServiceResponseType.Singleton, null);
|
||||
|
||||
var result = acct.GetExactServiceExport(subject);
|
||||
|
||||
result.ShouldNotBeNull();
|
||||
result.IsWildcard.ShouldBe(expectedWildcard);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user