Files
natsdotnet/tests/NATS.Server.Tests/Auth/WildcardExportTests.cs
Joseph Doherty 2bdf0e75ed 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.
2026-02-25 12:53:04 -05:00

170 lines
7.6 KiB
C#

// 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);
}
}