Files
natsdotnet/tests/NATS.Server.Transport.Tests/TlsHelperTests.cs
Joseph Doherty d2c04fcca5 refactor: extract NATS.Server.Transport.Tests project
Move TLS, OCSP, WebSocket, Networking, and IO test files from
NATS.Server.Tests into a dedicated NATS.Server.Transport.Tests
project. Update namespaces, replace private GetFreePort/ReadUntilAsync
with shared TestUtilities helpers, extract TestCertHelper to
TestUtilities, and replace Task.Delay polling loops with
PollHelper.WaitUntilAsync/YieldForAsync for proper synchronization.
2026-03-12 14:57:35 -04:00

134 lines
4.1 KiB
C#

using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using NATS.Server;
using NATS.Server.TestUtilities;
using NATS.Server.Tls;
namespace NATS.Server.Transport.Tests;
public class TlsHelperTests
{
[Fact]
public void LoadCertificate_loads_pem_cert_and_key()
{
var (certPath, keyPath) = TestCertHelper.GenerateTestCertFiles();
try
{
var cert = TlsHelper.LoadCertificate(certPath, keyPath);
cert.ShouldNotBeNull();
cert.HasPrivateKey.ShouldBeTrue();
}
finally { File.Delete(certPath); File.Delete(keyPath); }
}
[Fact]
public void BuildServerAuthOptions_creates_valid_options()
{
var (certPath, keyPath) = TestCertHelper.GenerateTestCertFiles();
try
{
var opts = new NatsOptions { TlsCert = certPath, TlsKey = keyPath };
var authOpts = TlsHelper.BuildServerAuthOptions(opts);
authOpts.ShouldNotBeNull();
authOpts.ServerCertificate.ShouldNotBeNull();
}
finally { File.Delete(certPath); File.Delete(keyPath); }
}
[Fact]
public void LoadCaCertificates_rejects_non_certificate_pem_block()
{
var (_, key) = TestCertHelper.GenerateTestCert();
var pemPath = Path.GetTempFileName();
try
{
File.WriteAllText(pemPath, key.ExportPkcs8PrivateKeyPem());
Should.Throw<InvalidDataException>(() => TlsHelper.LoadCaCertificates(pemPath));
}
finally
{
File.Delete(pemPath);
key.Dispose();
}
}
[Fact]
public void LoadCaCertificates_loads_multiple_certificate_blocks()
{
var (certA, keyA) = GenerateTestCert();
var (certB, keyB) = GenerateTestCert();
var pemPath = Path.GetTempFileName();
try
{
File.WriteAllText(pemPath, certA.ExportCertificatePem() + certB.ExportCertificatePem());
var collection = TlsHelper.LoadCaCertificates(pemPath);
collection.Count.ShouldBe(2);
}
finally
{
File.Delete(pemPath);
certA.Dispose();
certB.Dispose();
keyA.Dispose();
keyB.Dispose();
}
}
[Fact]
public void MatchesPinnedCert_matches_correct_hash()
{
var (cert, _) = GenerateTestCert();
var hash = TlsHelper.GetCertificateHash(cert);
var pinned = new HashSet<string> { hash };
TlsHelper.MatchesPinnedCert(cert, pinned).ShouldBeTrue();
}
[Fact]
public void MatchesPinnedCert_rejects_wrong_hash()
{
var (cert, _) = GenerateTestCert();
var pinned = new HashSet<string> { "0000000000000000000000000000000000000000000000000000000000000000" };
TlsHelper.MatchesPinnedCert(cert, pinned).ShouldBeFalse();
}
[Fact]
public async Task PeekableStream_peeks_and_replays()
{
var data = "Hello, World!"u8.ToArray();
using var ms = new MemoryStream(data);
using var peekable = new PeekableStream(ms);
var peeked = await peekable.PeekAsync(1);
peeked.Length.ShouldBe(1);
peeked[0].ShouldBe((byte)'H');
var buf = new byte[data.Length];
int total = 0;
while (total < data.Length)
{
var read = await peekable.ReadAsync(buf.AsMemory(total));
if (read == 0) break;
total += read;
}
total.ShouldBe(data.Length);
buf.ShouldBe(data);
}
[Fact]
public async Task TlsRateLimiter_allows_within_limit()
{
using var limiter = new TlsRateLimiter(10);
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
for (int i = 0; i < 5; i++)
await limiter.WaitAsync(cts.Token);
}
// Delegate to shared TestCertHelper in TestUtilities
public static (string certPath, string keyPath) GenerateTestCertFiles()
=> TestCertHelper.GenerateTestCertFiles();
public static (X509Certificate2 cert, RSA key) GenerateTestCert()
=> TestCertHelper.GenerateTestCert();
}