// Copyright 2020-2026 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
using System.Reflection;
using Shouldly;
namespace ZB.MOM.NatsNet.Server.Tests.JetStream;
///
/// Tests for JetStream API error helpers.
/// Mirrors server/jetstream_errors_test.go.
///
public sealed class JetStreamErrorsTests
{
[Fact] // T:1381
public void IsNatsErr_ShouldSucceed()
{
JsApiErrors.IsNatsErr(
JsApiErrors.NotEnabledForAccount,
JsApiErrors.NotEnabledForAccount.ErrCode).ShouldBeTrue();
JsApiErrors.IsNatsErr(
JsApiErrors.NotEnabledForAccount,
JsApiErrors.ClusterNotActive.ErrCode).ShouldBeFalse();
JsApiErrors.IsNatsErr(
JsApiErrors.NotEnabledForAccount,
JsApiErrors.ClusterNotActive.ErrCode,
JsApiErrors.ClusterNotAvail.ErrCode).ShouldBeFalse();
JsApiErrors.IsNatsErr(
JsApiErrors.NotEnabledForAccount,
JsApiErrors.ClusterNotActive.ErrCode,
JsApiErrors.NotEnabledForAccount.ErrCode).ShouldBeTrue();
JsApiErrors.IsNatsErr(
new JsApiError { ErrCode = JsApiErrors.NotEnabledForAccount.ErrCode },
1,
JsApiErrors.ClusterNotActive.ErrCode,
JsApiErrors.NotEnabledForAccount.ErrCode).ShouldBeTrue();
JsApiErrors.IsNatsErr(
new JsApiError { ErrCode = JsApiErrors.NotEnabledForAccount.ErrCode },
1,
2,
JsApiErrors.ClusterNotActive.ErrCode).ShouldBeFalse();
JsApiErrors.IsNatsErr(null, JsApiErrors.ClusterNotActive.ErrCode).ShouldBeFalse();
JsApiErrors.IsNatsErr(new InvalidOperationException("x"), JsApiErrors.ClusterNotActive.ErrCode).ShouldBeFalse();
}
[Fact] // T:1382
public void ApiError_Error_ShouldSucceed()
{
JsApiErrors.Error(JsApiErrors.ClusterNotActive).ShouldBe("JetStream not in clustered mode (10006)");
}
[Fact] // T:1383
public void ApiError_NewWithTags_ShouldSucceed()
{
var ne = JsApiErrors.NewJSRestoreSubscribeFailedError(new Exception("failed error"), "the.subject");
ne.Description.ShouldBe("JetStream unable to subscribe to restore snapshot the.subject: failed error");
ReferenceEquals(ne, JsApiErrors.RestoreSubscribeFailed).ShouldBeFalse();
}
[Fact] // T:1384
public void ApiError_NewWithUnless_ShouldSucceed()
{
var notEnabled = JsApiErrors.NotEnabledForAccount.ErrCode;
var streamRestore = JsApiErrors.StreamRestore.ErrCode;
var peerRemap = JsApiErrors.PeerRemap.ErrCode;
JsApiErrors.IsNatsErr(
JsApiErrors.NewJSStreamRestoreError(
new Exception("failed error"),
JsApiErrors.Unless(JsApiErrors.NotEnabledForAccount)),
notEnabled).ShouldBeTrue();
JsApiErrors.IsNatsErr(
JsApiErrors.NewJSStreamRestoreError(new Exception("failed error")),
streamRestore).ShouldBeTrue();
JsApiErrors.IsNatsErr(
JsApiErrors.NewJSStreamRestoreError(
new Exception("failed error"),
JsApiErrors.Unless(new Exception("other error"))),
streamRestore).ShouldBeTrue();
JsApiErrors.IsNatsErr(
JsApiErrors.NewJSPeerRemapError(JsApiErrors.Unless(JsApiErrors.NotEnabledForAccount)),
notEnabled).ShouldBeTrue();
JsApiErrors.IsNatsErr(
JsApiErrors.NewJSPeerRemapError(JsApiErrors.Unless(null)),
peerRemap).ShouldBeTrue();
JsApiErrors.IsNatsErr(
JsApiErrors.NewJSPeerRemapError(JsApiErrors.Unless(new Exception("other error"))),
peerRemap).ShouldBeTrue();
}
[Fact]
public void ParseOpts_WithUnlessApiErrorOption_ReturnsOverride()
{
var parseOpts = typeof(JsApiErrors).GetMethod(
"ParseOpts",
BindingFlags.NonPublic | BindingFlags.Static);
parseOpts.ShouldNotBeNull();
var expected = new JsApiError { Code = 401, ErrCode = 2048, Description = "override" };
var result = parseOpts.Invoke(
null,
[new JsApiErrors.ErrorOption[] { JsApiErrors.Unless(expected) }]);
result.ShouldBeAssignableTo()
.ErrCode.ShouldBe(expected.ErrCode);
}
[Fact]
public void ToReplacerArgs_WithStringErrorAndObject_ConvertsToStringValues()
{
var toReplacerArgs = typeof(JsApiErrors).GetMethod(
"ToReplacerArgs",
BindingFlags.NonPublic | BindingFlags.Static);
toReplacerArgs.ShouldNotBeNull();
var result = toReplacerArgs.Invoke(
null,
[new object?[] { "{string}", "value", "{error}", new InvalidOperationException("boom"), "{number}", 42 }]);
result.ShouldBeAssignableTo()
.ShouldBe(["{string}", "value", "{error}", "boom", "{number}", "42"]);
}
}