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