// Ported from golang/nats-server/server/jetstream_test.go // Admin operations: stream/consumer list/names, account info, stream leader stepdown, // peer info, account purge, server remove, API routing using System.Text; using NATS.Server.Auth; using NATS.Server.JetStream; using NATS.Server.JetStream.Api; using NATS.Server.JetStream.Cluster; using NATS.Server.JetStream.Models; using NATS.Server.TestUtilities; namespace NATS.Server.Tests.JetStream; public class JetStreamAdminTests { // Go: TestJetStreamRequestAPI server/jetstream_test.go:5429 [Fact] public async Task Account_info_returns_stream_and_consumer_counts() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("S1", "s1.>"); _ = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.S2", """{"subjects":["s2.>"]}"""); _ = await fx.CreateConsumerAsync("S1", "C1", "s1.>"); var info = await fx.RequestLocalAsync("$JS.API.INFO", "{}"); info.Error.ShouldBeNull(); info.AccountInfo.ShouldNotBeNull(); info.AccountInfo!.Streams.ShouldBe(2); info.AccountInfo.Consumers.ShouldBe(1); } // Go: TestJetStreamRequestAPI — account info with zero [Fact] public void Account_info_empty_returns_zero_counts() { var router = new JetStreamApiRouter(new StreamManager(), new ConsumerManager()); var resp = router.Route("$JS.API.INFO", "{}"u8); resp.Error.ShouldBeNull(); resp.AccountInfo.ShouldNotBeNull(); resp.AccountInfo!.Streams.ShouldBe(0); resp.AccountInfo.Consumers.ShouldBe(0); } // Go: TestJetStreamFilteredStreamNames server/jetstream_test.go:5392 [Fact] public async Task Stream_names_returns_all_streams() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("ALPHA", "alpha.>"); _ = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.BETA", """{"subjects":["beta.>"]}"""); _ = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.GAMMA", """{"subjects":["gamma.>"]}"""); var names = await fx.RequestLocalAsync("$JS.API.STREAM.NAMES", "{}"); names.StreamNames.ShouldNotBeNull(); names.StreamNames!.Count.ShouldBe(3); names.StreamNames.ShouldContain("ALPHA"); names.StreamNames.ShouldContain("BETA"); names.StreamNames.ShouldContain("GAMMA"); } // Go: TestJetStreamFilteredStreamNames — names sorted [Fact] public async Task Stream_names_are_sorted() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("ZZZ", "zzz.>"); _ = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.AAA", """{"subjects":["aaa.>"]}"""); _ = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.MMM", """{"subjects":["mmm.>"]}"""); var names = await fx.RequestLocalAsync("$JS.API.STREAM.NAMES", "{}"); names.StreamNames![0].ShouldBe("AAA"); names.StreamNames[1].ShouldBe("MMM"); names.StreamNames[2].ShouldBe("ZZZ"); } // Go: TestJetStreamStreamList [Fact] public async Task Stream_list_returns_same_as_names() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("L1", "l1.>"); _ = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.L2", """{"subjects":["l2.>"]}"""); var names = await fx.RequestLocalAsync("$JS.API.STREAM.NAMES", "{}"); var list = await fx.RequestLocalAsync("$JS.API.STREAM.LIST", "{}"); list.StreamNames!.Count.ShouldBe(names.StreamNames!.Count); } // Go: TestJetStreamFilteredStreamNames — empty after delete all [Fact] public async Task Stream_names_empty_after_all_deleted() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("DEL1", "del1.>"); _ = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.DEL2", """{"subjects":["del2.>"]}"""); _ = await fx.RequestLocalAsync("$JS.API.STREAM.DELETE.DEL1", "{}"); _ = await fx.RequestLocalAsync("$JS.API.STREAM.DELETE.DEL2", "{}"); var names = await fx.RequestLocalAsync("$JS.API.STREAM.NAMES", "{}"); names.StreamNames!.Count.ShouldBe(0); } // Go: TestJetStreamConsumerList [Fact] public async Task Consumer_names_returns_all_consumers() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("CL", "cl.>"); _ = await fx.CreateConsumerAsync("CL", "A", "cl.a"); _ = await fx.CreateConsumerAsync("CL", "B", "cl.b"); _ = await fx.CreateConsumerAsync("CL", "C", "cl.c"); var names = await fx.RequestLocalAsync("$JS.API.CONSUMER.NAMES.CL", "{}"); names.ConsumerNames.ShouldNotBeNull(); names.ConsumerNames!.Count.ShouldBe(3); } // Go: TestJetStreamConsumerList — names sorted [Fact] public async Task Consumer_names_are_sorted() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("CS", "cs.>"); _ = await fx.CreateConsumerAsync("CS", "ZZZ", "cs.>"); _ = await fx.CreateConsumerAsync("CS", "AAA", "cs.>"); _ = await fx.CreateConsumerAsync("CS", "MMM", "cs.>"); var names = await fx.RequestLocalAsync("$JS.API.CONSUMER.NAMES.CS", "{}"); names.ConsumerNames![0].ShouldBe("AAA"); names.ConsumerNames[1].ShouldBe("MMM"); names.ConsumerNames[2].ShouldBe("ZZZ"); } // Go: TestJetStreamConsumerList — list matches names [Fact] public async Task Consumer_list_returns_same_as_names() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("CLM", "clm.>"); _ = await fx.CreateConsumerAsync("CLM", "C1", "clm.>"); _ = await fx.CreateConsumerAsync("CLM", "C2", "clm.>"); var names = await fx.RequestLocalAsync("$JS.API.CONSUMER.NAMES.CLM", "{}"); var list = await fx.RequestLocalAsync("$JS.API.CONSUMER.LIST.CLM", "{}"); list.ConsumerNames!.Count.ShouldBe(names.ConsumerNames!.Count); } // Go: TestJetStreamConsumerList — empty after delete all [Fact] public async Task Consumer_names_empty_after_all_deleted() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("CD", "cd.>"); _ = await fx.CreateConsumerAsync("CD", "C1", "cd.>"); _ = await fx.RequestLocalAsync("$JS.API.CONSUMER.DELETE.CD.C1", "{}"); var names = await fx.RequestLocalAsync("$JS.API.CONSUMER.NAMES.CD", "{}"); names.ConsumerNames!.Count.ShouldBe(0); } // Go: TestJetStreamStreamLeaderStepdown [Fact] public async Task Stream_leader_stepdown_returns_success() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("SLD", "sld.>"); var resp = await fx.RequestLocalAsync("$JS.API.STREAM.LEADER.STEPDOWN.SLD", "{}"); resp.Success.ShouldBeTrue(); } // Go: TestJetStreamStreamPeerRemove [Fact] public async Task Stream_peer_remove_returns_success() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("SPR", "spr.>"); var resp = await fx.RequestLocalAsync("$JS.API.STREAM.PEER.REMOVE.SPR", "{}"); resp.Success.ShouldBeTrue(); } // Go: TestJetStreamConsumerLeaderStepdown [Fact] public async Task Consumer_leader_stepdown_returns_success() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("CLSD", "clsd.>"); _ = await fx.CreateConsumerAsync("CLSD", "C1", "clsd.>"); var resp = await fx.RequestLocalAsync("$JS.API.CONSUMER.LEADER.STEPDOWN.CLSD.C1", "{}"); resp.Success.ShouldBeTrue(); } // Go: TestJetStreamAccountPurge server/jetstream_test.go [Fact] public async Task Account_purge_returns_success() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("AP", "ap.>"); var resp = await fx.RequestLocalAsync("$JS.API.ACCOUNT.PURGE.DEFAULT", "{}"); resp.Success.ShouldBeTrue(); } // Go: TestJetStreamServerRemove [Fact] public void Server_remove_returns_success() { var router = new JetStreamApiRouter(); var resp = router.Route("$JS.API.SERVER.REMOVE", "{}"u8); resp.Success.ShouldBeTrue(); } // Go: TestJetStreamAccountStreamMove [Fact] public async Task Account_stream_move_returns_success() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("ASM", "asm.>"); var resp = await fx.RequestLocalAsync("$JS.API.ACCOUNT.STREAM.MOVE.MYSTREAM", "{}"); resp.Success.ShouldBeTrue(); } // Go: TestJetStreamAccountStreamMoveCancel [Fact] public async Task Account_stream_move_cancel_returns_success() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("ASMC", "asmc.>"); var resp = await fx.RequestLocalAsync("$JS.API.ACCOUNT.STREAM.MOVE.CANCEL.MYSTREAM", "{}"); resp.Success.ShouldBeTrue(); } // Go: TestJetStreamRequestAPI — unknown subject [Fact] public void Unknown_api_subject_returns_not_found() { var router = new JetStreamApiRouter(); var resp = router.Route("$JS.API.UNKNOWN.THING", "{}"u8); resp.Error.ShouldNotBeNull(); resp.Error!.Code.ShouldBe(404); } // Go: TestJetStreamRequestAPI — multiple API calls [Fact] public async Task Multiple_api_calls_in_sequence() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("MULTI", "multi.>"); // INFO var info = await fx.RequestLocalAsync("$JS.API.INFO", "{}"); info.AccountInfo.ShouldNotBeNull(); // STREAM.NAMES var names = await fx.RequestLocalAsync("$JS.API.STREAM.NAMES", "{}"); names.StreamNames.ShouldNotBeNull(); // STREAM.INFO var sInfo = await fx.RequestLocalAsync("$JS.API.STREAM.INFO.MULTI", "{}"); sInfo.StreamInfo.ShouldNotBeNull(); } // Go: TestJetStreamDisabledLimitsEnforcementJWT server/jetstream_test.go [Fact] public async Task Jwt_limited_account_enforces_max_streams() { await using var fx = await JetStreamApiFixture.StartJwtLimitedAccountAsync(maxStreams: 1); var s1 = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.S1", """{"subjects":["s1.>"]}"""); s1.Error.ShouldBeNull(); var s2 = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.S2", """{"subjects":["s2.>"]}"""); s2.Error.ShouldNotBeNull(); s2.Error!.Code.ShouldBe(10027); } // Go: TestJetStreamDisabledLimitsEnforcementJWT — delete frees slot [Fact] public async Task Jwt_limited_account_delete_frees_slot() { await using var fx = await JetStreamApiFixture.StartJwtLimitedAccountAsync(maxStreams: 1); _ = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.S1", """{"subjects":["s1.>"]}"""); _ = await fx.RequestLocalAsync("$JS.API.STREAM.DELETE.S1", "{}"); var s2 = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.S2", """{"subjects":["s2.>"]}"""); s2.Error.ShouldBeNull(); } // Go: TestJetStreamSystemLimits server/jetstream_test.go:4636 [Fact] public async Task Account_info_updates_after_consumer_creation() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("AI", "ai.>"); var before = await fx.RequestLocalAsync("$JS.API.INFO", "{}"); before.AccountInfo!.Consumers.ShouldBe(0); _ = await fx.CreateConsumerAsync("AI", "C1", "ai.>"); var after = await fx.RequestLocalAsync("$JS.API.INFO", "{}"); after.AccountInfo!.Consumers.ShouldBe(1); } // Go: TestJetStreamSystemLimits — account info updates after stream deletion [Fact] public async Task Account_info_updates_after_stream_deletion() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("AID", "aid.>"); var before = await fx.RequestLocalAsync("$JS.API.INFO", "{}"); before.AccountInfo!.Streams.ShouldBe(1); _ = await fx.RequestLocalAsync("$JS.API.STREAM.DELETE.AID", "{}"); var after = await fx.RequestLocalAsync("$JS.API.INFO", "{}"); after.AccountInfo!.Streams.ShouldBe(0); } // Go: TestJetStreamConsumerList — consumer names scoped to stream [Fact] public async Task Consumer_names_for_non_existent_stream_returns_empty() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("X", "x.>"); var names = await fx.RequestLocalAsync("$JS.API.CONSUMER.NAMES.NOPE", "{}"); names.ConsumerNames.ShouldNotBeNull(); names.ConsumerNames!.Count.ShouldBe(0); } // Go: TestJetStreamMetaLeaderStepdown [Fact] public void Meta_leader_stepdown_with_meta_group_returns_success() { var metaGroup = new JetStreamMetaGroup(3); var router = new JetStreamApiRouter(new StreamManager(), new ConsumerManager(), metaGroup); var resp = router.Route("$JS.API.META.LEADER.STEPDOWN", "{}"u8); resp.Success.ShouldBeTrue(); } // Go: TestJetStreamMetaLeaderStepdown — without meta group [Fact] public void Meta_leader_stepdown_without_meta_group_returns_not_found() { var router = new JetStreamApiRouter(); var resp = router.Route("$JS.API.META.LEADER.STEPDOWN", "{}"u8); resp.Error.ShouldNotBeNull(); resp.Error!.Code.ShouldBe(404); } // Go: TestJetStreamStreamLeaderStepdown — non-existent stream [Fact] public async Task Stream_leader_stepdown_non_existent_still_succeeds() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("LS", "ls.>"); // Stepdown for non-existent stream doesn't error (no-op) var resp = await fx.RequestLocalAsync("$JS.API.STREAM.LEADER.STEPDOWN.NOPE", "{}"); resp.Success.ShouldBeTrue(); } // Go: TestJetStreamConsumerNext — via API router [Fact] public async Task Consumer_next_via_api_returns_messages() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("NEXT", "next.>"); _ = await fx.CreateConsumerAsync("NEXT", "C1", "next.>"); _ = await fx.PublishAndGetAckAsync("next.x", "data1"); _ = await fx.PublishAndGetAckAsync("next.x", "data2"); var resp = await fx.RequestLocalAsync( "$JS.API.CONSUMER.MSG.NEXT.NEXT.C1", """{"batch":2}"""); resp.PullBatch.ShouldNotBeNull(); resp.PullBatch!.Messages.Count.ShouldBe(2); } // Go: TestJetStreamConsumerNext — empty [Fact] public async Task Consumer_next_with_no_messages_returns_empty() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("NE", "ne.>"); _ = await fx.CreateConsumerAsync("NE", "C1", "ne.>"); var resp = await fx.RequestLocalAsync( "$JS.API.CONSUMER.MSG.NEXT.NE.C1", """{"batch":1}"""); resp.PullBatch.ShouldNotBeNull(); resp.PullBatch!.Messages.Count.ShouldBe(0); } // Go: TestJetStreamStorageSelection [Fact] public async Task Storage_selection_file() { await using var fx = await JetStreamApiFixture.StartWithStreamConfigAsync(new StreamConfig { Name = "FILE", Subjects = ["file.>"], Storage = StorageType.File, }); var backend = await fx.GetStreamBackendTypeAsync("FILE"); backend.ShouldBe("file"); } // Go: TestJetStreamStorageSelection — memory [Fact] public async Task Storage_selection_memory() { await using var fx = await JetStreamApiFixture.StartWithStreamConfigAsync(new StreamConfig { Name = "MEM", Subjects = ["mem.>"], Storage = StorageType.Memory, }); var backend = await fx.GetStreamBackendTypeAsync("MEM"); backend.ShouldBe("memory"); } // Go: TestJetStreamStorageSelection — non-existent [Fact] public async Task Storage_backend_type_for_missing_stream() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("X", "x.>"); var backend = await fx.GetStreamBackendTypeAsync("NOPE"); backend.ShouldBe("missing"); } // Go: TestJetStreamConsumerNames — for specific stream [Fact] public async Task Consumer_names_only_include_target_stream() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("S1", "s1.>"); _ = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.S2", """{"subjects":["s2.>"]}"""); _ = await fx.CreateConsumerAsync("S1", "C1", "s1.>"); _ = await fx.CreateConsumerAsync("S2", "C2", "s2.>"); var names = await fx.RequestLocalAsync("$JS.API.CONSUMER.NAMES.S1", "{}"); names.ConsumerNames!.Count.ShouldBe(1); names.ConsumerNames.ShouldContain("C1"); names.ConsumerNames.ShouldNotContain("C2"); } // Go: TestJetStreamConsumerDelete — delete decrements count [Fact] public async Task Delete_consumer_decrements_account_info_count() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("DCC", "dcc.>"); _ = await fx.CreateConsumerAsync("DCC", "C1", "dcc.>"); _ = await fx.CreateConsumerAsync("DCC", "C2", "dcc.>"); var before = await fx.RequestLocalAsync("$JS.API.INFO", "{}"); before.AccountInfo!.Consumers.ShouldBe(2); _ = await fx.RequestLocalAsync("$JS.API.CONSUMER.DELETE.DCC.C1", "{}"); var after = await fx.RequestLocalAsync("$JS.API.INFO", "{}"); after.AccountInfo!.Consumers.ShouldBe(1); } // Go: TestJetStreamAccountPurge — empty account name fails [Fact] public void Account_purge_without_name_returns_not_found() { var router = new JetStreamApiRouter(); var resp = router.Route("$JS.API.ACCOUNT.PURGE.", "{}"u8); resp.Error.ShouldNotBeNull(); } // Go: TestJetStreamAccountStreamMove — empty stream name fails [Fact] public void Account_stream_move_without_name_returns_not_found() { var router = new JetStreamApiRouter(); var resp = router.Route("$JS.API.ACCOUNT.STREAM.MOVE.", "{}"u8); resp.Error.ShouldNotBeNull(); } // Go: TestJetStreamStreamLeaderStepdown — empty stream name fails [Fact] public void Stream_leader_stepdown_without_name_returns_not_found() { var router = new JetStreamApiRouter(); var resp = router.Route("$JS.API.STREAM.LEADER.STEPDOWN.", "{}"u8); resp.Error.ShouldNotBeNull(); } // Go: TestJetStreamStreamPeerRemove — empty stream name fails [Fact] public void Stream_peer_remove_without_name_returns_not_found() { var router = new JetStreamApiRouter(); var resp = router.Route("$JS.API.STREAM.PEER.REMOVE.", "{}"u8); resp.Error.ShouldNotBeNull(); } // Go: TestJetStreamConsumerLeaderStepdown — malformed subject [Fact] public void Consumer_leader_stepdown_with_single_token_returns_not_found() { var router = new JetStreamApiRouter(); var resp = router.Route("$JS.API.CONSUMER.LEADER.STEPDOWN.ONLYONE", "{}"u8); resp.Error.ShouldNotBeNull(); } // Go: TestJetStreamConsumerReset — non-existent consumer [Fact] public async Task Consumer_reset_non_existent_returns_not_found() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("RNE", "rne.>"); var resp = await fx.RequestLocalAsync("$JS.API.CONSUMER.RESET.RNE.NOPE", "{}"); resp.Success.ShouldBeFalse(); } // Go: TestJetStreamConsumerUnpin — non-existent consumer [Fact] public async Task Consumer_unpin_non_existent_returns_not_found() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("UNE", "une.>"); var resp = await fx.RequestLocalAsync("$JS.API.CONSUMER.UNPIN.UNE.NOPE", "{}"); resp.Success.ShouldBeFalse(); } // Go: TestJetStreamLimits server/jetstream_test.go [Fact] public async Task Jwt_limited_account_allows_within_limit() { await using var fx = await JetStreamApiFixture.StartJwtLimitedAccountAsync(maxStreams: 3); var s1 = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.S1", """{"subjects":["s1.>"]}"""); s1.Error.ShouldBeNull(); var s2 = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.S2", """{"subjects":["s2.>"]}"""); s2.Error.ShouldBeNull(); var s3 = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.S3", """{"subjects":["s3.>"]}"""); s3.Error.ShouldBeNull(); // Fourth should fail var s4 = await fx.RequestLocalAsync("$JS.API.STREAM.CREATE.S4", """{"subjects":["s4.>"]}"""); s4.Error.ShouldNotBeNull(); } // Go: TestJetStreamStreamMessageDeleteViaAPI [Fact] public async Task Message_delete_via_api_and_verify() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("MDAPI", "mdapi.>"); _ = await fx.PublishAndGetAckAsync("mdapi.x", "msg1"); var ack2 = await fx.PublishAndGetAckAsync("mdapi.x", "msg2"); _ = await fx.PublishAndGetAckAsync("mdapi.x", "msg3"); var del = await fx.RequestLocalAsync( "$JS.API.STREAM.MSG.DELETE.MDAPI", $$"""{ "seq": {{ack2.Seq}} }"""); del.Success.ShouldBeTrue(); // Verify the deleted message is gone var msg = await fx.RequestLocalAsync( "$JS.API.STREAM.MSG.GET.MDAPI", $$"""{ "seq": {{ack2.Seq}} }"""); msg.Error.ShouldNotBeNull(); // Other messages still exist var state = await fx.GetStreamStateAsync("MDAPI"); state.Messages.ShouldBe(2UL); } // Go: TestJetStreamRequestAPI — direct get missing sequence [Fact] public async Task Direct_get_with_zero_sequence_returns_error() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("DGZ", "dgz.>"); _ = await fx.PublishAndGetAckAsync("dgz.x", "data"); var resp = await fx.RequestLocalAsync("$JS.API.DIRECT.GET.DGZ", """{"seq":0}"""); resp.Error.ShouldNotBeNull(); } // Go: TestJetStreamRequestAPI — direct get non-existent stream [Fact] public void Direct_get_non_existent_stream_returns_error() { var router = new JetStreamApiRouter(); var resp = router.Route("$JS.API.DIRECT.GET.NOPE", """{"seq":1}"""u8); resp.Error.ShouldNotBeNull(); } // Go: TestJetStreamConsumerNext — batch default [Fact] public async Task Consumer_next_with_no_batch_defaults_to_one() { await using var fx = await JetStreamApiFixture.StartWithStreamAsync("NBAT", "nbat.>"); _ = await fx.CreateConsumerAsync("NBAT", "C1", "nbat.>"); _ = await fx.PublishAndGetAckAsync("nbat.x", "data1"); _ = await fx.PublishAndGetAckAsync("nbat.x", "data2"); var resp = await fx.RequestLocalAsync( "$JS.API.CONSUMER.MSG.NEXT.NBAT.C1", "{}"); resp.PullBatch!.Messages.Count.ShouldBe(1); } }