diff --git a/.github/workflows/branch-snapshot.yml b/.github/workflows/branch-snapshot.yml index 90e24b384..e45b383b6 100644 --- a/.github/workflows/branch-snapshot.yml +++ b/.github/workflows/branch-snapshot.yml @@ -19,7 +19,7 @@ jobs: - name: Setup JDK uses: actions/setup-java@v4 with: - java-version: '8' + java-version: '21' distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 diff --git a/.github/workflows/build-main.yml b/.github/workflows/build-main.yml index ff88fba3a..e13a556f0 100644 --- a/.github/workflows/build-main.yml +++ b/.github/workflows/build-main.yml @@ -20,7 +20,7 @@ jobs: - name: Setup JDK uses: actions/setup-java@v4 with: - java-version: '8' + java-version: '21' distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 359ffca6d..c1ec61f11 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -16,9 +16,9 @@ jobs: SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} steps: - name: Setup JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: - java-version: '8' + java-version: '21' distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 8acc3e576..6f25ef039 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -20,7 +20,7 @@ jobs: - name: Setup JDK uses: actions/setup-java@v4 with: - java-version: '8' + java-version: '21' distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 diff --git a/build.gradle b/build.gradle index e15e3f40a..df7bd6d3c 100644 --- a/build.gradle +++ b/build.gradle @@ -6,23 +6,34 @@ plugins { id("maven-publish") id("jacoco") id("biz.aQute.bnd.builder") version "5.1.2" +// id("biz.aQute.bnd.builder") version "7.1.0" id("org.gradle.test-retry") version "1.6.4" id("io.github.gradle-nexus.publish-plugin") version "2.0.0" id("signing") } def jarVersion = "2.24.1" +group = 'io.nats' def isRelease = System.getenv("BUILD_EVENT") == "release" def brn = System.getenv("BRANCH_REF_NAME") def snap = brn == null || brn.equals("") ? "-SNAPSHOT" : "." + brn + "-SNAPSHOT" -group = 'io.nats' +def tc = System.getenv("TARGET_COMPATIBILITY"); +def targetCompat = tc == "21" ? JavaVersion.VERSION_21 : (tc == "17" ? JavaVersion.VERSION_17 : JavaVersion.VERSION_1_8) +def jarEnd = tc == "21" ? "-jdk21" : (tc == "17" ? "-jdk17" : "") +def jarAndArtifactName = "jnats" + jarEnd + version = isRelease ? jarVersion : jarVersion + snap // version is the variable the build actually uses. +def javaVersion = System.getProperty("java.version"); +System.out.println("Java: " + javaVersion) +System.out.println("Target Compatibility: " + targetCompat) +System.out.println(group + ":" + jarAndArtifactName + ":" + version) + java { sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = targetCompat } repositories { @@ -37,8 +48,13 @@ dependencies { implementation 'org.jspecify:jspecify:1.0.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' - testImplementation 'io.nats:jnats-server-runner:3.0.1' - testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.12.3' + testImplementation 'io.nats:jnats-server-runner:3.0.2-SNAPSHOT' + if (javaVersion.startsWith("1.8")) { + testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.12.3' + } + else { + testImplementation 'nl.jqno.equalsverifier:equalsverifier:4.2.2' + } testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } @@ -189,10 +205,10 @@ publishing { artifact javadocJar artifact testsJar pom { - name = "jnats" + name = jarAndArtifactName packaging = "jar" groupId = group - artifactId = "jnats" + artifactId = jarAndArtifactName description = 'Client library for working with the NATS messaging system.' url = 'https://github.com/nats-io/nats.java' licenses { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000..2cfe86acd --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,12 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +commons-math3 = "3.6.1" +guava = "33.4.5-jre" +junit-jupiter = "5.12.1" + +[libraries] +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/settings.gradle b/settings.gradle index ea03d6ce4..0eaa8335e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,8 +1,13 @@ pluginManagement { repositories { + gradlePluginPortal() mavenCentral() - maven { url "https://oss.sonatype.org/content/repositories/releases/" } - maven { url "https://plugins.gradle.org/m2/" } + maven { url="https://repo1.maven.org/maven2/" } + maven { url="https://central.sonatype.com/repository/maven-snapshots/" } + maven { url="https://plugins.gradle.org/m2/" } + } + plugins { + id("biz.aQute.bnd.builder") version "7.1.0" } } rootProject.name = 'java-nats' diff --git a/src/test/java/io/nats/client/AuthTests.java b/src/test/java/io/nats/client/AuthTests.java index 8ef43046d..156d6c7a3 100644 --- a/src/test/java/io/nats/client/AuthTests.java +++ b/src/test/java/io/nats/client/AuthTests.java @@ -13,6 +13,7 @@ package io.nats.client; +import io.nats.NatsServerRunner; import io.nats.client.Connection.Status; import io.nats.client.ConnectionListener.Events; import io.nats.client.impl.ListenerForTesting; @@ -22,6 +23,8 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; import javax.net.ssl.SSLContext; import java.io.BufferedWriter; @@ -34,9 +37,14 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static io.nats.client.NatsTestServer.configuredServer; +import static io.nats.client.NatsTestServer.skipValidateServer; +import static io.nats.client.utils.ConnectionUtils.*; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.condition.OS.WINDOWS; +@Execution(ExecutionMode.SAME_THREAD) public class AuthTests extends TestBase { @Test @@ -44,7 +52,7 @@ public void testUserPass() throws Exception { String[] customArgs = { "--user", "stephen", "--pass", "password" }; try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { // See config file for user/pass - Options options = new Options.Builder().server(ts.getURI()).maxReconnects(0) + Options options = optionsBuilder(ts.getURI()).maxReconnects(0) .userInfo("stephen".toCharArray(), "password".toCharArray()).build(); assertCanConnect(options); } @@ -84,7 +92,7 @@ public void testEncodedPassword() throws Exception { private void assertEncoded(String encoded, int port) throws IOException, InterruptedException { String url = "nats://u" + encoded + ":p" + encoded + "@localhost:" + port; - Options options = new Options.Builder().server(url).build(); + Options options = optionsBuilder(url).build(); Connection c = Nats.connect(options); c.getServerInfo(); c.close(); @@ -107,9 +115,10 @@ private static void assertNeedsJsonEncoding(String test) throws Exception { String user = "u" + test + "u"; String pass = "p" + test + "p"; String[] customArgs = {"--user", "\"" + user + "\"", "--pass", "\"" + pass + "\""}; - try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { + try (NatsTestServer ts = new NatsTestServer( + NatsServerRunner.builder().customArgs(customArgs))) { // See config file for user/pass - Options options = new Options.Builder().server("nats://localhost:" + ts.getPort()) + Options options = optionsBuilder("nats://localhost:" + ts.getPort()) .userInfo(user, pass) .maxReconnects(0).build(); assertCanConnect(options); @@ -127,9 +136,9 @@ public void testUserPassOnReconnect() throws Exception { try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { port = ts.getPort(); // See config file for user/pass - Options options = new Options.Builder().server(ts.getURI()).maxReconnects(-1) + Options options = optionsBuilder(ts.getURI()).maxReconnects(-1) .userInfo("stephen".toCharArray(), "password".toCharArray()).connectionListener(listener).build(); - nc = standardConnection(options); + nc = standardConnectionWait(options); sub = nc.subscribe("test"); nc.publish("test", null); @@ -168,7 +177,7 @@ public void testUserBCryptPass() throws Exception { "$2a$11$1oJy/wZYNTxr9jNwMNwS3eUGhBpHT3On8CL9o7ey89mpgo88VG6ba" }; try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { // See config file for user/pass - Options options = new Options.Builder().server(ts.getURI()).maxReconnects(0) + Options options = optionsBuilder(ts.getURI()).maxReconnects(0) .userInfo("ginger".toCharArray(), "password".toCharArray()).build(); assertCanConnect(options); } @@ -179,7 +188,7 @@ public void testUserPassInURL() throws Exception { String[] customArgs = { "--user", "stephen", "--pass", "password" }; try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { // See config file for user/pass - Options options = new Options.Builder().server("nats://stephen:password@localhost:" + ts.getPort()) + Options options = optionsBuilder("nats://stephen:password@localhost:" + ts.getPort()) .maxReconnects(0).build(); assertCanConnect(options); } @@ -189,16 +198,16 @@ public void testUserPassInURL() throws Exception { public void testUserPassInURLOnReconnect() throws Exception { ListenerForTesting listener = new ListenerForTesting(); int port; - Connection nc = null; - Subscription sub = null; + Connection nc; + Subscription sub; String[] customArgs = { "--user", "stephen", "--pass", "password" }; try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { port = ts.getPort(); // See config file for user/pass - Options options = new Options.Builder().server("nats://stephen:password@localhost:" + ts.getPort()) + Options options = optionsBuilder("nats://stephen:password@localhost:" + ts.getPort()) .maxReconnects(-1).connectionListener(listener).build(); - nc = standardConnection(options); + nc = standardConnectionWait(options); sub = nc.subscribe("test"); nc.publish("test", null); @@ -235,10 +244,10 @@ public void testUserPassInURLClusteredWithDifferentUser() throws Exception { try (NatsTestServer ts1 = new NatsTestServer(customArgs1, false); NatsTestServer ts2 = new NatsTestServer(customArgs2, false)) { // See config file for user/pass - Options options = new Options.Builder().server("nats://stephen:password@localhost:" + ts1.getPort()) + Options options = optionsBuilder("nats://stephen:password@localhost:" + ts1.getPort()) .server("nats://alberto:casadecampo@localhost:" + ts2.getPort()).maxReconnects(4).noRandomize() .connectionListener(listener).pingInterval(Duration.ofMillis(100)).build(); - Connection nc = standardConnection(options); + Connection nc = standardConnectionWait(options); assertEquals(nc.getConnectedUrl(), "nats://stephen:password@localhost:" + ts1.getPort()); listener.prepForStatusChange(Events.RESUBSCRIBED); @@ -257,11 +266,11 @@ public void testUserPassInURLWithFallback() throws Exception { try (NatsTestServer ts1 = new NatsTestServer(customArgs1, false); NatsTestServer ts2 = new NatsTestServer(customArgs2, false)) { // See config file for user/pass - Options options = new Options.Builder().server("nats://stephen:password@localhost:" + ts1.getPort()) + Options options = optionsBuilder("nats://stephen:password@localhost:" + ts1.getPort()) .server("nats://localhost:" + ts2.getPort()).noRandomize() .userInfo("alberto".toCharArray(), "casadecampo".toCharArray()).maxReconnects(4).noRandomize() .connectionListener(listener).pingInterval(Duration.ofMillis(100)).build(); - Connection nc = standardConnection(options); + Connection nc = standardConnectionWait(options); assertEquals(nc.getConnectedUrl(), "nats://stephen:password@localhost:" + ts1.getPort()); listener.prepForStatusChange(Events.RESUBSCRIBED); @@ -281,10 +290,10 @@ public void testTokenInURLClusteredWithDifferentUser() throws Exception { try (NatsTestServer ts1 = new NatsTestServer(customArgs1, false); NatsTestServer ts2 = new NatsTestServer(customArgs2, false)) { // See config file for user/pass - Options options = new Options.Builder().server("nats://token_one@localhost:" + ts1.getPort()) + Options options = optionsBuilder("nats://token_one@localhost:" + ts1.getPort()) .server("nats://token_two@localhost:" + ts2.getPort()).maxReconnects(4).noRandomize() .connectionListener(listener).pingInterval(Duration.ofMillis(100)).build(); - Connection nc = standardConnection(options); + Connection nc = standardConnectionWait(options); assertEquals(nc.getConnectedUrl(), "nats://token_one@localhost:" + ts1.getPort()); listener.prepForStatusChange(Events.RESUBSCRIBED); @@ -302,14 +311,14 @@ public void testTokenInURLWithFallback() throws Exception { String[] customArgs1 = { "--auth", "token_one" }; String[] customArgs2 = { "--auth", "token_two" }; ListenerForTesting listener = new ListenerForTesting(); - Connection nc = null; + Connection nc; try (NatsTestServer ts1 = new NatsTestServer(customArgs1, false); NatsTestServer ts2 = new NatsTestServer(customArgs2, false)) { // See config file for user/pass - Options options = new Options.Builder().server("nats://token_one@localhost:" + ts1.getPort()) + Options options = optionsBuilder("nats://token_one@localhost:" + ts1.getPort()) .server("nats://localhost:" + ts2.getPort()).token("token_two".toCharArray()).maxReconnects(4) .noRandomize().connectionListener(listener).pingInterval(Duration.ofMillis(100)).build(); - nc = standardConnection(options); + nc = standardConnectionWait(options); assertEquals(nc.getConnectedUrl(), "nats://token_one@localhost:" + ts1.getPort()); listener.prepForStatusChange(Events.RESUBSCRIBED); @@ -327,7 +336,7 @@ public void testToken() throws Exception { String[] customArgs = { "--auth", "derek" }; try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { // See config file for user/pass - Options options = new Options.Builder().server(ts.getURI()).maxReconnects(0).token("derek".toCharArray()) + Options options = optionsBuilder(ts.getURI()).maxReconnects(0).token("derek".toCharArray()) .build(); assertCanConnect(options); } @@ -338,7 +347,7 @@ public void testTokenInURL() throws Exception { String[] customArgs = { "--auth", "alberto" }; try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { // See config file for user/pass - Options options = new Options.Builder().server("nats://alberto@localhost:" + ts.getPort()).maxReconnects(0) + Options options = optionsBuilder("nats://alberto@localhost:" + ts.getPort()).maxReconnects(0) .build(); assertCanConnect(options); } @@ -350,7 +359,7 @@ public void testBadUserBadPass() { String[] customArgs = { "--user", "stephen", "--pass", "password" }; try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { // See config file for user/pass - Options options = new Options.Builder().server(ts.getURI()).maxReconnects(0) + Options options = optionsBuilder(ts.getURI()).maxReconnects(0) .userInfo("sam".toCharArray(), "notthepassword".toCharArray()).build(); Nats.connect(options); // expected to fail } @@ -363,7 +372,7 @@ public void testMissingUserPass() { String[] customArgs = { "--user", "wally", "--pass", "password" }; try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { // See config file for user/pass - Options options = new Options.Builder().server(ts.getURI()).maxReconnects(0).build(); + Options options = optionsBuilder(ts.getURI()).maxReconnects(0).build(); Nats.connect(options); // expected to fail } }); @@ -375,10 +384,8 @@ public void testBadToken() { String[] customArgs = { "--auth", "colin" }; try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { // See config file for user/pass - Options options = new Options.Builder() - .server(ts.getURI()) + Options options = optionsBuilder(ts.getURI()) .maxReconnects(0) - .errorListener(new ErrorListener() {}) .token("notthetoken".toCharArray()) .build(); Nats.connect(options); // expected to fail @@ -392,7 +399,7 @@ public void testMissingToken() { String[] customArgs = { "--auth", "ivan" }; try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { // See config file for user/pass - Options options = new Options.Builder().server(ts.getURI()).maxReconnects(0).build(); + Options options = optionsBuilder(ts.getURI()).maxReconnects(0).build(); Nats.connect(options); // expected to fail } }); @@ -429,7 +436,7 @@ public void testNKeyAuth() throws Exception { String configFile = createNKeyConfigFile(theKey.getPublicKey()); try (NatsTestServer ts = new NatsTestServer(configFile, false)) { - Options options = new Options.Builder().server(ts.getURI()).maxReconnects(0) + Options options = optionsBuilder(ts.getURI()).maxReconnects(0) .authHandler(new AuthHandlerForTesting(theKey)).build(); assertCanConnect(options); } @@ -443,7 +450,7 @@ public void testStaticNKeyAuth() throws Exception { String configFile = createNKeyConfigFile(theKey.getPublicKey()); try (NatsTestServer ts = new NatsTestServer(configFile, false)) { - Options options = new Options.Builder().server(ts.getURI()).maxReconnects(0) + Options options = optionsBuilder(ts.getURI()).maxReconnects(0) .authHandler(Nats.staticCredentials(null, theKey.getSeed())).build(); assertCanConnect(options); } @@ -459,27 +466,27 @@ public void testStaticNKeyAuth() throws Exception { @Test public void testJWTAuthWithCredsFile() throws Exception { // manual auth handler or credential path - try (NatsTestServer ts = new NatsTestServer("src/test/resources/operator.conf", false)) { - Options options = new Options.Builder().server(ts.getURI()).maxReconnects(0) + try (NatsTestServer ts = NatsTestServer.configuredServer("operator.conf")) { + Options options = optionsBuilder(ts.getURI()).maxReconnects(0) .authHandler(Nats.credentials("src/test/resources/jwt_nkey/user.creds")) .build(); assertCanConnect(options); - options = new Options.Builder().server(ts.getURI()).maxReconnects(0) + options = optionsBuilder(ts.getURI()).maxReconnects(0) .credentialPath("src/test/resources/jwt_nkey/user.creds") .build(); assertCanConnect(options); } //test Nats.connect method - try (NatsTestServer ts = new NatsTestServer("src/test/resources/operator.conf", false)) { + try (NatsTestServer ts = NatsTestServer.configuredServer("operator.conf")) { Connection nc = Nats.connect(ts.getURI(), Nats.credentials("src/test/resources/jwt_nkey/user.creds")); standardConnectionWait(nc); standardCloseConnection(nc); } //test Nats.connect method - try (NatsTestServer ts = new NatsTestServer("src/test/resources/operatorJnatsTest.conf", false)) { + try (NatsTestServer ts = NatsTestServer.configuredServer("operatorJnatsTest.conf")) { Connection nc = Nats.connect(ts.getURI(), Nats.credentials("src/test/resources/jwt_nkey/userJnatsTest.creds")); standardConnectionWait(nc); standardCloseConnection(nc); @@ -488,15 +495,15 @@ public void testJWTAuthWithCredsFile() throws Exception { @Test public void testWsJWTAuthWithCredsFile() throws Exception { - try (NatsTestServer ts = new NatsTestServer("src/test/resources/ws_operator.conf", false)) { + try (NatsTestServer ts = skipValidateServer("ws_operator.conf")) { String uri = ts.getLocalhostUri("ws"); - Options options = new Options.Builder().server(uri).maxReconnects(0) + Options options = optionsBuilder(uri).maxReconnects(0) .authHandler(Nats.credentials("src/test/resources/jwt_nkey/user.creds")).build(); assertCanConnect(options); } //test Nats.connect method - try (NatsTestServer ts = new NatsTestServer("src/test/resources/ws_operator.conf", false)) { + try (NatsTestServer ts = skipValidateServer("ws_operator.conf")) { String uri = ts.getLocalhostUri("ws"); Connection nc = Nats.connect(uri, Nats.credentials("src/test/resources/jwt_nkey/user.creds")); standardConnectionWait(nc); @@ -507,9 +514,10 @@ public void testWsJWTAuthWithCredsFile() throws Exception { @Test public void testWssJWTAuthWithCredsFile() throws Exception { SSLContext ctx = SslTestingHelper.createTestSSLContext(); - try (NatsTestServer ts = new NatsTestServer("src/test/resources/wss_operator.conf", false)) { + try (NatsTestServer ts = skipValidateServer("wss_operator.conf")) + { String uri = ts.getLocalhostUri("wss"); - Options options = new Options.Builder().server(uri).maxReconnects(0).sslContext(ctx) + Options options = optionsBuilder(uri).maxReconnects(0).sslContext(ctx) .authHandler(Nats.credentials("src/test/resources/jwt_nkey/user.creds")).build(); assertCanConnect(options); } @@ -522,7 +530,7 @@ public void testStaticJWTAuth() throws Exception { String nkey = "SUAFYHVVQVOIDOOQ4MTOCTLGNZCJ5PZ4HPV5WAPROGTEIOF672D3R7GBY4"; try (NatsTestServer ts = new NatsTestServer("src/test/resources/operator.conf", false)) { - Options options = new Options.Builder().server(ts.getURI()).maxReconnects(0) + Options options = optionsBuilder(ts.getURI()).maxReconnects(0) .authHandler(Nats.staticCredentials(jwt.toCharArray(), nkey.toCharArray())).build(); assertCanConnect(options); } @@ -537,7 +545,7 @@ public void testBadAuthHandler() { String configFile = createNKeyConfigFile(theKey.getPublicKey()); try (NatsTestServer ts = new NatsTestServer(configFile, false)) { - Options options = new Options.Builder().server(ts.getURI()).maxReconnects(0) + Options options = optionsBuilder(ts.getURI()).maxReconnects(0) .authHandler(new AuthHandlerForTesting(null)). // No nkey build(); Connection nc = Nats.connect(options); @@ -549,13 +557,11 @@ public void testBadAuthHandler() { @Test public void testReconnectWithAuth() throws Exception { ListenerForTesting listener = new ListenerForTesting(); - // Connect should fail on ts2 - try (NatsTestServer ts = new NatsTestServer("src/test/resources/operator.conf", false); - NatsTestServer ts2 = new NatsTestServer("src/test/resources/operator.conf", false)) { - Options options = new Options.Builder().servers(new String[]{ts.getURI(), ts2.getURI()}) + try (NatsTestServer ts = NatsTestServer.configuredServer("operator.conf"); NatsTestServer ts2 = NatsTestServer.configuredServer("operator.conf")) { + Options options = optionsBuilder(ts.getURI(), ts2.getURI()) .noRandomize().maxReconnects(-1).authHandler(Nats.credentials("src/test/resources/jwt_nkey/user.creds")).build(); - Connection nc = standardConnection(options); + Connection nc = standardConnectionWait(options); assertEquals(ts.getURI(), nc.getConnectedUrl()); listener.prepForStatusChange(Events.RECONNECTED); @@ -575,12 +581,11 @@ public void testCloseOnReconnectWithSameError() throws Exception { ListenerForTesting listener = new ListenerForTesting(); // Connect should fail on ts1 - try (NatsTestServer ts = new NatsTestServer("src/test/resources/operator_noacct.conf", false); - NatsTestServer ts2 = new NatsTestServer("src/test/resources/operator.conf", false)) { - Options options = new Options.Builder().servers(new String[]{ts.getURI(), ts2.getURI()}) + try (NatsTestServer ts = NatsTestServer.configuredServer("operator_noacct.conf"); NatsTestServer ts2 = NatsTestServer.configuredServer("operator.conf")) { + Options options = optionsBuilder(ts.getURI(), ts2.getURI()) .maxReconnects(-1).connectionTimeout(Duration.ofSeconds(2)).noRandomize() .authHandler(Nats.credentials("src/test/resources/jwt_nkey/user.creds")).build(); - Connection nc = standardConnection(options); + Connection nc = standardConnectionWait(options); assertEquals(ts2.getURI(), nc.getConnectedUrl()); listener.prepForStatusChange(Events.CLOSED); @@ -596,18 +601,18 @@ public void testCloseOnReconnectWithSameError() throws Exception { @Test public void testThatAuthErrorIsCleared() throws Exception { // Connect should fail on ts1 - try (NatsTestServer ts1 = new NatsTestServer("src/test/resources/operator_noacct.conf", false); - NatsTestServer ts2 = new NatsTestServer("src/test/resources/operator.conf", false)) { + try (NatsTestServer ts1 = NatsTestServer.configuredServer("operator_noacct.conf"); + NatsTestServer ts2 = NatsTestServer.configuredServer("operator.conf")) { - Options options = new Options.Builder() - .servers(new String[]{ts1.getURI(), ts2.getURI()}).noRandomize() + Options options = optionsBuilder(ts1.getURI(), ts2.getURI()) + .noRandomize() .maxReconnects(-1) .connectionTimeout(Duration.ofSeconds(5)) .reconnectWait(Duration.ofSeconds(1)) // wait a tad to allow restarts .authHandler(Nats.credentials("src/test/resources/jwt_nkey/user.creds")) .errorListener(new ListenerForTesting()) .build(); - Connection nc = standardConnection(options); + Connection nc = standardConnectionWait(options); assertEquals(ts2.getURI(), nc.getConnectedUrl()); String tsURI = ts1.getURI(); @@ -615,16 +620,15 @@ public void testThatAuthErrorIsCleared() throws Exception { int port2 = ts2.getPort(); ts1.close(); - // ts3 will be at the same port that ts was - try (NatsTestServer ts3 = new NatsTestServer("src/test/resources/operator.conf", port1, false)) { + try (NatsTestServer ts3 = configuredServer("operator.conf", port1)) { ListenerForTesting listener = new ListenerForTesting(); listener.prepForStatusChange(Events.RECONNECTED); ts2.close(); // reconnect should work because we are now running with the good config - listenerConnectionWait(nc, listener, 10000); + listenerConnectionWait(nc, listener, VERY_LONG_CONNECTION_WAIT_MS); assertEquals(ts3.getURI(), nc.getConnectedUrl()); assertEquals(tsURI, ts3.getURI()); @@ -633,9 +637,9 @@ public void testThatAuthErrorIsCleared() throws Exception { listener.prepForStatusChange(Events.RECONNECTED); ts3.close(); - try (NatsTestServer ignored = new NatsTestServer("src/test/resources/operator_noacct.conf", port1, false); - NatsTestServer ts5 = new NatsTestServer("src/test/resources/operator.conf", port2, false)) { - listenerConnectionWait(nc, listener, 10000); + try (NatsTestServer ignored = configuredServer("operator_noacct.conf", port1); + NatsTestServer ts5 = configuredServer("operator.conf", port2)) { + listenerConnectionWait(nc, listener, VERY_LONG_CONNECTION_WAIT_MS); assertEquals(ts5.getURI(), nc.getConnectedUrl()); } } @@ -666,7 +670,7 @@ public void testReconnectAfterAccountAuthenticationExpired() throws Exception { private static void _testReconnectAfter(String errText) throws Exception { ListenerForTesting listener = new ListenerForTesting(); - CompletableFuture f = new CompletableFuture(); + CompletableFuture f = new CompletableFuture<>(); NatsServerProtocolMock.Customizer timeoutCustomizer = (ts, r, w) -> { f.join(); // wait until we are ready @@ -678,16 +682,15 @@ private static void _testReconnectAfter(String errText) throws Exception { // Connect should fail on ts1 try (NatsServerProtocolMock ts = new NatsServerProtocolMock(timeoutCustomizer, port, true); - NatsTestServer ts2 = new NatsTestServer("src/test/resources/operator.conf", false)) { - Options options = new Options.Builder() + NatsTestServer ts2 = skipValidateServer("operator.conf")) { + Options options = optionsBuilder() .servers(new String[]{ts.getURI(), ts2.getURI()}) .maxReconnects(-1) .noRandomize() .authHandler(Nats.credentials("src/test/resources/jwt_nkey/user.creds")) - .errorListener(new ErrorListener() {}) .build(); - Connection nc = standardConnection(options); + Connection nc = standardConnectionWait(options); assertEquals(ts.getURI(), nc.getConnectedUrl()); listener.prepForStatusChange(Events.RECONNECTED); @@ -750,7 +753,7 @@ else if (error.equalsIgnoreCase("authorization violation")) { String creds = String.format(JwtUtils.NATS_USER_JWT_FORMAT, jwt, new String(nKeyUser.getSeed())); String credsFile = ResourceUtils.createTempFile("nats_java_test", ".creds", creds.split("\\Q\\n\\E")); - try (NatsTestServer ts = new NatsTestServer("src/test/resources/operatorJnatsTest.conf", false)) { + try (NatsTestServer ts = NatsTestServer.configuredServer("operatorJnatsTest.conf")) { Options options = Options.builder() .server(ts.getURI()) diff --git a/src/test/java/io/nats/client/ConnectTests.java b/src/test/java/io/nats/client/ConnectTests.java index 12ba692b9..03242df46 100644 --- a/src/test/java/io/nats/client/ConnectTests.java +++ b/src/test/java/io/nats/client/ConnectTests.java @@ -1,678 +1,676 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.nats.client; - -import io.nats.client.ConnectionListener.Events; -import io.nats.client.NatsServerProtocolMock.ExitAt; -import io.nats.client.api.ServerInfo; -import io.nats.client.impl.ListenerForTesting; -import io.nats.client.impl.SimulateSocketDataPortException; -import io.nats.client.utils.TestBase; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.net.InetAddress; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; - -import static io.nats.client.utils.TestBase.*; -import static org.junit.jupiter.api.Assertions.*; - -public class ConnectTests { - @Test - public void testDefaultConnection() throws Exception { - try (NatsTestServer ignored = new NatsTestServer(Options.DEFAULT_PORT, false)) { - Connection nc = standardConnection(); - assertEquals(Options.DEFAULT_PORT, nc.getServerInfo().getPort()); - standardCloseConnection(nc); - } - } - - @Test - public void testConnection() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Connection nc = standardConnection(ts.getURI()); - assertEquals(ts.getPort(), nc.getServerInfo().getPort()); - // coverage for getClientAddress - InetAddress inetAddress = nc.getClientInetAddress(); - assertTrue(inetAddress.equals(InetAddress.getLoopbackAddress()) - || inetAddress.equals(InetAddress.getLocalHost())); - standardCloseConnection(nc); - } - } - - @Test - public void testConnectionWithOptions() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()).build(); - assertCanConnect(options); - } - } - - @Test - public void testFullFakeConnect() throws Exception { - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT)) { - assertCanConnect(ts.getURI()); - } - } - - @Test - public void testFullFakeConnectWithTabs() throws Exception { - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT)) { - ts.useTabs(); - assertCanConnect(ts.getURI()); - } - } - - @Test - public void testConnectExitBeforeInfo() { - assertThrows(IOException.class, () -> { - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.EXIT_BEFORE_INFO)) { - Options options = new Options.Builder().server(ts.getURI()).noReconnect().build(); - assertCanConnect(options); - } - }); - } - - @Test - public void testConnectExitAfterInfo() { - assertThrows(IOException.class, () -> { - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.EXIT_AFTER_INFO)) { - Options options = new Options.Builder().server(ts.getURI()).noReconnect().build(); - assertCanConnect(options); - } - }); - } - - @Test - public void testConnectExitAfterConnect() { - assertThrows(IOException.class, () -> { - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.EXIT_AFTER_CONNECT)) { - Options options = new Options.Builder().server(ts.getURI()).noReconnect().build(); - assertCanConnect(options); - } - }); - } - - @Test - public void testConnectExitAfterPing() { - assertThrows(IOException.class, () -> { - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.EXIT_AFTER_PING)) { - Options options = new Options.Builder().server(ts.getURI()).noReconnect().build(); - assertCanConnect(options); - } - }); - } - - @Test - public void testConnectionFailureWithFallback() throws Exception { - - try (NatsTestServer ts = new NatsTestServer(false)) { - try (NatsServerProtocolMock fake = new NatsServerProtocolMock(ExitAt.EXIT_AFTER_PING)) { - Options options = new Options.Builder().connectionTimeout(Duration.ofSeconds(5)).server(fake.getURI()) - .server(ts.getURI()).build(); - assertCanConnect(options); - } - } - } - - @Test - public void testConnectWithConfig() throws Exception { - try (NatsTestServer ts = new NatsTestServer("src/test/resources/simple.conf", false)) { - assertCanConnect(ts.getURI()); - } - } - - @Test - public void testConnectWithCommas() throws Exception { - try (NatsTestServer ts1 = new NatsTestServer(false)) { - try (NatsTestServer ts2 = new NatsTestServer(false)) { - assertCanConnect(ts1.getURI() + "," + ts2.getURI()); - } - } - } - - @Test - public void testConnectRandomize() throws Exception { - try (NatsTestServer ts1 = new NatsTestServer(false)) { - try (NatsTestServer ts2 = new NatsTestServer(false)) { - boolean needOne = true; - boolean needTwo = true; - int count = 0; - int maxTries = 100; - while (count++ < maxTries && (needOne || needTwo)) { - Connection nc = standardConnection(ts1.getURI() + "," + ts2.getURI()); - if (nc.getConnectedUrl().equals(ts1.getURI())) { - needOne = false; - } else { - needTwo = false; - } - Collection servers = nc.getServers(); - assertTrue(servers.contains(ts1.getURI())); - assertTrue(servers.contains(ts2.getURI())); - standardCloseConnection(nc); - } - assertFalse(needOne); - assertFalse(needTwo); - } - } - } - - @Test - public void testConnectNoRandomize() throws Exception { - try (NatsTestServer ts1 = new NatsTestServer(false)) { - try (NatsTestServer ts2 = new NatsTestServer(false)) { - int one = 0; - int two = 0; - - // should get at least 1 for each - for (int i = 0; i < 10; i++) { - String[] servers = { ts1.getURI(), ts2.getURI() }; - Options options = new Options.Builder().noRandomize().servers(servers).build(); - Connection nc = standardConnection(options); - if (nc.getConnectedUrl().equals(ts1.getURI())) { - one++; - } else { - two++; - } - standardCloseConnection(nc); - } - - assertEquals(one, 10, "always got one"); - assertEquals(two, 0, "never got two"); - } - } - } - - @Test - public void testFailWithMissingLineFeedAfterInfo() { - assertThrows(IOException.class, () -> { - String badInfo = "{\"server_id\":\"test\", \"version\":\"9.9.99\"}\rmore stuff"; - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(null, badInfo)) { - Options options = new Options.Builder().server(ts.getURI()).reconnectWait(Duration.ofDays(1)).build(); - Nats.connect(options); - } - }); - } - - @Test - public void testFailWithStuffAfterInitialInfo() { - assertThrows(IOException.class, () -> { - String badInfo = "{\"server_id\":\"test\", \"version\":\"9.9.99\"}\r\nmore stuff"; - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(null, badInfo)) { - Options options = new Options.Builder().server(ts.getURI()).reconnectWait(Duration.ofDays(1)).build(); - Nats.connect(options); - } - }); - } - - @Test - public void testFailWrongInitialInfoOP() { - assertThrows(IOException.class, () -> { - String badInfo = "PING {\"server_id\":\"test\", \"version\":\"9.9.99\"}\r\n"; // wrong op code - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(null, badInfo)) { - ts.useCustomInfoAsFullInfo(); - Options options = new Options.Builder().server(ts.getURI()).reconnectWait(Duration.ofDays(1)).build(); - Nats.connect(options); - } - }); - } - - @Test - public void testIncompleteInitialInfo() { - assertThrows(IOException.class, () -> { - String badInfo = "{\"server_id\"\r\n"; - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(null, badInfo)) { - Options options = new Options.Builder().server(ts.getURI()).reconnectWait(Duration.ofDays(1)).build(); - Nats.connect(options); - } - }); - } - - @Test - public void testAsyncConnection() throws Exception { - ListenerForTesting listener = new ListenerForTesting(); - Connection nc = null; - - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()).connectionListener(listener).build(); - listener.prepForStatusChange(Events.CONNECTED); - - Nats.connectAsynchronously(options, false); - - listener.waitForStatusChange(1, TimeUnit.SECONDS); - - nc = listener.getLastEventConnection(); - assertNotNull(nc); - assertConnected(nc); - standardCloseConnection(nc); - } - } - - @Test - public void testAsyncConnectionWithReconnect() throws Exception { - ListenerForTesting listener = new ListenerForTesting(); - int port = NatsTestServer.nextPort(); - Options options = new Options.Builder().server("nats://localhost:" + port).maxReconnects(-1) - .reconnectWait(Duration.ofMillis(100)).connectionListener(listener).build(); - - Nats.connectAsynchronously(options, true); - - sleep(5000); // No server at this point, let it fail and try to start over - - Connection nc = listener.getLastEventConnection(); // will be disconnected, but should be there - assertNotNull(nc); - - listener.prepForStatusChange(Events.RECONNECTED); - try (NatsTestServer ignored = new NatsTestServer(port, false)) { - listenerConnectionWait(nc, listener); - standardCloseConnection(nc); - } - } - - @Test - public void testThrowOnAsyncWithoutListener() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()).build(); - Nats.connectAsynchronously(options, false); - } - }); - } - - @Test - public void testErrorOnAsync() throws Exception { - ListenerForTesting listener = new ListenerForTesting(); - Options options = new Options.Builder().server("nats://localhost:" + NatsTestServer.nextPort()) - .connectionListener(listener).errorListener(listener).noReconnect().build(); - listener.prepForStatusChange(Events.CLOSED); - Nats.connectAsynchronously(options, false); - listener.waitForStatusChange(10, TimeUnit.SECONDS); - - assertTrue(listener.getExceptionCount() > 0); - assertTrue(listener.getEventCount(Events.CLOSED) > 0); - } - - @Test - public void testConnectionTimeout() { - assertThrows(IOException.class, () -> { - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.SLEEP_BEFORE_INFO)) { // will sleep for 3 - Options options = new Options.Builder().server(ts.getURI()).noReconnect().traceConnection() - .connectionTimeout(Duration.ofSeconds(2)). // 2 is also the default but explicit for test - build(); - Connection nc = Nats.connect(options); - assertNotSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - } - }); - } - - @Test - public void testSlowConnectionNoTimeout() throws Exception { - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.SLEEP_BEFORE_INFO)) { - Options options = new Options.Builder().server(ts.getURI()).noReconnect() - .connectionTimeout(Duration.ofSeconds(6)). // longer than the sleep - build(); - assertCanConnect(options); - } - } - - @Test - public void testTimeCheckCoverage() throws Exception { - List traces = new ArrayList<>(); - TimeTraceLogger l = (f, a) -> traces.add(String.format(f, a)); - - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()).traceConnection().build(); - assertCanConnect(options); - - options = new Options.Builder().server(ts.getURI()).timeTraceLogger(l).build(); - assertCanConnect(options); - } - - int i = 0; - assertTrue(traces.get(i++).startsWith("creating connection object")); - assertTrue(traces.get(i++).startsWith("creating NUID")); - assertTrue(traces.get(i++).startsWith("creating executors")); - assertTrue(traces.get(i++).startsWith("creating reader and writer")); - assertTrue(traces.get(i++).startsWith("connection object created")); - assertTrue(traces.get(i++).startsWith("starting connect loop")); - assertTrue(traces.get(i++).startsWith("setting status to connecting")); - assertTrue(traces.get(i++).startsWith("trying to connect")); - assertTrue(traces.get(i++).startsWith("starting connection attempt")); - assertTrue(traces.get(i++).startsWith("waiting for reader")); - assertTrue(traces.get(i++).startsWith("waiting for writer")); - assertTrue(traces.get(i++).startsWith("cleaning pong queue")); - assertTrue(traces.get(i++).startsWith("connecting data port")); - assertTrue(traces.get(i++).startsWith("reading info")); - assertTrue(traces.get(i++).startsWith("starting reader")); - assertTrue(traces.get(i++).startsWith("starting writer")); - assertTrue(traces.get(i++).startsWith("sending connect message")); - assertTrue(traces.get(i++).startsWith("sending initial ping")); - assertTrue(traces.get(i++).startsWith("starting ping and cleanup timers")); - assertTrue(traces.get(i++).startsWith("updating status to connected")); - assertTrue(traces.get(i++).startsWith("status updated")); - assertTrue(traces.get(i).startsWith("connect complete")); - } - - @Test - public void testReconnectLogging() throws Exception { - List traces = new ArrayList<>(); - TimeTraceLogger l = (f, a) -> traces.add(String.format(f, a)); - - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder() - .server(ts.getURI()) - .traceConnection() - .timeTraceLogger(l) - .reconnectWait(Duration.ofSeconds(1)) - .maxReconnects(1) - .connectionTimeout(Duration.ofSeconds(2)) - .build(); - - try (Connection nc = Nats.connect(options)) { - assertConnected(nc); - ts.close(); - Thread.sleep(3000); - } - } - - boolean foundReconnectLog = traces.stream().anyMatch(s -> s.contains("reconnecting to server")); - assertTrue(foundReconnectLog, "Reconnect log not found"); - } - - @Test - public void testConnectExceptionHasURLS() { - try { - Nats.connect("nats://testserver.notnats:4222, nats://testserver.alsonotnats:4223"); - } catch (Exception e) { - assertTrue(e.getMessage().contains("testserver.notnats:4222")); - assertTrue(e.getMessage().contains("testserver.alsonotnats:4223")); - } - } - - @Test - public void testFlushBuffer() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Connection nc = standardConnection(ts.getURI()); - - // test connected - nc.flushBuffer(); - - ts.shutdown(); - while (nc.getStatus() == Connection.Status.CONNECTED) { - sleep(10); - } - - // test while reconnecting - assertThrows(IllegalStateException.class, nc::flushBuffer); - standardCloseConnection(nc); - - // test when closed. - assertThrows(IllegalStateException.class, nc::flushBuffer); - } - } - - @Test - public void testFlushBufferThreadSafety() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Connection nc = standardConnection(ts.getURI()); - - // use two latches to sync the threads as close as - // possible. - CountDownLatch pubLatch = new CountDownLatch(1); - CountDownLatch flushLatch = new CountDownLatch(1); - CountDownLatch completedLatch = new CountDownLatch(1); - - Thread t = new Thread("publisher") { - @SuppressWarnings("ResultOfMethodCallIgnored") - public void run() { - byte[] payload = new byte[5]; - pubLatch.countDown(); - try { - flushLatch.await(2, TimeUnit.SECONDS); - } catch (Exception e) { - // NOOP - } - for (int i = 1; i <= 50000; i++) { - nc.publish("foo", payload); - if (i % 2000 == 0) { - try { - nc.flushBuffer(); - } catch (IOException e) { - break; - } - } - } - completedLatch.countDown(); - } - }; - - t.start(); - - // sync up the current thread and the publish thread - // to get the most out of the test. - try { - pubLatch.await(2, TimeUnit.SECONDS); - } catch (Exception e) { - // NOOP - } - flushLatch.countDown(); - - // flush as fast as we can while the publisher - // is publishing. - - while (t.isAlive()) { - nc.flushBuffer(); - } - - // cleanup and doublecheck the thread is done. - t.join(2000); - - // make sure the publisher actually completed. - assertTrue(completedLatch.await(10, TimeUnit.SECONDS)); - - standardCloseConnection(nc); - } - } - - @SuppressWarnings({"unused", "UnusedAssignment"}) - @Test - public void testSocketLevelException() throws Exception { - int port = NatsTestServer.nextPort(); - - AtomicBoolean simExReceived = new AtomicBoolean(); - ListenerForTesting listener = new ListenerForTesting(); - ErrorListener el = new ErrorListener() { - @Override - public void exceptionOccurred(Connection conn, Exception exp) { - if (exp.getMessage().contains("Simulated Exception")) { - simExReceived.set(true); - } - } - }; - - Options options = new Options.Builder() - .server(NatsTestServer.getNatsLocalhostUri(port)) - .dataPortType("io.nats.client.impl.SimulateSocketDataPortException") - .connectionListener(listener) - .errorListener(el) - .reconnectDelayHandler(l -> Duration.ofSeconds(1)) - .build(); - - Connection connection = null; - - // 1. DO NOT RECONNECT ON CONNECT - try (NatsTestServer ts = new NatsTestServer(port, false)) { - try { - SimulateSocketDataPortException.THROW_ON_CONNECT.set(true); - connection = Nats.connect(options); - fail(); - } - catch (Exception ignore) {} - } - - Thread.sleep(200); // just making sure messages get through - assertNull(connection); - assertTrue(simExReceived.get()); - simExReceived.set(false); - - // 2. RECONNECT ON CONNECT - try (NatsTestServer ts = new NatsTestServer(port, false)) { - try { - SimulateSocketDataPortException.THROW_ON_CONNECT.set(true); - listener.prepForStatusChange(Events.RECONNECTED); - connection = Nats.connectReconnectOnConnect(options); - assertTrue(listener.waitForStatusChange(5, TimeUnit.SECONDS)); - listener.prepForStatusChange(Events.DISCONNECTED); - } - catch (Exception e) { - fail("should have connected " + e); - } - } - assertTrue(listener.waitForStatusChange(5, TimeUnit.SECONDS)); - assertTrue(simExReceived.get()); - simExReceived.set(false); - - // 2. NORMAL RECONNECT - listener.prepForStatusChange(Events.RECONNECTED); - try (NatsTestServer ts = new NatsTestServer(port, false)) { - SimulateSocketDataPortException.THROW_ON_CONNECT.set(true); - try { - assertTrue(listener.waitForStatusChange(5, TimeUnit.SECONDS)); - } - catch (Exception e) { - fail("should have reconnected " + e); - } - } - } - - @Test - public void testRunInJsCluster() throws Exception { - ListenerForTesting[] listeners = new ListenerForTesting[3]; - listeners[0] = new ListenerForTesting(); - listeners[1] = new ListenerForTesting(); - listeners[2] = new ListenerForTesting(); - - ThreeServerTestOptions tstOpts = new ThreeServerTestOptions() { - @Override - public void append(int index, Options.Builder builder) { - builder.connectionListener(listeners[index]).errorListener(listeners[index]); - } - - @Override - public boolean configureAccount() { - return true; - } - - @Override - public boolean includeAllServers() { - return true; - } - }; - - runInJsCluster(ConnectTests::validateRunInJsCluster); - - listeners[0] = new ListenerForTesting(); - listeners[1] = new ListenerForTesting(); - listeners[2] = new ListenerForTesting(); - - runInJsCluster(tstOpts, ConnectTests::validateRunInJsCluster); - } - - private static void validateRunInJsCluster(Connection nc1, Connection nc2, Connection nc3) throws InterruptedException { - Thread.sleep(200); - ServerInfo si1 = nc1.getServerInfo(); - ServerInfo si2 = nc2.getServerInfo(); - ServerInfo si3 = nc3.getServerInfo(); - assertEquals(si1.getCluster(), si2.getCluster()); - assertEquals(si1.getCluster(), si3.getCluster()); - String port1 = "" + si1.getPort(); - String port2 = "" + si2.getPort(); - String port3 = "" + si3.getPort(); - String urls1 = String.join(",", si1.getConnectURLs()); - String urls2 = String.join(",", si2.getConnectURLs()); - String urls3 = String.join(",", si3.getConnectURLs()); - assertTrue(urls1.contains(port1)); - assertTrue(urls1.contains(port2)); - assertTrue(urls1.contains(port3)); - assertTrue(urls2.contains(port1)); - assertTrue(urls2.contains(port2)); - assertTrue(urls2.contains(port3)); - assertTrue(urls3.contains(port1)); - assertTrue(urls3.contains(port2)); - assertTrue(urls3.contains(port3)); - } - - // https://github.com/nats-io/nats.java/issues/1201 - @Test - void testLowConnectionTimeoutResultsInIOException() { - Options options = Options.builder() - .connectionTimeout(Duration.ZERO) - .build(); - - assertThrows(IOException.class, () -> { - Connection nc = Nats.connect(options); - nc.close(); - }); - } - - @Test - void testConnectWithFastFallback() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()).enableFastFallback().build(); - Connection nc = standardConnection(options); - standardCloseConnection(nc); - } - } - - @Test - void testConnectPendingCountCoverage() throws Exception { - TestBase.runInJsServer(nc -> { - AtomicLong outgoingPendingMessageCount = new AtomicLong(); - AtomicLong outgoingPendingBytes = new AtomicLong(); - - AtomicBoolean tKeepGoing = new AtomicBoolean(true); - Thread t = new Thread(() -> { - while (tKeepGoing.get()) { - outgoingPendingMessageCount.set(Math.max(outgoingPendingMessageCount.get(), nc.outgoingPendingMessageCount())); - outgoingPendingBytes.set(Math.max(outgoingPendingBytes.get(), nc.outgoingPendingBytes())); - try { - Thread.sleep(10); - } - catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - }); - t.start(); - - String subject = subject(); - byte[] data = new byte[8 * 1024]; - for (int x = 0; x < 5000; x++) { - nc.publish(subject, data); - } - tKeepGoing.set(false); - t.join(); - - assertTrue(outgoingPendingMessageCount.get() > 0); - assertTrue(outgoingPendingBytes.get() > outgoingPendingMessageCount.get() * 1000); - }); - } -} +// Copyright 2015-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client; + +import io.nats.client.ConnectionListener.Events; +import io.nats.client.NatsServerProtocolMock.ExitAt; +import io.nats.client.api.ServerInfo; +import io.nats.client.impl.ListenerForTesting; +import io.nats.client.impl.SimulateSocketDataPortException; +import io.nats.client.utils.TestBase; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Isolated; + +import java.io.IOException; +import java.net.InetAddress; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +import static io.nats.client.utils.ConnectionUtils.*; +import static io.nats.client.utils.OptionsUtils.options; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.TestBase.*; +import static io.nats.client.utils.ThreadUtils.sleep; +import static org.junit.jupiter.api.Assertions.*; + +@Isolated +public class ConnectTests { + @Test + public void testDefaultConnection() throws Exception { + try (NatsTestServer ignored = new NatsTestServer(Options.DEFAULT_PORT, false)) { + Connection nc = standardConnectionWait(); + assertEquals(Options.DEFAULT_PORT, nc.getServerInfo().getPort()); + standardCloseConnection(nc); + } + } + + @Test + public void testConnection() throws Exception { + try (NatsTestServer ts = new NatsTestServer()) { + Connection nc = standardConnectionWait(ts.getURI()); + assertEquals(ts.getPort(), nc.getServerInfo().getPort()); + // coverage for getClientAddress + InetAddress inetAddress = nc.getClientInetAddress(); + assertNotNull(inetAddress); + assertTrue(inetAddress.equals(InetAddress.getLoopbackAddress()) + || inetAddress.equals(InetAddress.getLocalHost())); + standardCloseConnection(nc); + } + } + + @Test + public void testConnectionWithOptions() throws Exception { + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts).build(); + assertCanConnect(options); + } + } + + @Test + public void testFullFakeConnect() throws Exception { + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT)) { + assertCanConnect(ts.getURI()); + } + } + + @Test + public void testFullFakeConnectWithTabs() throws Exception { + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT)) { + ts.useTabs(); + assertCanConnect(ts.getURI()); + } + } + + @Test + public void testConnectExitBeforeInfo() { + assertThrows(IOException.class, () -> { + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.EXIT_BEFORE_INFO)) { + Options options = optionsBuilder(ts.getURI()).noReconnect().build(); + assertCanConnect(options); + } + }); + } + + @Test + public void testConnectExitAfterInfo() { + assertThrows(IOException.class, () -> { + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.EXIT_AFTER_INFO)) { + Options options = optionsBuilder(ts.getURI()).noReconnect().build(); + assertCanConnect(options); + } + }); + } + + @Test + public void testConnectExitAfterConnect() { + assertThrows(IOException.class, () -> { + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.EXIT_AFTER_CONNECT)) { + Options options = optionsBuilder(ts.getURI()).noReconnect().build(); + assertCanConnect(options); + } + }); + } + + @Test + public void testConnectExitAfterPing() { + assertThrows(IOException.class, () -> { + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.EXIT_AFTER_PING)) { + Options options = optionsBuilder(ts.getURI()).noReconnect().build(); + assertCanConnect(options); + } + }); + } + + @Test + public void testConnectionFailureWithFallback() throws Exception { + try (NatsTestServer ts = new NatsTestServer()) { + try (NatsServerProtocolMock fake = new NatsServerProtocolMock(ExitAt.EXIT_AFTER_PING)) { + Options options = optionsBuilder(fake.getURI(), ts.getNatsLocalhostUri()) + .connectionTimeout(Duration.ofSeconds(5)) + .build(); + assertCanConnect(options); + } + } + } + + @Test + public void testConnectWithConfig() throws Exception { + try (NatsTestServer ts = new NatsTestServer("src/test/resources/simple.conf", false)) { + assertCanConnect(ts.getURI()); + } + } + + @Test + public void testConnectWithCommas() throws Exception { + try (NatsTestServer ts1 = new NatsTestServer()) { + try (NatsTestServer ts2 = new NatsTestServer()) { + assertCanConnect(ts1.getURI() + "," + ts2.getURI()); + } + } + } + + @Test + public void testConnectRandomize() throws Exception { + try (NatsTestServer ts1 = new NatsTestServer()) { + try (NatsTestServer ts2 = new NatsTestServer()) { + boolean needOne = true; + boolean needTwo = true; + int count = 0; + int maxTries = 100; + while (count++ < maxTries && (needOne || needTwo)) { + Connection nc = standardConnectionWait(ts1.getURI() + "," + ts2.getURI()); + if (ts1.getURI().equals(nc.getConnectedUrl())) { + needOne = false; + } else { + needTwo = false; + } + Collection servers = nc.getServers(); + assertTrue(servers.contains(ts1.getURI())); + assertTrue(servers.contains(ts2.getURI())); + standardCloseConnection(nc); + } + assertFalse(needOne); + assertFalse(needTwo); + } + } + } + + @Test + public void testConnectNoRandomize() throws Exception { + try (NatsTestServer ts1 = new NatsTestServer()) { + try (NatsTestServer ts2 = new NatsTestServer()) { + int one = 0; + int two = 0; + + // should get at least 1 for each + for (int i = 0; i < 10; i++) { + Options options = optionsBuilder(ts1.getURI(), ts2.getURI()).noRandomize().build(); + Connection nc = standardConnectionWait(options); + if (ts1.getURI().equals(nc.getConnectedUrl())) { + one++; + } else { + two++; + } + standardCloseConnection(nc); + } + + assertEquals(10, one, "always got one"); + assertEquals(0, two, "never got two"); + } + } + } + + @Test + public void testFailWithMissingLineFeedAfterInfo() { + assertThrows(IOException.class, () -> { + String badInfo = "{\"server_id\":\"test\", \"version\":\"9.9.99\"}\rmore stuff"; + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(null, badInfo)) { + Options options = optionsBuilder(ts.getURI()).reconnectWait(Duration.ofDays(1)).build(); + Nats.connect(options); + } + }); + } + + @Test + public void testFailWithStuffAfterInitialInfo() { + assertThrows(IOException.class, () -> { + String badInfo = "{\"server_id\":\"test\", \"version\":\"9.9.99\"}\r\nmore stuff"; + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(null, badInfo)) { + Options options = optionsBuilder(ts.getURI()).reconnectWait(Duration.ofDays(1)).build(); + Nats.connect(options); + } + }); + } + + @Test + public void testFailWrongInitialInfoOP() { + assertThrows(IOException.class, () -> { + String badInfo = "PING {\"server_id\":\"test\", \"version\":\"9.9.99\"}\r\n"; // wrong op code + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(null, badInfo)) { + ts.useCustomInfoAsFullInfo(); + Options options = optionsBuilder(ts.getURI()).reconnectWait(Duration.ofDays(1)).build(); + Nats.connect(options); + } + }); + } + + @Test + public void testIncompleteInitialInfo() { + assertThrows(IOException.class, () -> { + String badInfo = "{\"server_id\"\r\n"; + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(null, badInfo)) { + Options options = optionsBuilder(ts.getURI()).reconnectWait(Duration.ofDays(1)).build(); + Nats.connect(options); + } + }); + } + + @Test + public void testAsyncConnection() throws Exception { + ListenerForTesting listener = new ListenerForTesting(); + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts).connectionListener(listener).build(); + listener.prepForStatusChange(Events.CONNECTED); + + Nats.connectAsynchronously(options, false); + + listener.waitForStatusChange(1, TimeUnit.SECONDS); + + Connection nc = listener.getLastEventConnection(); + assertNotNull(nc); + assertConnected(nc); + standardCloseConnection(nc); + } + } + + @Test + public void testAsyncConnectionWithReconnect() throws Exception { + ListenerForTesting listener = new ListenerForTesting(); + int port = NatsTestServer.nextPort(); + Options options = optionsBuilder(port).maxReconnects(-1) + .reconnectWait(Duration.ofMillis(100)).connectionListener(listener).build(); + + Nats.connectAsynchronously(options, true); + + sleep(5000); // No server at this point, let it fail and try to start over + + Connection nc = listener.getLastEventConnection(); // will be disconnected, but should be there + assertNotNull(nc); + + listener.prepForStatusChange(Events.RECONNECTED); + try (NatsTestServer ignored = new NatsTestServer(port, false)) { + listenerConnectionWait(nc, listener); + standardCloseConnection(nc); + } + } + + @Test + public void testThrowOnAsyncWithoutListener() { + assertThrows(IllegalArgumentException.class, () -> { + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts).build(); + Nats.connectAsynchronously(options, false); + } + }); + } + + @Test + public void testErrorOnAsync() throws Exception { + ListenerForTesting listener = new ListenerForTesting(); + Options options = optionsBuilder(NatsTestServer.nextPort()) + .connectionListener(listener).errorListener(listener).noReconnect().build(); + listener.prepForStatusChange(Events.CLOSED); + Nats.connectAsynchronously(options, false); + listener.waitForStatusChange(10, TimeUnit.SECONDS); + + assertTrue(listener.getExceptionCount() > 0); + assertTrue(listener.getEventCount(Events.CLOSED) > 0); + } + + @Test + public void testConnectionTimeout() { + assertThrows(IOException.class, () -> { + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.SLEEP_BEFORE_INFO)) { // will sleep for 3 + Options options = optionsBuilder(ts.getURI()).noReconnect().traceConnection() + .connectionTimeout(Duration.ofSeconds(2)). // 2 is also the default but explicit for test + build(); + Connection nc = Nats.connect(options); + assertNotSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); + } + }); + } + + @Test + public void testSlowConnectionNoTimeout() throws Exception { + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.SLEEP_BEFORE_INFO)) { + Options options = optionsBuilder(ts.getURI()).noReconnect() + .connectionTimeout(Duration.ofSeconds(6)). // longer than the sleep + build(); + assertCanConnect(options); + } + } + + @Test + public void testTimeCheckCoverage() throws Exception { + List traces = new ArrayList<>(); + TimeTraceLogger l = (f, a) -> traces.add(String.format(f, a)); + + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts).traceConnection().build(); + assertCanConnect(options); + + options = optionsBuilder(ts).timeTraceLogger(l).build(); + assertCanConnect(options); + } + + int i = 0; + assertTrue(traces.get(i++).startsWith("creating connection object")); + assertTrue(traces.get(i++).startsWith("creating NUID")); + assertTrue(traces.get(i++).startsWith("creating executors")); + assertTrue(traces.get(i++).startsWith("creating reader and writer")); + assertTrue(traces.get(i++).startsWith("connection object created")); + assertTrue(traces.get(i++).startsWith("starting connect loop")); + assertTrue(traces.get(i++).startsWith("setting status to connecting")); + assertTrue(traces.get(i++).startsWith("trying to connect")); + assertTrue(traces.get(i++).startsWith("starting connection attempt")); + assertTrue(traces.get(i++).startsWith("waiting for reader")); + assertTrue(traces.get(i++).startsWith("waiting for writer")); + assertTrue(traces.get(i++).startsWith("cleaning pong queue")); + assertTrue(traces.get(i++).startsWith("connecting data port")); + assertTrue(traces.get(i++).startsWith("reading info")); + assertTrue(traces.get(i++).startsWith("starting reader")); + assertTrue(traces.get(i++).startsWith("starting writer")); + assertTrue(traces.get(i++).startsWith("sending connect message")); + assertTrue(traces.get(i++).startsWith("sending initial ping")); + assertTrue(traces.get(i++).startsWith("starting ping and cleanup timers")); + assertTrue(traces.get(i++).startsWith("updating status to connected")); + assertTrue(traces.get(i++).startsWith("status updated")); + assertTrue(traces.get(i).startsWith("connect complete")); + } + + @Test + public void testReconnectLogging() throws Exception { + List traces = new ArrayList<>(); + TimeTraceLogger l = (f, a) -> traces.add(String.format(f, a)); + + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts) + .traceConnection() + .timeTraceLogger(l) + .reconnectWait(Duration.ofSeconds(1)) + .maxReconnects(1) + .connectionTimeout(Duration.ofSeconds(2)) + .build(); + + try (Connection nc = Nats.connect(options)) { + assertConnected(nc); + ts.close(); + Thread.sleep(3000); + } + } + + boolean foundReconnectLog = traces.stream().anyMatch(s -> s.contains("reconnecting to server")); + assertTrue(foundReconnectLog, "Reconnect log not found"); + } + + @Test + public void testConnectExceptionHasURLS() { + try { + Nats.connect(options("nats://testserver.notnats:4222, nats://testserver.alsonotnats:4223")); + } catch (Exception e) { + assertTrue(e.getMessage().contains("testserver.notnats:4222")); + assertTrue(e.getMessage().contains("testserver.alsonotnats:4223")); + } + } + + @Test + public void testFlushBuffer() throws Exception { + try (NatsTestServer ts = new NatsTestServer()) { + Connection nc = standardConnectionWait(ts.getURI()); + + // test connected + nc.flushBuffer(); + + ts.shutdown(); + while (nc.getStatus() == Connection.Status.CONNECTED) { + sleep(10); + } + + // test while reconnecting + assertThrows(IllegalStateException.class, nc::flushBuffer); + standardCloseConnection(nc); + + // test when closed. + assertThrows(IllegalStateException.class, nc::flushBuffer); + } + } + + @Test + public void testFlushBufferThreadSafety() throws Exception { + try (NatsTestServer ts = new NatsTestServer()) { + Connection nc = standardConnectionWait(ts.getURI()); + + // use two latches to sync the threads as close as + // possible. + CountDownLatch pubLatch = new CountDownLatch(1); + CountDownLatch flushLatch = new CountDownLatch(1); + CountDownLatch completedLatch = new CountDownLatch(1); + + Thread t = new Thread("publisher") { + @SuppressWarnings("ResultOfMethodCallIgnored") + public void run() { + byte[] payload = new byte[5]; + pubLatch.countDown(); + try { + flushLatch.await(2, TimeUnit.SECONDS); + } catch (Exception e) { + // NOOP + } + for (int i = 1; i <= 50000; i++) { + nc.publish("foo", payload); + if (i % 2000 == 0) { + try { + nc.flushBuffer(); + } catch (IOException e) { + break; + } + } + } + completedLatch.countDown(); + } + }; + + t.start(); + + // sync up the current thread and the publish thread + // to get the most out of the test. + try { + //noinspection ResultOfMethodCallIgnored + pubLatch.await(2, TimeUnit.SECONDS); + } catch (Exception e) { + // NOOP + } + flushLatch.countDown(); + + // flush as fast as we can while the publisher + // is publishing. + + while (t.isAlive()) { + nc.flushBuffer(); + } + + // cleanup and doublecheck the thread is done. + t.join(2000); + + // make sure the publisher actually completed. + assertTrue(completedLatch.await(10, TimeUnit.SECONDS)); + + standardCloseConnection(nc); + } + } + + @SuppressWarnings({"unused", "UnusedAssignment"}) + @Test + public void testSocketLevelException() throws Exception { + int port = NatsTestServer.nextPort(); + + AtomicBoolean simExReceived = new AtomicBoolean(); + ListenerForTesting listener = new ListenerForTesting(); + ErrorListener el = new ErrorListener() { + @Override + public void exceptionOccurred(Connection conn, Exception exp) { + if (exp.getMessage().contains("Simulated Exception")) { + simExReceived.set(true); + } + } + }; + + Options options = optionsBuilder(port) + .dataPortType("io.nats.client.impl.SimulateSocketDataPortException") + .connectionListener(listener) + .errorListener(el) + .reconnectDelayHandler(l -> Duration.ofSeconds(1)) + .build(); + + Connection connection = null; + + // 1. DO NOT RECONNECT ON CONNECT + try (NatsTestServer ts = new NatsTestServer(port, false)) { + try { + SimulateSocketDataPortException.THROW_ON_CONNECT.set(true); + connection = Nats.connect(options); + fail(); + } + catch (Exception ignore) {} + } + + Thread.sleep(200); // just making sure messages get through + assertNull(connection); + assertTrue(simExReceived.get()); + simExReceived.set(false); + + // 2. RECONNECT ON CONNECT + try (NatsTestServer ts = new NatsTestServer(port, false)) { + try { + SimulateSocketDataPortException.THROW_ON_CONNECT.set(true); + listener.prepForStatusChange(Events.RECONNECTED); + connection = Nats.connectReconnectOnConnect(options); + assertTrue(listener.waitForStatusChange(5, TimeUnit.SECONDS)); + listener.prepForStatusChange(Events.DISCONNECTED); + } + catch (Exception e) { + fail("should have connected " + e); + } + } + assertTrue(listener.waitForStatusChange(5, TimeUnit.SECONDS)); + assertTrue(simExReceived.get()); + simExReceived.set(false); + + // 2. NORMAL RECONNECT + listener.prepForStatusChange(Events.RECONNECTED); + try (NatsTestServer ts = new NatsTestServer(port, false)) { + SimulateSocketDataPortException.THROW_ON_CONNECT.set(true); + try { + assertTrue(listener.waitForStatusChange(5, TimeUnit.SECONDS)); + } + catch (Exception e) { + fail("should have reconnected " + e); + } + } + } + + @Test + public void testRunInJsCluster() throws Exception { + ListenerForTesting[] listeners = new ListenerForTesting[3]; + listeners[0] = new ListenerForTesting(); + listeners[1] = new ListenerForTesting(); + listeners[2] = new ListenerForTesting(); + + ThreeServerTestOptions tstOpts = new ThreeServerTestOptions() { + @Override + public void append(int index, Options.Builder builder) { + builder.connectionListener(listeners[index]).errorListener(listeners[index]); + } + + @Override + public boolean configureAccount() { + return true; + } + + @Override + public boolean includeAllServers() { + return true; + } + }; + + runInJsCluster(ConnectTests::validateRunInJsCluster); + + listeners[0] = new ListenerForTesting(); + listeners[1] = new ListenerForTesting(); + listeners[2] = new ListenerForTesting(); + + runInJsCluster(tstOpts, ConnectTests::validateRunInJsCluster); + } + + private static void validateRunInJsCluster(Connection nc1, Connection nc2, Connection nc3) throws InterruptedException { + Thread.sleep(200); + ServerInfo si1 = nc1.getServerInfo(); + ServerInfo si2 = nc2.getServerInfo(); + ServerInfo si3 = nc3.getServerInfo(); + assertEquals(si1.getCluster(), si2.getCluster()); + assertEquals(si1.getCluster(), si3.getCluster()); + String port1 = "" + si1.getPort(); + String port2 = "" + si2.getPort(); + String port3 = "" + si3.getPort(); + String urls1 = String.join(",", si1.getConnectURLs()); + String urls2 = String.join(",", si2.getConnectURLs()); + String urls3 = String.join(",", si3.getConnectURLs()); + assertTrue(urls1.contains(port1)); + assertTrue(urls1.contains(port2)); + assertTrue(urls1.contains(port3)); + assertTrue(urls2.contains(port1)); + assertTrue(urls2.contains(port2)); + assertTrue(urls2.contains(port3)); + assertTrue(urls3.contains(port1)); + assertTrue(urls3.contains(port2)); + assertTrue(urls3.contains(port3)); + } + + // https://github.com/nats-io/nats.java/issues/1201 + @Test + void testLowConnectionTimeoutResultsInIOException() { + Options options = Options.builder() + .connectionTimeout(Duration.ZERO) + .build(); + + assertThrows(IOException.class, () -> { + Connection nc = Nats.connect(options); + nc.close(); + }); + } + + @Test + void testConnectWithFastFallback() throws Exception { + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts).enableFastFallback().build(); + Connection nc = standardConnectionWait(options); + standardCloseConnection(nc); + } + } + + @Test + void testConnectPendingCountCoverage() throws Exception { + runInServer(nc -> { + AtomicLong outgoingPendingMessageCount = new AtomicLong(); + AtomicLong outgoingPendingBytes = new AtomicLong(); + + AtomicBoolean tKeepGoing = new AtomicBoolean(true); + Thread t = new Thread(() -> { + while (tKeepGoing.get()) { + outgoingPendingMessageCount.set(Math.max(outgoingPendingMessageCount.get(), nc.outgoingPendingMessageCount())); + outgoingPendingBytes.set(Math.max(outgoingPendingBytes.get(), nc.outgoingPendingBytes())); + sleep(10); + } + }); + t.start(); + + String subject = TestBase.random(); + byte[] data = new byte[8 * 1024]; + for (int x = 0; x < 5000; x++) { + nc.publish(subject, data); + } + tKeepGoing.set(false); + t.join(); + + assertTrue(outgoingPendingMessageCount.get() > 0); + assertTrue(outgoingPendingBytes.get() > outgoingPendingMessageCount.get() * 1000); + }); + } +} diff --git a/src/test/java/io/nats/client/EchoTests.java b/src/test/java/io/nats/client/EchoTests.java index 6429ed843..60f3676a1 100644 --- a/src/test/java/io/nats/client/EchoTests.java +++ b/src/test/java/io/nats/client/EchoTests.java @@ -13,31 +13,31 @@ package io.nats.client; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import io.nats.client.NatsServerProtocolMock.ExitAt; +import io.nats.client.utils.LongRunningServer; +import io.nats.client.utils.TestBase; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.time.Duration; -import org.junit.jupiter.api.Test; - -import io.nats.client.NatsServerProtocolMock.ExitAt; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static org.junit.jupiter.api.Assertions.*; -public class EchoTests { +public class EchoTests extends TestBase { @Test public void testFailWithBadServerProtocol() { assertThrows(IOException.class, () -> { Connection nc = null; try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT)) { - Options opt = new Options.Builder().server(ts.getURI()).noEcho().noReconnect().build(); + Options opt = optionsBuilder(ts.getURI()).noEcho().noReconnect().build(); try { nc = Nats.connect(opt); // Should fail - } finally { + } + finally { if (nc != null) { nc.close(); - assertTrue(Connection.Status.CLOSED == nc.getStatus(), "Closed Status"); + assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); } } } @@ -48,13 +48,13 @@ public void testFailWithBadServerProtocol() { public void testConnectToOldServerWithEcho() throws Exception { Connection nc = null; try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT)) { - Options opt = new Options.Builder().server(ts.getURI()).noReconnect().build(); + Options opt = optionsBuilder(ts.getURI()).noReconnect().build(); try { nc = Nats.connect(opt); } finally { if (nc != null) { nc.close(); - assertTrue(Connection.Status.CLOSED == nc.getStatus(), "Closed Status"); + assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); } } } @@ -62,65 +62,46 @@ public void testConnectToOldServerWithEcho() throws Exception { @Test public void testWithEcho() throws Exception { - try (NatsTestServer ts = new NatsTestServer()) { - Options options = new Options.Builder().server(ts.getURI()).noReconnect().build(); - try (Connection nc1 = Nats.connect(options); - Connection nc2 = Nats.connect(options);) { + // do not open LrConns in try-resources + Connection nc1 = LongRunningServer.getLrConn(); + Connection nc2 = LongRunningServer.getLrConn2(); + // Echo is on so both sub should get messages from both pub + String subject = TestBase.random(); + Subscription sub1 = nc1.subscribe(subject); + nc1.flush(Duration.ofSeconds(1)); + Subscription sub2 = nc2.subscribe(subject); + nc2.flush(Duration.ofSeconds(1)); - // Echo is on so both sub should get messages from both pub - Subscription sub1 = nc1.subscribe("test"); - nc1.flush(Duration.ofSeconds(1)); - Subscription sub2 = nc2.subscribe("test"); - nc2.flush(Duration.ofSeconds(1)); + // Pub from connect 1 + nc1.publish(subject, null); + nc1.flush(Duration.ofSeconds(1)); + Message msg = sub1.nextMessage(Duration.ofSeconds(1)); + assertNotNull(msg); + msg = sub2.nextMessage(Duration.ofSeconds(1)); + assertNotNull(msg); - // Pub from connect 1 - nc1.publish("test", null); - nc1.flush(Duration.ofSeconds(1)); - Message msg = sub1.nextMessage(Duration.ofSeconds(1)); - assertNotNull(msg); - msg = sub2.nextMessage(Duration.ofSeconds(1)); - assertNotNull(msg); - - // Pub from connect 2 - nc2.publish("test", null); - nc2.flush(Duration.ofSeconds(1)); - msg = sub1.nextMessage(Duration.ofSeconds(1)); - assertNotNull(msg); - msg = sub2.nextMessage(Duration.ofSeconds(1)); - assertNotNull(msg); - } - } + // Pub from connect 2 + nc2.publish(subject, null); + nc2.flush(Duration.ofSeconds(1)); + msg = sub1.nextMessage(Duration.ofSeconds(1)); + assertNotNull(msg); + msg = sub2.nextMessage(Duration.ofSeconds(1)); + assertNotNull(msg); } - + @Test public void testWithNoEcho() throws Exception { - try (NatsTestServer ts = new NatsTestServer()) { - Options options = new Options.Builder().server(ts.getURI()).noEcho().noReconnect().build(); - try (Connection nc1 = Nats.connect(options); - Connection nc2 = Nats.connect(options);) { - - // Echo is on so both sub should get messages from both pub - Subscription sub1 = nc1.subscribe("test"); - nc1.flush(Duration.ofSeconds(1)); - Subscription sub2 = nc2.subscribe("test"); - nc2.flush(Duration.ofSeconds(1)); + runInLrServer(optionsBuilder().noEcho().noReconnect(), nc1 -> { + String subject = TestBase.random(); + // Echo is off so sub should get messages from pub from other connections + Subscription sub1 = nc1.subscribe(subject); + nc1.flush(Duration.ofSeconds(1)); - // Pub from connect 1 - nc1.publish("test", null); - nc1.flush(Duration.ofSeconds(1)); - Message msg = sub1.nextMessage(Duration.ofSeconds(1)); - assertNull(msg); // no message for sub1 from pub 1 - msg = sub2.nextMessage(Duration.ofSeconds(1)); - assertNotNull(msg); - - // Pub from connect 2 - nc2.publish("test", null); - nc2.flush(Duration.ofSeconds(1)); - msg = sub1.nextMessage(Duration.ofSeconds(1)); - assertNotNull(msg); - msg = sub2.nextMessage(Duration.ofSeconds(1)); - assertNull(msg); // no message for sub2 from pub 2 - } - } + // Pub from connect 1 + nc1.publish(subject, null); + nc1.flush(Duration.ofSeconds(1)); + Message msg = sub1.nextMessage(Duration.ofSeconds(1)); + assertNull(msg); // no message for sub1 from pub 1 + }); } } \ No newline at end of file diff --git a/src/test/java/io/nats/client/NatsServerProtocolMock.java b/src/test/java/io/nats/client/NatsServerProtocolMock.java index 8051c2696..6fd0583ea 100644 --- a/src/test/java/io/nats/client/NatsServerProtocolMock.java +++ b/src/test/java/io/nats/client/NatsServerProtocolMock.java @@ -1,270 +1,270 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.nats.client; - -import java.io.*; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.concurrent.CompletableFuture; - -import static io.nats.client.support.Encoding.base64UrlEncodeToString; - -/** - * Handles the begining of the connect sequence, all hard coded, but - * is configurable to fail at specific points to allow client testing. - */ -public class NatsServerProtocolMock implements Closeable{ - - // Default is to exit after pong - public enum ExitAt { - SLEEP_BEFORE_INFO, - EXIT_BEFORE_INFO, - EXIT_AFTER_INFO, - EXIT_AFTER_CONNECT, - EXIT_AFTER_PING, - EXIT_AFTER_CUSTOM, - NO_EXIT - } - - public enum Progress { - NO_CLIENT, - CLIENT_CONNECTED, - SENT_INFO, - GOT_CONNECT, - GOT_PING, - SENT_PONG, - STARTED_CUSTOM_CODE, - COMPLETED_CUSTOM_CODE, - } - - public interface Customizer { - public void customizeTest(NatsServerProtocolMock ts, BufferedReader reader, PrintWriter writer); - } - - private int port; - private ExitAt exitAt; - private Progress progress; - private boolean protocolFailure; - private CompletableFuture waitForIt; - private Customizer customizer; - private String customInfo; - private String separator = " "; - - private boolean customInfoIsFullInfo = false; - - public NatsServerProtocolMock(ExitAt exitAt) throws IOException { - this(NatsTestServer.nextPort(), exitAt); - } - - public NatsServerProtocolMock(int port, ExitAt exitAt) { - this.port = port; - this.exitAt = exitAt; - start(); - } - - public NatsServerProtocolMock(Customizer custom) throws IOException { - this.port = NatsTestServer.nextPort(); - this.exitAt = ExitAt.NO_EXIT; - this.customizer = custom; - start(); - } - - public NatsServerProtocolMock(Customizer custom, int port, boolean exitAfterCustom) { - this.port = port; - - if (exitAfterCustom) { - this.exitAt = ExitAt.EXIT_AFTER_CUSTOM; - } else { - this.exitAt = ExitAt.NO_EXIT; - } - this.customizer = custom; - start(); - } - - // CustomInfo is just the JSON string, not the full protocol string - // or the \r\n. - public NatsServerProtocolMock(Customizer custom, String customInfo) throws IOException { - this.port = NatsTestServer.nextPort(); - this.exitAt = ExitAt.NO_EXIT; - this.customizer = custom; - this.customInfo = customInfo; - start(); - } - - private void start() { - this.progress = Progress.NO_CLIENT; - this.waitForIt = new CompletableFuture<>(); - Thread t = new Thread(() -> {accept();}); - t.start(); - try { - Thread.sleep(100); - } catch (Exception exp) { - //Give the server time to get going - } - } - - public void useTabs() { - this.separator = "\t"; - } - - public void useCustomInfoAsFullInfo() { - customInfoIsFullInfo = true; - } - - public int getPort() { - return port; - } - - public String getURI() { - return "nats://localhost:" + this.getPort(); - } - - public Progress getProgress() { - return this.progress; - } - - // True if the failure was not intentional - public boolean wasProtocolFailure() { - return protocolFailure; - } - - public void close() { - waitForIt.complete(Boolean.TRUE); - } - - public void accept() { - ServerSocket serverSocket = null; - Socket socket = null; - PrintWriter writer = null; - BufferedReader reader = null; - - try { - serverSocket = new ServerSocket(this.port); - serverSocket.setSoTimeout(5000); - - // System.out.println("*** Mock Server @" + this.port + " started..."); - socket = serverSocket.accept(); - - this.progress = Progress.CLIENT_CONNECTED; - // System.out.println("*** Mock Server @" + this.port + " got client..."); - - writer = new PrintWriter(socket.getOutputStream()); - reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); - - if (exitAt == ExitAt.EXIT_BEFORE_INFO) { - throw new Exception("exit"); - } - - if (exitAt == ExitAt.SLEEP_BEFORE_INFO) { - try { - Thread.sleep(3000); - } catch ( InterruptedException e) { - // ignore - } - } - - String encodedNonce = base64UrlEncodeToString("abcdefg".getBytes()); - - if (this.customInfo != null) { - if (customInfoIsFullInfo) { - writer.write(customInfo); - } else { - writer.write("INFO" + this.separator + customInfo + "\r\n"); - } - } else { - writer.write("INFO" + this.separator + "{\"server_id\":\"test\", \"version\":\"9.9.99\", \"nonce\":\""+encodedNonce+"\", \"headers\":true}\r\n"); - } - writer.flush(); - this.progress = Progress.SENT_INFO; - // System.out.println("*** Mock Server @" + this.port + " sent info..."); - - if (exitAt == ExitAt.EXIT_AFTER_INFO) { - throw new Exception("exit"); - } - - String connect = reader.readLine(); - - if (connect != null && connect.startsWith("CONNECT")) { - this.progress = Progress.GOT_CONNECT; - // System.out.println("*** Mock Server @" + this.port + " got connect..."); - } else { - throw new IOException("First message wasn't CONNECT"); - } - - if (exitAt == ExitAt.EXIT_AFTER_CONNECT) { - throw new Exception("exit"); - } - - String ping = reader.readLine(); - - if (ping.startsWith("PING")) { - this.progress = Progress.GOT_PING; - // System.out.println("*** Mock Server @" + this.port + " got ping..."); - } else { - throw new IOException("Second message wasn't PING"); - } - - if (exitAt == ExitAt.EXIT_AFTER_PING) { - throw new Exception("exit"); - } - - writer.write("PONG\r\n"); - writer.flush(); - this.progress = Progress.SENT_PONG; - // System.out.println("*** Mock Server @" + this.port + " sent pong..."); - - if (this.customizer != null) { - this.progress = Progress.STARTED_CUSTOM_CODE; - // System.out.println("*** Mock Server @" + this.port + " starting custom code..."); - this.customizer.customizeTest(this, reader, writer); - this.progress = Progress.COMPLETED_CUSTOM_CODE; - } - - if (exitAt == ExitAt.EXIT_AFTER_CUSTOM) { - throw new Exception("exit"); - } - waitForIt.get(); // Wait for the test to cancel us - - } catch (IOException io) { - protocolFailure = true; - // System.out.println("\n*** Mock Server @" + this.port + " got exception "+io.getMessage()); - io.printStackTrace(); - } catch (Exception ex) { - // System.out.println("\n*** Mock Server @" + this.port + " got exception "+ex.getMessage()); - - if (!"exit".equals(ex.getMessage())) { - ex.printStackTrace(); - } - } - finally { - if (serverSocket != null) { - try { - serverSocket.close(); - } catch (IOException ex) { - // System.out.println("\n*** Mock Server @" + this.port + " got exception "+ex.getMessage()); - } - } - if (socket != null) { - try { - writer.close(); - reader.close(); - socket.close(); - } catch (IOException ex) { - // System.out.println("\n*** Mock Server @" + this.port + " got exception "+ex.getMessage()); - } - } - } - // System.out.println("*** Mock Server @" + this.port + " completed..."); - } +// Copyright 2015-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client; + +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.CompletableFuture; + +import static io.nats.client.support.Encoding.base64UrlEncodeToString; + +/** + * Handles the begining of the connect sequence, all hard coded, but + * is configurable to fail at specific points to allow client testing. + */ +public class NatsServerProtocolMock implements Closeable { + + // Default is to exit after pong + public enum ExitAt { + SLEEP_BEFORE_INFO, + EXIT_BEFORE_INFO, + EXIT_AFTER_INFO, + EXIT_AFTER_CONNECT, + EXIT_AFTER_PING, + EXIT_AFTER_CUSTOM, + NO_EXIT + } + + public enum Progress { + NO_CLIENT, + CLIENT_CONNECTED, + SENT_INFO, + GOT_CONNECT, + GOT_PING, + SENT_PONG, + STARTED_CUSTOM_CODE, + COMPLETED_CUSTOM_CODE, + } + + public interface Customizer { + void customizeTest(NatsServerProtocolMock ts, BufferedReader reader, PrintWriter writer); + } + + private final int port; + private final ExitAt exitAt; + private Progress progress; + private boolean protocolFailure; + private CompletableFuture waitForIt; + private Customizer customizer; + private String customInfo; + private String separator = " "; + + private boolean customInfoIsFullInfo = false; + + public NatsServerProtocolMock(ExitAt exitAt) throws IOException { + this(NatsTestServer.nextPort(), exitAt); + } + + public NatsServerProtocolMock(int port, ExitAt exitAt) { + this.port = port; + this.exitAt = exitAt; + start(); + } + + public NatsServerProtocolMock(Customizer custom) throws IOException { + this.port = NatsTestServer.nextPort(); + this.exitAt = ExitAt.NO_EXIT; + this.customizer = custom; + start(); + } + + public NatsServerProtocolMock(Customizer custom, int port, boolean exitAfterCustom) { + this.port = port; + + if (exitAfterCustom) { + this.exitAt = ExitAt.EXIT_AFTER_CUSTOM; + } else { + this.exitAt = ExitAt.NO_EXIT; + } + this.customizer = custom; + start(); + } + + // CustomInfo is just the JSON string, not the full protocol string + // or the \r\n. + public NatsServerProtocolMock(Customizer custom, String customInfo) throws IOException { + this.port = NatsTestServer.nextPort(); + this.exitAt = ExitAt.NO_EXIT; + this.customizer = custom; + this.customInfo = customInfo; + start(); + } + + private void start() { + this.progress = Progress.NO_CLIENT; + this.waitForIt = new CompletableFuture<>(); + Thread t = new Thread(this::accept); + t.start(); + try { + Thread.sleep(100); + } catch (Exception exp) { + //Give the server time to get going + } + } + + public void useTabs() { + this.separator = "\t"; + } + + public void useCustomInfoAsFullInfo() { + customInfoIsFullInfo = true; + } + + public int getPort() { + return port; + } + + public String getURI() { + return "nats://0.0.0.0:" + this.getPort(); + } + + public Progress getProgress() { + return this.progress; + } + + // True if the failure was not intentional + public boolean wasProtocolFailure() { + return protocolFailure; + } + + public void close() { + waitForIt.complete(Boolean.TRUE); + } + + public void accept() { + ServerSocket serverSocket = null; + Socket socket = null; + PrintWriter writer = null; + BufferedReader reader = null; + + try { + serverSocket = new ServerSocket(this.port); + serverSocket.setSoTimeout(5000); + + // System.out.println("*** Mock Server @" + this.port + " started..."); + socket = serverSocket.accept(); + + this.progress = Progress.CLIENT_CONNECTED; + // System.out.println("*** Mock Server @" + this.port + " got client..."); + + writer = new PrintWriter(socket.getOutputStream()); + reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + + if (exitAt == ExitAt.EXIT_BEFORE_INFO) { + throw new Exception("exit"); + } + + if (exitAt == ExitAt.SLEEP_BEFORE_INFO) { + try { + Thread.sleep(3000); + } catch ( InterruptedException e) { + // ignore + } + } + + String encodedNonce = base64UrlEncodeToString("abcdefg".getBytes()); + + if (this.customInfo != null) { + if (customInfoIsFullInfo) { + writer.write(customInfo); + } else { + writer.write("INFO" + this.separator + customInfo + "\r\n"); + } + } else { + writer.write("INFO" + this.separator + "{\"server_id\":\"test\", \"version\":\"9.9.99\", \"nonce\":\""+encodedNonce+"\", \"headers\":true}\r\n"); + } + writer.flush(); + this.progress = Progress.SENT_INFO; + // System.out.println("*** Mock Server @" + this.port + " sent info..."); + + if (exitAt == ExitAt.EXIT_AFTER_INFO) { + throw new Exception("exit"); + } + + String connect = reader.readLine(); + + if (connect != null && connect.startsWith("CONNECT")) { + this.progress = Progress.GOT_CONNECT; + // System.out.println("*** Mock Server @" + this.port + " got connect..."); + } else { + throw new IOException("First message wasn't CONNECT"); + } + + if (exitAt == ExitAt.EXIT_AFTER_CONNECT) { + throw new Exception("exit"); + } + + String ping = reader.readLine(); + + if (ping.startsWith("PING")) { + this.progress = Progress.GOT_PING; + // System.out.println("*** Mock Server @" + this.port + " got ping..."); + } else { + throw new IOException("Second message wasn't PING"); + } + + if (exitAt == ExitAt.EXIT_AFTER_PING) { + throw new Exception("exit"); + } + + writer.write("PONG\r\n"); + writer.flush(); + this.progress = Progress.SENT_PONG; + // System.out.println("*** Mock Server @" + this.port + " sent pong..."); + + if (this.customizer != null) { + this.progress = Progress.STARTED_CUSTOM_CODE; + // System.out.println("*** Mock Server @" + this.port + " starting custom code..."); + this.customizer.customizeTest(this, reader, writer); + this.progress = Progress.COMPLETED_CUSTOM_CODE; + } + + if (exitAt == ExitAt.EXIT_AFTER_CUSTOM) { + throw new Exception("exit"); + } + waitForIt.get(); // Wait for the test to cancel us + + } catch (IOException io) { + protocolFailure = true; + // System.out.println("\n*** Mock Server @" + this.port + " got exception "+io.getMessage()); + // io.printStackTrace(); + } catch (Exception ex) { + // System.out.println("\n*** Mock Server @" + this.port + " got exception "+ex.getMessage()); + + if (!"exit".equals(ex.getMessage())) { + ex.printStackTrace(); + } + } + finally { + if (serverSocket != null) { + try { + serverSocket.close(); + } catch (IOException ex) { + // System.out.println("\n*** Mock Server @" + this.port + " got exception "+ex.getMessage()); + } + } + if (socket != null) { + try { + writer.close(); + reader.close(); + socket.close(); + } catch (IOException ex) { + // System.out.println("\n*** Mock Server @" + this.port + " got exception "+ex.getMessage()); + } + } + } + // System.out.println("*** Mock Server @" + this.port + " completed..."); + } } \ No newline at end of file diff --git a/src/test/java/io/nats/client/NatsTestServer.java b/src/test/java/io/nats/client/NatsTestServer.java index 0a4ae36ed..c8340655d 100644 --- a/src/test/java/io/nats/client/NatsTestServer.java +++ b/src/test/java/io/nats/client/NatsTestServer.java @@ -21,68 +21,101 @@ import java.util.logging.Level; public class NatsTestServer extends NatsServerRunner { + + private static final String CONFIG_FILE_BASE = "src/test/resources/"; + static { NatsTestServer.quiet(); - NatsServerRunner.setDefaultOutputSupplier(ConsoleOutput::new); - NatsServerRunner.setDefaultValidateTries(5); - NatsServerRunner.setDefaultInitialValidateDelay(100); - NatsServerRunner.setDefaultSubsequentValidateDelay(50); + NatsRunnerUtils.setDefaultConnectValidateTries(10); + NatsRunnerUtils.setDefaultConnectValidateTimeout(200); + NatsRunnerUtils.setDefaultOutputSupplier(ConsoleOutput::new); } public static void quiet() { - NatsServerRunner.setDefaultOutputLevel(Level.WARNING); + NatsRunnerUtils.setDefaultOutputLevel(Level.WARNING); } public static void verbose() { - NatsServerRunner.setDefaultOutputLevel(Level.ALL); + NatsRunnerUtils.setDefaultOutputLevel(Level.ALL); + } + + public static String configFilePath(String configFilePath) { + return configFilePath.startsWith(CONFIG_FILE_BASE) ? configFilePath : CONFIG_FILE_BASE + configFilePath; + } + + public static NatsTestServer configuredServer(String configFilePath) throws IOException { + return new NatsTestServer( + NatsServerRunner.builder() + .configFilePath(configFilePath(configFilePath))); + } + + public static NatsTestServer configuredJsServer(String configFilePath) throws IOException { + return new NatsTestServer( + NatsServerRunner.builder() + .jetstream(true) + .configFilePath(configFilePath(configFilePath))); + } + + public static NatsTestServer configuredServer(String configFilePath, int port) throws IOException { + return new NatsTestServer( + NatsServerRunner.builder() + .port(port) + .configFilePath(configFilePath(configFilePath))); + } + + public static NatsTestServer skipValidateServer(String configFilePath) throws IOException { + return new NatsTestServer( + NatsServerRunner.builder() + .configFilePath(configFilePath(configFilePath)) + .skipConnectValidate()); } public NatsTestServer() throws IOException { - super(); + this(builder()); } public NatsTestServer(boolean debug) throws IOException { - super(debug); + this(builder().debug(debug)); } public NatsTestServer(boolean debug, boolean jetstream) throws IOException { - super(debug, jetstream); + this(builder().debug(debug).jetstream(jetstream)); } public NatsTestServer(int port, boolean debug) throws IOException { - super(port, debug); + this(builder().port(port).debug(debug)); } public NatsTestServer(int port, boolean debug, boolean jetstream) throws IOException { - super(port, debug, jetstream); + this(builder().port(port).debug(debug).jetstream(jetstream)); } public NatsTestServer(String configFilePath, boolean debug) throws IOException { - super(configFilePath, debug); + this(builder().configFilePath(configFilePath).debug(debug)); } public NatsTestServer(String configFilePath, boolean debug, boolean jetstream) throws IOException { - super(configFilePath, debug, jetstream); + this(builder().configFilePath(configFilePath).debug(debug).jetstream(jetstream)); } public NatsTestServer(String configFilePath, String[] configInserts, int port, boolean debug) throws IOException { - super(configFilePath, configInserts, port, debug); + this(builder().configFilePath(configFilePath).configInserts(configInserts).debug(debug)); } public NatsTestServer(String configFilePath, int port, boolean debug) throws IOException { - super(configFilePath, port, debug); + this(builder().configFilePath(configFilePath).port(port).debug(debug)); } public NatsTestServer(String[] customArgs, boolean debug) throws IOException { - super(customArgs, debug); + this(builder().customArgs(customArgs).debug(debug)); } public NatsTestServer(String[] customArgs, int port, boolean debug) throws IOException { - super(customArgs, port, debug); + this(builder().customArgs(customArgs).port(port).debug(debug)); } public NatsTestServer(int port, boolean debug, boolean jetstream, String configFilePath, String[] configInserts, String[] customArgs) throws IOException { - super(port, debug, jetstream, configFilePath, configInserts, customArgs); + this(builder().port(port).debug(debug).jetstream(jetstream).configFilePath(configFilePath).configInserts(configInserts).customArgs(customArgs)); } public NatsTestServer(Builder b) throws IOException { diff --git a/src/test/java/io/nats/client/OptionsTests.java b/src/test/java/io/nats/client/OptionsTests.java index 6ca01a87f..36db53161 100644 --- a/src/test/java/io/nats/client/OptionsTests.java +++ b/src/test/java/io/nats/client/OptionsTests.java @@ -39,6 +39,8 @@ import static io.nats.client.Options.*; import static io.nats.client.support.Encoding.base64UrlEncodeToString; import static io.nats.client.support.NatsConstants.DEFAULT_PORT; +import static io.nats.client.utils.OptionsUtils.options; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; import static org.junit.jupiter.api.Assertions.*; public class OptionsTests { @@ -54,7 +56,7 @@ public void testClientVersion() { @Test public void testDefaultOptions() { - Options o = new Options.Builder().build(); + Options o = optionsBuilder().errorListener(null).build(); _testDefaultOptions(o); _testDefaultOptions(new Options.Builder(o).build()); } @@ -105,7 +107,7 @@ private static void _testDefaultOptions(Options o) { @Test public void testOldStyle() { - Options o = new Options.Builder().build(); + Options o = options(); assertFalse(o.isOldRequestStyle(), "default oldstyle"); //noinspection deprecation o.setOldRequestStyle(true); @@ -117,7 +119,7 @@ public void testOldStyle() { @Test public void testChainedBooleanOptions() { - Options o = new Options.Builder().verbose().pedantic().noRandomize() + Options o = optionsBuilder().verbose().pedantic().noRandomize() .noEcho().oldRequestStyle().noHeaders().noNoResponders() .discardMessagesWhenOutgoingQueueFull() .build(); @@ -139,7 +141,7 @@ private static void _testChainedBooleanOptions(Options o) { @Test public void testChainedStringOptions() { - Options o = new Options.Builder().userInfo("hello".toCharArray(), "world".toCharArray()).connectionName("name").build(); + Options o = optionsBuilder().userInfo("hello".toCharArray(), "world".toCharArray()).connectionName("name").build(); _testChainedStringOptions(o); _testChainedStringOptions(new Options.Builder(o).build()); } @@ -155,7 +157,7 @@ private static void _testChainedStringOptions(Options o) { public void testChainedSecure() throws Exception { SSLContext ctx = SslTestingHelper.createTestSSLContext(); SSLContext.setDefault(ctx); - Options o = new Options.Builder().secure().build(); + Options o = optionsBuilder().secure().build(); _testChainedSecure(ctx, o); _testChainedSecure(ctx, new Options.Builder(o).build()); } @@ -167,7 +169,7 @@ private static void _testChainedSecure(SSLContext ctx, Options o) { @Test public void testChainedSSLOptions() throws Exception { SSLContext ctx = SslTestingHelper.createTestSSLContext(); - Options o = new Options.Builder().sslContext(ctx).build(); + Options o = optionsBuilder().sslContext(ctx).build(); _testChainedSSLOptions(ctx, o); _testChainedSSLOptions(ctx, new Options.Builder(o).build()); } @@ -179,7 +181,7 @@ private static void _testChainedSSLOptions(SSLContext ctx, Options o) { @Test public void testChainedIntOptions() { - Options o = new Options.Builder().maxReconnects(100).maxPingsOut(200).reconnectBufferSize(300) + Options o = optionsBuilder().maxReconnects(100).maxPingsOut(200).reconnectBufferSize(300) .maxControlLine(400) .maxMessagesInOutgoingQueue(500) .build(); @@ -198,7 +200,7 @@ private static void _testChainedIntOptions(Options o) { @Test public void testChainedDurationOptions() { - Options o = new Options.Builder().reconnectWait(Duration.ofMillis(101)) + Options o = optionsBuilder().reconnectWait(Duration.ofMillis(101)) .connectionTimeout(Duration.ofMillis(202)).pingInterval(Duration.ofMillis(303)) .requestCleanupInterval(Duration.ofMillis(404)) .reconnectJitter(Duration.ofMillis(505)) @@ -226,13 +228,13 @@ public void testHttpRequestInterceptors() { java.util.function.Consumer interceptor2 = req -> { req.getHeaders().add("Test2", "Header"); }; - Options o = new Options.Builder() + Options o = optionsBuilder() .httpRequestInterceptor(interceptor1) .httpRequestInterceptor(interceptor2) .build(); assertEquals(o.getHttpRequestInterceptors(), Arrays.asList(interceptor1, interceptor2)); - o = new Options.Builder() + o = optionsBuilder() .httpRequestInterceptors(Arrays.asList(interceptor2, interceptor1)) .build(); assertEquals(o.getHttpRequestInterceptors(), Arrays.asList(interceptor2, interceptor1)); @@ -360,10 +362,10 @@ private static void _testPropertiesSSLOptions(Options o) { @Test public void testSupportUTF8Subjects() { - Options o = new Options.Builder().build(); + Options o = options(); assertFalse(o.supportUTF8Subjects()); - o = new Options.Builder().supportUTF8Subjects().build(); + o = optionsBuilder().supportUTF8Subjects().build(); assertTrue(o.supportUTF8Subjects()); Properties props = new Properties(); @@ -374,13 +376,13 @@ public void testSupportUTF8Subjects() { @Test public void testBuilderCoverageOptions() { - Options o = new Options.Builder().build(); + Options o = options(); assertTrue(o.clientSideLimitChecks()); assertNull(o.getServerPool()); // there is a default provider - o = new Options.Builder().clientSideLimitChecks(true).build(); + o = optionsBuilder().clientSideLimitChecks(true).build(); assertTrue(o.clientSideLimitChecks()); - o = new Options.Builder() + o = optionsBuilder() .clientSideLimitChecks(false) .serverPool(new NatsServerPool()) .build(); @@ -456,7 +458,7 @@ public void testProperties() throws Exception { .build(); assertEquals(1000, o.getMaxMessagesInOutgoingQueue()); - o = new Options.Builder() + o = optionsBuilder() .maxMessagesInOutgoingQueue(1000) .properties(props) .build(); @@ -702,7 +704,7 @@ public void testChainOverridesProperties() { @Test public void testDefaultConnectOptions() { - Options o = new Options.Builder().build(); + Options o = options(); String expected = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\"" + ",\"protocol\":1,\"verbose\":false,\"pedantic\":false,\"tls_required\":false,\"echo\":true,\"headers\":true,\"no_responders\":true}"; assertEquals(expected, o.buildProtocolConnectOptionsString("nats://localhost:4222", false, null).toString(), "default connect options"); @@ -710,7 +712,7 @@ public void testDefaultConnectOptions() { @Test public void testNonDefaultConnectOptions() { - Options o = new Options.Builder().noNoResponders().noHeaders().noEcho().pedantic().verbose().build(); + Options o = optionsBuilder().noNoResponders().noHeaders().noEcho().pedantic().verbose().build(); String expected = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\"" + ",\"protocol\":1,\"verbose\":true,\"pedantic\":true,\"tls_required\":false,\"echo\":false,\"headers\":false,\"no_responders\":false}"; assertEquals(expected, o.buildProtocolConnectOptionsString("nats://localhost:4222", false, null).toString(), "non default connect options"); @@ -719,7 +721,7 @@ public void testNonDefaultConnectOptions() { @Test public void testConnectOptionsWithNameAndContext() throws Exception { SSLContext ctx = SslTestingHelper.createTestSSLContext(); - Options o = new Options.Builder().sslContext(ctx).connectionName("c1").build(); + Options o = optionsBuilder().sslContext(ctx).connectionName("c1").build(); String expected = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\",\"name\":\"c1\"" + ",\"protocol\":1,\"verbose\":false,\"pedantic\":false,\"tls_required\":true,\"echo\":true,\"headers\":true,\"no_responders\":true}"; assertEquals(expected, o.buildProtocolConnectOptionsString("nats://localhost:4222", false, null).toString(), "default connect options"); @@ -727,7 +729,7 @@ public void testConnectOptionsWithNameAndContext() throws Exception { @Test public void testAuthConnectOptions() { - Options o = new Options.Builder().userInfo("hello".toCharArray(), "world".toCharArray()).build(); + Options o = optionsBuilder().userInfo("hello".toCharArray(), "world".toCharArray()).build(); String expectedNoAuth = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\"" + ",\"protocol\":1,\"verbose\":false,\"pedantic\":false,\"tls_required\":false,\"echo\":true,\"headers\":true,\"no_responders\":true}"; String expectedWithAuth = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\"" @@ -747,7 +749,7 @@ public void testNKeyConnectOptions() throws Exception { byte[] nonce = "abcdefg".getBytes(StandardCharsets.UTF_8); String sig = base64UrlEncodeToString(th.sign(nonce)); - Options o = new Options.Builder().authHandler(th).build(); + Options o = optionsBuilder().authHandler(th).build(); String expectedNoAuth = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\"" + ",\"protocol\":1,\"verbose\":false,\"pedantic\":false,\"tls_required\":false,\"echo\":true,\"headers\":true,\"no_responders\":true}"; String expectedWithAuth = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\"" @@ -775,7 +777,7 @@ public void testNKeyJWTAndUserInfoOptions() throws Exception { String sig = Base64.getUrlEncoder().withoutPadding().encodeToString(th.sign(nonce)); // Assert that no auth and user info is given - Options options = new Options.Builder().authHandler(th) + Options options = optionsBuilder().authHandler(th) .userInfo(username.toCharArray(), password.toCharArray()).build(); String expectedWithoutAuth = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\"" + ",\"protocol\":1,\"verbose\":false,\"pedantic\":false,\"tls_required\":false,\"echo\":true," @@ -794,7 +796,7 @@ public void testNKeyJWTAndUserInfoOptions() throws Exception { assertEquals(expectedWithAuth, actualWithAuthInOptions); // Assert that auth is given via options and user info is given via server URI - Options optionsWithoutUserInfo = new Options.Builder().authHandler(th).build(); + Options optionsWithoutUserInfo = optionsBuilder().authHandler(th).build(); String serverUriWithAuth = "nats://" + username + ":" + password + "@localhost:4222"; String actualWithAuthInServerUri = optionsWithoutUserInfo .buildProtocolConnectOptionsString(serverUriWithAuth, true, nonce).toString(); @@ -804,12 +806,12 @@ public void testNKeyJWTAndUserInfoOptions() throws Exception { @Test public void testDefaultDataPort() { - Options o = new Options.Builder().socketWriteTimeout(null).build(); + Options o = optionsBuilder().socketWriteTimeout(null).build(); DataPort dataPort = o.buildDataPort(); assertNotNull(dataPort); assertEquals(Options.DEFAULT_DATA_PORT_TYPE, dataPort.getClass().getCanonicalName(), "old default dataPort"); - o = new Options.Builder().build(); + o = options(); dataPort = o.buildDataPort(); assertNotNull(dataPort); assertEquals(SocketDataPortWithWriteTimeout.class.getCanonicalName(), dataPort.getClass().getCanonicalName(), "new default dataPort"); @@ -842,7 +844,7 @@ public void testJetStreamProperties() { @Test public void testUserPassInURL() { String serverURI = "nats://derek:password@localhost:2222"; - Options o = new Options.Builder().server(serverURI).build(); + Options o = optionsBuilder(serverURI).build(); String connectString = o.buildProtocolConnectOptionsString(serverURI, true, null).toString(); assertTrue(connectString.contains("\"user\":\"derek\"")); @@ -853,7 +855,7 @@ public void testUserPassInURL() { @Test public void testTokenInURL() { String serverURI = "nats://alberto@localhost:2222"; - Options o = new Options.Builder().server(serverURI).build(); + Options o = optionsBuilder(serverURI).build(); String connectString = o.buildProtocolConnectOptionsString(serverURI, true, null).toString(); assertTrue(connectString.contains("\"auth_token\":\"alberto\"")); @@ -864,31 +866,31 @@ public void testTokenInURL() { @Test public void testTokenSupplier() { String serverURI = "nats://localhost:2222"; - Options o = new Options.Builder().build(); + Options o = options(); String connectString = o.buildProtocolConnectOptionsString(serverURI, true, null).toString(); assertFalse(connectString.contains("\"auth_token\"")); //noinspection deprecation - o = new Options.Builder().token((String)null).build(); + o = optionsBuilder().token((String)null).build(); connectString = o.buildProtocolConnectOptionsString(serverURI, true, null).toString(); assertFalse(connectString.contains("\"auth_token\"")); //noinspection deprecation - o = new Options.Builder().token(" ").build(); + o = optionsBuilder().token(" ").build(); connectString = o.buildProtocolConnectOptionsString(serverURI, true, null).toString(); assertFalse(connectString.contains("\"auth_token\"")); - o = new Options.Builder().token((char[])null).build(); + o = optionsBuilder().token((char[])null).build(); connectString = o.buildProtocolConnectOptionsString(serverURI, true, null).toString(); assertFalse(connectString.contains("\"auth_token\"")); - o = new Options.Builder().token(new char[0]).build(); + o = optionsBuilder().token(new char[0]).build(); connectString = o.buildProtocolConnectOptionsString(serverURI, true, null).toString(); assertFalse(connectString.contains("\"auth_token\"")); AtomicInteger counter = new AtomicInteger(0); Supplier tokenSupplier = () -> ("short-lived-token-" + counter.incrementAndGet()).toCharArray(); - o = new Options.Builder().tokenSupplier(tokenSupplier).build(); + o = optionsBuilder().tokenSupplier(tokenSupplier).build(); connectString = o.buildProtocolConnectOptionsString(serverURI, true, null).toString(); assertTrue(connectString.contains("\"auth_token\":\"short-lived-token-1\"")); @@ -898,7 +900,7 @@ public void testTokenSupplier() { Properties properties = new Properties(); properties.setProperty(PROP_TOKEN_SUPPLIER, TestingDynamicTokenSupplier.class.getCanonicalName()); - o = new Options.Builder().properties(properties).build(); + o = optionsBuilder().properties(properties).build(); connectString = o.buildProtocolConnectOptionsString(serverURI, true, null).toString(); assertTrue(connectString.contains("\"auth_token\":\"dynamic-token-1\"")); @@ -935,20 +937,20 @@ public void testServersInProperties() { @Test public void testServers() { String[] serverUrls = {URL_PROTO_HOST_PORT_8080, URL_HOST_PORT_8081}; - Options options = new Options.Builder().servers(serverUrls).build(); + Options options = optionsBuilder(serverUrls).build(); assertServersAndUnprocessed(true, options); } @Test public void testServersWithCommas() { - String serverURLs = URL_PROTO_HOST_PORT_8080 + "," + URL_HOST_PORT_8081; - assertServersAndUnprocessed(true, new Options.Builder().server(serverURLs).build()); + String commaDelimited = URL_PROTO_HOST_PORT_8080 + "," + URL_HOST_PORT_8081; + assertServersAndUnprocessed(true, optionsBuilder().server(commaDelimited).build()); } @Test public void testEmptyAndNullStringsInServers() { String[] serverUrls = {"", null, URL_PROTO_HOST_PORT_8080, URL_HOST_PORT_8081}; - assertServersAndUnprocessed(true, new Options.Builder().servers(serverUrls).build()); + assertServersAndUnprocessed(true, optionsBuilder(serverUrls).build()); } private void assertServersAndUnprocessed(boolean two, Options o) { @@ -991,27 +993,27 @@ public void testBadClassInPropertyStatisticsCollector() { @Test public void testTokenAndUserThrows() { assertThrows(IllegalStateException.class, - () -> new Options.Builder().token("foo".toCharArray()).userInfo("foo".toCharArray(), "bar".toCharArray()).build()); + () -> optionsBuilder().token("foo".toCharArray()).userInfo("foo".toCharArray(), "bar".toCharArray()).build()); } @Test public void testThrowOnBadServerURI() { assertThrows(IllegalArgumentException.class, - () -> new Options.Builder().server("foo:/bar\\:blammer").build()); + () -> optionsBuilder("foo:/bar\\:blammer").build()); } @Test public void testThrowOnBadServersURI() { assertThrows(IllegalArgumentException.class, () -> { String[] serverUrls = {URL_PROTO_HOST_PORT_8080, "foo:/bar\\:blammer"}; - new Options.Builder().servers(serverUrls).build(); + optionsBuilder(serverUrls).build(); }); } @Test public void testSetExecutor() { ExecutorService exec = Executors.newCachedThreadPool(); - Options options = new Options.Builder().executor(exec).build(); + Options options = optionsBuilder().executor(exec).build(); assertEquals(exec, options.getExecutor()); } @@ -1031,7 +1033,7 @@ public void testDefaultExecutor() throws Exception { @Test public void testCallbackExecutor() throws ExecutionException, InterruptedException, TimeoutException { ThreadFactory threadFactory = r -> new Thread(r, "test"); - Options options = new Options.Builder() + Options options = optionsBuilder() .callbackThreadFactory(threadFactory) .build(); Future callbackFuture = options.getCallbackExecutor().submit(() -> { @@ -1043,7 +1045,7 @@ public void testCallbackExecutor() throws ExecutionException, InterruptedExcepti @Test public void testConnectExecutor() throws ExecutionException, InterruptedException, TimeoutException { ThreadFactory threadFactory = r -> new Thread(r, "test"); - Options options = new Options.Builder() + Options options = optionsBuilder() .connectThreadFactory(threadFactory) .build(); Future connectFuture = options.getConnectExecutor().submit(() -> { @@ -1147,7 +1149,7 @@ private static void checkCreate(NatsUri uri, boolean secure, boolean ws, boolean public void testReconnectDelayHandler() { ReconnectDelayHandler rdh = l -> Duration.ofSeconds(l * 2); - Options o = new Options.Builder().reconnectDelayHandler(rdh).build(); + Options o = optionsBuilder().reconnectDelayHandler(rdh).build(); ReconnectDelayHandler rdhO = o.getReconnectDelayHandler(); assertNotNull(rdhO); @@ -1156,43 +1158,39 @@ public void testReconnectDelayHandler() { @Test public void testInboxPrefixCoverage() { - Options o = new Options.Builder().inboxPrefix("foo").build(); + Options o = optionsBuilder().inboxPrefix("foo").build(); assertEquals("foo.", o.getInboxPrefix()); - o = new Options.Builder().inboxPrefix("foo.").build(); + o = optionsBuilder().inboxPrefix("foo.").build(); assertEquals("foo.", o.getInboxPrefix()); } @Test public void testSslContextIsProvided() { - Options o = new Options.Builder().server("localhost").build(); + Options o = options("localhost"); assertNull(o.getSslContext()); - o = new Options.Builder().server("ws://localhost").build(); + o = options("ws://localhost"); assertNull(o.getSslContext()); - o = new Options.Builder().server("localhost").build(); + o = options("localhost"); assertNull(o.getSslContext()); - o = new Options.Builder().server("tls://localhost").build(); + o = options("tls://localhost"); assertNotNull(o.getSslContext()); - o = new Options.Builder().server("wss://localhost").build(); + o = options("wss://localhost"); assertNotNull(o.getSslContext()); - o = new Options.Builder().server("opentls://localhost").build(); + o = options("opentls://localhost"); assertNotNull(o.getSslContext()); - o = new Options.Builder().server("nats://localhost,tls://localhost").build(); + o = optionsBuilder().server("nats://localhost,tls://localhost").build(); assertNotNull(o.getSslContext()); } @SuppressWarnings("deprecation") @Test public void coverageForDeprecated() { - Options o = new Options.Builder() - .token("deprecated") - .build(); + Options o = optionsBuilder().token("deprecated").build(); assertEquals("deprecated", o.getToken()); assertNull(o.getUsername()); assertNull(o.getPassword()); - o = new Options.Builder() - .userInfo("user", "pass") - .build(); + o = optionsBuilder().userInfo("user", "pass").build(); assertEquals("user", o.getUsername()); assertEquals("pass", o.getPassword()); assertNull(o.getToken()); @@ -1200,7 +1198,7 @@ public void coverageForDeprecated() { @Test public void testFastFallback() { - Options options = new Options.Builder().enableFastFallback().build(); + Options options = optionsBuilder().enableFastFallback().build(); assertTrue(options.isEnableFastFallback()); } @@ -1213,7 +1211,7 @@ public void testThrowOnBadContextForSecure() throws Exception { try { System.setProperty("javax.net.ssl.keyStore", "foo"); System.setProperty("javax.net.ssl.trustStore", "bar"); - new Options.Builder().secure().build(); + optionsBuilder().secure().build(); assertFalse(true); } finally { @@ -1227,7 +1225,7 @@ public void testThrowOnBadContextForTLSUrl() throws Exception { try { System.setProperty("javax.net.ssl.keyStore", "foo"); System.setProperty("javax.net.ssl.trustStore", "bar"); - new Options.Builder().server("tls://localhost:4242").build(); + optionsBuilder("tls://localhost:4242").build(); assertFalse(true); } finally { diff --git a/src/test/java/io/nats/client/PublishOptionsTests.java b/src/test/java/io/nats/client/PublishOptionsTests.java index 3e0a83369..93867a424 100644 --- a/src/test/java/io/nats/client/PublishOptionsTests.java +++ b/src/test/java/io/nats/client/PublishOptionsTests.java @@ -1,157 +1,158 @@ -// Copyright 2020 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.nats.client; - -import io.nats.client.utils.TestBase; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.util.Properties; - -import static org.junit.jupiter.api.Assertions.*; - -public class PublishOptionsTests extends TestBase { - - @Test - public void testBuilder() { - PublishOptions.Builder builder = PublishOptions.builder(); - PublishOptions po = builder.build(); - //noinspection deprecation - assertNull(po.getStream()); - assertEquals(PublishOptions.DEFAULT_TIMEOUT, po.getStreamTimeout()); - assertNull(po.getExpectedStream()); - assertNull(po.getExpectedLastMsgId()); - assertEquals(PublishOptions.UNSET_LAST_SEQUENCE, po.getExpectedLastSequence()); - assertEquals(PublishOptions.UNSET_LAST_SEQUENCE, po.getExpectedLastSubjectSequence()); - assertNull(po.getExpectedLastSubjectSequenceSubject()); - assertNull(po.getMessageTtl()); - - Duration streamTimeout = Duration.ofSeconds(99); - //noinspection deprecation - builder.stream("pubAckStream"); // DEPRECATED SO JUST COVERAGE - - po = builder - .streamTimeout(streamTimeout) - .expectedStream("expectedStream") - .expectedLastMsgId("1") - .expectedLastSequence(42) - .expectedLastSubjectSequence(43) - .expectedLastSubjectSequenceSubject("sss") - .messageId("msgId") - .messageTtlCustom("custom") - .build(); - - //noinspection deprecation - assertEquals("pubAckStream", po.getStream()); // DEPRECATED / COVERAGE - assertEquals(streamTimeout, po.getStreamTimeout()); - assertEquals("expectedStream", po.getExpectedStream()); - assertEquals("1", po.getExpectedLastMsgId()); - assertEquals(42, po.getExpectedLastSequence()); - assertEquals(43, po.getExpectedLastSubjectSequence()); - assertEquals("sss", po.getExpectedLastSubjectSequenceSubject()); - assertEquals("msgId", po.getMessageId()); - assertEquals("custom", po.getMessageTtl()); - - // test clearExpected - po = builder.clearExpected().build(); - - // these are not cleared - assertEquals("expectedStream", po.getExpectedStream()); - assertEquals(Duration.ofSeconds(99), po.getStreamTimeout()); - assertEquals("custom", po.getMessageTtl()); - - // these are cleared - assertNull(po.getExpectedLastMsgId()); - assertEquals(PublishOptions.UNSET_LAST_SEQUENCE, po.getExpectedLastSequence()); - assertEquals(PublishOptions.UNSET_LAST_SEQUENCE, po.getExpectedLastSubjectSequence()); - assertNull(po.getExpectedLastSubjectSequenceSubject()); - assertNull(po.getMessageId()); - - //noinspection deprecation - po = builder.stream(null).streamTimeout(null).build(); - //noinspection deprecation - assertNull(po.getStream()); - assertEquals(PublishOptions.DEFAULT_TIMEOUT, po.getStreamTimeout()); - - //noinspection deprecation - po = builder.stream("pubAckStream").build(); - //noinspection deprecation - assertEquals("pubAckStream", po.getStream()); - - //noinspection deprecation - po = builder.stream("").build(); - //noinspection deprecation - assertNull(po.getStream()); - } - - @Test - public void testProperties() { - Properties p = new Properties(); - p.setProperty(PublishOptions.PROP_PUBLISH_TIMEOUT, "PT20M"); - p.setProperty(PublishOptions.PROP_STREAM_NAME, STREAM); - PublishOptions po = new PublishOptions.Builder(p).build(); - //noinspection deprecation - assertEquals(STREAM, po.getStream(), "stream foo"); - assertEquals(Duration.ofMinutes(20), po.getStreamTimeout(), "20M timeout"); - - p = new Properties(); - po = new PublishOptions.Builder(p).build(); - //noinspection deprecation - assertNull(po.getStream()); - assertEquals(PublishOptions.DEFAULT_TIMEOUT, po.getStreamTimeout()); - } - - @Test - public void testMessageTtl() { - PublishOptions po = PublishOptions.builder().messageTtlSeconds(3).build(); - assertEquals("3s", po.getMessageTtl()); - - po = PublishOptions.builder().messageTtlCustom("abcd").build(); - assertEquals("abcd", po.getMessageTtl()); - - po = PublishOptions.builder().messageTtlNever().build(); - assertEquals("never", po.getMessageTtl()); - - po = PublishOptions.builder().messageTtl(MessageTtl.seconds(3)).build(); - assertEquals("3s", po.getMessageTtl()); - - po = PublishOptions.builder().messageTtl(MessageTtl.custom("abcd")).build(); - assertEquals("abcd", po.getMessageTtl()); - - po = PublishOptions.builder().messageTtl(MessageTtl.never()).build(); - assertEquals("never", po.getMessageTtl()); - - po = PublishOptions.builder().messageTtlSeconds(0).build(); - assertNull(po.getMessageTtl()); - - po = PublishOptions.builder().messageTtlSeconds(-1).build(); - assertNull(po.getMessageTtl()); - - po = PublishOptions.builder().messageTtlCustom(null).build(); - assertNull(po.getMessageTtl()); - - po = PublishOptions.builder().messageTtlCustom("").build(); - assertNull(po.getMessageTtl()); - - po = PublishOptions.builder().messageTtl(null).build(); - assertNull(po.getMessageTtl()); - - assertThrows(IllegalArgumentException.class, () -> MessageTtl.seconds(0)); - assertThrows(IllegalArgumentException.class, () -> MessageTtl.seconds(-1)); - assertThrows(IllegalArgumentException.class, () -> MessageTtl.custom(null)); - assertThrows(IllegalArgumentException.class, () -> MessageTtl.custom("")); - - assertTrue(MessageTtl.seconds(3).toString().contains("3s")); // COVERAGE - } -} +// Copyright 2020 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client; + +import io.nats.client.utils.TestBase; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.*; + +public class PublishOptionsTests extends TestBase { + + @Test + public void testBuilder() { + PublishOptions.Builder builder = PublishOptions.builder(); + PublishOptions po = builder.build(); + //noinspection deprecation + assertNull(po.getStream()); + assertEquals(PublishOptions.DEFAULT_TIMEOUT, po.getStreamTimeout()); + assertNull(po.getExpectedStream()); + assertNull(po.getExpectedLastMsgId()); + assertEquals(PublishOptions.UNSET_LAST_SEQUENCE, po.getExpectedLastSequence()); + assertEquals(PublishOptions.UNSET_LAST_SEQUENCE, po.getExpectedLastSubjectSequence()); + assertNull(po.getExpectedLastSubjectSequenceSubject()); + assertNull(po.getMessageTtl()); + + Duration streamTimeout = Duration.ofSeconds(99); + //noinspection deprecation + builder.stream("pubAckStream"); // DEPRECATED SO JUST COVERAGE + + po = builder + .streamTimeout(streamTimeout) + .expectedStream("expectedStream") + .expectedLastMsgId("1") + .expectedLastSequence(42) + .expectedLastSubjectSequence(43) + .expectedLastSubjectSequenceSubject("sss") + .messageId("msgId") + .messageTtlCustom("custom") + .build(); + + //noinspection deprecation + assertEquals("pubAckStream", po.getStream()); // DEPRECATED / COVERAGE + assertEquals(streamTimeout, po.getStreamTimeout()); + assertEquals("expectedStream", po.getExpectedStream()); + assertEquals("1", po.getExpectedLastMsgId()); + assertEquals(42, po.getExpectedLastSequence()); + assertEquals(43, po.getExpectedLastSubjectSequence()); + assertEquals("sss", po.getExpectedLastSubjectSequenceSubject()); + assertEquals("msgId", po.getMessageId()); + assertEquals("custom", po.getMessageTtl()); + + // test clearExpected + po = builder.clearExpected().build(); + + // these are not cleared + assertEquals("expectedStream", po.getExpectedStream()); + assertEquals(Duration.ofSeconds(99), po.getStreamTimeout()); + assertEquals("custom", po.getMessageTtl()); + + // these are cleared + assertNull(po.getExpectedLastMsgId()); + assertEquals(PublishOptions.UNSET_LAST_SEQUENCE, po.getExpectedLastSequence()); + assertEquals(PublishOptions.UNSET_LAST_SEQUENCE, po.getExpectedLastSubjectSequence()); + assertNull(po.getExpectedLastSubjectSequenceSubject()); + assertNull(po.getMessageId()); + + //noinspection deprecation + po = builder.stream(null).streamTimeout(null).build(); + //noinspection deprecation + assertNull(po.getStream()); + assertEquals(PublishOptions.DEFAULT_TIMEOUT, po.getStreamTimeout()); + + //noinspection deprecation + po = builder.stream("pubAckStream").build(); + //noinspection deprecation + assertEquals("pubAckStream", po.getStream()); + + //noinspection deprecation + po = builder.stream("").build(); + //noinspection deprecation + assertNull(po.getStream()); + } + + @Test + public void testProperties() { + String stream = random(); + Properties p = new Properties(); + p.setProperty(PublishOptions.PROP_PUBLISH_TIMEOUT, "PT20M"); + p.setProperty(PublishOptions.PROP_STREAM_NAME, stream); + PublishOptions po = new PublishOptions.Builder(p).build(); + //noinspection deprecation + assertEquals(stream, po.getStream(), "stream foo"); + assertEquals(Duration.ofMinutes(20), po.getStreamTimeout(), "20M timeout"); + + p = new Properties(); + po = new PublishOptions.Builder(p).build(); + //noinspection deprecation + assertNull(po.getStream()); + assertEquals(PublishOptions.DEFAULT_TIMEOUT, po.getStreamTimeout()); + } + + @Test + public void testMessageTtl() { + PublishOptions po = PublishOptions.builder().messageTtlSeconds(3).build(); + assertEquals("3s", po.getMessageTtl()); + + po = PublishOptions.builder().messageTtlCustom("abcd").build(); + assertEquals("abcd", po.getMessageTtl()); + + po = PublishOptions.builder().messageTtlNever().build(); + assertEquals("never", po.getMessageTtl()); + + po = PublishOptions.builder().messageTtl(MessageTtl.seconds(3)).build(); + assertEquals("3s", po.getMessageTtl()); + + po = PublishOptions.builder().messageTtl(MessageTtl.custom("abcd")).build(); + assertEquals("abcd", po.getMessageTtl()); + + po = PublishOptions.builder().messageTtl(MessageTtl.never()).build(); + assertEquals("never", po.getMessageTtl()); + + po = PublishOptions.builder().messageTtlSeconds(0).build(); + assertNull(po.getMessageTtl()); + + po = PublishOptions.builder().messageTtlSeconds(-1).build(); + assertNull(po.getMessageTtl()); + + po = PublishOptions.builder().messageTtlCustom(null).build(); + assertNull(po.getMessageTtl()); + + po = PublishOptions.builder().messageTtlCustom("").build(); + assertNull(po.getMessageTtl()); + + po = PublishOptions.builder().messageTtl(null).build(); + assertNull(po.getMessageTtl()); + + assertThrows(IllegalArgumentException.class, () -> MessageTtl.seconds(0)); + assertThrows(IllegalArgumentException.class, () -> MessageTtl.seconds(-1)); + assertThrows(IllegalArgumentException.class, () -> MessageTtl.custom(null)); + assertThrows(IllegalArgumentException.class, () -> MessageTtl.custom("")); + + assertTrue(MessageTtl.seconds(3).toString().contains("3s")); // COVERAGE + } +} diff --git a/src/test/java/io/nats/client/PublishTests.java b/src/test/java/io/nats/client/PublishTests.java index 271c3bc1c..a0e1a1071 100644 --- a/src/test/java/io/nats/client/PublishTests.java +++ b/src/test/java/io/nats/client/PublishTests.java @@ -1,336 +1,328 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.nats.client; - -import io.nats.client.api.StreamConfiguration; -import io.nats.client.impl.Headers; -import io.nats.client.impl.NatsMessage; -import org.junit.jupiter.api.Test; - -import java.net.SocketException; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import static io.nats.client.support.NatsConstants.*; -import static io.nats.client.utils.ResourceUtils.dataAsLines; -import static io.nats.client.utils.TestBase.*; -import static org.junit.jupiter.api.Assertions.*; - -public class PublishTests { - @Test - public void throwsIfClosedOnPublish() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - nc.close(); - nc.publish("subject", "replyto", null); - fail(); - } - }); - } - - @Test - public void throwsIfClosedOnFlush() { - assertThrows(TimeoutException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - nc.close(); - nc.flush(null); - fail(); - } - }); - } - - @Test - public void testThrowsWithoutSubject() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - nc.publish(null, null); - fail(); - } - }); - } - - @Test - public void testThrowsIfTooBig() throws Exception { - try (NatsTestServer ts = new NatsTestServer("src/test/resources/max_payload.conf", false, false)) - { - Connection nc = Nats.connect(ts.getURI()); - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - - byte[] body = new byte[1001]; - assertThrows(IllegalArgumentException.class, () -> nc.publish("subject", null, null, body)); - nc.close(); - - AtomicBoolean mpv = new AtomicBoolean(false); - AtomicBoolean se = new AtomicBoolean(false); - ErrorListener el = new ErrorListener() { - @Override - public void errorOccurred(Connection conn, String error) { - mpv.set(error.contains("Maximum Payload Violation")); - } - - @Override - public void exceptionOccurred(Connection conn, Exception exp) { - se.set(exp instanceof SocketException); - } - }; - Options options = Options.builder() - .server(ts.getURI()) - .clientSideLimitChecks(false) - .errorListener(el) - .build(); - Connection nc2 = Nats.connect(options); - assertSame(Connection.Status.CONNECTED, nc2.getStatus(), "Connected Status"); - nc2.publish("subject", null, null, body); - - sleep(100); - assertTrue(mpv.get()); - assertTrue(se.get()); - } - } - - @Test - public void testThrowsIfheadersNotSupported() { - assertThrows(IllegalArgumentException.class, () -> { - String customInfo = "{\"server_id\":\"test\", \"version\":\"9.9.99\"}"; - - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(null, customInfo); - Connection nc = Nats.connect(ts.getURI())) { - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - - nc.publish(NatsMessage.builder() - .subject("testThrowsIfheadersNotSupported") - .headers(new Headers().add("key", "value")) - .build()); - fail(); - } - }); - } - - @Test - public void testEmptyPublish() throws Exception { - runSimplePublishTest("testsubemptybody", null, null, ""); - } - - @Test - public void testEmptyByDefaultPublish() throws Exception { - runSimplePublishTest("testsubemptybody", null, null, null); - } - - @Test - public void testNoReplyPublish() throws Exception { - runSimplePublishTest("testsub", null, null, "This is the message."); - } - - @Test - public void testReplyToInPublish() throws Exception { - runSimplePublishTest("testsubforreply", "replyTo", null, "This is the message to reply to."); - runSimplePublishTest("testsubforreply", "replyTo", new Headers().add("key", "value"), "This is the message to reply to."); - } - - private void runSimplePublishTest(String subject, String replyTo, Headers headers, String bodyString) - throws Exception { - CompletableFuture gotPub = new CompletableFuture<>(); - AtomicReference hdrProto = new AtomicReference<>(""); - AtomicReference body = new AtomicReference<>(""); - AtomicReference protocol = new AtomicReference<>(""); - - boolean hPub = headers != null && !headers.isEmpty(); - String proto = hPub ? OP_HPUB : OP_PUB; - int hdrlen = hPub ? headers.serializedLength() : 0; - - NatsServerProtocolMock.Customizer receiveMessageCustomizer = (ts, r,w) -> { - String pubLine; - StringBuilder headerLine; - String bodyLine; - - System.out.println("*** Mock Server @" + ts.getPort() + " waiting for " + proto + " ..."); - try { - pubLine = r.readLine(); - if (hPub) { - // the version \r\n, each header \r\n, then separator \r\n - headerLine = new StringBuilder(r.readLine()).append("\r\n"); - while (headerLine.length() < hdrlen) { - headerLine.append(r.readLine()).append("\r\n"); - } - } - else { - headerLine = new StringBuilder(); - } - bodyLine = r.readLine(); // Ignores encoding, but ok for test - } catch(Exception e) { - gotPub.cancel(true); - return; - } - - if (pubLine.startsWith(proto)) { - System.out.println("*** Mock Server @" + ts.getPort() + " got " + proto + " ..."); - protocol.set(pubLine); - hdrProto.set(headerLine.toString()); - body.set(bodyLine); - gotPub.complete(Boolean.TRUE); - } - }; - - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(receiveMessageCustomizer); - Connection nc = standardConnection(ts.getURI())) { - - byte[] bodyBytes; - if (bodyString == null || bodyString.isEmpty()) { - bodyBytes = EMPTY_BODY; - bodyString = ""; - } - else { - bodyBytes = bodyString.getBytes(StandardCharsets.UTF_8); - } - - nc.publish(NatsMessage.builder().subject(subject).replyTo(replyTo).headers(headers).data(bodyBytes).build()); - - assertTrue(gotPub.get(), "Got " + proto + "."); //wait for receipt to close up - standardCloseConnection(nc); - - if (proto.equals(OP_PUB)) { - String expectedProtocol; - if (replyTo == null) { - expectedProtocol = proto + " " + subject + " " + bodyBytes.length; - } else { - expectedProtocol = proto + " " + subject + " " + replyTo + " " + bodyBytes.length; - } - assertEquals(expectedProtocol, protocol.get(), "Protocol matches"); - assertEquals(bodyString, body.get(), "Body matches"); - } - else { - String expectedProtocol; - int hdrLen = headers.serializedLength(); - int totLen = hdrLen + bodyBytes.length; - if (replyTo == null) { - expectedProtocol = proto + " " + subject + " " + hdrLen + " " + totLen; - } else { - expectedProtocol = proto + " " + subject + " " + replyTo + " " + hdrLen + " " + totLen; - } - assertEquals(expectedProtocol, protocol.get(), "Protocol matches"); - assertEquals(bodyString, body.get(), "Body matches"); - assertEquals(new String(headers.getSerialized()), hdrProto.get()); - } - } - } - - @Test - public void testMaxPayload() throws Exception { - runInServer(standardOptionsBuilder().noReconnect(), nc -> { - int maxPayload = (int)nc.getServerInfo().getMaxPayload(); - nc.publish("mptest", new byte[maxPayload-1]); - nc.publish("mptest", new byte[maxPayload]); - }); - - try { - runInServer(standardOptionsBuilder().noReconnect().clientSideLimitChecks(false), nc -> { - int maxPayload = (int)nc.getServerInfo().getMaxPayload(); - for (int x = 1; x < 1000; x++) { - nc.publish("mptest", new byte[maxPayload + x]); - } - }); - fail("Expecting IllegalStateException"); - } - catch (IllegalStateException ignore) {} - - try { - runInServer(standardOptionsBuilder().noReconnect(), nc -> { - int maxPayload = (int)nc.getServerInfo().getMaxPayload(); - for (int x = 1; x < 1000; x++) { - nc.publish("mptest", new byte[maxPayload + x]); - } - }); - fail("Expecting IllegalArgumentException"); - } - catch (IllegalArgumentException ignore) {} - } - - @Test - public void testUtf8Subjects() throws Exception { - String subject = dataAsLines("utf8-test-strings.txt").get(0); - String jsSubject = variant() + "-" + subject; // just to have a different; - - AtomicReference coreReceivedSubjectNotSupported = new AtomicReference<>(); - AtomicReference coreReceivedSubjectWhenSupported = new AtomicReference<>(); - AtomicReference jsReceivedSubjectNotSupported = new AtomicReference<>(); - AtomicReference jsReceivedSubjectWhenSupported = new AtomicReference<>(); - CountDownLatch coreReceivedLatchNotSupported = new CountDownLatch(1); - CountDownLatch coreReceivedLatchWhenSupported = new CountDownLatch(1); - CountDownLatch jsReceivedLatchNotSupported = new CountDownLatch(1); - CountDownLatch jsReceivedLatchWhenSupported = new CountDownLatch(1); - - try (NatsTestServer ts = new NatsTestServer(false, true); - Connection ncNotSupported = standardConnection(standardOptionsBuilder(ts.getURI()).build()); - Connection ncSupported = standardConnection(standardOptionsBuilder(ts.getURI()).supportUTF8Subjects().build())) - { - try { - ncNotSupported.jetStreamManagement().addStream( - StreamConfiguration.builder() - .name(stream()) - .subjects(jsSubject) - .build()); - JetStream jsNotSupported = ncNotSupported.jetStream(); - JetStream jsSupported = ncNotSupported.jetStream(); - - Dispatcher dNotSupported = ncNotSupported.createDispatcher(); - Dispatcher dSupported = ncSupported.createDispatcher(); - - dNotSupported.subscribe(subject, m -> { - coreReceivedSubjectNotSupported.set(m.getSubject()); - coreReceivedLatchNotSupported.countDown(); - }); - - dSupported.subscribe(subject, m -> { - coreReceivedSubjectWhenSupported.set(m.getSubject()); - coreReceivedLatchWhenSupported.countDown(); - }); - - jsNotSupported.subscribe(jsSubject, dNotSupported, m -> { - jsReceivedSubjectNotSupported.set(m.getSubject()); - jsReceivedLatchNotSupported.countDown(); - }, false); - - jsSupported.subscribe(jsSubject, dSupported, m -> { - jsReceivedSubjectWhenSupported.set(m.getSubject()); - jsReceivedLatchWhenSupported.countDown(); - }, false); - - ncNotSupported.publish(subject, null); // demonstrates that publishing always does utf8 - jsSupported.publish(jsSubject, null); - - assertTrue(coreReceivedLatchNotSupported.await(1, TimeUnit.SECONDS)); - assertTrue(coreReceivedLatchWhenSupported.await(1, TimeUnit.SECONDS)); - assertTrue(jsReceivedLatchNotSupported.await(1, TimeUnit.SECONDS)); - assertTrue(jsReceivedLatchWhenSupported.await(1, TimeUnit.SECONDS)); - - assertNotEquals(subject, coreReceivedSubjectNotSupported.get()); - assertEquals(subject, coreReceivedSubjectWhenSupported.get()); - assertNotEquals(jsSubject, jsReceivedSubjectNotSupported.get()); - assertEquals(jsSubject, jsReceivedSubjectWhenSupported.get()); - - } - finally { - cleanupJs(ncSupported); - } - } - } -} +// Copyright 2015-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client; + +import io.nats.client.api.StreamConfiguration; +import io.nats.client.impl.Headers; +import io.nats.client.impl.NatsMessage; +import io.nats.client.utils.TestBase; +import org.junit.jupiter.api.Test; + +import java.net.SocketException; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; + +import static io.nats.client.support.NatsConstants.*; +import static io.nats.client.utils.ConnectionUtils.*; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.ResourceUtils.dataAsLines; +import static io.nats.client.utils.TestBase.*; +import static org.junit.jupiter.api.Assertions.*; + +public class PublishTests { + @Test + public void throwsIfClosed() throws Exception { + runInLrServerCloseableConnection(nc -> { + nc.close(); + // can't publish after close + assertThrows(IllegalStateException.class, () -> nc.publish("subject", "replyto", null)); + + // flush after close always times out + assertThrows(TimeoutException.class, () -> nc.flush(null)); + }); + } + + @Test + public void testThrowsWithoutSubject() throws Exception { + runInLrServer(nc -> { + //noinspection DataFlowIssue + assertThrows(IllegalArgumentException.class, () -> nc.publish(null, null)); + }); + } + + @Test + public void testThrowsIfTooBig() throws Exception { + try (NatsTestServer ts = NatsTestServer.configuredServer("max_payload.conf")) { + Connection nc = standardConnectionWait(ts.getURI()); + + byte[] body = new byte[1024]; // 1024 is > than max_payload.conf max_payload: 1000 + assertThrows(IllegalArgumentException.class, () -> nc.publish(random(), null, null, body)); + nc.close(); + Thread.sleep(1000); + + CountDownLatch mpvLatch = new CountDownLatch(1); + CountDownLatch seLatch = new CountDownLatch(1); + ErrorListener el = new ErrorListener() { + @Override + public void errorOccurred(Connection conn, String error) { + if (error.contains("Maximum Payload Violation")) { + mpvLatch.countDown(); + } + } + + @Override + public void exceptionOccurred(Connection conn, Exception exp) { + if (exp instanceof SocketException) { + seLatch.countDown(); + } + } + }; + Options options = optionsBuilder(ts) + .clientSideLimitChecks(false) + .errorListener(el) + .build(); + Connection nc2 = longConnectionWait(options); + nc2.publish(random(), null, null, body); + + if (!mpvLatch.await(1, TimeUnit.SECONDS)) { + fail(); + } + if (!seLatch.await(1, TimeUnit.SECONDS)) { + fail(); + } + } + } + + @Test + public void testThrowsIfHeadersNotSupported() { + assertThrows(IllegalArgumentException.class, () -> { + String customInfo = "{\"server_id\":\"test\", \"version\":\"9.9.99\"}"; + + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(null, customInfo); + Connection nc = Nats.connect(ts.getURI())) + { + assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); + + nc.publish(NatsMessage.builder() + .subject("testThrowsIfheadersNotSupported") + .headers(new Headers().add("key", "value")) + .build()); + fail(); + } + }); + } + + @Test + public void testEmptyPublish() throws Exception { + runSimplePublishTest("testsubemptybody", null, null, ""); + } + + @Test + public void testEmptyByDefaultPublish() throws Exception { + runSimplePublishTest("testsubemptybody", null, null, null); + } + + @Test + public void testNoReplyPublish() throws Exception { + runSimplePublishTest("testsub", null, null, "This is the message."); + } + + @Test + public void testReplyToInPublish() throws Exception { + runSimplePublishTest("testsubforreply", "replyTo", null, "This is the message to reply to."); + runSimplePublishTest("testsubforreply", "replyTo", new Headers().add("key", "value"), "This is the message to reply to."); + } + + private void runSimplePublishTest(String subject, String replyTo, Headers headers, String bodyString) + throws Exception { + CompletableFuture gotPub = new CompletableFuture<>(); + AtomicReference hdrProto = new AtomicReference<>(""); + AtomicReference body = new AtomicReference<>(""); + AtomicReference protocol = new AtomicReference<>(""); + + boolean hPub = headers != null && !headers.isEmpty(); + String proto = hPub ? OP_HPUB : OP_PUB; + int hdrlen = hPub ? headers.serializedLength() : 0; + + NatsServerProtocolMock.Customizer receiveMessageCustomizer = (ts, r,w) -> { + String pubLine; + StringBuilder headerLine; + String bodyLine; + + System.out.println("*** Mock Server @" + ts.getPort() + " waiting for " + proto + " ..."); + try { + pubLine = r.readLine(); + if (hPub) { + // the version \r\n, each header \r\n, then separator \r\n + headerLine = new StringBuilder(r.readLine()).append("\r\n"); + while (headerLine.length() < hdrlen) { + headerLine.append(r.readLine()).append("\r\n"); + } + } + else { + headerLine = new StringBuilder(); + } + bodyLine = r.readLine(); // Ignores encoding, but ok for test + } catch(Exception e) { + gotPub.cancel(true); + return; + } + + if (pubLine.startsWith(proto)) { + System.out.println("*** Mock Server @" + ts.getPort() + " got " + proto + " ..."); + protocol.set(pubLine); + hdrProto.set(headerLine.toString()); + body.set(bodyLine); + gotPub.complete(Boolean.TRUE); + } + }; + + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(receiveMessageCustomizer); + Connection nc = standardConnectionWait(ts.getURI())) { + + byte[] bodyBytes; + if (bodyString == null || bodyString.isEmpty()) { + bodyBytes = EMPTY_BODY; + bodyString = ""; + } + else { + bodyBytes = bodyString.getBytes(StandardCharsets.UTF_8); + } + + nc.publish(NatsMessage.builder().subject(subject).replyTo(replyTo).headers(headers).data(bodyBytes).build()); + + assertTrue(gotPub.get(), "Got " + proto + "."); //wait for receipt to close up + standardCloseConnection(nc); + + if (proto.equals(OP_PUB)) { + String expectedProtocol; + if (replyTo == null) { + expectedProtocol = proto + " " + subject + " " + bodyBytes.length; + } else { + expectedProtocol = proto + " " + subject + " " + replyTo + " " + bodyBytes.length; + } + assertEquals(expectedProtocol, protocol.get(), "Protocol matches"); + assertEquals(bodyString, body.get(), "Body matches"); + } + else { + String expectedProtocol; + int hdrLen = headers.serializedLength(); + int totLen = hdrLen + bodyBytes.length; + if (replyTo == null) { + expectedProtocol = proto + " " + subject + " " + hdrLen + " " + totLen; + } else { + expectedProtocol = proto + " " + subject + " " + replyTo + " " + hdrLen + " " + totLen; + } + assertEquals(expectedProtocol, protocol.get(), "Protocol matches"); + assertEquals(bodyString, body.get(), "Body matches"); + assertEquals(new String(headers.getSerialized()), hdrProto.get()); + } + } + } + + @Test + public void testMaxPayload() throws Exception { + runInServer(standardOptionsBuilder().noReconnect(), nc -> { + int maxPayload = (int)nc.getServerInfo().getMaxPayload(); + nc.publish("mptest", new byte[maxPayload-1]); + nc.publish("mptest", new byte[maxPayload]); + }); + + try { + runInServer(standardOptionsBuilder().noReconnect().clientSideLimitChecks(false), nc -> { + int maxPayload = (int)nc.getServerInfo().getMaxPayload(); + for (int x = 1; x < 1000; x++) { + nc.publish("mptest", new byte[maxPayload + x]); + } + }); + fail("Expecting IllegalStateException"); + } + catch (IllegalStateException ignore) {} + + try { + runInServer(standardOptionsBuilder().noReconnect(), nc -> { + int maxPayload = (int)nc.getServerInfo().getMaxPayload(); + for (int x = 1; x < 1000; x++) { + nc.publish("mptest", new byte[maxPayload + x]); + } + }); + fail("Expecting IllegalArgumentException"); + } + catch (IllegalArgumentException ignore) {} + } + + @Test + public void testUtf8Subjects() throws Exception { + String subject = dataAsLines("utf8-test-strings.txt").get(0); + String jsSubject = random() + "-" + subject; // just to have a different; + + AtomicReference coreReceivedSubjectNotSupported = new AtomicReference<>(); + AtomicReference coreReceivedSubjectWhenSupported = new AtomicReference<>(); + AtomicReference jsReceivedSubjectNotSupported = new AtomicReference<>(); + AtomicReference jsReceivedSubjectWhenSupported = new AtomicReference<>(); + CountDownLatch coreReceivedLatchNotSupported = new CountDownLatch(1); + CountDownLatch coreReceivedLatchWhenSupported = new CountDownLatch(1); + CountDownLatch jsReceivedLatchNotSupported = new CountDownLatch(1); + CountDownLatch jsReceivedLatchWhenSupported = new CountDownLatch(1); + + try (NatsTestServer ts = new NatsTestServer(false, true); + Connection ncNotSupported = standardConnectionWait(standardOptionsBuilder(ts.getURI()).build()); + Connection ncSupported = standardConnectionWait(standardOptionsBuilder(ts.getURI()).supportUTF8Subjects().build())) + { + try { + ncNotSupported.jetStreamManagement().addStream( + StreamConfiguration.builder() + .name(TestBase.random()) + .subjects(jsSubject) + .build()); + JetStream jsNotSupported = ncNotSupported.jetStream(); + JetStream jsSupported = ncNotSupported.jetStream(); + + Dispatcher dNotSupported = ncNotSupported.createDispatcher(); + Dispatcher dSupported = ncSupported.createDispatcher(); + + dNotSupported.subscribe(subject, m -> { + coreReceivedSubjectNotSupported.set(m.getSubject()); + coreReceivedLatchNotSupported.countDown(); + }); + + dSupported.subscribe(subject, m -> { + coreReceivedSubjectWhenSupported.set(m.getSubject()); + coreReceivedLatchWhenSupported.countDown(); + }); + + jsNotSupported.subscribe(jsSubject, dNotSupported, m -> { + jsReceivedSubjectNotSupported.set(m.getSubject()); + jsReceivedLatchNotSupported.countDown(); + }, false); + + jsSupported.subscribe(jsSubject, dSupported, m -> { + jsReceivedSubjectWhenSupported.set(m.getSubject()); + jsReceivedLatchWhenSupported.countDown(); + }, false); + + ncNotSupported.publish(subject, null); // demonstrates that publishing always does utf8 + jsSupported.publish(jsSubject, null); + + assertTrue(coreReceivedLatchNotSupported.await(1, TimeUnit.SECONDS)); + assertTrue(coreReceivedLatchWhenSupported.await(1, TimeUnit.SECONDS)); + assertTrue(jsReceivedLatchNotSupported.await(1, TimeUnit.SECONDS)); + assertTrue(jsReceivedLatchWhenSupported.await(1, TimeUnit.SECONDS)); + + assertNotEquals(subject, coreReceivedSubjectNotSupported.get()); + assertEquals(subject, coreReceivedSubjectWhenSupported.get()); + assertNotEquals(jsSubject, jsReceivedSubjectNotSupported.get()); + assertEquals(jsSubject, jsReceivedSubjectWhenSupported.get()); + + } + finally { + cleanupJs(ncSupported); + } + } + } +} diff --git a/src/test/java/io/nats/client/RequestTests.java b/src/test/java/io/nats/client/RequestTests.java deleted file mode 100644 index 639e0b2a3..000000000 --- a/src/test/java/io/nats/client/RequestTests.java +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2020 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.nats.client; - -import io.nats.client.api.StorageType; -import io.nats.client.api.StreamConfiguration; -import io.nats.client.impl.ListenerForTesting; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; - -import static io.nats.client.utils.TestBase.standardConnection; -import static io.nats.client.utils.TestBase.subject; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class RequestTests { - - @Test - public void testRequestNoResponder() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false, true)) { - Options optCancel = Options.builder().server(ts.getURI()).errorListener(new ListenerForTesting()).build(); - Options optReport = Options.builder().server(ts.getURI()).reportNoResponders().errorListener(new ListenerForTesting()).build(); - try (Connection ncCancel = standardConnection(optCancel); - Connection ncReport = standardConnection(optReport); - ) - { - assertThrows(CancellationException.class, () -> ncCancel.request(subject(999), null).get()); - ExecutionException ee = assertThrows(ExecutionException.class, () -> ncReport.request(subject(999), null).get()); - assertTrue(ee.getCause() instanceof JetStreamStatusException); - assertTrue(ee.getMessage().contains("503 No Responders Available For Request")); - - ncCancel.jetStreamManagement().addStream( - StreamConfiguration.builder() - .name("testRequestNoResponder").subjects("trnrExists").storageType(StorageType.Memory) - .build()); - - JetStream jsCancel = ncCancel.jetStream(); - JetStream jsReport = ncReport.jetStream(); - - IOException ioe = assertThrows(IOException.class, () -> jsCancel.publish("not-exist", null)); - assertTrue(ioe.getMessage().contains("503")); - ioe = assertThrows(IOException.class, () -> jsReport.publish("trnrNotExist", null)); - assertTrue(ioe.getMessage().contains("503")); - } - } - } -} \ No newline at end of file diff --git a/src/test/java/io/nats/client/SubscribeOptionsTests.java b/src/test/java/io/nats/client/SubscribeOptionsTests.java index 3ca4b61a8..eb19d829a 100644 --- a/src/test/java/io/nats/client/SubscribeOptionsTests.java +++ b/src/test/java/io/nats/client/SubscribeOptionsTests.java @@ -36,13 +36,16 @@ public void testPushAffirmative() { assertNull(so.getName()); assertNull(so.getDeliverSubject()); + String stream = random(); + String durable = random(); + String deliver = random(); so = PushSubscribeOptions.builder() - .stream(STREAM).durable(DURABLE).deliverSubject(DELIVER).build(); + .stream(stream).durable(durable).deliverSubject(deliver).build(); - assertEquals(STREAM, so.getStream()); - assertEquals(DURABLE, so.getDurable()); - assertEquals(DURABLE, so.getName()); - assertEquals(DELIVER, so.getDeliverSubject()); + assertEquals(stream, so.getStream()); + assertEquals(durable, so.getDurable()); + assertEquals(durable, so.getName()); + assertEquals(deliver, so.getDeliverSubject()); // demonstrate that you can clear the builder so = PushSubscribeOptions.builder() @@ -187,14 +190,16 @@ public void testDeliverSubjectValidation() { @Test public void testPullAffirmative() { + String stream = random(); + String durable = random(); PullSubscribeOptions.Builder builder = PullSubscribeOptions.builder() - .stream(STREAM) - .durable(DURABLE); + .stream(stream) + .durable(durable); PullSubscribeOptions so = builder.build(); - assertEquals(STREAM, so.getStream()); - assertEquals(DURABLE, so.getDurable()); - assertEquals(DURABLE, so.getName()); + assertEquals(stream, so.getStream()); + assertEquals(durable, so.getDurable()); + assertEquals(durable, so.getName()); assertTrue(so.isPull()); assertNotNull(so.toString()); // COVERAGE @@ -211,29 +216,31 @@ public void testPushFieldValidation() { assertThrows(IllegalArgumentException.class, () -> ConsumerConfiguration.builder().name(bad).build()); } - assertClientError(JsConsumerNameDurableMismatch, () -> PushSubscribeOptions.builder().name(NAME).durable(DURABLE).build()); + String name = random(); + String durable = random(); + assertClientError(JsConsumerNameDurableMismatch, () -> PushSubscribeOptions.builder().name(name).durable(durable).build()); // durable directly - PushSubscribeOptions uso = PushSubscribeOptions.builder().durable(DURABLE).build(); - assertEquals(DURABLE, uso.getDurable()); - assertEquals(DURABLE, uso.getName()); - uso = PushSubscribeOptions.builder().name(NAME).build(); + PushSubscribeOptions uso = PushSubscribeOptions.builder().durable(durable).build(); + assertEquals(durable, uso.getDurable()); + assertEquals(durable, uso.getName()); + uso = PushSubscribeOptions.builder().name(name).build(); assertNull(uso.getDurable()); - assertEquals(NAME, uso.getName()); + assertEquals(name, uso.getName()); // in configuration - ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(DURABLE).build(); + ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(durable).build(); uso = PushSubscribeOptions.builder().configuration(cc).build(); - assertEquals(DURABLE, uso.getDurable()); - assertEquals(DURABLE, uso.getName()); - cc = ConsumerConfiguration.builder().name(NAME).build(); + assertEquals(durable, uso.getDurable()); + assertEquals(durable, uso.getName()); + cc = ConsumerConfiguration.builder().name(name).build(); uso = PushSubscribeOptions.builder().configuration(cc).build(); assertNull(uso.getDurable()); - assertEquals(NAME, uso.getName()); + assertEquals(name, uso.getName()); // new helper - ConsumerConfiguration.builder().durable(DURABLE).buildPushSubscribeOptions(); - ConsumerConfiguration.builder().name(NAME).buildPushSubscribeOptions(); + ConsumerConfiguration.builder().durable(durable).buildPushSubscribeOptions(); + ConsumerConfiguration.builder().name(name).buildPushSubscribeOptions(); } @Test @@ -247,87 +254,94 @@ public void testPullFieldValidation() { assertThrows(IllegalArgumentException.class, () -> ConsumerConfiguration.builder().name(bad).build()); } - assertClientError(JsConsumerNameDurableMismatch, () -> PullSubscribeOptions.builder().name(NAME).durable(DURABLE).build()); + String durable = random(); + assertClientError(JsConsumerNameDurableMismatch, () -> PullSubscribeOptions.builder().name(random()).durable(durable).build()); // durable directly - PullSubscribeOptions lso = PullSubscribeOptions.builder().durable(DURABLE).build(); - assertEquals(DURABLE, lso.getDurable()); - assertEquals(DURABLE, lso.getName()); + PullSubscribeOptions lso = PullSubscribeOptions.builder().durable(durable).build(); + assertEquals(durable, lso.getDurable()); + assertEquals(durable, lso.getName()); // in configuration - ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(DURABLE).build(); + ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(durable).build(); lso = PullSubscribeOptions.builder().configuration(cc).build(); - assertEquals(DURABLE, lso.getDurable()); - assertEquals(DURABLE, lso.getName()); + assertEquals(durable, lso.getDurable()); + assertEquals(durable, lso.getName()); // new helper - lso = ConsumerConfiguration.builder().durable(DURABLE).buildPullSubscribeOptions(); - assertEquals(DURABLE, lso.getDurable()); - assertEquals(DURABLE, lso.getName()); + lso = ConsumerConfiguration.builder().durable(durable).buildPullSubscribeOptions(); + assertEquals(durable, lso.getDurable()); + assertEquals(durable, lso.getName()); } @Test public void testCreationErrors() { - ConsumerConfiguration cc1 = ConsumerConfiguration.builder().durable(durable((1))).build(); + String durable1 = random(); + String durable2 = random(); + ConsumerConfiguration cc1 = ConsumerConfiguration.builder().durable(durable1).build(); assertClientError(JsSoDurableMismatch, - () -> PushSubscribeOptions.builder().configuration(cc1).durable(durable(2)).build()); + () -> PushSubscribeOptions.builder().configuration(cc1).durable(durable2).build()); - ConsumerConfiguration cc2 = ConsumerConfiguration.builder().deliverGroup(deliver((1))).build(); + String deliver1 = random(); + String deliver2 = random(); + ConsumerConfiguration cc2 = ConsumerConfiguration.builder().deliverGroup(deliver1).build(); assertClientError(JsSoDeliverGroupMismatch, - () -> PushSubscribeOptions.builder().configuration(cc2).deliverGroup(deliver(2)).build()); + () -> PushSubscribeOptions.builder().configuration(cc2).deliverGroup(deliver2).build()); - ConsumerConfiguration cc3 = ConsumerConfiguration.builder().name(name((1))).build(); + String name1 = random(); + String name2 = random(); + ConsumerConfiguration cc3 = ConsumerConfiguration.builder().name(name1).build(); assertClientError(JsSoNameMismatch, - () -> PushSubscribeOptions.builder().configuration(cc3).name(name(2)).build()); + () -> PushSubscribeOptions.builder().configuration(cc3).name(name2).build()); } @Test public void testBindCreationErrors() { - String durOrName = name(); + String random = random(); // bind - assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.bind(null, durOrName)); - assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.bind(EMPTY, durOrName)); - assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.bind(STREAM, null)); - assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.bind(STREAM, EMPTY)); - assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().stream(STREAM).bind(true).build()); - - assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().stream(EMPTY).durable(durOrName).bind(true).build()); - assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().durable(durOrName).bind(true).build()); - assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().stream(STREAM).durable(EMPTY).bind(true).build()); - - assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().stream(EMPTY).name(durOrName).bind(true).build()); - assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().name(durOrName).bind(true).build()); - assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().stream(STREAM).name(EMPTY).bind(true).build()); - - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.bind(null, durOrName)); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.bind(EMPTY, durOrName)); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.bind(STREAM, null)); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.bind(STREAM, EMPTY)); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(STREAM).bind(true).build()); - - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(EMPTY).durable(durOrName).bind(true).build()); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().durable(durOrName).bind(true).build()); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(STREAM).durable(EMPTY).bind(true).build()); - - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(EMPTY).name(durOrName).bind(true).build()); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().name(durOrName).bind(true).build()); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(STREAM).name(EMPTY).bind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.bind(null, random)); + assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.bind(EMPTY, random)); + assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.bind(random, null)); + assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.bind(random, EMPTY)); + assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().stream(random).bind(true).build()); + + assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().stream(EMPTY).durable(random).bind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().durable(random).bind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().stream(random).durable(EMPTY).bind(true).build()); + + assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().stream(EMPTY).name(random).bind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().name(random).bind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().stream(random).name(EMPTY).bind(true).build()); + + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.bind(null, random)); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.bind(EMPTY, random)); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.bind(random, null)); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.bind(random, EMPTY)); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(random).bind(true).build()); + + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(EMPTY).durable(random).bind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().durable(random).bind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(random).durable(EMPTY).bind(true).build()); + + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(EMPTY).name(random).bind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().name(random).bind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(random).name(EMPTY).bind(true).build()); // fast bind - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.fastBind(null, durOrName)); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.fastBind(EMPTY, durOrName)); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.fastBind(STREAM, null)); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.fastBind(STREAM, EMPTY)); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(STREAM).fastBind(true).build()); - - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(EMPTY).durable(durOrName).fastBind(true).build()); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().durable(durOrName).fastBind(true).build()); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(STREAM).durable(EMPTY).fastBind(true).build()); - - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(EMPTY).name(durOrName).fastBind(true).build()); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().name(durOrName).fastBind(true).build()); - assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(STREAM).name(EMPTY).fastBind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.fastBind(null, random)); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.fastBind(EMPTY, random)); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.fastBind(random, null)); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.fastBind(random, EMPTY)); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(random).fastBind(true).build()); + + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(EMPTY).durable(random).fastBind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().durable(random).fastBind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(random).durable(EMPTY).fastBind(true).build()); + + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(EMPTY).name(random).fastBind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().name(random).fastBind(true).build()); + assertThrows(IllegalArgumentException.class, () -> PullSubscribeOptions.builder().stream(random).name(EMPTY).fastBind(true).build()); } @Test @@ -335,17 +349,18 @@ public void testOrderedCreation() { ConsumerConfiguration ccEmpty = ConsumerConfiguration.builder().build(); PushSubscribeOptions.builder().configuration(ccEmpty).ordered(true).build(); + String random = random(); assertClientError(JsSoOrderedNotAllowedWithBind, - () -> PushSubscribeOptions.builder().stream(STREAM).bind(true).ordered(true).build()); + () -> PushSubscribeOptions.builder().stream(random).bind(true).ordered(true).build()); assertClientError(JsSoOrderedNotAllowedWithDeliverGroup, - () -> PushSubscribeOptions.builder().deliverGroup(DELIVER).ordered(true).build()); + () -> PushSubscribeOptions.builder().deliverGroup(random).ordered(true).build()); assertClientError(JsSoOrderedNotAllowedWithDurable, - () -> PushSubscribeOptions.builder().durable(DURABLE).ordered(true).build()); + () -> PushSubscribeOptions.builder().durable(random).ordered(true).build()); assertClientError(JsSoOrderedNotAllowedWithDeliverSubject, - () -> PushSubscribeOptions.builder().deliverSubject(DELIVER).ordered(true).build()); + () -> PushSubscribeOptions.builder().deliverSubject(random).ordered(true).build()); ConsumerConfiguration ccAckNotNone1 = ConsumerConfiguration.builder().ackPolicy(AckPolicy.All).build(); assertClientError(JsSoOrderedRequiresAckPolicyNone, diff --git a/src/test/java/io/nats/client/SubscriberTests.java b/src/test/java/io/nats/client/SubscriberTests.java index da184c887..bdb4c98b0 100644 --- a/src/test/java/io/nats/client/SubscriberTests.java +++ b/src/test/java/io/nats/client/SubscriberTests.java @@ -18,65 +18,60 @@ import java.time.Duration; import java.util.HashSet; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeoutException; -import static io.nats.client.utils.TestBase.*; +import static io.nats.client.utils.ConnectionUtils.standardCloseConnection; +import static io.nats.client.utils.ConnectionUtils.standardConnectionWait; +import static io.nats.client.utils.TestBase.random; +import static io.nats.client.utils.TestBase.runInLrServer; import static org.junit.jupiter.api.Assertions.*; public class SubscriberTests { @Test public void testCreateInbox() throws Exception { - HashSet check = new HashSet<>(); - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - - for (int i=0; i < 10_000; i++) { + runInLrServer(nc -> { + HashSet check = new HashSet<>(); + for (int i=0; i < 100; i++) { String inbox = nc.createInbox(); assertFalse(check.contains(inbox)); check.add(inbox); } - } + }); } @Test public void testSingleMessage() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - - Subscription sub = nc.subscribe("subject"); - nc.publish("subject", new byte[16]); + runInLrServer(nc -> { + String subject = random(); + Subscription sub = nc.subscribe(subject); + nc.publish(subject, new byte[16]); Message msg = sub.nextMessage(Duration.ofMillis(500)); assertTrue(sub.isActive()); - assertEquals("subject", msg.getSubject()); + assertEquals(subject, msg.getSubject()); assertEquals(sub, msg.getSubscription()); assertNull(msg.getReplyTo()); assertEquals(16, msg.getData().length); - } + }); } @Test public void testMessageFromSubscriptionContainsConnection() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - - Subscription sub = nc.subscribe("subject"); - nc.publish("subject", new byte[16]); + runInLrServer(nc -> { + String subject = random(); + Subscription sub = nc.subscribe(subject); + nc.publish(subject, new byte[16]); Message msg = sub.nextMessage(Duration.ofMillis(500)); assertTrue(sub.isActive()); - assertEquals("subject", msg.getSubject()); + assertEquals(subject, msg.getSubject()); assertEquals(sub, msg.getSubscription()); assertNull(msg.getReplyTo()); assertEquals(16, msg.getData().length); assertSame(msg.getConnection(), nc); - } + }); } @Test @@ -85,7 +80,7 @@ public void testTabInProtocolLine() throws Exception { CompletableFuture sendMsg = new CompletableFuture<>(); NatsServerProtocolMock.Customizer receiveMessageCustomizer = (ts, r,w) -> { - String subLine = ""; + String subLine; // System.out.println("*** Mock Server @" + ts.getPort() + " waiting for SUB ..."); try { @@ -114,10 +109,10 @@ public void testTabInProtocolLine() throws Exception { }; try (NatsServerProtocolMock ts = new NatsServerProtocolMock(receiveMessageCustomizer); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); + Connection nc = standardConnectionWait(ts.getURI())) { - Subscription sub = nc.subscribe("subject"); + String subject = random(); + Subscription sub = nc.subscribe(subject); gotSub.get(); sendMsg.complete(Boolean.TRUE); @@ -126,7 +121,7 @@ public void testTabInProtocolLine() throws Exception { assertTrue(sub.isActive()); assertNotNull(msg); - assertEquals("subject", msg.getSubject()); + assertEquals(subject, msg.getSubject()); assertEquals(sub, msg.getSubscription()); assertNull(msg.getReplyTo()); assertEquals(0, msg.getData().length); @@ -137,18 +132,16 @@ public void testTabInProtocolLine() throws Exception { @Test public void testMultiMessage() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - - Subscription sub = nc.subscribe("subject"); - nc.publish("subject", new byte[16]); - nc.publish("subject", new byte[16]); - nc.publish("subject", new byte[16]); + runInLrServer(nc -> { + String subject = random(); + Subscription sub = nc.subscribe(subject); + nc.publish(subject, new byte[16]); + nc.publish(subject, new byte[16]); + nc.publish(subject, new byte[16]); Message msg = sub.nextMessage(Duration.ofMillis(500)); - assertEquals("subject", msg.getSubject()); + assertEquals(subject, msg.getSubject()); assertEquals(sub, msg.getSubscription()); assertNull(msg.getReplyTo()); assertEquals(16, msg.getData().length); @@ -158,26 +151,25 @@ public void testMultiMessage() throws Exception { assertNotNull(msg); msg = sub.nextMessage(100); // coverage for nextMessage(millis) assertNull(msg); - } + }); } @Test - public void testQueueSubscribers() throws Exception, TimeoutException { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - + public void testQueueSubscribers() throws Exception { + runInLrServer(nc -> { int msgs = 100; int received = 0; int sub1Count = 0; int sub2Count = 0; Message msg; - Subscription sub1 = nc.subscribe("subject", "queue"); - Subscription sub2 = nc.subscribe("subject", "queue"); + String subject = random(); + String queue = random(); + Subscription sub1 = nc.subscribe(subject, queue); + Subscription sub2 = nc.subscribe(subject, queue); for (int i = 0; i < msgs; i++) { - nc.publish("subject", new byte[16]); + nc.publish(subject, new byte[16]); } nc.flush(Duration.ofMillis(200));// Get them all to the server @@ -186,7 +178,7 @@ public void testQueueSubscribers() throws Exception, TimeoutException { msg = sub1.nextMessage(null); if (msg != null) { - assertEquals("subject", msg.getSubject()); + assertEquals(subject, msg.getSubject()); assertNull(msg.getReplyTo()); assertEquals(16, msg.getData().length); received++; @@ -198,7 +190,7 @@ public void testQueueSubscribers() throws Exception, TimeoutException { msg = sub2.nextMessage(null); if (msg != null) { - assertEquals("subject", msg.getSubject()); + assertEquals(subject, msg.getSubject()); assertNull(msg.getReplyTo()); assertEquals(16, msg.getData().length); received++; @@ -208,299 +200,194 @@ public void testQueueSubscribers() throws Exception, TimeoutException { assertEquals(msgs, received); assertEquals(msgs, sub1Count + sub2Count); - - // System.out.println("### Sub 1 " + sub1Count); - // System.out.println("### Sub 2 " + sub2Count); - } - } - - @Test - public void testUnsubscribe() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - - Subscription sub = nc.subscribe("subject"); - nc.publish("subject", new byte[16]); - - Message msg = sub.nextMessage(Duration.ofMillis(500)); - assertNotNull(msg); - - sub.unsubscribe(); - assertFalse(sub.isActive()); - sub.nextMessage(Duration.ofMillis(500)); // Will throw an exception - } }); } @Test - public void testAutoUnsubscribe() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - - Subscription sub = nc.subscribe("subject").unsubscribe(1); - nc.publish("subject", new byte[16]); + public void testUnsubscribe() throws Exception { + runInLrServer(nc -> { + String subject = random(); + Subscription sub = nc.subscribe(subject); + nc.publish(subject, new byte[16]); - Message msg = sub.nextMessage(Duration.ofMillis(500)); // should get 1 - assertNotNull(msg); + Message msg = sub.nextMessage(Duration.ofMillis(500)); + assertNotNull(msg); - sub.nextMessage(Duration.ofMillis(500)); // Will throw an exception - } + sub.unsubscribe(); + assertFalse(sub.isActive()); + assertThrows(IllegalStateException.class, () -> sub.nextMessage(Duration.ofMillis(500))); }); } @Test - public void testMultiAutoUnsubscribe() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - - int msgCount = 10; - Subscription sub = nc.subscribe("subject").unsubscribe(msgCount); + public void testAutoUnsubscribe() throws Exception { + runInLrServer(nc -> { + String subject = random(); + Subscription sub = nc.subscribe(subject).unsubscribe(1); + nc.publish(subject, new byte[16]); - for (int i = 0; i < msgCount; i++) { - nc.publish("subject", new byte[16]); - } - - Message msg; - for (int i = 0; i < msgCount; i++) { - msg = sub.nextMessage(Duration.ofMillis(500)); // should get 1 - assertNotNull(msg); - } + Message msg = sub.nextMessage(Duration.ofMillis(500)); // should get 1 + assertNotNull(msg); - sub.nextMessage(Duration.ofMillis(500)); // Will throw an exception - } + assertThrows(IllegalStateException.class, () -> sub.nextMessage(Duration.ofMillis(500))); }); } @Test - public void testOnlyOneUnsubscribe() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - - Subscription sub = nc.subscribe("subject"); - - sub.unsubscribe(); - sub.unsubscribe(); // Will throw an exception + public void testMultiAutoUnsubscribe() throws Exception { + runInLrServer(nc -> { + String subject = random(); + int msgCount = 10; + Subscription sub = nc.subscribe(subject).unsubscribe(msgCount); + + for (int i = 0; i < msgCount; i++) { + nc.publish(subject, new byte[16]); } - }); - } - - @Test - public void testOnlyOneAutoUnsubscribe() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - - Subscription sub = nc.subscribe("subject").unsubscribe(1); - nc.publish("subject", new byte[16]); - Message msg = sub.nextMessage(Duration.ofMillis(500)); // should get 1 + Message msg; + for (int i = 0; i < msgCount; i++) { + msg = sub.nextMessage(Duration.ofMillis(500)); // should get 1 assertNotNull(msg); - - sub.unsubscribe(); // Will throw an exception } + + assertThrows(IllegalStateException.class, () -> sub.nextMessage(Duration.ofMillis(500))); }); } @Test - public void testUnsubscribeInAnotherThread() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); + public void testOnlyOneUnsubscribe() throws Exception { + runInLrServer(nc -> { + String subject = random(); + Subscription sub = nc.subscribe(subject); + sub.unsubscribe(); - Subscription sub = nc.subscribe("subject"); - - new Thread(sub::unsubscribe).start(); - - sub.nextMessage(Duration.ofMillis(5000)); // throw - } + assertThrows(IllegalStateException.class, sub::unsubscribe); }); } @Test - public void testAutoUnsubAfterMaxIsReached() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); + public void testOnlyOneAutoUnsubscribe() throws Exception { + runInLrServer(nc -> { + String subject = random(); - Subscription sub = nc.subscribe("subject"); - - int msgCount = 10; - for (int i = 0; i < msgCount; i++) { - nc.publish("subject", new byte[16]); - } - - nc.flush(Duration.ofMillis(1000)); // Slow things down so we have time to unsub - - for (int i = 0; i < msgCount; i++) { - sub.nextMessage(null); - } + Subscription sub = nc.subscribe(subject).unsubscribe(1); + nc.publish(subject, new byte[16]); - sub.unsubscribe(msgCount); // we already have that many + Message msg = sub.nextMessage(Duration.ofMillis(500)); // should get 1 + assertNotNull(msg); - sub.nextMessage(Duration.ofMillis(500)); // Will throw an exception - } + assertThrows(IllegalStateException.class, sub::unsubscribe); }); } @Test - public void testThrowOnNullSubject() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - nc.subscribe(null); - } + public void testUnsubscribeInAnotherThread() throws Exception { + runInLrServer(nc -> { + String subject = random(); + Subscription sub = nc.subscribe(subject); + new Thread(sub::unsubscribe).start(); + assertThrows(IllegalStateException.class, () -> sub.nextMessage(Duration.ofMillis(5000))); }); } @Test - public void testThrowOnEmptySubject() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - nc.subscribe(""); + public void testAutoUnsubAfterMaxIsReached() throws Exception { + runInLrServer(nc -> { + String subject = random(); + Subscription sub = nc.subscribe(subject); + + int msgCount = 10; + for (int i = 0; i < msgCount; i++) { + nc.publish(subject, new byte[16]); } - }); - } - @Test - public void testThrowOnNullQueue() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - nc.subscribe("subject", null); + nc.flush(Duration.ofMillis(1000)); // Slow things down so we have time to unsub + + for (int i = 0; i < msgCount; i++) { + sub.nextMessage(null); } + + sub.unsubscribe(msgCount); // we already have that many + + assertThrows(IllegalStateException.class, () -> sub.nextMessage(Duration.ofMillis(5000))); }); } @Test - public void testThrowOnEmptyQueue() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - nc.subscribe("subject", ""); - } + public void testThrowOnNullSubject() throws Exception { + runInLrServer(nc -> { + //noinspection DataFlowIssue + assertThrows(IllegalArgumentException.class, () -> nc.subscribe(null)); }); } @Test - public void testThrowOnNullSubjectWithQueue() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - nc.subscribe(null, "quque"); - } - }); + public void testThrowOnEmptySubject() throws Exception { + runInLrServer(nc -> assertThrows(IllegalArgumentException.class, () -> nc.subscribe(""))); } @Test - public void testThrowOnEmptySubjectWithQueue() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - nc.subscribe("", "quque"); - } + public void testThrowOnNullQueue() throws Exception { + runInLrServer(nc -> { + //noinspection DataFlowIssue + assertThrows(IllegalArgumentException.class, () -> nc.subscribe(random(), null)); }); } @Test - public void throwsOnSubscribeIfClosed() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - nc.close(); - nc.subscribe("subject"); - } - }); + public void testThrowOnEmptyQueue() throws Exception { + runInLrServer(nc -> assertThrows(IllegalArgumentException.class, () -> nc.subscribe(random(), ""))); } @Test - public void throwsOnUnsubscribeIfClosed() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Subscription sub = nc.subscribe("subject"); - nc.close(); - sub.unsubscribe(); - } + public void testThrowOnNullSubjectWithQueue() throws Exception { + runInLrServer(nc -> { + //noinspection DataFlowIssue + assertThrows(IllegalArgumentException.class, () -> nc.subscribe(null, random())); }); } @Test - public void throwsOnAutoUnsubscribeIfClosed() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - Subscription sub = nc.subscribe("subject"); - nc.close(); - sub.unsubscribe(1); - } - }); + public void testThrowOnEmptySubjectWithQueue() throws Exception { + runInLrServer(nc -> assertThrows(IllegalArgumentException.class, () -> nc.subscribe("", random()))); } @Test - public void testUnsubscribeWhileWaiting() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); + public void throwsSubscriptionExceptions() throws Exception { + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = standardConnectionWait(ts.getURI())) { + String subject = random(); + Subscription sub = nc.subscribe(subject); - Subscription sub = nc.subscribe("subject"); - nc.flush(Duration.ofMillis(1000)); + nc.close(); - new Thread(()->{ - try { Thread.sleep(100); } catch(Exception e) { /* ignored */ } - sub.unsubscribe(); - }).start(); + /// can't subscribe when closed + assertThrows(IllegalStateException.class, () -> nc.subscribe(random())); - sub.nextMessage(Duration.ofMillis(5000)); // Should throw - } - }); - } + /// can't unsubscribe when closed + assertThrows(IllegalStateException.class, sub::unsubscribe); - @Test - public void testInvalidSubjectsAndQueueNames() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - Dispatcher d = nc.createDispatcher(m -> {}); - for (String bad : BAD_SUBJECTS_OR_QUEUES) { - assertThrows(IllegalArgumentException.class, () -> nc.subscribe(bad)); - assertThrows(IllegalArgumentException.class, () -> d.subscribe(bad)); - assertThrows(IllegalArgumentException.class, () -> d.subscribe(bad, m -> {})); - assertThrows(IllegalArgumentException.class, () -> d.subscribe(bad, "q")); - assertThrows(IllegalArgumentException.class, () -> d.subscribe(bad, "q", m -> {})); - assertThrows(IllegalArgumentException.class, () -> nc.subscribe("s", bad)); - assertThrows(IllegalArgumentException.class, () -> d.subscribe("s", bad)); - assertThrows(IllegalArgumentException.class, () -> d.subscribe("s", bad, m -> {})); - } + /// can't auto unsubscribe when closed + assertThrows(IllegalStateException.class, () -> sub.unsubscribe(1)); } } - private static int getDataId(Message m) { - return Integer.parseInt(new String(m.getData())); - } - @Test - public void testDispatcherDefaultSubscribeWhenNoDefaultHandler() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - String subject = subject(); - - Dispatcher d = nc.createDispatcher(); - assertThrows(IllegalStateException.class, () -> d.subscribe(subject)); - } + public void testUnsubscribeWhileWaiting() throws Exception { + runInLrServer(nc -> { + String subject = random(); + Subscription sub = nc.subscribe(subject); + nc.flush(Duration.ofMillis(1000)); + + new Thread(() -> { + try { + Thread.sleep(100); + } + catch (Exception e) { /* ignored */ } + sub.unsubscribe(); + }).start(); + + assertThrows(IllegalStateException.class, () -> sub.nextMessage(Duration.ofMillis(5000))); + }); } } diff --git a/src/test/java/io/nats/client/api/AccountStatisticsTests.java b/src/test/java/io/nats/client/api/AccountStatisticsTests.java index 47ac8c661..76253541e 100644 --- a/src/test/java/io/nats/client/api/AccountStatisticsTests.java +++ b/src/test/java/io/nats/client/api/AccountStatisticsTests.java @@ -39,7 +39,9 @@ public void testAccountStatsImpl() { assertEquals("ngs", as.getDomain()); ApiStats api = as.getApi(); + //noinspection deprecation assertEquals(301, api.getTotal()); // COVERAGE + //noinspection deprecation assertEquals(302, api.getErrors()); // COVERAGE assertEquals(301, api.getTotalApiRequests()); assertEquals(302, api.getErrorCount()); @@ -70,7 +72,9 @@ public void testAccountStatsImpl() { api = as.getApi(); assertNotNull(api); + //noinspection deprecation assertEquals(0, api.getTotal()); // COVERAGE + //noinspection deprecation assertEquals(0, api.getErrors()); // COVERAGE assertEquals(0, api.getTotalApiRequests()); assertEquals(0, api.getErrorCount()); @@ -80,8 +84,10 @@ public void testAccountStatsImpl() { private void validateTier(AccountTier tier, int tierBase, int limitsIdBase) { assertNotNull(tier); + //noinspection deprecation assertEquals(tierBase + 1, tier.getMemory()); // COVERAGE assertEquals(tierBase + 1, tier.getMemoryBytes()); + //noinspection deprecation assertEquals(tierBase + 2, tier.getStorage()); // COVERAGE assertEquals(tierBase + 2, tier.getStorageBytes()); assertEquals(tierBase + 3, tier.getStreams()); diff --git a/src/test/java/io/nats/client/api/ConsumerConfigurationTests.java b/src/test/java/io/nats/client/api/ConsumerConfigurationTests.java index 40b7ef6c4..0e2db8b8c 100644 --- a/src/test/java/io/nats/client/api/ConsumerConfigurationTests.java +++ b/src/test/java/io/nats/client/api/ConsumerConfigurationTests.java @@ -47,8 +47,8 @@ public void testBuilder() throws Exception { .ackWait(Duration.ofSeconds(99)) // duration .deliverPolicy(DeliverPolicy.ByStartSequence) .description("blah") - .name(NAME) - .durable(NAME) + .name("name") + .durable("name") .filterSubject("fs") .maxDeliver(5555) .maxAckPending(6666) @@ -57,7 +57,7 @@ public void testBuilder() throws Exception { .sampleFrequency("10s") .startSequence(2001) .startTime(zdt) - .deliverSubject(DELIVER) + .deliverSubject("deliver") .flowControl(66000) // duration .maxPullWaiting(73) .maxBatch(55) @@ -81,9 +81,10 @@ public void testBuilder() throws Exception { assertNotNull(c.toString()); // COVERAGE assertAsBuilt(c, zdt); - ConsumerCreateRequest ccr = new ConsumerCreateRequest(STREAM, c); + String stream = random(); + ConsumerCreateRequest ccr = new ConsumerCreateRequest(stream, c); assertNotNull(ccr.toString()); // COVERAGE - assertEquals(STREAM, ccr.getStreamName()); + assertEquals(stream, ccr.getStreamName()); assertNotNull(ccr.getConfig()); assertNotNull(ccr.getAction()); assertSame(ConsumerCreateRequest.Action.CreateOrUpdate, ccr.getAction()); @@ -206,7 +207,7 @@ public void testBuilder() throws Exception { assertThrows(IllegalArgumentException.class, () -> ConsumerConfiguration.builder().backoff(DURATION_MIN_LONG - 1).build()); - assertClientError(JsConsumerNameDurableMismatch, () -> ConsumerConfiguration.builder().name(NAME).durable(DURABLE).build()); + assertClientError(JsConsumerNameDurableMismatch, () -> ConsumerConfiguration.builder().name(random()).durable(random()).build()); // filter subjects vs filter subject builder.filterSubjects("subject-0", "subject-1"); @@ -252,8 +253,8 @@ private void assertAsBuilt(ConsumerConfiguration c, ZonedDateTime zdt) { assertEquals(Duration.ofSeconds(99), c.getAckWait()); assertEquals(DeliverPolicy.ByStartSequence, c.getDeliverPolicy()); assertEquals("blah", c.getDescription()); - assertEquals(NAME, c.getDurable()); - assertEquals(NAME, c.getName()); + assertEquals("name", c.getDurable()); + assertEquals("name", c.getName()); assertEquals("fs", c.getFilterSubject()); assertEquals(5555, c.getMaxDeliver()); assertEquals(6666, c.getMaxAckPending()); @@ -262,7 +263,7 @@ private void assertAsBuilt(ConsumerConfiguration c, ZonedDateTime zdt) { assertEquals("10s", c.getSampleFrequency()); assertEquals(2001, c.getStartSequence()); assertEquals(zdt, c.getStartTime()); - assertEquals(DELIVER, c.getDeliverSubject()); + assertEquals("deliver", c.getDeliverSubject()); assertTrue(c.isFlowControl()); assertEquals(Duration.ofSeconds(66), c.getIdleHeartbeat()); assertEquals(73, c.getMaxPullWaiting()); diff --git a/src/test/java/io/nats/client/api/ObjectStoreApiTests.java b/src/test/java/io/nats/client/api/ObjectStoreApiTests.java index 75d0f6239..1a7518366 100644 --- a/src/test/java/io/nats/client/api/ObjectStoreApiTests.java +++ b/src/test/java/io/nats/client/api/ObjectStoreApiTests.java @@ -94,7 +94,7 @@ public void testObjectInfoConstruction() { } private void validateObjectInfo(ObjectInfo oi, ZonedDateTime modified) { - assertEquals(BUCKET, oi.getBucket()); + assertEquals("bucket", oi.getBucket()); assertEquals("object-name", oi.getObjectName()); assertEquals("object-desc", oi.getDescription()); assertEquals(344000, oi.getSize()); @@ -107,11 +107,11 @@ private void validateObjectInfo(ObjectInfo oi, ZonedDateTime modified) { assertEquals(8196, oi.getObjectMeta().getObjectMetaOptions().getChunkSize()); assertNotNull(oi.getHeaders()); assertEquals(2, oi.getHeaders().size()); - List list = oi.getHeaders().get(key(1)); + List list = oi.getHeaders().get("key-1"); assertNotNull(list); assertEquals(1, list.size()); - assertEquals(data(1), oi.getHeaders().getFirst(key(1))); - list = oi.getHeaders().get(key(2)); + assertEquals(data(1), oi.getHeaders().getFirst("key-1")); + list = oi.getHeaders().get("key-2"); assertNotNull(list); assertEquals(2, list.size()); assertTrue(list.contains(data(21))); @@ -126,11 +126,12 @@ private void validateObjectInfo(ObjectInfo oi, ZonedDateTime modified) { @Test public void testObjectInfoCoverage() { - ObjectLink link1a = ObjectLink.object(BUCKET, "name"); - ObjectLink link1b = ObjectLink.object(BUCKET, "name"); - ObjectLink link2 = ObjectLink.object(BUCKET, "name2"); - ObjectLink blink1a = ObjectLink.bucket(BUCKET); - ObjectLink blink1b = ObjectLink.bucket(BUCKET); + String bucket = random(); + ObjectLink link1a = ObjectLink.object(bucket, "name"); + ObjectLink link1b = ObjectLink.object(bucket, "name"); + ObjectLink link2 = ObjectLink.object(bucket, "name2"); + ObjectLink blink1a = ObjectLink.bucket(bucket); + ObjectLink blink1b = ObjectLink.bucket(bucket); ObjectLink blink2 = ObjectLink.bucket("bucket2"); ObjectMetaOptions metaOptions = ObjectMetaOptions.builder().link(link1a).chunkSize(1024).build(); diff --git a/src/test/java/io/nats/client/api/StreamConfigurationTests.java b/src/test/java/io/nats/client/api/StreamConfigurationTests.java index 7298b504b..39aabe826 100644 --- a/src/test/java/io/nats/client/api/StreamConfigurationTests.java +++ b/src/test/java/io/nats/client/api/StreamConfigurationTests.java @@ -13,7 +13,6 @@ package io.nats.client.api; -import io.nats.client.JetStreamManagement; import io.nats.client.impl.JetStreamTestBase; import io.nats.client.support.DateTimeUtils; import io.nats.client.support.JsonParseException; @@ -31,6 +30,7 @@ import static io.nats.client.api.CompressionOption.S2; import static io.nats.client.api.ConsumerConfiguration.*; import static io.nats.client.support.ApiConstants.*; +import static io.nats.client.utils.VersionUtils.atLeast2_10; import static org.junit.jupiter.api.Assertions.*; public class StreamConfigurationTests extends JetStreamTestBase { @@ -54,9 +54,9 @@ private StreamConfiguration getTestConfiguration() { @Test public void testRoundTrip() throws Exception { - runInJsServer(si -> si.isNewerVersionThan("2.8.4"), nc -> { - CompressionOption compressionOption = atLeast2_10(ensureRunServerInfo()) ? S2 : None; - String stream = stream(); + runInLrServer((nc, jsm, js) -> { + CompressionOption compressionOption = atLeast2_10() ? S2 : None; + String stream = random(); StreamConfiguration sc = StreamConfiguration.builder(getTestConfiguration()) .name(stream) .mirror(null) @@ -71,7 +71,6 @@ public void testRoundTrip() throws Exception { .allowMessageCounter(false) .persistMode(null) .build(); - JetStreamManagement jsm = nc.jetStreamManagement(); validateTestStreamConfiguration(jsm.addStream(sc).getConfiguration(), true, stream); }); } @@ -148,7 +147,7 @@ public void testMissingJsonFields() throws Exception{ public void testInvalidNameInJson() throws Exception{ String originalJson = getStreamConfigurationJson(); JsonValue originalParsedJson = JsonParser.parse(originalJson); - originalParsedJson.map.put(NAME, new JsonValue("Inavlid*Name")); + originalParsedJson.map.put("name", new JsonValue("Invalid*Name")); assertThrows(IllegalArgumentException.class, () -> StreamConfiguration.instance(originalParsedJson.toJson())); } @@ -319,7 +318,7 @@ public void testConstruction() { // coverage for null StreamConfiguration, millis maxAge, millis duplicateWindow StreamConfiguration scCov = StreamConfiguration.builder(null) - .name(name()) + .name(random()) .maxAge(1111) .duplicateWindow(2222) .build(); @@ -455,63 +454,74 @@ private void assertNotEqualsEqualsHashcode(Source s, Mirror m, Source.Builder sb @Test public void testSubjects() { - StreamConfiguration.Builder builder = StreamConfiguration.builder().name(name()); + StreamConfiguration.Builder builder = StreamConfiguration.builder().name(random()); + String subject = random(); // subjects(...) replaces - builder.subjects(subject(0)); - assertSubjects(builder.build(), 0); + builder.subjects(subject); + assertSubjects(builder.build(), subject); // subjects(...) replaces builder.subjects(); assertSubjects(builder.build()); // subjects(...) replaces - builder.subjects(subject(1)); - assertSubjects(builder.build(), 1); + subject = random(); + builder.subjects(subject); + assertSubjects(builder.build(), subject); // subjects(...) replaces builder.subjects((String)null); assertSubjects(builder.build()); // subjects(...) replaces - builder.subjects(subject(2), subject(3)); - assertSubjects(builder.build(), 2, 3); + String subjectA = random(); + String subjectB = random(); + builder.subjects(subjectA, subjectB); + assertSubjects(builder.build(), subjectA, subjectB); // subjects(...) replaces - builder.subjects(subject(101), null, subject(102)); - assertSubjects(builder.build(), 101, 102); + subjectA = random(); + subjectB = random(); + builder.subjects(subjectA, null, subjectB); + assertSubjects(builder.build(), subjectA, subjectB); // subjects(...) replaces - builder.subjects(Arrays.asList(subject(4), subject(5))); - assertSubjects(builder.build(), 4, 5); + subjectA = random(); + subjectB = random(); + builder.subjects(Arrays.asList(subjectA, subjectB)); + assertSubjects(builder.build(), subjectA, subjectB); // addSubjects(...) adds unique - builder.addSubjects(subject(5), subject(6)); - assertSubjects(builder.build(), 4, 5, 6); + String subjectC = random(); + builder.addSubjects(subjectB, subjectC); + assertSubjects(builder.build(), subjectA, subjectB, subjectC); // addSubjects(...) adds unique - builder.addSubjects(Arrays.asList(subject(6), subject(7), subject(8))); - assertSubjects(builder.build(), 4, 5, 6, 7, 8); + String subjectD = random(); + String subjectE = random(); + builder.addSubjects(Arrays.asList(subjectC, subjectD, subjectE)); + assertSubjects(builder.build(), subjectA, subjectB, subjectC, subjectD, subjectE); // addSubjects(...) null check builder.addSubjects((String[]) null); - assertSubjects(builder.build(), 4, 5, 6, 7, 8); + assertSubjects(builder.build(), subjectA, subjectB, subjectC, subjectD, subjectE); // addSubjects(...) null check builder.addSubjects((Collection) null); - assertSubjects(builder.build(), 4, 5, 6, 7, 8); + assertSubjects(builder.build(), subjectA, subjectB, subjectC, subjectD, subjectE); } - private void assertSubjects(StreamConfiguration sc, int... subIds) { - assertEquals(subIds.length, sc.getSubjects().size()); - for (int subId : subIds) { - assertTrue(sc.getSubjects().contains(subject(subId))); + private void assertSubjects(StreamConfiguration sc, String... subjects) { + assertEquals(subjects.length, sc.getSubjects().size()); + for (String s : subjects) { + assertTrue(sc.getSubjects().contains(s)); } } @Test public void testRetentionPolicy() { - StreamConfiguration.Builder builder = StreamConfiguration.builder().name(name()); + StreamConfiguration.Builder builder = StreamConfiguration.builder().name(random()); assertEquals(RetentionPolicy.Limits, builder.build().getRetentionPolicy()); builder.retentionPolicy(RetentionPolicy.Limits); @@ -529,7 +539,7 @@ public void testRetentionPolicy() { @Test public void testCompressionOption() { - StreamConfiguration.Builder builder = StreamConfiguration.builder().name(name()); + StreamConfiguration.Builder builder = StreamConfiguration.builder().name(random()); assertEquals(None, builder.build().getCompressionOption()); builder.compressionOption(None); @@ -546,7 +556,7 @@ public void testCompressionOption() { @Test public void testStorageType() { - StreamConfiguration.Builder builder = StreamConfiguration.builder().name(name()); + StreamConfiguration.Builder builder = StreamConfiguration.builder().name(random()); assertEquals(StorageType.File, builder.build().getStorageType()); builder.storageType(StorageType.Memory); @@ -558,7 +568,7 @@ public void testStorageType() { @Test public void testDiscardPolicy() { - StreamConfiguration.Builder builder = StreamConfiguration.builder().name(name()); + StreamConfiguration.Builder builder = StreamConfiguration.builder().name(random()); assertEquals(DiscardPolicy.Old, builder.build().getDiscardPolicy()); builder.discardPolicy(DiscardPolicy.New); diff --git a/src/test/java/io/nats/client/impl/AuthAndConnectTests.java b/src/test/java/io/nats/client/impl/AuthAndConnectTests.java index 2e5dd750e..2035e6a98 100644 --- a/src/test/java/io/nats/client/impl/AuthAndConnectTests.java +++ b/src/test/java/io/nats/client/impl/AuthAndConnectTests.java @@ -17,22 +17,21 @@ import io.nats.client.ErrorListener; import io.nats.client.NatsTestServer; import io.nats.client.Options; -import java.time.Duration; -import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; -import static io.nats.client.utils.TestBase.*; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; +import java.time.Duration; +import java.util.concurrent.atomic.AtomicBoolean; + +import static io.nats.client.utils.ConnectionUtils.*; +import static io.nats.client.utils.ThreadUtils.sleep; +import static org.junit.jupiter.api.Assertions.*; public class AuthAndConnectTests { @Test public void testIsAuthError() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Connection nc = standardConnection(ts.getURI()); + try (NatsTestServer ts = new NatsTestServer()) { + Connection nc = standardConnectionWait(ts.getURI()); NatsConnection nats = (NatsConnection)nc; assertTrue(nats.isAuthenticationError("user authentication expired")); @@ -48,8 +47,8 @@ public void testIsAuthError() throws Exception { @Test() public void testConnectWhenClosed() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - NatsConnection nc = (NatsConnection)standardConnection(ts.getURI()); + try (NatsTestServer ts = new NatsTestServer()) { + NatsConnection nc = (NatsConnection) standardConnectionWait(ts.getURI()); standardCloseConnection(nc); nc.connect(false); // should do nothing assertClosed(nc); @@ -72,7 +71,7 @@ public void errorOccurred(Connection conn, String error) { } }; - try (NatsTestServer ts = new NatsTestServer(false)) { + try (NatsTestServer ts = new NatsTestServer()) { Options options = Options.builder() .server(ts.getURI()) .maxReconnects(-1) @@ -80,7 +79,7 @@ public void errorOccurred(Connection conn, String error) { .errorListener(noopErrorListener) .build(); - NatsConnection nc = (NatsConnection) standardConnection(options); + NatsConnection nc = (NatsConnection) standardConnectionWait(options); // After we've connected, shut down, so we can attempt reconnecting. ts.shutdown(true); diff --git a/src/test/java/io/nats/client/impl/AuthViolationDuringReconnectOnFlushTimeoutTest.java b/src/test/java/io/nats/client/impl/AuthViolationDuringReconnectOnFlushTimeoutTest.java index dd3cb2fb8..52ca34a0d 100644 --- a/src/test/java/io/nats/client/impl/AuthViolationDuringReconnectOnFlushTimeoutTest.java +++ b/src/test/java/io/nats/client/impl/AuthViolationDuringReconnectOnFlushTimeoutTest.java @@ -1,18 +1,15 @@ package io.nats.client.impl; import io.nats.client.*; -import io.nats.client.support.NatsUri; -import org.jspecify.annotations.NonNull; -import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import java.io.IOException; import java.time.Duration; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -44,8 +41,7 @@ public void testAuthViolationDuringReconnect() throws Exception { ctx.port = NatsTestServer.nextPort(); startServer(ctx); - Options options = new Options.Builder() - .servers(new String[]{"nats://" + "127.0.0.1:" + ctx.port}) + Options options = optionsBuilder("nats://" + "127.0.0.1:" + ctx.port) .noRandomize() .token(new char[]{'1', '2', '3', '4'}) @@ -54,8 +50,7 @@ public void testAuthViolationDuringReconnect() throws Exception { .connectionTimeout(Duration.ofMillis(10)) .reconnectWait(Duration.ofMillis(2000)) .connectionListener((conn, e) -> - System.out.println(String.format("Tid: %d, NATS: connection event - %s, connected url: %s. servers: %s ", Thread.currentThread().getId(), e, conn.getConnectedUrl(), conn.getServers()) - )) + System.out.printf("Tid: %d, NATS: connection event - %s, connected url: %s. servers: %s %n", Thread.currentThread().getId(), e, conn.getConnectedUrl(), conn.getServers())) .errorListener(ctx.errorListener) .build(); @@ -76,8 +71,8 @@ public void testAuthViolationDuringReconnect() throws Exception { TimeUnit.SECONDS.sleep(20); // give time to restore all subscriptions synchronized(ctx.nc.getStatus()) { - while (ctx.nc.getStatus() != Connection.Status.CONNECTED && ctx.nc.getStatus() != Connection.Status.CLOSED) { - } + //noinspection StatementWithEmptyBody + while (ctx.nc.getStatus() != Connection.Status.CONNECTED && ctx.nc.getStatus() != Connection.Status.CLOSED) {} } assertFalse(ctx.violated.get()); @@ -100,19 +95,18 @@ static class Context implements AutoCloseable { ErrorListener errorListener = new ErrorListener() { @Override public void slowConsumerDetected(Connection conn, Consumer consumer) { - System.out.println(String.format("Tid: %d, %s: Slow Consumer", Thread.currentThread().getId(), conn.getConnectedUrl())); + System.out.printf("Tid: %d, %s: Slow Consumer%n", Thread.currentThread().getId(), conn.getConnectedUrl()); } @Override public void exceptionOccurred(Connection conn, Exception exp) { exp.printStackTrace(); - System.out.println(String.format("Tid: %d, Nats '%s' exception: %s", Thread.currentThread().getId(), conn.getConnectedUrl(), exp.toString())); + System.out.printf("Tid: %d, Nats '%s' exception: %s%n", Thread.currentThread().getId(), conn.getConnectedUrl(), exp); } @Override public void errorOccurred(Connection conn, String error) { - System.out.println(String.format("Tid: %d, Nats '%s': Error %s", Thread.currentThread().getId(), conn.getConnectedUrl(), error.toString())); - + System.out.printf("Tid: %d, Nats '%s': Error %s%n", Thread.currentThread().getId(), conn.getConnectedUrl(), error); if (error.contains("Authorization Violation")) { violated.set(true); } diff --git a/src/test/java/io/nats/client/impl/ConnectionListenerTests.java b/src/test/java/io/nats/client/impl/ConnectionListenerTests.java index 67b7d5b59..9417012a4 100644 --- a/src/test/java/io/nats/client/impl/ConnectionListenerTests.java +++ b/src/test/java/io/nats/client/impl/ConnectionListenerTests.java @@ -24,7 +24,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import static io.nats.client.utils.TestBase.*; +import static io.nats.client.utils.ConnectionUtils.*; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; import static org.junit.jupiter.api.Assertions.*; public class ConnectionListenerTests { @@ -36,13 +37,12 @@ public void testToString() { @Test public void testCloseCount() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { + try (NatsTestServer ts = new NatsTestServer()) { ListenerForTesting listener = new ListenerForTesting(); - Options options = new Options.Builder(). - server(ts.getURI()). + Options options = optionsBuilder(ts). connectionListener(listener). build(); - Connection nc = standardConnection(options); + Connection nc = standardConnectionWait(options); assertEquals(ts.getURI(), nc.getConnectedUrl()); standardCloseConnection(nc); assertNull(nc.getConnectedUrl()); @@ -57,11 +57,11 @@ public void testDiscoveredServersCountAndListenerInOptions() throws Exception { String customInfo = "{\"server_id\":\"myid\", \"version\":\"9.9.99\",\"connect_urls\": [\""+ts.getURI()+"\"]}"; try (NatsServerProtocolMock ts2 = new NatsServerProtocolMock(null, customInfo)) { ListenerForTesting listener = new ListenerForTesting(); - Options options = new Options.Builder(). - server(ts2.getURI()). - maxReconnects(0). - connectionListener(listener). - build(); + Options options = optionsBuilder() + .server(ts2.getURI()) + .maxReconnects(0) + .connectionListener(listener) + .build(); listener.prepForStatusChange(Events.CONNECTED); standardCloseConnection( listenerConnectionWait(options, listener) ); @@ -75,15 +75,14 @@ public void testDisconnectReconnectCount() throws Exception { int port; Connection nc; ListenerForTesting listener = new ListenerForTesting(); - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder(). - server(ts.getURI()). + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts). reconnectWait(Duration.ofMillis(100)). maxReconnects(-1). connectionListener(listener). build(); port = ts.getPort(); - nc = standardConnection(options); + nc = standardConnectionWait(options); assertEquals(ts.getURI(), nc.getConnectedUrl()); listener.prepForStatusChange(Events.DISCONNECTED); } @@ -104,13 +103,10 @@ public void testDisconnectReconnectCount() throws Exception { @Test public void testExceptionInConnectionListener() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { + try (NatsTestServer ts = new NatsTestServer()) { BadHandler listener = new BadHandler(); - Options options = new Options.Builder(). - server(ts.getURI()). - connectionListener(listener). - build(); - Connection nc = standardConnection(options); + Options options = optionsBuilder(ts).connectionListener(listener).build(); + Connection nc = standardConnectionWait(options); standardCloseConnection(nc); assertTrue(((NatsConnection)nc).getStatisticsCollector().getExceptions() > 0); } @@ -120,13 +116,10 @@ public void testExceptionInConnectionListener() throws Exception { public void testMultipleConnectionListeners() throws Exception { Set capturedEvents = ConcurrentHashMap.newKeySet(); - try (NatsTestServer ts = new NatsTestServer(false)) { + try (NatsTestServer ts = new NatsTestServer()) { ListenerForTesting listener = new ListenerForTesting(); - Options options = new Options.Builder(). - server(ts.getURI()). - connectionListener(listener). - build(); - Connection nc = standardConnection(options); + Options options = optionsBuilder(ts).connectionListener(listener).build(); + Connection nc = standardConnectionWait(options); assertEquals(ts.getURI(), nc.getConnectedUrl()); //noinspection DataFlowIssue // addConnectionListener parameter is annotated as @NonNull diff --git a/src/test/java/io/nats/client/impl/DispatcherTests.java b/src/test/java/io/nats/client/impl/DispatcherTests.java index dc69bc526..f48663673 100644 --- a/src/test/java/io/nats/client/impl/DispatcherTests.java +++ b/src/test/java/io/nats/client/impl/DispatcherTests.java @@ -14,6 +14,9 @@ package io.nats.client.impl; import io.nats.client.*; +import io.nats.client.utils.LongRunningServer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -25,232 +28,342 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static io.nats.client.utils.TestBase.*; +import static io.nats.client.utils.ConnectionUtils.longConnectionWait; +import static io.nats.client.utils.ConnectionUtils.standardConnectionWait; +import static io.nats.client.utils.OptionsUtils.options; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.TestBase.BAD_SUBJECTS_OR_QUEUES; +import static io.nats.client.utils.TestBase.random; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; - // Some tests are a bit tricky, and depend on the fact that the dispatcher -// uses a single queue, so the "subject" messages go through before +// uses a single queue, so the subject messages go through before // the done message (or should) - wanted to note that somewhere public class DispatcherTests { + static Connection nc; + + @BeforeAll + public static void beforeAll() { + try { + nc = longConnectionWait(options(LongRunningServer.server())); + } + catch (IOException e) { + throw new RuntimeException(e); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + + @AfterAll + public static void afterAll() { + try { + nc.close(); + } + catch (Exception ignore) {} + } + + @Test + public void testDispatcherSubscribingExceptions() throws Exception { + // InvalidSubjectsAndQueueNames + Dispatcher dx = nc.createDispatcher(m -> {}); + for (String bad : BAD_SUBJECTS_OR_QUEUES) { + assertThrows(IllegalArgumentException.class, () -> nc.subscribe(bad)); + assertThrows(IllegalArgumentException.class, () -> dx.subscribe(bad)); + assertThrows(IllegalArgumentException.class, () -> dx.subscribe(bad, m -> {})); + assertThrows(IllegalArgumentException.class, () -> dx.subscribe(bad, "q")); + assertThrows(IllegalArgumentException.class, () -> dx.subscribe(bad, "q", m -> {})); + assertThrows(IllegalArgumentException.class, () -> nc.subscribe("s", bad)); + assertThrows(IllegalArgumentException.class, () -> dx.subscribe("s", bad)); + assertThrows(IllegalArgumentException.class, () -> dx.subscribe("s", bad, m -> {})); + } + + String subject = random(); + dx.subscribe(subject); + + // can't subscribe to empty subject -> subject, handler + assertThrows(IllegalArgumentException.class, () -> dx.subscribe("", msg -> {})); + + // can't subscribe to null handler -> subject, handler + assertThrows(IllegalArgumentException.class, () -> dx.subscribe(random(), (MessageHandler) null)); + + // can't subscribe null subject -> subject, queue, handler + assertThrows(IllegalArgumentException.class, () -> dx.subscribe(null, random(), msg -> {})); + + // can't subscribe empty subject -> subject, queue, handler + assertThrows(IllegalArgumentException.class, () -> dx.subscribe("", random(), msg -> {})); + + // can't subscribe with null queue -> subject, queue, handler + assertThrows(IllegalArgumentException.class, () -> dx.subscribe(random(), null, msg -> {})); + + // can't subscribe with empty queue -> subject, queue, handler + assertThrows(IllegalArgumentException.class, () -> dx.subscribe(random(), "", msg -> {})); + + // can't subscribe with null handler -> subject, queue, handler + assertThrows(IllegalArgumentException.class, () -> dx.subscribe(random(), random(), null)); + + // can't unsubscribe with null subject + assertThrows(IllegalArgumentException.class, () -> dx.unsubscribe((String) null)); + + // can't unsubscribe with empty subject + assertThrows(IllegalArgumentException.class, () -> dx.unsubscribe("")); + + nc.closeDispatcher(dx); + + // can't close if already closed + assertThrows(IllegalArgumentException.class, () -> nc.closeDispatcher(dx)); + + // can't unsubscribe if dispatcher is closed + assertThrows(IllegalStateException.class, () -> dx.unsubscribe(subject)); + + // can't subscribe if dispatcher is closed + assertThrows(IllegalStateException.class, () -> dx.subscribe(random())); + + // If dispatcher was made without a default handler, + // you must subscribe with a specific handler + Dispatcher dNoHandler = nc.createDispatcher(); + dNoHandler.subscribe(random(), m -> {}); // This is fine + IllegalStateException ise = assertThrows(IllegalStateException.class, () -> dNoHandler.subscribe(random())); + assertTrue(ise.getMessage().contains("Dispatcher was made without a default handler.")); + nc.closeDispatcher(dNoHandler); + } + @Test - public void testDispatcherMultipleSubscriptionsBySubject() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - String subject1 = subject(); - String subject2 = subject(); - - List dflt = Collections.synchronizedList(new ArrayList<>()); - List sub21 = Collections.synchronizedList(new ArrayList<>()); - List sub22 = Collections.synchronizedList(new ArrayList<>()); - List sub31 = Collections.synchronizedList(new ArrayList<>()); - List sub32 = Collections.synchronizedList(new ArrayList<>()); - Dispatcher d1 = nc.createDispatcher(m -> dflt.add(getDataId(m))); - d1.subscribe(subject1); - d1.subscribe(subject1, m -> sub21.add(getDataId(m))); - d1.subscribe(subject1, m -> sub22.add(getDataId(m))); - d1.subscribe(subject2, m -> sub31.add(getDataId(m))); - d1.subscribe(subject2, m -> sub32.add(getDataId(m))); - - nc.publish(subject1, "1".getBytes()); - nc.publish(subject2, "1".getBytes()); - Thread.sleep(1000); - d1.unsubscribe(subject1); - nc.publish(subject1, "2".getBytes()); - nc.publish(subject2, "2".getBytes()); - Thread.sleep(1000); - - assertTrue(dflt.contains(1)); - assertTrue(sub21.contains(1)); - assertTrue(sub22.contains(1)); - assertTrue(sub31.contains(1)); - assertTrue(sub32.contains(1)); - - assertFalse(dflt.contains(2)); - assertFalse(sub21.contains(2)); - assertFalse(sub22.contains(2)); - assertTrue(sub31.contains(2)); - assertTrue(sub32.contains(2)); + public void throwsOnCreateIfConnectionClosed() throws Exception { + // custom connection since we must close it. + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = longConnectionWait(ts.getURI())) + { + Dispatcher d = nc.createDispatcher(msg -> {}); + // close connection + nc.close(); + + // can't create if connection is closed + assertThrows(IllegalStateException.class, () -> nc.createDispatcher(msg -> {})); + + // can't subscribe if connection is closed + assertThrows(IllegalStateException.class, () -> d.subscribe(random())); + + // can't subscribe if connection is closed + assertThrows(IllegalStateException.class, () -> d.subscribe(random())); + + // can't close dispatcher if connection is closed + assertThrows(IllegalStateException.class, () -> nc.closeDispatcher(d)); } } + @Test + public void testProperlyUnsubscribeBySubject() throws Exception { + // MultipleSubscriptionsBySubject + String subject1 = random(); + String subject2 = random(); + + List dflt = Collections.synchronizedList(new ArrayList<>()); + List sub21 = Collections.synchronizedList(new ArrayList<>()); + List sub22 = Collections.synchronizedList(new ArrayList<>()); + List sub31 = Collections.synchronizedList(new ArrayList<>()); + List sub32 = Collections.synchronizedList(new ArrayList<>()); + Dispatcher d1 = nc.createDispatcher(m -> dflt.add(getDataId(m))); + d1.subscribe(subject1); + d1.subscribe(subject1, m -> sub21.add(getDataId(m))); + d1.subscribe(subject1, m -> sub22.add(getDataId(m))); + d1.subscribe(subject2, m -> sub31.add(getDataId(m))); + d1.subscribe(subject2, m -> sub32.add(getDataId(m))); + + nc.publish(subject1, "1".getBytes()); + nc.publish(subject2, "1".getBytes()); + Thread.sleep(1000); + d1.unsubscribe(subject1); + nc.publish(subject1, "2".getBytes()); + nc.publish(subject2, "2".getBytes()); + Thread.sleep(1000); + + assertTrue(dflt.contains(1)); + assertTrue(sub21.contains(1)); + assertTrue(sub22.contains(1)); + assertTrue(sub31.contains(1)); + assertTrue(sub32.contains(1)); + + assertFalse(dflt.contains(2)); + assertFalse(sub21.contains(2)); + assertFalse(sub22.contains(2)); + assertTrue(sub31.contains(2)); + assertTrue(sub32.contains(2)); + } + private static int getDataId(Message m) { return Integer.parseInt(new String(m.getData())); } @Test public void testSingleMessage() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); + final CompletableFuture msgFuture = new CompletableFuture<>(); + Dispatcher d = nc.createDispatcher(msgFuture::complete); - final CompletableFuture msgFuture = new CompletableFuture<>(); - Dispatcher d = nc.createDispatcher((msg) -> { - msgFuture.complete(msg); - }); + String subject = random(); + d.subscribe(subject); + nc.flush(Duration.ofMillis(500));// Get them all to the server - d.subscribe("subject"); - nc.flush(Duration.ofMillis(500));// Get them all to the server + nc.publish(subject, new byte[16]); - nc.publish("subject", new byte[16]); + Message msg = msgFuture.get(500, TimeUnit.MILLISECONDS); - Message msg = msgFuture.get(500, TimeUnit.MILLISECONDS); + assertTrue(d.isActive()); + assertEquals(subject, msg.getSubject()); + assertNotNull(msg.getSubscription()); + assertNull(msg.getReplyTo()); + assertEquals(16, msg.getData().length); - assertTrue(d.isActive()); - assertEquals("subject", msg.getSubject()); - assertNotNull(msg.getSubscription()); - assertNull(msg.getReplyTo()); - assertEquals(16, msg.getData().length); - } + nc.closeDispatcher(d); } @Test public void testDispatcherMessageContainsConnection() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = standardConnectionWait(ts.getURI())) { final CompletableFuture msgFuture = new CompletableFuture<>(); final CompletableFuture connFuture = new CompletableFuture<>(); - Dispatcher d = nc.createDispatcher((msg) -> { + Dispatcher d = nc.createDispatcher(msg -> { msgFuture.complete(msg); connFuture.complete(msg.getConnection()); }); - d.subscribe("subject"); + String subject = random(); + d.subscribe(subject); nc.flush(Duration.ofMillis(5000));// Get them all to the server - nc.publish("subject", new byte[16]); + nc.publish(subject, new byte[16]); Message msg = msgFuture.get(5000, TimeUnit.MILLISECONDS); Connection conn = connFuture.get(5000, TimeUnit.MILLISECONDS); assertTrue(d.isActive()); - assertEquals("subject", msg.getSubject()); + assertEquals(subject, msg.getSubject()); assertNotNull(msg.getSubscription()); assertNull(msg.getReplyTo()); assertEquals(16, msg.getData().length); - assertTrue(conn == nc); + assertSame(conn, nc); } } @Test public void testMultiSubject() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = standardConnectionWait(ts.getURI())) { final CompletableFuture one = new CompletableFuture<>(); final CompletableFuture two = new CompletableFuture<>(); - Dispatcher d = nc.createDispatcher((msg) -> { - if (msg.getSubject().equals("one")) { + String subject1 = random(); + String subject2 = random(); + Dispatcher d = nc.createDispatcher(msg -> { + if (msg.getSubject().equals(subject1)) { one.complete(msg); - } else if (msg.getSubject().equals("two")) { + } else if (msg.getSubject().equals(subject2)) { two.complete(msg); } }); - d.subscribe("one"); - d.subscribe("two"); + d.subscribe(subject1); + d.subscribe(subject2); nc.flush(Duration.ofMillis(500));// Get them all to the server - nc.publish("one", new byte[16]); - nc.publish("two", new byte[16]); + nc.publish(subject1, new byte[16]); + nc.publish(subject2, new byte[16]); Message msg = one.get(500, TimeUnit.MILLISECONDS); - assertEquals("one", msg.getSubject()); + assertEquals(subject1, msg.getSubject()); msg = two.get(500, TimeUnit.MILLISECONDS); - assertEquals("two", msg.getSubject()); + assertEquals(subject2, msg.getSubject()); + + nc.closeDispatcher(d); } } @Test public void testMultiMessage() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - final CompletableFuture done = new CompletableFuture<>(); - int msgCount = 100; - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); - - final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); - Dispatcher d = nc.createDispatcher((msg) -> { - if (msg.getSubject().equals("done")) { - done.complete(Boolean.TRUE); - } else { - q.add(msg); - } - }); - - d.subscribe("subject"); - d.subscribe("done"); - nc.flush(Duration.ofMillis(1000)); // wait for them to go through + final CompletableFuture done = new CompletableFuture<>(); + int msgCount = 100; - for (int i = 0; i < msgCount; i++) { - nc.publish("subject", new byte[16]); + final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); + Dispatcher d = nc.createDispatcher(msg -> { + if (msg.getSubject().equals("done")) { + done.complete(Boolean.TRUE); } - nc.publish("done", new byte[16]); - nc.flush(Duration.ofMillis(1000)); // wait for them to go through + else { + q.add(msg); + } + }); - done.get(500, TimeUnit.MILLISECONDS); + String subject = random(); + d.subscribe(subject); + d.subscribe("done"); + nc.flush(Duration.ofMillis(1000)); // wait for them to go through - assertEquals(msgCount, q.size()); + for (int i = 0; i < msgCount; i++) { + nc.publish(subject, new byte[16]); } + nc.publish("done", new byte[16]); + nc.flush(Duration.ofMillis(1000)); // wait for them to go through + + done.get(500, TimeUnit.MILLISECONDS); + + assertEquals(msgCount, q.size()); } @Test - public void testClose() { - assertThrows(TimeoutException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - final CompletableFuture phase1 = new CompletableFuture<>(); - final CompletableFuture phase2 = new CompletableFuture<>(); - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); - - final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); - Dispatcher d = nc.createDispatcher((msg) -> { - if (msg.getSubject().equals("phase1")) { - phase1.complete(Boolean.TRUE); - } else if (msg.getSubject().equals("phase1")) { - phase2.complete(Boolean.TRUE); - } else { - q.add(msg); - } - }); + public void testClosedDispatcherBehavior() throws Exception { + final CompletableFuture fPhase1 = new CompletableFuture<>(); + final CompletableFuture fPhase2 = new CompletableFuture<>(); - d.subscribe("subject"); - d.subscribe("phase1"); - d.subscribe("phase2"); - nc.flush(Duration.ofMillis(500));// Get them all to the server + final ConcurrentLinkedQueue received = new ConcurrentLinkedQueue<>(); + String subject = random(); + String phase1 = random(); + String phase2 = random(); + Dispatcher d = nc.createDispatcher(msg -> { + if (msg.getSubject().equals(phase1)) { + fPhase1.complete(Boolean.TRUE); + } + else if (msg.getSubject().equals(phase2)) { + fPhase2.complete(Boolean.TRUE); + } + else { + received.add(msg); + } + }); - nc.publish("subject", new byte[16]); - nc.publish("phase1", null); + d.subscribe(subject); + d.subscribe(phase1); + d.subscribe(phase2); + nc.flush(Duration.ofMillis(500));// Get them all to the server - nc.flush(Duration.ofMillis(1000)); // wait for them to go through - phase1.get(200, TimeUnit.MILLISECONDS); + nc.publish(subject, new byte[16]); + nc.publish(phase1, null); - assertEquals(1, q.size()); + nc.flush(Duration.ofMillis(1000)); // wait for them to go through + fPhase1.get(200, TimeUnit.MILLISECONDS); - nc.closeDispatcher(d); + assertEquals(1, received.size()); - assertFalse(d.isActive()); + nc.closeDispatcher(d); - // This won't arrive - nc.publish("phase2", new byte[16]); + assertFalse(d.isActive()); - nc.flush(Duration.ofMillis(1000)); // wait for them to go through - phase2.get(200, TimeUnit.MILLISECONDS); - } - }); - } + // This won't arrive + nc.publish(phase2, new byte[16]); + nc.flush(Duration.ofMillis(1000)); // wait for them to go through + assertThrows(TimeoutException.class, () -> fPhase2.get(200, TimeUnit.MILLISECONDS)); + } @Test public void testQueueSubscribers() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = standardConnectionWait(ts.getURI())) { int msgs = 100; AtomicInteger received = new AtomicInteger(); AtomicInteger sub1Count = new AtomicInteger(); @@ -259,10 +372,12 @@ public void testQueueSubscribers() throws Exception { final CompletableFuture done1 = new CompletableFuture<>(); final CompletableFuture done2 = new CompletableFuture<>(); - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); + String subject = random(); + String done = random(); + String queue = random(); - Dispatcher d1 = nc.createDispatcher((msg) -> { - if (msg.getSubject().equals("done")) { + Dispatcher d1 = nc.createDispatcher(msg -> { + if (msg.getSubject().equals(done)) { done1.complete(Boolean.TRUE); } else { sub1Count.incrementAndGet(); @@ -270,8 +385,8 @@ public void testQueueSubscribers() throws Exception { } }); - Dispatcher d2 = nc.createDispatcher((msg) -> { - if (msg.getSubject().equals("done")) { + Dispatcher d2 = nc.createDispatcher(msg -> { + if (msg.getSubject().equals(done)) { done2.complete(Boolean.TRUE); } else { sub2Count.incrementAndGet(); @@ -279,17 +394,17 @@ public void testQueueSubscribers() throws Exception { } }); - d1.subscribe("subject", "queue"); - d2.subscribe("subject", "queue"); - d1.subscribe("done"); - d2.subscribe("done"); + d1.subscribe(subject, queue); + d2.subscribe(subject, queue); + d1.subscribe(done); + d2.subscribe(done); nc.flush(Duration.ofMillis(500)); for (int i = 0; i < msgs; i++) { - nc.publish("subject", new byte[16]); + nc.publish(subject, new byte[16]); } - nc.publish("done", null); + nc.publish(done, null); nc.flush(Duration.ofMillis(500)); done1.get(500, TimeUnit.MILLISECONDS); @@ -298,687 +413,448 @@ public void testQueueSubscribers() throws Exception { assertEquals(msgs, received.get()); assertEquals(msgs, sub1Count.get() + sub2Count.get()); - // They won't be equal but print to make sure they are close (human testing) - System.out.println("### Sub 1 " + sub1Count.get()); - System.out.println("### Sub 2 " + sub2Count.get()); + nc.closeDispatcher(d1); + nc.closeDispatcher(d2); } } @Test - public void testCantUnsubSubFromDispatcher() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) - { - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); + public void testCantUnsubSubFromDispatcher() throws Exception { + final CompletableFuture msgFuture = new CompletableFuture<>(); + Dispatcher d = nc.createDispatcher(msgFuture::complete); - final CompletableFuture msgFuture = new CompletableFuture<>(); - Dispatcher d = nc.createDispatcher((msg) -> { - msgFuture.complete(msg); - }); + String subject = random(); + d.subscribe(subject); + nc.flush(Duration.ofMillis(500));// Get them all to the server - d.subscribe("subject"); - nc.flush(Duration.ofMillis(500));// Get them all to the server + nc.publish(subject, new byte[16]); - nc.publish("subject", new byte[16]); + Message msg = msgFuture.get(500, TimeUnit.MILLISECONDS); - Message msg = msgFuture.get(500, TimeUnit.MILLISECONDS); + assertThrows(IllegalStateException.class, () -> msg.getSubscription().unsubscribe()); - msg.getSubscription().unsubscribe(); // Should throw - assertFalse(true); - } - }); + nc.closeDispatcher(d); } @Test - public void testCantAutoUnsubSubFromDispatcher() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) - { - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); + public void testCantAutoUnsubSubFromDispatcher() throws Exception { + final CompletableFuture msgFuture = new CompletableFuture<>(); + Dispatcher d = nc.createDispatcher(msgFuture::complete); - final CompletableFuture msgFuture = new CompletableFuture<>(); - Dispatcher d = nc.createDispatcher((msg) -> { - msgFuture.complete(msg); - }); + String subject = random(); + d.subscribe(subject); + nc.flush(Duration.ofMillis(500));// Get them all to the server - d.subscribe("subject"); - nc.flush(Duration.ofMillis(500));// Get them all to the server + nc.publish(subject, new byte[16]); - nc.publish("subject", new byte[16]); + Message msg = msgFuture.get(500, TimeUnit.MILLISECONDS); - Message msg = msgFuture.get(500, TimeUnit.MILLISECONDS); + assertThrows(IllegalStateException.class, () -> msg.getSubscription().unsubscribe(1)); - msg.getSubscription().unsubscribe(1); // Should throw - assertFalse(true); - } - }); + nc.closeDispatcher(d); } @Test - public void testPublishAndFlushFromCallback() - throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); + public void testPublishAndFlushFromCallback() throws Exception { + String subject = random(); - final CompletableFuture msgFuture = new CompletableFuture<>(); - Dispatcher d = nc.createDispatcher((msg) -> { - try { - nc.flush(Duration.ofMillis(1000)); - } catch (Exception ex) { - ex.printStackTrace(); - } - msgFuture.complete(msg); - }); + long startCount = nc.getStatistics().getFlushCounter(); - d.subscribe("subject"); - nc.flush(Duration.ofMillis(500));// Get them all to the server + final CompletableFuture msgFuture = new CompletableFuture<>(); + Dispatcher d = nc.createDispatcher(msg -> { + try { + nc.flush(Duration.ofMillis(1000)); + } catch (Exception ex) { + ex.printStackTrace(); + } + msgFuture.complete(msg); + }); - nc.publish("subject", new byte[16]); // publish one to kick it off + d.subscribe(subject); + nc.flush(Duration.ofMillis(500));// Get them all to the server - Message msg = msgFuture.get(500, TimeUnit.MILLISECONDS); - assertNotNull(msg); + nc.publish(subject, new byte[16]); // publish one to kick it off - assertEquals(2, ((NatsStatistics)(nc.getStatistics())).getFlushCounter()); - } + Message msg = msgFuture.get(500, TimeUnit.MILLISECONDS); + assertNotNull(msg); + + long diffCount = nc.getStatistics().getFlushCounter() - startCount; + assertEquals(2, diffCount); + nc.closeDispatcher(d); } @Test public void testUnsub() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - final CompletableFuture phase1 = new CompletableFuture<>(); - final CompletableFuture phase2 = new CompletableFuture<>(); - int msgCount = 10; - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); - - final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); - Dispatcher d = nc.createDispatcher((msg) -> { - if (msg.getSubject().equals("phase1")) { - phase1.complete(Boolean.TRUE); - } else if (msg.getSubject().equals("phase2")) { - phase2.complete(Boolean.TRUE); - } else { - q.add(msg); - } - }); + final CompletableFuture fPhase1 = new CompletableFuture<>(); + final CompletableFuture fPhase2 = new CompletableFuture<>(); + int msgCount = 10; - d.subscribe("subject"); - d.subscribe("phase1"); - d.subscribe("phase2"); - nc.flush(Duration.ofMillis(1000));// Get them all to the server + String subject = random(); + String phase1 = random(); + String phase2 = random(); - for (int i = 0; i < msgCount; i++) { - nc.publish("subject", new byte[16]); + final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); + Dispatcher d = nc.createDispatcher(msg -> { + if (msg.getSubject().equals(phase1)) { + fPhase1.complete(Boolean.TRUE); } - nc.publish("phase1", new byte[16]); - nc.flush(Duration.ofMillis(1000)); // wait for them to go through + else if (msg.getSubject().equals(phase2)) { + fPhase2.complete(Boolean.TRUE); + } + else { + q.add(msg); + } + }); - phase1.get(5000, TimeUnit.MILLISECONDS); + d.subscribe(subject); + d.subscribe(phase1); + d.subscribe(phase2); + nc.flush(Duration.ofMillis(1000));// Get them all to the server - d.unsubscribe("subject"); - nc.flush(Duration.ofMillis(1000));// Get them all to the server + for (int i = 0; i < msgCount; i++) { + nc.publish(subject, new byte[16]); + } + nc.publish(phase1, new byte[16]); + nc.flush(Duration.ofMillis(1000)); // wait for them to go through - for (int i = 0; i < msgCount; i++) { - nc.publish("subject", new byte[16]); - } - nc.publish("phase2", new byte[16]); - nc.flush(Duration.ofMillis(1000)); // wait for them to go through + fPhase1.get(5000, TimeUnit.MILLISECONDS); - phase2.get(1000, TimeUnit.MILLISECONDS); // make sure we got them + d.unsubscribe(subject); + nc.flush(Duration.ofMillis(1000));// Get them all to the server - assertEquals(msgCount, q.size()); + for (int i = 0; i < msgCount; i++) { + nc.publish(subject, new byte[16]); } + nc.publish(phase2, new byte[16]); + nc.flush(Duration.ofMillis(1000)); // wait for them to go through + + fPhase2.get(1000, TimeUnit.MILLISECONDS); // make sure we got them + + assertEquals(msgCount, q.size()); + + nc.closeDispatcher(d); } @Test public void testAutoUnsub() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - final CompletableFuture phase1 = new CompletableFuture<>(); - final CompletableFuture phase2 = new CompletableFuture<>(); - int msgCount = 100; - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); - - final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); - NatsDispatcher d = (NatsDispatcher) nc.createDispatcher((msg) -> { - if (msg.getSubject().equals("phase1")) { - phase1.complete(Boolean.TRUE); - }else if (msg.getSubject().equals("phase2")) { - phase2.complete(Boolean.TRUE); - } else { - q.add(msg); - } - }); + final CompletableFuture fPhase1 = new CompletableFuture<>(); + final CompletableFuture fPhase2 = new CompletableFuture<>(); + int msgCount = 100; - d.subscribe("subject"); - d.subscribe("phase1"); - d.subscribe("phase2"); - nc.flush(Duration.ofMillis(500));// Get them all to the server + String subject = random(); + String phase1 = random(); + String phase2 = random(); - for (int i = 0; i < msgCount; i++) { - nc.publish("subject", new byte[16]); + final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); + NatsDispatcher d = (NatsDispatcher) nc.createDispatcher(msg -> { + if (msg.getSubject().equals(phase1)) { + fPhase1.complete(Boolean.TRUE); + } + else if (msg.getSubject().equals(phase2)) { + fPhase2.complete(Boolean.TRUE); } - nc.publish("phase1", new byte[16]); + else { + q.add(msg); + } + }); - nc.flush(Duration.ofMillis(1000)); // wait for them to go through - phase1.get(1000, TimeUnit.MILLISECONDS); // make sure we got them + d.subscribe(subject); + d.subscribe(phase1); + d.subscribe(phase2); + nc.flush(Duration.ofMillis(500));// Get them all to the server - assertEquals(msgCount, q.size()); + for (int i = 0; i < msgCount; i++) { + nc.publish(subject, new byte[16]); + } + nc.publish(phase1, new byte[16]); - d.unsubscribe("subject", msgCount + 1); + nc.flush(Duration.ofMillis(1000)); // wait for them to go through + fPhase1.get(1000, TimeUnit.MILLISECONDS); // make sure we got them - for (int i = 0; i < msgCount; i++) { - nc.publish("subject", new byte[16]); - } - nc.publish("phase2", new byte[16]); + assertEquals(msgCount, q.size()); - nc.flush(Duration.ofMillis(1000)); // Wait for it all to get processed - phase2.get(1000, TimeUnit.MILLISECONDS); // make sure we got them + d.unsubscribe(subject, msgCount + 1); - assertEquals(msgCount + 1, q.size()); + for (int i = 0; i < msgCount; i++) { + nc.publish(subject, new byte[16]); } + nc.publish(phase2, new byte[16]); + + nc.flush(Duration.ofMillis(1000)); // Wait for it all to get processed + fPhase2.get(1000, TimeUnit.MILLISECONDS); // make sure we got them + + assertEquals(msgCount + 1, q.size()); + + nc.closeDispatcher(d); } @Test public void testUnsubFromCallback() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - final CompletableFuture done = new CompletableFuture<>(); - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); - - final AtomicReference dispatcher = new AtomicReference<>(); - final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); - final Dispatcher d = nc.createDispatcher((msg) -> { - if (msg.getSubject().equals("done")) { - done.complete(Boolean.TRUE); - } else { - q.add(msg); - dispatcher.get().unsubscribe("subject"); - } - }); + final CompletableFuture fDone = new CompletableFuture<>(); + String subject = random(); + String done = random(); - dispatcher.set(d); + final AtomicReference dispatcher = new AtomicReference<>(); + final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); + final Dispatcher d = nc.createDispatcher(msg -> { + if (msg.getSubject().equals(done)) { + fDone.complete(Boolean.TRUE); + } + else { + q.add(msg); + dispatcher.get().unsubscribe(subject); + } + }); - d.subscribe("subject"); - d.subscribe("done"); - nc.flush(Duration.ofMillis(500));// Get them all to the server + dispatcher.set(d); - nc.publish("subject", new byte[16]); - nc.publish("subject", new byte[16]); - nc.publish("done", new byte[16]); // when we get this we know the others are dispatched - nc.flush(Duration.ofMillis(1000)); // Wait for the publish, or we will get multiples for sure - done.get(200, TimeUnit.MILLISECONDS); // make sure we got them + d.subscribe(subject); + d.subscribe(done); + nc.flush(Duration.ofMillis(500));// Get them all to the server - assertEquals(1, q.size()); - } + nc.publish(subject, new byte[16]); + nc.publish(subject, new byte[16]); + nc.publish(done, new byte[16]); // when we get this we know the others are dispatched + nc.flush(Duration.ofMillis(1000)); // Wait for the publish, or we will get multiples for sure + fDone.get(200, TimeUnit.MILLISECONDS); // make sure we got them + + assertEquals(1, q.size()); + + nc.closeDispatcher(d); } @Test - public void testAutoUnsubFromCallback() - throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - final CompletableFuture done = new CompletableFuture<>(); - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); - - final AtomicReference dispatcher = new AtomicReference<>(); - final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); - final Dispatcher d = nc.createDispatcher((msg) -> { - if (msg.getSubject().equals("done")) { - done.complete(Boolean.TRUE); - } else { - q.add(msg); - dispatcher.get().unsubscribe("subject", 2); // get 1 more, for a total of 2 - } - }); + public void testAutoUnsubFromCallback() throws Exception { + final CompletableFuture fDone = new CompletableFuture<>(); - dispatcher.set(d); + String subject = random(); + String done = random(); + final AtomicReference dispatcher = new AtomicReference<>(); + final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); + final Dispatcher d = nc.createDispatcher(msg -> { + if (msg.getSubject().equals(done)) { + fDone.complete(Boolean.TRUE); + } + else { + q.add(msg); + dispatcher.get().unsubscribe(subject, 2); // get 1 more, for a total of 2 + } + }); - d.subscribe("subject"); - d.subscribe("done"); - nc.flush(Duration.ofMillis(1000));// Get them all to the server + dispatcher.set(d); - nc.publish("subject", new byte[16]); - nc.publish("subject", new byte[16]); - nc.publish("subject", new byte[16]); - nc.publish("done", new byte[16]); // when we get this we know the others are dispatched - nc.flush(Duration.ofMillis(1000)); // Wait for the publish + d.subscribe(subject); + d.subscribe(done); + nc.flush(Duration.ofMillis(1000));// Get them all to the server - done.get(200, TimeUnit.MILLISECONDS); // make sure we got them + nc.publish(subject, new byte[16]); + nc.publish(subject, new byte[16]); + nc.publish(subject, new byte[16]); + nc.publish(done, new byte[16]); // when we get this we know the others are dispatched + nc.flush(Duration.ofMillis(1000)); // Wait for the publish - assertEquals(2, q.size()); - } + fDone.get(200, TimeUnit.MILLISECONDS); // make sure we got them + + assertEquals(2, q.size()); + + nc.closeDispatcher(d); } @Test public void testCloseFromCallback() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - final CompletableFuture done = new CompletableFuture<>(); - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = standardConnectionWait(ts.getURI())) + { + final CompletableFuture fDone = new CompletableFuture<>(); - final Dispatcher d = nc.createDispatcher((msg) -> { + String subject = random(); + final Dispatcher d = nc.createDispatcher(msg -> { try { - if (msg.getSubject().equals("done")) { + if (msg.getSubject().equals(subject)) { nc.close(); - done.complete(Boolean.TRUE); + fDone.complete(Boolean.TRUE); } - } catch (InterruptedException e) { + } + catch (InterruptedException e) { e.printStackTrace(); } }); - d.subscribe("done"); + d.subscribe(subject); sleep(500); // Making sure the "subscribe" has been registered on the server - nc.publish("done", new byte[16]); + nc.publish(subject, new byte[16]); - done.get(5000, TimeUnit.MILLISECONDS); - - waitUntilStatus(nc, 5000, Connection.Status.CLOSED); + fDone.get(5000, TimeUnit.MILLISECONDS); } } @Test public void testDispatchHandlesExceptionInHandler() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - final CompletableFuture done = new CompletableFuture<>(); - int msgCount = 100; - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); - - final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); - Dispatcher d = nc.createDispatcher((msg) -> { - if (msg.getSubject().equals("done")) { - done.complete(Boolean.TRUE); - } else { - q.add(msg); - throw new NumberFormatException(); - } - }); - - d.subscribe("subject"); - d.subscribe("done"); - nc.flush(Duration.ofMillis(500));// Get them all to the server - - for (int i = 0; i < msgCount; i++) { - nc.publish("subject", new byte[16]); - } - nc.publish("done", new byte[16]); - - nc.flush(Duration.ofMillis(1000)); // wait for them to go through - done.get(200, TimeUnit.MILLISECONDS); - - assertEquals(msgCount, q.size()); - } - } - - @Test - public void testThrowOnNullSubject() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - d.subscribe(null); - assertFalse(true); + final CompletableFuture fDone = new CompletableFuture<>(); + int msgCount = 100; + + String subject = random(); + String done = random(); + final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); + Dispatcher d = nc.createDispatcher(msg -> { + if (msg.getSubject().equals(done)) { + fDone.complete(Boolean.TRUE); + } else { + q.add(msg); + throw new NumberFormatException(); } }); - } - @Test - public void testThrowOnEmptySubject() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - - d.subscribe(""); - assertFalse(true); - } - }); - } + d.subscribe(subject); + d.subscribe(done); + nc.flush(Duration.ofMillis(500));// Get them all to the server - @Test - public void testThrowOnEmptyQueue() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - d.subscribe("subject", ""); - assertFalse(true); - } - }); - } + for (int i = 0; i < msgCount; i++) { + nc.publish(subject, new byte[16]); + } + nc.publish(done, new byte[16]); - @Test - public void testThrowOnNullSubjectWithQueue() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - d.subscribe(null, "quque"); - assertFalse(true); - } - }); - } + nc.flush(Duration.ofMillis(1000)); // wait for them to go through + fDone.get(200, TimeUnit.MILLISECONDS); - @Test - public void testThrowOnEmptySubjectWithQueue() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - d.subscribe("", "quque"); - assertFalse(true); - } - }); - } + assertEquals(msgCount, q.size()); - @Test - public void throwsOnCreateIfClosed() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - nc.close(); - nc.createDispatcher((msg) -> {}); - assertFalse(true); - } - }); + nc.closeDispatcher(d); } @Test - public void throwsOnSubscribeIfClosed() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - nc.close(); - d.subscribe("subject"); - assertFalse(true); - } - }); + public void testThrowOnBadInput() { + Dispatcher d = nc.createDispatcher(msg -> {}); + // Null Subject + assertThrows(IllegalArgumentException.class, () -> d.subscribe(null)); + // Empty Subject + assertThrows(IllegalArgumentException.class, () -> d.subscribe("")); + // Empty Subject + assertThrows(IllegalArgumentException.class, () -> d.subscribe("")); + // Null Subject With Queue + assertThrows(IllegalArgumentException.class, () -> d.subscribe(null, random())); + // Empty Subject With Queue + assertThrows(IllegalArgumentException.class, () -> d.subscribe("", random())); + nc.closeDispatcher(d); } @Test - public void testThrowOnSubscribeWhenClosed() throws IOException, InterruptedException, TimeoutException { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - nc.closeDispatcher(d); - d.subscribe("foo"); - assertFalse(true); - } - }); - } + public void testDoubleSubscribe() throws Exception { + final CompletableFuture fDone = new CompletableFuture<>(); + int msgCount = 100; - @Test - public void testThrowOnUnsubscribeWhenClosed() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - d.subscribe("foo"); - nc.closeDispatcher(d); - d.unsubscribe("foo"); - assertFalse(true); - } - }); - } + String subject = random(); + String done = random(); - @Test - public void testThrowOnDoubleClose() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - nc.closeDispatcher(d); - nc.closeDispatcher(d); - assertFalse(true); + final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); + Dispatcher d = nc.createDispatcher(msg -> { + if (msg.getSubject().equals(done)) { + fDone.complete(Boolean.TRUE); + } else { + q.add(msg); } }); - } - @Test - public void testThrowOnConnClosed() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - nc.close(); - nc.closeDispatcher(d); - assertFalse(true); - } - }); - } + d.subscribe(subject).subscribe(subject).subscribe(subject).subscribe(done); + nc.flush(Duration.ofSeconds(5)); // wait for them to go through - @Test - public void testDoubleSubscribe() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - final CompletableFuture done = new CompletableFuture<>(); - int msgCount = 100; - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); - - final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); - Dispatcher d = nc.createDispatcher((msg) -> { - if (msg.getSubject().equals("done")) { - done.complete(Boolean.TRUE); - } else { - q.add(msg); - } - }); + for (int i = 0; i < msgCount; i++) { + nc.publish(subject, new byte[16]); + } + nc.publish(done, new byte[16]); + nc.flush(Duration.ofSeconds(5)); // wait for them to go through - d.subscribe("subject").subscribe("subject").subscribe("subject").subscribe("done"); - nc.flush(Duration.ofSeconds(5)); // wait for them to go through + fDone.get(5, TimeUnit.SECONDS); - for (int i = 0; i < msgCount; i++) { - nc.publish("subject", new byte[16]); - } - nc.publish("done", new byte[16]); - nc.flush(Duration.ofSeconds(5)); // wait for them to go through + assertEquals(msgCount, q.size()); // Should only get one since all the extra subs do nothing?? - done.get(5, TimeUnit.SECONDS); - - assertEquals(msgCount, q.size()); // Shoudl only get one since all the extra subs do nothing?? - } + nc.closeDispatcher(d); } @Test public void testDoubleSubscribeWithCustomHandler() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - final CompletableFuture done = new CompletableFuture<>(); - int msgCount = 100; - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); - - final AtomicInteger count = new AtomicInteger(0); - Dispatcher d = nc.createDispatcher((msg) -> {}); - - d.subscribe("subject", (msg) -> { count.incrementAndGet(); }); - d.subscribe("subject", "queue", (msg) -> { count.incrementAndGet(); }); - d.subscribe("done", (msg) -> { done.complete(Boolean.TRUE); }); + final CompletableFuture fDone = new CompletableFuture<>(); + int msgCount = 100; - nc.flush(Duration.ofSeconds(5)); // wait for them to go through + final AtomicInteger count = new AtomicInteger(0); + Dispatcher d = nc.createDispatcher(msg -> { + }); - for (int i = 0; i < msgCount; i++) { - nc.publish("subject", new byte[16]); - } - nc.publish("done", new byte[16]); - nc.flush(Duration.ofSeconds(5)); // wait for them to go through + String subject = random(); + String done = random(); + String queue = random(); + d.subscribe(subject, msg -> count.incrementAndGet()); + d.subscribe(subject, queue, msg -> count.incrementAndGet()); + d.subscribe(done, msg -> fDone.complete(Boolean.TRUE)); - done.get(5, TimeUnit.SECONDS); + nc.flush(Duration.ofSeconds(5)); // wait for them to go through - assertEquals(msgCount * 2, count.get()); // We should get 2x the messages because we subscribed 2 times. + for (int i = 0; i < msgCount; i++) { + nc.publish(subject, new byte[16]); } - } - - @Test - public void testDoubleSubscribeWithUnsubscribeAfterWithCustomHandler() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - final CompletableFuture done1 = new CompletableFuture<>(); - final CompletableFuture done2 = new CompletableFuture<>(); - int msgCount = 100; - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); - - final AtomicInteger count = new AtomicInteger(0); - Dispatcher d = nc.createDispatcher((msg) -> {}); - Subscription s1 = d.subscribe("subject", (msg) -> { count.incrementAndGet(); }); - Subscription doneSub = d.subscribe("done", (msg) -> { done1.complete(Boolean.TRUE); }); - d.subscribe("subject", (msg) -> { count.incrementAndGet(); }); - - nc.flush(Duration.ofSeconds(5)); // wait for the subs to go through - - for (int i = 0; i < msgCount; i++) { - nc.publish("subject", new byte[16]); - } - nc.publish("done", new byte[16]); - nc.flush(Duration.ofSeconds(5)); // wait for the messages to go through - - done1.get(5, TimeUnit.SECONDS); - - assertEquals(msgCount * 2, count.get()); // We should get 2x the messages because we subscribed 2 times. - - count.set(0); - d.unsubscribe(s1); - d.unsubscribe(doneSub); - d.subscribe("done", (msg) -> { done2.complete(Boolean.TRUE); }); - nc.flush(Duration.ofSeconds(5)); // wait for the unsub to go through + nc.publish(done, new byte[16]); + nc.flush(Duration.ofSeconds(5)); // wait for them to go through - for (int i = 0; i < msgCount; i++) { - nc.publish("subject", new byte[16]); - } - nc.publish("done", new byte[16]); - nc.flush(Duration.ofSeconds(5)); // wait for the messages to go through + fDone.get(5, TimeUnit.SECONDS); - done2.get(5, TimeUnit.SECONDS); + assertEquals(msgCount * 2, count.get()); // We should get 2x the messages because we subscribed 2 times. - assertEquals(msgCount, count.get()); // We only have 1 active subscription, so we should only get msgCount. - } + nc.closeDispatcher(d); } @Test - public void testThrowOnEmptySubjectWithMessageHandler() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - d.subscribe("", (msg) -> {}); - assertFalse(true); - } - }); - } - - @Test - public void testThrowOnNullHandler() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - d.subscribe("test", (MessageHandler)null); - assertFalse(true); - } - }); - } + public void testDoubleSubscribeWithUnsubscribeAfterWithCustomHandler() throws Exception { + final CompletableFuture fDone1 = new CompletableFuture<>(); + final CompletableFuture fDone2 = new CompletableFuture<>(); + int msgCount = 100; + + String subject = random(); + String done = random(); + final AtomicInteger count = new AtomicInteger(0); + Dispatcher d = nc.createDispatcher(msg -> {}); + Subscription s1 = d.subscribe(subject, msg -> count.incrementAndGet()); + Subscription doneSub = d.subscribe(done, msg -> fDone1.complete(Boolean.TRUE)); + d.subscribe(subject, msg -> count.incrementAndGet()); + + nc.flush(Duration.ofSeconds(5)); // wait for the subs to go through + + for (int i = 0; i < msgCount; i++) { + nc.publish(subject, new byte[16]); + } + nc.publish(done, new byte[16]); + nc.flush(Duration.ofSeconds(5)); // wait for the messages to go through - @Test - public void testThrowOnNullHandlerWithQueue() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - d.subscribe("test", "queue", (MessageHandler)null); - assertFalse(true); - } - }); - } + fDone1.get(5, TimeUnit.SECONDS); - @Test - public void testThrowOnEmptyQueueWithMessageHandler() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - d.subscribe("subject", "", (msg) -> {}); - assertFalse(true); - } - }); - } + assertEquals(msgCount * 2, count.get()); // We should get 2x the messages because we subscribed 2 times. - @Test - public void testThrowOnNullSubjectWithQueueWithMessageHandler() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - d.subscribe(null, "quque", (msg) -> {}); - assertFalse(true); - } - }); - } + count.set(0); + d.unsubscribe(s1); + d.unsubscribe(doneSub); + d.subscribe(done, msg -> fDone2.complete(Boolean.TRUE)); + nc.flush(Duration.ofSeconds(5)); // wait for the unsub to go through - @Test - public void testThrowOnEmptySubjectWithQueueWithMessageHandler() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - d.subscribe("", "quque", (msg) -> {}); - assertFalse(true); - } - }); - } + for (int i = 0; i < msgCount; i++) { + nc.publish(subject, new byte[16]); + } + nc.publish(done, new byte[16]); + nc.flush(Duration.ofSeconds(5)); // wait for the messages to go through - @Test - public void testThrowOnEmptySubjectInUnsub() { - assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - d.unsubscribe(""); - assertFalse(true); - } - }); - } + fDone2.get(5, TimeUnit.SECONDS); - @Test - public void testThrowOnUnsubWhenClosed() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - Subscription sub = d.subscribe("subject", (msg) -> {}); - nc.closeDispatcher(d); - d.unsubscribe(sub); - assertFalse(true); - } - }); - } + assertEquals(msgCount, count.get()); // We only have 1 active subscription, so we should only get msgCount. - @Test - public void testThrowOnWrongSubscription() { - assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { - Dispatcher d = nc.createDispatcher((msg) -> {}); - Subscription sub2 = nc.subscribe("test"); - d.unsubscribe(sub2); - assertFalse(true); - } - }); + nc.closeDispatcher(d); } @Test public void testDispatcherFactoryCoverage() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(Options.builder().server(ts.getURI()).useDispatcherWithExecutor().build())) + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = longConnectionWait(optionsBuilder(ts).useDispatcherWithExecutor().build())) { CountDownLatch latch = new CountDownLatch(1); - Dispatcher d = nc.createDispatcher((msg) -> latch.countDown()); + Dispatcher d = nc.createDispatcher(msg -> latch.countDown()); assertInstanceOf(NatsDispatcherWithExecutor.class, d); String subject = NUID.nextGlobalSequence(); d.subscribe(subject); diff --git a/src/test/java/io/nats/client/impl/DrainTests.java b/src/test/java/io/nats/client/impl/DrainTests.java index 7f3d18d24..07e675e21 100644 --- a/src/test/java/io/nats/client/impl/DrainTests.java +++ b/src/test/java/io/nats/client/impl/DrainTests.java @@ -25,7 +25,11 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static io.nats.client.utils.TestBase.*; +import static io.nats.client.utils.ConnectionUtils.standardConnectionWait; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.TestBase.flushConnection; +import static io.nats.client.utils.ThreadUtils.park; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; public class DrainTests { @@ -33,8 +37,8 @@ public class DrainTests { @SuppressWarnings("resource") @Test public void testCloseOnDrainFailure() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - final Connection nc = standardConnection(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); + try (NatsTestServer ts = new NatsTestServer()) { + final Connection nc = standardConnectionWait(optionsBuilder(ts).maxReconnects(0).build()); nc.subscribe("draintest"); nc.flush(Duration.ofSeconds(1)); // Get the sub to the server, so drain has things to do @@ -47,9 +51,9 @@ public void testCloseOnDrainFailure() throws Exception { @Test public void testSimpleSubDrain() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -71,15 +75,15 @@ public void testSimpleSubDrain() throws Exception { assertTrue(tracker.get(1, TimeUnit.SECONDS)); assertFalse(sub.isActive()); - assertEquals(((NatsConnection) subCon).getConsumerCount(), 0); + assertEquals(0, ((NatsConnection) subCon).getConsumerCount()); } } @Test public void testSimpleDispatchDrain() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -89,7 +93,7 @@ public void testSimpleDispatchDrain() throws Exception { sleep(2000); // go slow so the main app can drain us }); d.subscribe("draintest"); - d.subscribe("draintest", (msg) -> { count.incrementAndGet(); }); + d.subscribe("draintest", (msg) -> count.incrementAndGet()); subCon.flush(Duration.ofSeconds(5)); // Get the sub to the server pubCon.publish("draintest", null); @@ -102,17 +106,17 @@ public void testSimpleDispatchDrain() throws Exception { CompletableFuture tracker = d.drain(Duration.ofSeconds(8)); assertTrue(tracker.get(10, TimeUnit.SECONDS)); // wait for the drain to complete - assertEquals(count.get(), 4); // Should get both, two times. + assertEquals(4, count.get()); // Should get both, two times. assertFalse(d.isActive()); - assertEquals(((NatsConnection) subCon).getConsumerCount(), 0); + assertEquals(0, ((NatsConnection) subCon).getConsumerCount()); } } @Test public void testSimpleConnectionDrain() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -142,16 +146,16 @@ public void testSimpleConnectionDrain() throws Exception { assertTrue(tracker.get(2, TimeUnit.SECONDS)); assertTrue(((NatsConnection) subCon).isDrained()); - assertEquals(count.get(), 2); // Should get both + assertEquals(2, count.get()); // Should get both assertSame(Connection.Status.CLOSED, subCon.getStatus()); } } @Test public void testConnectionDrainWithZeroTimeout() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -188,9 +192,9 @@ public void testConnectionDrainWithZeroTimeout() throws Exception { @Test public void testDrainWithZeroTimeout() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -218,9 +222,9 @@ public void testDrainWithZeroTimeout() throws Exception { @Test public void testSubDuringDrainThrows() { assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -245,9 +249,9 @@ public void testSubDuringDrainThrows() { @Test public void testCreateDispatcherDuringDrainThrows() { assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -271,9 +275,9 @@ public void testCreateDispatcherDuringDrainThrows() { @Test public void testUnsubDuringDrainIsNoop() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -307,16 +311,16 @@ public void testUnsubDuringDrainIsNoop() throws Exception { assertNotNull(msg); assertTrue(tracker.get(2, TimeUnit.SECONDS)); - assertEquals(count.get(), 2); // Should get both + assertEquals(2, count.get()); // Should get both assertSame(Connection.Status.CLOSED, subCon.getStatus()); } } @Test public void testDrainInMessageHandler() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -339,17 +343,17 @@ public void testDrainInMessageHandler() throws Exception { sleep(500); // give the msgs time to get to subCon assertTrue(tracker.get().get(5, TimeUnit.SECONDS)); // wait for the drain to complete - assertEquals(count.get(), 2); // Should get both + assertEquals(2, count.get()); // Should get both assertFalse(d.isActive()); - assertEquals(((NatsConnection) subCon).getConsumerCount(), 0); + assertEquals(0, ((NatsConnection) subCon).getConsumerCount()); } } @Test public void testDrainFutureMatches() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -385,7 +389,7 @@ public void testDrainFutureMatches() throws Exception { assertNotNull(msg); assertTrue(tracker.get(2, TimeUnit.SECONDS)); - assertEquals(count.get(), 2); // Should get both + assertEquals(2, count.get()); // Should get both assertSame(Connection.Status.CLOSED, subCon.getStatus()); } } @@ -393,18 +397,16 @@ public void testDrainFutureMatches() throws Exception { @Test public void testFirstTimeRequestReplyDuringDrain() { assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); Subscription sub = subCon.subscribe("draintest"); subCon.flush(Duration.ofSeconds(1)); // Get the sub to the server - Dispatcher d = pubCon.createDispatcher((msg) -> { - pubCon.publish(msg.getReplyTo(), null); - }); + Dispatcher d = pubCon.createDispatcher((msg) -> pubCon.publish(msg.getReplyTo(), null)); d.subscribe("reply"); pubCon.flush(Duration.ofSeconds(1)); // Get the sub to the server @@ -433,18 +435,16 @@ public void testFirstTimeRequestReplyDuringDrain() { @Test public void testRequestReplyDuringDrain() { assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); Subscription sub = subCon.subscribe("draintest"); subCon.flush(Duration.ofSeconds(1)); // Get the sub to the server - Dispatcher d = pubCon.createDispatcher((msg) -> { - pubCon.publish(msg.getReplyTo(), null); - }); + Dispatcher d = pubCon.createDispatcher((msg) -> pubCon.publish(msg.getReplyTo(), null)); d.subscribe("reply"); pubCon.flush(Duration.ofSeconds(1)); // Get the sub to the server @@ -476,8 +476,8 @@ public void testRequestReplyDuringDrain() { @Test public void testQueueHandoffWithDrain() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); final int total = 5_000; @@ -488,16 +488,14 @@ public void testQueueHandoffWithDrain() throws Exception { AtomicInteger count = new AtomicInteger(); Instant start = Instant.now(); Instant now = start; - Connection working = null; - NatsDispatcher workingD = null; - NatsDispatcher drainingD = null; + Connection working; + NatsDispatcher workingD; + NatsDispatcher drainingD; - Connection draining = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); + Connection draining = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); assertSame(Connection.Status.CONNECTED, draining.getStatus(), "Connected Status"); - drainingD = (NatsDispatcher) draining.createDispatcher((msg) -> { - count.incrementAndGet(); - }).subscribe("draintest", "queue"); + drainingD = (NatsDispatcher) draining.createDispatcher((msg) -> count.incrementAndGet()).subscribe("draintest", "queue"); draining.flush(Duration.ofSeconds(5)); Thread pubThread = new Thread(() -> { @@ -512,11 +510,9 @@ public void testQueueHandoffWithDrain() throws Exception { while (count.get() < total && Duration.between(start, now).compareTo(testTimeout) < 0) { - working = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); + working = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); assertSame(Connection.Status.CONNECTED, working.getStatus(), "Connected Status"); - workingD = (NatsDispatcher) working.createDispatcher((msg) -> { - count.incrementAndGet(); - }).subscribe("draintest", "queue"); + workingD = (NatsDispatcher) working.createDispatcher((msg) -> count.incrementAndGet()).subscribe("draintest", "queue"); working.flush(Duration.ofSeconds(5)); park(sleepBetweenDrains); @@ -542,9 +538,9 @@ public void testQueueHandoffWithDrain() throws Exception { @Test public void testDrainWithLotsOfMessages() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -576,15 +572,15 @@ public void testDrainWithLotsOfMessages() throws Exception { assertTrue(tracker.get(5, TimeUnit.SECONDS)); assertFalse(sub.isActive()); - assertEquals(((NatsConnection) subCon).getConsumerCount(), 0); + assertEquals(0, ((NatsConnection) subCon).getConsumerCount()); } } @Test public void testSlowAsyncDuringDrainCanFinishIfTime() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -614,7 +610,7 @@ public void testSlowAsyncDuringDrainCanFinishIfTime() throws Exception { assertTrue(tracker.get(10, TimeUnit.SECONDS)); assertTrue(((NatsConnection) subCon).isDrained()); - assertEquals(count.get(), 2); // Should get both + assertEquals(2, count.get()); // Should get both assertSame(Connection.Status.CLOSED, subCon.getStatus()); } } @@ -622,9 +618,9 @@ public void testSlowAsyncDuringDrainCanFinishIfTime() throws Exception { @Test public void testSlowAsyncDuringDrainCanBeInterrupted() throws Exception { ListenerForTesting listener = new ListenerForTesting(); - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).errorListener(listener).maxReconnects(0).build()); - Connection pubCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).errorListener(listener).maxReconnects(0).build()); + Connection pubCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); assertSame(Connection.Status.CONNECTED, pubCon.getStatus(), "Connected Status"); @@ -664,9 +660,9 @@ public void testSlowAsyncDuringDrainCanBeInterrupted() throws Exception { public void testThrowIfCantFlush() { assertThrows(TimeoutException.class, () -> { ListenerForTesting listener = new ListenerForTesting(); - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().connectionListener(listener).server(ts.getURI()).build())) { - assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = standardConnectionWait(optionsBuilder(ts).connectionListener(listener).build())) + { subCon.flush(Duration.ofSeconds(1)); // Get the sub to the server listener.prepForStatusChange(Events.DISCONNECTED); @@ -680,8 +676,8 @@ public void testThrowIfCantFlush() { @Test public void testThrowIfClosing() { assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection subCon = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection subCon = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertSame(Connection.Status.CONNECTED, subCon.getStatus(), "Connected Status"); subCon.close(); diff --git a/src/test/java/io/nats/client/impl/ErrorListenerTests.java b/src/test/java/io/nats/client/impl/ErrorListenerTests.java index 6bd7351a4..ee2ec5434 100644 --- a/src/test/java/io/nats/client/impl/ErrorListenerTests.java +++ b/src/test/java/io/nats/client/impl/ErrorListenerTests.java @@ -17,6 +17,7 @@ import io.nats.client.ConnectionListener.Events; import io.nats.client.support.Status; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Isolated; import java.io.IOException; import java.time.Duration; @@ -26,9 +27,13 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; -import static io.nats.client.utils.TestBase.*; +import static io.nats.client.utils.ConnectionUtils.standardCloseConnection; +import static io.nats.client.utils.ConnectionUtils.standardConnectionWait; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; +@Isolated public class ErrorListenerTests { @Test @@ -40,7 +45,7 @@ public void testLastError() throws Exception { try (NatsTestServer ts = new NatsTestServer(); NatsTestServer ts2 = new NatsTestServer(customArgs, false); //ts2 requires auth NatsTestServer ts3 = new NatsTestServer()) { - Options options = new Options.Builder() + Options options = optionsBuilder() .server(ts.getURI()) .server(ts2.getURI()) .server(ts3.getURI()) @@ -86,7 +91,7 @@ public void testClearLastError() throws Exception { try (NatsTestServer ts = new NatsTestServer(); NatsTestServer ts2 = new NatsTestServer(customArgs, false); //ts2 requires auth NatsTestServer ts3 = new NatsTestServer()) { - Options options = new Options.Builder() + Options options = optionsBuilder() .server(ts.getURI()) .server(ts2.getURI()) .server(ts3.getURI()) @@ -135,11 +140,10 @@ public void testErrorOnNoAuth() throws Exception { sleep(1000); // give the server time to get ready, otherwise sometimes this test flaps // See config file for user/pass // no or wrong u/p in the options is an error - Options options = new Options.Builder(). - server(ts.getURI()) - .maxReconnects(0) - .errorListener(listener) - .build(); + Options options = optionsBuilder(ts) + .maxReconnects(0) + .errorListener(listener) + .build(); try { Nats.connect(options); fail(); @@ -158,11 +162,10 @@ public void testErrorOnNoAuth() throws Exception { public void testExceptionOnBadDispatcher() throws Exception { ListenerForTesting listener = new ListenerForTesting(); try (NatsTestServer ts = new NatsTestServer()) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(0). - errorListener(listener). - build(); + Options options = optionsBuilder(ts) + .maxReconnects(0) + .errorListener(listener) + .build(); Connection nc = Nats.connect(options); try { Dispatcher d = nc.createDispatcher((msg) -> { @@ -193,12 +196,11 @@ public void testExceptionInErrorHandler() throws Exception { BadHandler listener = new BadHandler(); try (NatsTestServer ts = new NatsTestServer(customArgs, false)) { // See config file for user/pass - Options options = new Options.Builder() - .server(ts.getURI()) - .maxReconnects(0) - .errorListener(listener) - // skip this so we get an error userInfo("stephen", "password"). - .build(); + // don't put u/p in options + Options options = optionsBuilder(ts) + .maxReconnects(0) + .errorListener(listener) + .build(); assertThrows(IOException.class, () -> Nats.connect(options)); } } @@ -206,11 +208,8 @@ public void testExceptionInErrorHandler() throws Exception { @Test public void testExceptionInSlowConsumerHandler() throws Exception { BadHandler listener = new BadHandler(); - try (NatsTestServer ts = new NatsTestServer(false); - NatsConnection nc = (NatsConnection) Nats.connect(new Options.Builder(). - server(ts.getURI()). - errorListener(listener). - build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = Nats.connect(optionsBuilder(ts).errorListener(listener).build())) { Subscription sub = nc.subscribe("subject"); sub.setPendingLimits(1, -1); @@ -226,7 +225,7 @@ public void testExceptionInSlowConsumerHandler() throws Exception { nc.close(); // should force the exception listener through - assertTrue(nc.getStatisticsCollector().getExceptions() > 0); + assertTrue(nc.getStatistics().getExceptions() > 0); } } @@ -234,11 +233,7 @@ public void testExceptionInSlowConsumerHandler() throws Exception { public void testExceptionInExceptionHandler() throws Exception { BadHandler listener = new BadHandler(); try (NatsTestServer ts = new NatsTestServer()) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(0). - errorListener(listener). - build(); + Options options = optionsBuilder(ts).maxReconnects(0).errorListener(listener).build(); Connection nc = Nats.connect(options); try { Dispatcher d = nc.createDispatcher((msg) -> { @@ -256,7 +251,7 @@ public void testExceptionInExceptionHandler() throws Exception { } assertNull(msg); - assertEquals(((NatsConnection) nc).getStatisticsCollector().getExceptions(), 2); // 1 for the dispatcher, 1 for the handlers + assertEquals(2, nc.getStatistics().getExceptions()); // 1 for the dispatcher, 1 for the handlers } finally { standardCloseConnection(nc); } @@ -268,13 +263,12 @@ public void testDiscardedMessageFastProducer() throws Exception { int maxMessages = 10; ListenerForTesting listener = new ListenerForTesting(); try (NatsTestServer ts = new NatsTestServer()) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxMessagesInOutgoingQueue(maxMessages). - discardMessagesWhenOutgoingQueueFull(). - errorListener(listener). - pingInterval(Duration.ofSeconds(100)). // make this long so we don't ping during test - build(); + Options options = optionsBuilder(ts) + .maxMessagesInOutgoingQueue(maxMessages) + .discardMessagesWhenOutgoingQueueFull() + .errorListener(listener) + .pingInterval(Duration.ofSeconds(100)) // make this long so we don't ping during test + .build(); NatsConnection nc = (NatsConnection) Nats.connect(options); try { @@ -303,16 +297,14 @@ public void testDiscardedMessageFastProducer() throws Exception { public void testDiscardedMessageServerClosed() throws Exception { int maxMessages = 10; ListenerForTesting listener = new ListenerForTesting(); - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxMessagesInOutgoingQueue(maxMessages). - discardMessagesWhenOutgoingQueueFull(). - pingInterval(Duration.ofSeconds(100)). // make this long so we don't ping during test - connectionListener(listener). - errorListener(listener). - build(); - Connection nc = standardConnection(options); + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts) + .maxMessagesInOutgoingQueue(maxMessages) + .discardMessagesWhenOutgoingQueueFull() + .errorListener(listener) + .pingInterval(Duration.ofSeconds(100)) // make this long so we don't ping during test + .build(); + Connection nc = standardConnectionWait(options); try { nc.flush(Duration.ofSeconds(1)); // Get the sub to the server diff --git a/src/test/java/io/nats/client/impl/InfoHandlerTests.java b/src/test/java/io/nats/client/impl/InfoHandlerTests.java index 3890d8709..6efead94b 100644 --- a/src/test/java/io/nats/client/impl/InfoHandlerTests.java +++ b/src/test/java/io/nats/client/impl/InfoHandlerTests.java @@ -22,8 +22,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static org.junit.jupiter.api.Assertions.*; public class InfoHandlerTests { @Test @@ -33,17 +33,15 @@ public void testInitialInfo() throws IOException, InterruptedException { try (NatsServerProtocolMock ts = new NatsServerProtocolMock(null, customInfo)) { Connection nc = Nats.connect(ts.getURI()); try { - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); + assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); assertEquals("myid", nc.getServerInfo().getServerId(), "got custom info"); } finally { nc.close(); - assertTrue(Connection.Status.CLOSED == nc.getStatus(), "Closed Status"); + assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); } } } - - @Test public void testUnsolicitedInfo() throws IOException, InterruptedException, ExecutionException { String customInfo = "{\"server_id\":\"myid\", \"version\":\"9.9.99\"}"; @@ -69,9 +67,8 @@ public void testUnsolicitedInfo() throws IOException, InterruptedException, Exec w.write("PING\r\n"); w.flush(); - String pong = ""; - System.out.println("*** Mock Server @" + ts.getPort() + " waiting for PONG ..."); + String pong; try { pong = r.readLine(); } catch (Exception e) { @@ -91,15 +88,15 @@ public void testUnsolicitedInfo() throws IOException, InterruptedException, Exec try (NatsServerProtocolMock ts = new NatsServerProtocolMock(infoCustomizer, customInfo)) { Connection nc = Nats.connect(ts.getURI()); try { - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); + assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); assertEquals("myid", nc.getServerInfo().getServerId(), "got custom info"); sendInfo.complete(Boolean.TRUE); - assertTrue(gotPong.get().booleanValue(), "Got pong."); // Server round tripped so we should have new info + assertTrue(gotPong.get(), "Got pong."); // Server round tripped so we should have new info assertEquals("replacement", nc.getServerInfo().getServerId(), "got replacement info"); } finally { nc.close(); - assertTrue(Connection.Status.CLOSED == nc.getStatus(), "Closed Status"); + assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); } } } @@ -114,7 +111,6 @@ public void testLDM() throws IOException, InterruptedException, ExecutionExcepti CompletableFuture connectLDM = new CompletableFuture<>(); NatsServerProtocolMock.Customizer infoCustomizer = (ts, r, w) -> { - // Wait for client to be ready. try { sendInfo.get(); @@ -132,9 +128,8 @@ public void testLDM() throws IOException, InterruptedException, ExecutionExcepti w.write("PING\r\n"); w.flush(); - String pong = ""; - System.out.println("*** Mock Server @" + ts.getPort() + " waiting for PONG ..."); + String pong; try { pong = r.readLine(); } catch (Exception e) { @@ -153,16 +148,15 @@ public void testLDM() throws IOException, InterruptedException, ExecutionExcepti try (NatsServerProtocolMock ts = new NatsServerProtocolMock(infoCustomizer, customInfo)) { - Options options = new Options.Builder().server(ts.getURI()).connectionListener(new ConnectionListener() { - @Override - public void connectionEvent(Connection conn, Events type) { - if (type.equals(Events.LAME_DUCK)) connectLDM.complete(type); - } - }).build(); + ConnectionListener cl = (conn, type) -> { + if (type.equals(ConnectionListener.Events.LAME_DUCK)) connectLDM.complete(type); + }; + + Options options = optionsBuilder().server(ts.getURI()).connectionListener(cl).build(); Connection nc = Nats.connect(options); try { - assertTrue(Connection.Status.CONNECTED == nc.getStatus(), "Connected Status"); + assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); assertEquals("myid", nc.getServerInfo().getServerId(), "got custom info"); sendInfo.complete(Boolean.TRUE); @@ -170,12 +164,12 @@ public void connectionEvent(Connection conn, Events type) { assertEquals("replacement", nc.getServerInfo().getServerId(), "got replacement info"); } finally { nc.close(); - assertTrue(Connection.Status.CLOSED == nc.getStatus(), "Closed Status"); + assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); } } ConnectionListener.Events event = connectLDM.get(5, TimeUnit.SECONDS); - assertEquals(event, ConnectionListener.Events.LAME_DUCK); + assertEquals(ConnectionListener.Events.LAME_DUCK, event); System.out.println(event); } } \ No newline at end of file diff --git a/src/test/java/io/nats/client/impl/JetStreamConsumerTests.java b/src/test/java/io/nats/client/impl/JetStreamConsumerTests.java index cb49fd537..318dadbe9 100644 --- a/src/test/java/io/nats/client/impl/JetStreamConsumerTests.java +++ b/src/test/java/io/nats/client/impl/JetStreamConsumerTests.java @@ -15,7 +15,7 @@ import io.nats.client.*; import io.nats.client.api.ConsumerConfiguration; -import io.nats.client.utils.TestBase; +import io.nats.client.utils.VersionUtils; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicLong; import static io.nats.client.support.NatsJetStreamClientError.JsSubOrderedNotAllowOnQueues; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; public class JetStreamConsumerTests extends JetStreamTestBase { @@ -59,35 +60,29 @@ protected Boolean beforeQueueProcessorImpl(NatsMessage msg) { @Test public void testOrderedConsumerSync() throws Exception { - jsServer.run(nc -> { - // Setup - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - + runInJsServer(nc -> { + JetStreamTestingContext jstc = new JetStreamTestingContext(nc); // Get this in place before any subscriptions are made - ((NatsJetStream)js)._pushOrderedMessageManagerFactory = OrderedTestDropSimulator::new; + jstc.js._pushOrderedMessageManagerFactory = OrderedTestDropSimulator::new; // Test queue exception IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, - () -> js.subscribe(tsc.subject(), QUEUE, PushSubscribeOptions.builder().ordered(true).build())); + () -> jstc.js.subscribe(jstc.subject(), random(), PushSubscribeOptions.builder().ordered(true).build())); assertTrue(iae.getMessage().contains(JsSubOrderedNotAllowOnQueues.id())); // Setup sync subscription - _testOrderedConsumerSync(js, tsc, null, PushSubscribeOptions.builder().ordered(true).build()); + _testOrderedConsumerSync(jstc, null, PushSubscribeOptions.builder().ordered(true).build()); - String consumerName = prefix(); - _testOrderedConsumerSync(js, tsc, consumerName, PushSubscribeOptions.builder().name(consumerName).ordered(true).build()); + _testOrderedConsumerSync(jstc, jstc.consumerName(), PushSubscribeOptions.builder().name(jstc.consumerName()).ordered(true).build()); }); } - private static void _testOrderedConsumerSync(JetStream js, TestingStreamContainer tsc, String consumerNamePrefix, PushSubscribeOptions pso) throws IOException, JetStreamApiException, TimeoutException, InterruptedException { - JetStreamSubscription sub = js.subscribe(tsc.subject(), pso); + private static void _testOrderedConsumerSync(JetStreamTestingContext jstc, String consumerNamePrefix, PushSubscribeOptions pso) throws IOException, JetStreamApiException, TimeoutException, InterruptedException { + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), pso); String firstConsumerName = validateOrderedConsumerNamePrefix(sub, consumerNamePrefix); // Published messages will be intercepted by the OrderedTestDropSimulator - jsPublish(js, tsc.subject(), 101, 6); + jsPublish(jstc.js, jstc.subject(), 101, 6); // Loop through the messages to make sure I get stream sequence 1 to 6 int expectedStreamSeq = 1; @@ -123,27 +118,28 @@ private static void reValidateOrderedConsumerNamePrefix(JetStreamSubscription su @Test public void testOrderedConsumerAsync() throws Exception { - jsServer.run(nc -> { - // Setup - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); - _testOrderedConsumerAsync(nc, jsm, js, null, PushSubscribeOptions.builder().ordered(true).build()); - String customName = variant(); - _testOrderedConsumerAsync(nc, jsm, js, customName, PushSubscribeOptions.builder().name(customName).ordered(true).build()); + runInJsServer(nc -> { + // without name (prefix) + JetStreamTestingContext jstc = new JetStreamTestingContext(nc); + _testOrderedConsumerAsync(nc, jstc, null, + PushSubscribeOptions.builder().ordered(true).build()); + + // with name (prefix) + jstc = new JetStreamTestingContext(nc); + _testOrderedConsumerAsync(nc, jstc, jstc.consumerName(), + PushSubscribeOptions.builder().name(jstc.consumerName()).ordered(true).build()); }); } - private static void _testOrderedConsumerAsync(Connection nc, JetStreamManagement jsm, JetStream js, String consumerNamePrefix, PushSubscribeOptions pso) throws JetStreamApiException, IOException, TimeoutException, InterruptedException { - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - + private static void _testOrderedConsumerAsync(Connection nc, JetStreamTestingContext jstc, String consumerNamePrefix, PushSubscribeOptions pso) throws JetStreamApiException, IOException, TimeoutException, InterruptedException { // Get this in place before any subscriptions are made - ((NatsJetStream) js)._pushOrderedMessageManagerFactory = OrderedTestDropSimulator::new; + jstc.js._pushOrderedMessageManagerFactory = OrderedTestDropSimulator::new; // We'll need a dispatcher Dispatcher d = nc.createDispatcher(); // Test queue exception - IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), QUEUE, d, m -> {}, false, pso)); + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), random(), d, m -> {}, false, pso)); assertTrue(iae.getMessage().contains(JsSubOrderedNotAllowOnQueues.id())); // Set up an async subscription @@ -158,11 +154,11 @@ private static void _testOrderedConsumerAsync(Connection nc, JetStreamManagement msgLatch.countDown(); }; - JetStreamSubscription sub = js.subscribe(tsc.subject(), d, handler, false, pso); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), d, handler, false, pso); String firstConsumerName = validateOrderedConsumerNamePrefix(sub, consumerNamePrefix); // publish after sub b/c interceptor is set during sub, so before messages come in - jsPublish(js, tsc.subject(), 201, 6); + jsPublish(jstc.js, jstc.subject(), 201, 6); // wait for the messages awaitAndAssert(msgLatch); @@ -257,37 +253,35 @@ protected Boolean beforeQueueProcessorImpl(NatsMessage msg) { @Test public void testHeartbeatError() throws Exception { - ListenerForTesting listenerForTesting = new ListenerForTesting(); - runInJsServer(listenerForTesting, nc -> { - TestingStreamContainer tsc = new TestingStreamContainer(nc); - - JetStream js = nc.jetStream(); + ListenerForTesting listener = new ListenerForTesting(); + runInJsServer(listener, nc -> { + JetStreamTestingContext jstc = new JetStreamTestingContext(nc); Dispatcher d = nc.createDispatcher(); ConsumerConfiguration cc = ConsumerConfiguration.builder().idleHeartbeat(100).build(); - + JetStream js = jstc.js; PushSubscribeOptions pso = PushSubscribeOptions.builder().configuration(cc).build(); SimulatorState state = setupFactory(js); - JetStreamSubscription sub = js.subscribe(tsc.subject(), pso); - validate(sub, listenerForTesting, state, null); + JetStreamSubscription sub = js.subscribe(jstc.subject(), pso); + validate(sub, listener, state, null); state = setupFactory(js); - sub = js.subscribe(tsc.subject(), d, m -> {}, false, pso); - validate(sub, listenerForTesting, state, d); + sub = js.subscribe(jstc.subject(), d, m -> {}, false, pso); + validate(sub, listener, state, d); pso = PushSubscribeOptions.builder().ordered(true).configuration(cc).build(); state = setupOrderedFactory(js); - sub = js.subscribe(tsc.subject(), pso); - validate(sub, listenerForTesting, state, null); + sub = js.subscribe(jstc.subject(), pso); + validate(sub, listener, state, null); state = setupOrderedFactory(js); - sub = js.subscribe(tsc.subject(), d, m -> {}, false, pso); - validate(sub, listenerForTesting, state, d); + sub = js.subscribe(jstc.subject(), d, m -> {}, false, pso); + validate(sub, listener, state, d); state = setupPullFactory(js); - sub = js.subscribe(tsc.subject(), PullSubscribeOptions.DEFAULT_PULL_OPTS); + sub = js.subscribe(jstc.subject(), PullSubscribeOptions.DEFAULT_PULL_OPTS); sub.pull(PullRequestOptions.builder(1).idleHeartbeat(100).expiresIn(2000).build()); - validate(sub, listenerForTesting, state, null); + validate(sub, listener, state, null); }); } @@ -342,53 +336,48 @@ private static SimulatorState setupPullFactory(JetStream js) { @Test public void testMultipleSubjectFilters() throws Exception { - jsServer.run(TestBase::atLeast2_10, nc -> { - // Setup - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(nc, 2); - - jsPublish(js, tsc.subject(0), 10); - jsPublish(js, tsc.subject(1), 5); + runInLrServer(VersionUtils::atLeast2_10, (nc, jsm, js) -> { + JetStreamTestingContext jstc = new JetStreamTestingContext(nc, 2); + jsPublish(jstc.js, jstc.subject(0), 10); + jsPublish(jstc.js, jstc.subject(1), 5); // push ephemeral - ConsumerConfiguration cc = ConsumerConfiguration.builder().filterSubjects(tsc.subject(0), tsc.subject(1)).build(); - JetStreamSubscription sub = js.subscribe(null, PushSubscribeOptions.builder().configuration(cc).build()); - validateMultipleSubjectFilterSub(sub, tsc.subject(0)); + ConsumerConfiguration cc = ConsumerConfiguration.builder().filterSubjects(jstc.subject(0), jstc.subject(1)).build(); + JetStreamSubscription sub = jstc.js.subscribe(null, PushSubscribeOptions.builder().configuration(cc).build()); + validateMultipleSubjectFilterSub(sub, jstc.subject(0)); // pull ephemeral - sub = js.subscribe(null, PullSubscribeOptions.builder().configuration(cc).build()); + sub = jstc.js.subscribe(null, PullSubscribeOptions.builder().configuration(cc).build()); sub.pullExpiresIn(15, 1000); - validateMultipleSubjectFilterSub(sub, tsc.subject(0)); + validateMultipleSubjectFilterSub(sub, jstc.subject(0)); // push named - String name = name(); - cc = ConsumerConfiguration.builder().filterSubjects(tsc.subject(0), tsc.subject(1)).name(name).deliverSubject(deliver()).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); - sub = js.subscribe(null, PushSubscribeOptions.builder().configuration(cc).build()); - validateMultipleSubjectFilterSub(sub, tsc.subject(0)); - - name = name(); - cc = ConsumerConfiguration.builder().filterSubjects(tsc.subject(0), tsc.subject(1)).name(name).deliverSubject(deliver()).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); - sub = js.subscribe(null, PushSubscribeOptions.bind(tsc.stream, name)); - validateMultipleSubjectFilterSub(sub, tsc.subject(0)); + String name = random(); + cc = ConsumerConfiguration.builder().filterSubjects(jstc.subject(0), jstc.subject(1)).name(name).deliverSubject(random()).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); + sub = jstc.js.subscribe(null, PushSubscribeOptions.builder().configuration(cc).build()); + validateMultipleSubjectFilterSub(sub, jstc.subject(0)); + + name = random(); + cc = ConsumerConfiguration.builder().filterSubjects(jstc.subject(0), jstc.subject(1)).name(name).deliverSubject(random()).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); + sub = jstc.js.subscribe(null, PushSubscribeOptions.bind(jstc.stream, name)); + validateMultipleSubjectFilterSub(sub, jstc.subject(0)); // pull named - name = name(); - cc = ConsumerConfiguration.builder().filterSubjects(tsc.subject(0), tsc.subject(1)).name(name).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); - sub = js.subscribe(null, PullSubscribeOptions.builder().configuration(cc).build()); + name = random(); + cc = ConsumerConfiguration.builder().filterSubjects(jstc.subject(0), jstc.subject(1)).name(name).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); + sub = jstc.js.subscribe(null, PullSubscribeOptions.builder().configuration(cc).build()); sub.pullExpiresIn(15, 1000); - validateMultipleSubjectFilterSub(sub, tsc.subject(0)); + validateMultipleSubjectFilterSub(sub, jstc.subject(0)); - name = name(); - cc = ConsumerConfiguration.builder().filterSubjects(tsc.subject(0), tsc.subject(1)).name(name).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); - sub = js.subscribe(null, PullSubscribeOptions.bind(tsc.stream, name)); + name = random(); + cc = ConsumerConfiguration.builder().filterSubjects(jstc.subject(0), jstc.subject(1)).name(name).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); + sub = jstc.js.subscribe(null, PullSubscribeOptions.bind(jstc.stream, name)); sub.pullExpiresIn(15, 1000); - validateMultipleSubjectFilterSub(sub, tsc.subject(0)); + validateMultipleSubjectFilterSub(sub, jstc.subject(0)); }); } @@ -412,15 +401,13 @@ private static void validateMultipleSubjectFilterSub(JetStreamSubscription sub, @Test public void testRaiseStatusWarnings1194() throws Exception { - ListenerForTesting listenerForTesting = new ListenerForTesting(false, false); - runInJsServer(listenerForTesting, nc -> { + ListenerForTesting listener = new ListenerForTesting(false, false); + runInLrServer(listener, (nc, jstc) -> { // Setup - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - StreamContext streamContext = nc.getStreamContext(tsc.stream); + StreamContext streamContext = nc.getStreamContext(jstc.stream); // Setting maxBatch=1, so we shouldn't allow fetching more messages at once. - ConsumerConfiguration consumerConfig = ConsumerConfiguration.builder().filterSubject(tsc.subject()).maxBatch(1).build(); + ConsumerConfiguration consumerConfig = ConsumerConfiguration.builder().filterSubject(jstc.subject()).maxBatch(1).build(); ConsumerContext consumerContext = streamContext.createOrUpdateConsumer(consumerConfig); int count = 0; @@ -439,7 +426,7 @@ public void testRaiseStatusWarnings1194() throws Exception { } } assertEquals(0, count); - assertEquals(0, listenerForTesting.getPullStatusWarnings().size()); + assertEquals(0, listener.getPullStatusWarnings().size()); fco = FetchConsumeOptions.builder() .maxMessages(100) @@ -454,7 +441,7 @@ public void testRaiseStatusWarnings1194() throws Exception { } } assertEquals(0, count); - assertEquals(1, listenerForTesting.getPullStatusWarnings().size()); + assertEquals(1, listener.getPullStatusWarnings().size()); }); } } diff --git a/src/test/java/io/nats/client/impl/JetStreamGeneralTests.java b/src/test/java/io/nats/client/impl/JetStreamGeneralTests.java index eb5bb4fcb..ae6e50631 100644 --- a/src/test/java/io/nats/client/impl/JetStreamGeneralTests.java +++ b/src/test/java/io/nats/client/impl/JetStreamGeneralTests.java @@ -16,7 +16,7 @@ import io.nats.client.*; import io.nats.client.api.*; import io.nats.client.support.NatsJetStreamUtil; -import io.nats.client.support.RandomUtils; +import io.nats.client.utils.LongRunningServer; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -25,21 +25,26 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import static io.nats.client.api.ConsumerConfiguration.*; import static io.nats.client.support.NatsConstants.EMPTY; import static io.nats.client.support.NatsJetStreamClientError.*; +import static io.nats.client.utils.ConnectionUtils.longConnectionWait; +import static io.nats.client.utils.ConnectionUtils.standardConnectionWait; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.VersionUtils.*; import static org.junit.jupiter.api.Assertions.*; public class JetStreamGeneralTests extends JetStreamTestBase { @Test public void testJetStreamContextCreate() throws Exception { - jsServer.run(nc -> { - TestingStreamContainer tsc = new TestingStreamContainer(nc); // tries management functions - nc.jetStreamManagement().getAccountStatistics(); // another management - nc.jetStream().publish(tsc.subject(), dataBytes(1)); + runInLrServer((nc, jstc) -> { + jstc.jsm.getAccountStatistics(); // another management + jstc.js.publish(jstc.subject(), dataBytes(1)); }); } @@ -48,7 +53,7 @@ public void testJetNotEnabled() throws Exception { runInServer(nc -> { // get normal context, try to do an operation JetStream js = nc.jetStream(); - assertThrows(IOException.class, () -> js.subscribe(SUBJECT)); + assertThrows(IOException.class, () -> js.subscribe(random())); // get management context, try to do an operation JetStreamManagement jsm = nc.jetStreamManagement(); @@ -58,28 +63,27 @@ public void testJetNotEnabled() throws Exception { @Test public void testJetEnabledGoodAccount() throws Exception { - try (NatsTestServer ts = new NatsTestServer("src/test/resources/js_authorization.conf", false, true)) { - Options options = new Options.Builder().server(ts.getURI()) - .userInfo("serviceup".toCharArray(), "uppass".toCharArray()).build(); - Connection nc = standardConnection(options); - nc.jetStreamManagement(); - nc.jetStream(); + try (NatsTestServer ts = NatsTestServer.configuredJsServer("js_authorization.conf")) { + Options options = optionsBuilder(ts) + .userInfo("serviceup".toCharArray(), "uppass".toCharArray()).build(); + try (Connection nc = longConnectionWait(options)) { + nc.jetStreamManagement(); + nc.jetStream(); + } } } @Test public void testJetStreamPublishDefaultOptions() throws Exception { - jsServer.run(nc -> { - TestingStreamContainer tsc = new TestingStreamContainer(nc); - JetStream js = nc.jetStream(); - PublishAck ack = jsPublish(js, tsc.subject()); + runInLrServer((nc, jstc) -> { + PublishAck ack = jsPublish(jstc.js, jstc.subject()); assertEquals(1, ack.getSeqno()); }); } @Test public void testConnectionClosing() throws Exception { - runInJsServer(nc -> { + runInJsServer(null, null, nc -> { nc.close(); assertThrows(IOException.class, nc::jetStream); assertThrows(IOException.class, nc::jetStreamManagement); @@ -88,7 +92,7 @@ public void testConnectionClosing() throws Exception { @Test public void testCreateWithOptionsForCoverage() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { JetStreamOptions jso = JetStreamOptions.builder().build(); nc.jetStream(jso); nc.jetStreamManagement(jso); @@ -107,227 +111,231 @@ public void testMiscMetaDataCoverage() { @Test public void testJetStreamSubscribe() throws Exception { - jsServer.run(nc -> { - - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - jsPublish(js, tsc.subject()); + runInLrServer((nc, jstc) -> { + jsPublish(jstc.js, jstc.subject()); // default ephemeral subscription. - Subscription s = js.subscribe(tsc.subject()); + Subscription s = jstc.js.subscribe(jstc.subject()); Message m = s.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); assertEquals(DATA, new String(m.getData())); - List names = jsm.getConsumerNames(tsc.stream); + List names = jstc.jsm.getConsumerNames(jstc.stream); assertEquals(1, names.size()); // default subscribe options // ephemeral subscription. - s = js.subscribe(tsc.subject(), PushSubscribeOptions.builder().build()); + s = jstc.js.subscribe(jstc.subject(), PushSubscribeOptions.builder().build()); m = s.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); assertEquals(DATA, new String(m.getData())); - names = jsm.getConsumerNames(tsc.stream); + names = jstc.jsm.getConsumerNames(jstc.stream); assertEquals(2, names.size()); // set the stream - PushSubscribeOptions pso = PushSubscribeOptions.builder().stream(tsc.stream).durable(DURABLE).build(); - s = js.subscribe(tsc.subject(), pso); + String durable = random(); + PushSubscribeOptions pso = PushSubscribeOptions.builder().stream(jstc.stream).durable(durable).build(); + s = jstc.js.subscribe(jstc.subject(), pso); m = s.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); assertEquals(DATA, new String(m.getData())); - names = jsm.getConsumerNames(tsc.stream); + names = jstc.jsm.getConsumerNames(jstc.stream); assertEquals(3, names.size()); // coverage Dispatcher dispatcher = nc.createDispatcher(); - js.subscribe(tsc.subject()); - js.subscribe(tsc.subject(), (PushSubscribeOptions)null); - js.subscribe(tsc.subject(), QUEUE, null); - js.subscribe(tsc.subject(), dispatcher, mh -> {}, false); - js.subscribe(tsc.subject(), dispatcher, mh -> {}, false, null); - js.subscribe(tsc.subject(), QUEUE, dispatcher, mh -> {}, false, null); + jstc.js.subscribe(jstc.subject()); + jstc.js.subscribe(jstc.subject(), (PushSubscribeOptions)null); + jstc.js.subscribe(jstc.subject(), random(), null); + jstc.js.subscribe(jstc.subject(), dispatcher, mh -> {}, false); + jstc.js.subscribe(jstc.subject(), dispatcher, mh -> {}, false, null); + jstc.js.subscribe(jstc.subject(), random(), dispatcher, mh -> {}, false, null); // bind with w/o subject - jsm.addOrUpdateConsumer(tsc.stream, + durable = random(); + String deliver = random(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, builder() - .durable(durable(101)) - .deliverSubject(deliver(101)) + .durable(durable) + .deliverSubject(deliver) .build()); - PushSubscribeOptions psoBind = PushSubscribeOptions.bind(tsc.stream, durable(101)); - unsubscribeEnsureNotBound(js.subscribe(null, psoBind)); - unsubscribeEnsureNotBound(js.subscribe("", psoBind)); - JetStreamSubscription sub = js.subscribe(null, dispatcher, mh -> {}, false, psoBind); + PushSubscribeOptions psoBind = PushSubscribeOptions.bind(jstc.stream, durable); + unsubscribeEnsureNotBound(jstc.js.subscribe(null, psoBind)); + unsubscribeEnsureNotBound(jstc.js.subscribe("", psoBind)); + JetStreamSubscription sub = jstc.js.subscribe(null, dispatcher, mh -> {}, false, psoBind); unsubscribeEnsureNotBound(dispatcher, sub); - js.subscribe("", dispatcher, mh -> {}, false, psoBind); + jstc.js.subscribe("", dispatcher, mh -> {}, false, psoBind); - jsm.addOrUpdateConsumer(tsc.stream, + durable = random(); + deliver = random(); + String queue = random(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, builder() - .durable(durable(102)) - .deliverSubject(deliver(102)) - .deliverGroup(queue(102)) + .durable(durable) + .deliverSubject(deliver) + .deliverGroup(queue) .build()); - psoBind = PushSubscribeOptions.bind(tsc.stream, durable(102)); - unsubscribeEnsureNotBound(js.subscribe(null, queue(102), psoBind)); - unsubscribeEnsureNotBound(js.subscribe("", queue(102), psoBind)); - sub = js.subscribe(null, queue(102), dispatcher, mh -> {}, false, psoBind); + psoBind = PushSubscribeOptions.bind(jstc.stream, durable); + unsubscribeEnsureNotBound(jstc.js.subscribe(null, queue, psoBind)); + unsubscribeEnsureNotBound(jstc.js.subscribe("", queue, psoBind)); + sub = jstc.js.subscribe(null, queue, dispatcher, mh -> {}, false, psoBind); unsubscribeEnsureNotBound(dispatcher, sub); - js.subscribe("", queue(102), dispatcher, mh -> {}, false, psoBind); + jstc.js.subscribe("", queue, dispatcher, mh -> {}, false, psoBind); if (atLeast2_9_0(nc)) { - ConsumerConfiguration cc = builder().name(name(1)).build(); + String name = random(); + ConsumerConfiguration cc = builder().name(name).build(); pso = PushSubscribeOptions.builder().configuration(cc).build(); - sub = js.subscribe(tsc.subject(), pso); + sub = jstc.js.subscribe(jstc.subject(), pso); m = sub.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); assertEquals(DATA, new String(m.getData())); ConsumerInfo ci = sub.getConsumerInfo(); - assertEquals(name(1), ci.getName()); - assertEquals(name(1), ci.getConsumerConfiguration().getName()); + assertEquals(name, ci.getName()); + assertEquals(name, ci.getConsumerConfiguration().getName()); assertNull(ci.getConsumerConfiguration().getDurable()); - cc = builder().durable(durable(1)).build(); + durable = random(); + cc = builder().durable(durable).build(); pso = PushSubscribeOptions.builder().configuration(cc).build(); - sub = js.subscribe(tsc.subject(), pso); + sub = jstc.js.subscribe(jstc.subject(), pso); m = sub.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); assertEquals(DATA, new String(m.getData())); ci = sub.getConsumerInfo(); - assertEquals(durable(1), ci.getName()); - assertEquals(durable(1), ci.getConsumerConfiguration().getName()); - assertEquals(durable(1), ci.getConsumerConfiguration().getDurable()); + assertEquals(durable, ci.getName()); + assertEquals(durable, ci.getConsumerConfiguration().getName()); + assertEquals(durable, ci.getConsumerConfiguration().getDurable()); - cc = builder().durable(name(2)).name(name(2)).build(); + String durName = random(); + cc = builder().durable(durName).name(durName).build(); pso = PushSubscribeOptions.builder().configuration(cc).build(); - sub = js.subscribe(tsc.subject(), pso); + sub = jstc.js.subscribe(jstc.subject(), pso); m = sub.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); assertEquals(DATA, new String(m.getData())); ci = sub.getConsumerInfo(); - assertEquals(name(2), ci.getName()); - assertEquals(name(2), ci.getConsumerConfiguration().getName()); - assertEquals(name(2), ci.getConsumerConfiguration().getDurable()); + assertEquals(durName, ci.getName()); + assertEquals(durName, ci.getConsumerConfiguration().getName()); + assertEquals(durName, ci.getConsumerConfiguration().getDurable()); // test opt out JetStreamOptions jso = JetStreamOptions.builder().optOut290ConsumerCreate(true).build(); JetStream jsOptOut = nc.jetStream(jso); - ConsumerConfiguration ccOptOut = builder().name(name(99)).build(); + ConsumerConfiguration ccOptOut = builder().name(random()).build(); PushSubscribeOptions psoOptOut = PushSubscribeOptions.builder().configuration(ccOptOut).build(); - assertClientError(JsConsumerCreate290NotAvailable, () -> jsOptOut.subscribe(tsc.subject(), psoOptOut)); + assertClientError(JsConsumerCreate290NotAvailable, () -> jsOptOut.subscribe(jstc.subject(), psoOptOut)); } }); } @Test public void testJetStreamSubscribeLenientSubject() throws Exception { - jsServer.run(nc -> { - TestingStreamContainer tsc = new TestingStreamContainer(nc); - JetStream js = nc.jetStream(); + runInLrServer((nc, jstc) -> { Dispatcher d = nc.createDispatcher(); - js.subscribe(tsc.subject(), (PushSubscribeOptions)null); - js.subscribe(tsc.subject(), null, (PushSubscribeOptions)null); // queue name is not required, just a weird way to call this api - js.subscribe(tsc.subject(), d, m -> {}, false, (PushSubscribeOptions)null); - js.subscribe(tsc.subject(), null, d, m -> {}, false, (PushSubscribeOptions)null); // queue name is not required, just a weird way to call this api + jstc.js.subscribe(jstc.subject(), (PushSubscribeOptions)null); + jstc.js.subscribe(jstc.subject(), null, (PushSubscribeOptions)null); // queue name is not required, just a weird way to call this api + jstc.js.subscribe(jstc.subject(), d, m -> {}, false, (PushSubscribeOptions)null); + jstc.js.subscribe(jstc.subject(), null, d, m -> {}, false, (PushSubscribeOptions)null); // queue name is not required, just a weird way to call this api - PushSubscribeOptions pso = ConsumerConfiguration.builder().filterSubject(tsc.subject()).buildPushSubscribeOptions(); - js.subscribe(null, pso); - js.subscribe(null, null, pso); - js.subscribe(null, d, m -> {}, false, pso); - js.subscribe(null, null, d, m -> {}, false, pso); + PushSubscribeOptions pso = ConsumerConfiguration.builder().filterSubject(jstc.subject()).buildPushSubscribeOptions(); + jstc.js.subscribe(null, pso); + jstc.js.subscribe(null, null, pso); + jstc.js.subscribe(null, d, m -> {}, false, pso); + jstc.js.subscribe(null, null, d, m -> {}, false, pso); PushSubscribeOptions psoF = ConsumerConfiguration.builder().buildPushSubscribeOptions(); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, psoF)); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, psoF)); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, null, psoF)); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, d, m -> {}, false, psoF)); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, null, d, m -> {}, false, psoF)); - - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, (PushSubscribeOptions)null)); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, (PushSubscribeOptions)null)); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, null, (PushSubscribeOptions)null)); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, d, m -> {}, false, (PushSubscribeOptions)null)); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, null, d, m -> {}, false, (PushSubscribeOptions)null)); - - PullSubscribeOptions lso = ConsumerConfiguration.builder().filterSubject(tsc.subject()).buildPullSubscribeOptions(); - js.subscribe(null, lso); - js.subscribe(null, d, m -> {}, lso); + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, psoF)); + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, psoF)); + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, null, psoF)); + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, d, m -> {}, false, psoF)); + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, null, d, m -> {}, false, psoF)); + + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, (PushSubscribeOptions)null)); + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, (PushSubscribeOptions)null)); + //noinspection RedundantCast + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, null, (PushSubscribeOptions)null)); + //noinspection RedundantCast + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, d, m -> {}, false, (PushSubscribeOptions)null)); + //noinspection RedundantCast + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, null, d, m -> {}, false, (PushSubscribeOptions)null)); + + PullSubscribeOptions lso = ConsumerConfiguration.builder().filterSubject(jstc.subject()).buildPullSubscribeOptions(); + jstc.js.subscribe(null, lso); + jstc.js.subscribe(null, d, m -> {}, lso); PullSubscribeOptions lsoF = ConsumerConfiguration.builder().buildPullSubscribeOptions(); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, lsoF)); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, d, m -> {}, lsoF)); + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, lsoF)); + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, d, m -> {}, lsoF)); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, (PullSubscribeOptions)null)); - assertThrows(IllegalArgumentException.class, () -> js.subscribe(null, d, m -> {}, (PullSubscribeOptions)null)); + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, (PullSubscribeOptions)null)); + //noinspection RedundantCast + assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(null, d, m -> {}, (PullSubscribeOptions)null)); }); } @Test public void testJetStreamSubscribeErrors() throws Exception { - jsServer.run(nc -> { - JetStream js = nc.jetStream(); - + runInLrServer((nc, jstc) -> { + String stream = random(); // stream not found - PushSubscribeOptions psoInvalidStream = PushSubscribeOptions.builder().stream(STREAM).build(); - assertThrows(JetStreamApiException.class, () -> js.subscribe(SUBJECT, psoInvalidStream)); + PushSubscribeOptions psoInvalidStream = PushSubscribeOptions.builder().stream(stream).build(); + assertThrows(JetStreamApiException.class, () -> jstc.js.subscribe(random(), psoInvalidStream)); Dispatcher d = nc.createDispatcher(); for (String bad : BAD_SUBJECTS_OR_QUEUES) { if (bad == null || bad.isEmpty()) { // subject - IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(bad)); + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(bad)); assertTrue(iae.getMessage().startsWith("Subject")); - assertClientError(JsSubSubjectNeededToLookupStream, () -> js.subscribe(bad, (PushSubscribeOptions)null)); + assertClientError(JsSubSubjectNeededToLookupStream, () -> jstc.js.subscribe(bad, (PushSubscribeOptions)null)); } else { // subject - IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(bad)); + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(bad)); assertTrue(iae.getMessage().startsWith("Subject")); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(bad, (PushSubscribeOptions)null)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(bad, (PushSubscribeOptions)null)); assertTrue(iae.getMessage().startsWith("Subject")); // queue - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, bad, null)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(random(), bad, null)); assertTrue(iae.getMessage().startsWith("Queue")); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, bad, d, m -> {}, false, null)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(random(), bad, d, m -> {}, false, null)); assertTrue(iae.getMessage().startsWith("Queue")); } } // dispatcher - IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, null, null, false)); + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(random(), null, null, false)); assertTrue(iae.getMessage().startsWith("Dispatcher")); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, null, null, false, null)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(random(), null, null, false, null)); assertTrue(iae.getMessage().startsWith("Dispatcher")); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, QUEUE, null, null, false, null)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(random(), random(), null, null, false, null)); assertTrue(iae.getMessage().startsWith("Dispatcher")); // handler Dispatcher dispatcher = nc.createDispatcher(); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, dispatcher, null, false)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(random(), dispatcher, null, false)); assertTrue(iae.getMessage().startsWith("Handler")); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, dispatcher, null, false, null)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(random(), dispatcher, null, false, null)); assertTrue(iae.getMessage().startsWith("Handler")); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(SUBJECT, QUEUE, dispatcher, null, false, null)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(random(), random(), dispatcher, null, false, null)); assertTrue(iae.getMessage().startsWith("Handler")); }); } @Test public void testFilterSubjectEphemeral() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - String subjectWild = SUBJECT + ".*"; - String subjectA = SUBJECT + ".A"; - String subjectB = SUBJECT + ".B"; - TestingStreamContainer tsc = new TestingStreamContainer(nc, subjectWild); + runInLrServer((nc, jsm, js) -> { + String stream = random(); + String subject = random(); + String subjectWild = subject + ".*"; + String subjectA = subject + ".A"; + String subjectB = subject + ".B"; + createMemoryStream(jsm, stream, subjectWild); jsPublish(js, subjectA, 1); jsPublish(js, subjectB, 1); @@ -393,15 +401,15 @@ public void testPrefix() throws Exception { String subjectMadeBySrc = "sub-made-by.src"; String subjectMadeByTar = "sub-made-by.tar"; - try (NatsTestServer ts = new NatsTestServer("src/test/resources/js_prefix.conf", false)) { - Options optionsSrc = new Options.Builder().server(ts.getURI()) + try (NatsTestServer ts = NatsTestServer.configuredJsServer("js_prefix.conf")) { + Options optionsSrc = optionsBuilder(ts) .userInfo("src".toCharArray(), "spass".toCharArray()).build(); - Options optionsTar = new Options.Builder().server(ts.getURI()) + Options optionsTar = optionsBuilder(ts) .userInfo("tar".toCharArray(), "tpass".toCharArray()).build(); - try (Connection ncSrc = Nats.connect(optionsSrc); - Connection ncTar = Nats.connect(optionsTar) + try (Connection ncSrc = standardConnectionWait(optionsSrc); + Connection ncTar = standardConnectionWait(optionsTar) ) { // Setup JetStreamOptions. SOURCE does not need prefix JetStreamOptions jsoSrc = JetStreamOptions.builder().build(); @@ -457,67 +465,61 @@ private void readPrefixMessages(Connection nc, JetStream js, String subject, Str @Test public void testBindPush() throws Exception { - jsServer.run(nc -> { - TestingStreamContainer tsc = new TestingStreamContainer(nc); - JetStream js = nc.jetStream(); - - jsPublish(js, tsc.subject(), 1, 1); + runInLrServer((nc, jstc) -> { + jsPublish(jstc.js, jstc.subject(), 1, 1); PushSubscribeOptions pso = PushSubscribeOptions.builder() - .durable(tsc.consumerName()) + .durable(jstc.consumerName()) .build(); - JetStreamSubscription s = js.subscribe(tsc.subject(), pso); + JetStreamSubscription s = jstc.js.subscribe(jstc.subject(), pso); Message m = s.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); assertEquals(data(1), new String(m.getData())); m.ack(); unsubscribeEnsureNotBound(s); - jsPublish(js, tsc.subject(), 2, 1); + jsPublish(jstc.js, jstc.subject(), 2, 1); pso = PushSubscribeOptions.builder() - .stream(tsc.stream) - .durable(tsc.consumerName()) + .stream(jstc.stream) + .durable(jstc.consumerName()) .bind(true) .build(); - s = js.subscribe(tsc.subject(), pso); + s = jstc.js.subscribe(jstc.subject(), pso); m = s.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); assertEquals(data(2), new String(m.getData())); m.ack(); unsubscribeEnsureNotBound(s); - jsPublish(js, tsc.subject(), 3, 1); - pso = PushSubscribeOptions.bind(tsc.stream, tsc.consumerName()); - s = js.subscribe(tsc.subject(), pso); + jsPublish(jstc.js, jstc.subject(), 3, 1); + pso = PushSubscribeOptions.bind(jstc.stream, jstc.consumerName()); + s = jstc.js.subscribe(jstc.subject(), pso); m = s.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); assertEquals(data(3), new String(m.getData())); assertThrows(IllegalArgumentException.class, - () -> PushSubscribeOptions.builder().stream(tsc.stream).bind(true).build()); + () -> PushSubscribeOptions.builder().stream(jstc.stream).bind(true).build()); assertThrows(IllegalArgumentException.class, - () -> PushSubscribeOptions.builder().durable(tsc.consumerName()).bind(true).build()); + () -> PushSubscribeOptions.builder().durable(jstc.consumerName()).bind(true).build()); assertThrows(IllegalArgumentException.class, () -> PushSubscribeOptions.builder().stream(EMPTY).bind(true).build()); assertThrows(IllegalArgumentException.class, - () -> PushSubscribeOptions.builder().stream(tsc.stream).durable(EMPTY).bind(true).build()); + () -> PushSubscribeOptions.builder().stream(jstc.stream).durable(EMPTY).bind(true).build()); }); } @Test public void testBindPull() throws Exception { - jsServer.run(nc -> { - TestingStreamContainer tsc = new TestingStreamContainer(nc); - JetStream js = nc.jetStream(); - - jsPublish(js, tsc.subject(), 1, 1); + runInLrServer((nc, jstc) -> { + jsPublish(jstc.js, jstc.subject(), 1, 1); PullSubscribeOptions pso = PullSubscribeOptions.builder() - .durable(tsc.consumerName()) + .durable(jstc.consumerName()) .build(); - JetStreamSubscription s = js.subscribe(tsc.subject(), pso); + JetStreamSubscription s = jstc.js.subscribe(jstc.subject(), pso); s.pull(1); Message m = s.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); @@ -525,13 +527,13 @@ public void testBindPull() throws Exception { m.ack(); unsubscribeEnsureNotBound(s); - jsPublish(js, tsc.subject(), 2, 1); + jsPublish(jstc.js, jstc.subject(), 2, 1); pso = PullSubscribeOptions.builder() - .stream(tsc.stream) - .durable(tsc.consumerName()) + .stream(jstc.stream) + .durable(jstc.consumerName()) .bind(true) .build(); - s = js.subscribe(tsc.subject(), pso); + s = jstc.js.subscribe(jstc.subject(), pso); s.pull(1); m = s.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); @@ -539,9 +541,9 @@ public void testBindPull() throws Exception { m.ack(); unsubscribeEnsureNotBound(s); - jsPublish(js, tsc.subject(), 3, 1); - pso = PullSubscribeOptions.bind(tsc.stream, tsc.consumerName()); - s = js.subscribe(tsc.subject(), pso); + jsPublish(jstc.js, jstc.subject(), 3, 1); + pso = PullSubscribeOptions.bind(jstc.stream, jstc.consumerName()); + s = jstc.js.subscribe(jstc.subject(), pso); s.pull(1); m = s.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); @@ -551,329 +553,318 @@ public void testBindPull() throws Exception { @Test public void testBindErrors() throws Exception { - jsServer.run(nc -> { - JetStream js = nc.jetStream(); - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { // bind errors - PushSubscribeOptions pushbinderr = PushSubscribeOptions.bind(tsc.stream, "binddur"); - IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), pushbinderr)); + PushSubscribeOptions pushbinderr = PushSubscribeOptions.bind(jstc.stream, "binddur"); + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), pushbinderr)); assertTrue(iae.getMessage().contains(JsSubConsumerNotFoundRequiredInBind.id())); - PullSubscribeOptions pullbinderr = PullSubscribeOptions.bind(tsc.stream, "binddur"); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), pullbinderr)); + PullSubscribeOptions pullbinderr = PullSubscribeOptions.bind(jstc.stream, "binddur"); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), pullbinderr)); assertTrue(iae.getMessage().contains(JsSubConsumerNotFoundRequiredInBind.id())); }); } @Test public void testFilterMismatchErrors() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - - // single subject - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { // will work as SubscribeSubject equals Filter Subject - filterMatchSubscribeOk(js, jsm, tsc.stream, tsc.subject(), tsc.subject()); - filterMatchSubscribeOk(js, jsm, tsc.stream, ">", ">"); - filterMatchSubscribeOk(js, jsm, tsc.stream, "*", "*"); + filterMatchSubscribeOk(jstc, jstc.stream, jstc.subject(), jstc.subject()); + filterMatchSubscribeOk(jstc, jstc.stream, ">", ">"); + filterMatchSubscribeOk(jstc, jstc.stream, "*", "*"); // will not work - filterMatchSubscribeEx(js, jsm, tsc.stream, tsc.subject(), ""); - filterMatchSubscribeEx(js, jsm, tsc.stream, tsc.subject(), ">"); - filterMatchSubscribeEx(js, jsm, tsc.stream, tsc.subject(), "*"); + filterMatchSubscribeEx(jstc, jstc.stream, jstc.subject(), ""); + filterMatchSubscribeEx(jstc, jstc.stream, jstc.subject(), ">"); + filterMatchSubscribeEx(jstc, jstc.stream, jstc.subject(), "*"); // multiple subjects no wildcards - jsm.deleteStream(tsc.stream); - createMemoryStream(jsm, tsc.stream, tsc.subject(), subject(2)); + jstc.jsm.deleteStream(jstc.stream); + createMemoryStream(jstc.jsm, jstc.stream, jstc.subject(), random()); // will work as SubscribeSubject equals Filter Subject - filterMatchSubscribeOk(js, jsm, tsc.stream, tsc.subject(), tsc.subject()); - filterMatchSubscribeOk(js, jsm, tsc.stream, ">", ">"); - filterMatchSubscribeOk(js, jsm, tsc.stream, "*", "*"); + filterMatchSubscribeOk(jstc, jstc.stream, jstc.subject(), jstc.subject()); + filterMatchSubscribeOk(jstc, jstc.stream, ">", ">"); + filterMatchSubscribeOk(jstc, jstc.stream, "*", "*"); // will not work because stream has more than 1 subject - filterMatchSubscribeEx(js, jsm, tsc.stream, tsc.subject(), ""); - filterMatchSubscribeEx(js, jsm, tsc.stream, tsc.subject(), ">"); - filterMatchSubscribeEx(js, jsm, tsc.stream, tsc.subject(), "*"); + filterMatchSubscribeEx(jstc, jstc.stream, jstc.subject(), ""); + filterMatchSubscribeEx(jstc, jstc.stream, jstc.subject(), ">"); + filterMatchSubscribeEx(jstc, jstc.stream, jstc.subject(), "*"); - String subjectGt = tsc.subject() + ".>"; - String subjectStar = tsc.subject() + ".*"; - String subjectDot = tsc.subject() + "." + name(); + String subjectGt = jstc.subject() + ".>"; + String subjectStar = jstc.subject() + ".*"; + String subjectDot = jstc.subject() + "." + random(); // multiple subjects via '>' - jsm.deleteStream(tsc.stream); - createMemoryStream(jsm, tsc.stream, subjectGt); + jstc.jsm.deleteStream(jstc.stream); + createMemoryStream(jstc.jsm, jstc.stream, subjectGt); // will work, exact matches - filterMatchSubscribeOk(js, jsm, tsc.stream, subjectDot, subjectDot); - filterMatchSubscribeOk(js, jsm, tsc.stream, ">", ">"); + filterMatchSubscribeOk(jstc, jstc.stream, subjectDot, subjectDot); + filterMatchSubscribeOk(jstc, jstc.stream, ">", ">"); // will not work because mismatch / stream has more than 1 subject - filterMatchSubscribeEx(js, jsm, tsc.stream, subjectDot, ""); - filterMatchSubscribeEx(js, jsm, tsc.stream, subjectDot, ">"); - filterMatchSubscribeEx(js, jsm, tsc.stream, subjectDot, subjectGt); + filterMatchSubscribeEx(jstc, jstc.stream, subjectDot, ""); + filterMatchSubscribeEx(jstc, jstc.stream, subjectDot, ">"); + filterMatchSubscribeEx(jstc, jstc.stream, subjectDot, subjectGt); // multiple subjects via '*' - jsm.deleteStream(tsc.stream); - createMemoryStream(jsm, tsc.stream, subjectStar); + jstc.jsm.deleteStream(jstc.stream); + createMemoryStream(jstc.jsm, jstc.stream, subjectStar); // will work, exact matches - filterMatchSubscribeOk(js, jsm, tsc.stream, subjectDot, subjectDot); - filterMatchSubscribeOk(js, jsm, tsc.stream, ">", ">"); + filterMatchSubscribeOk(jstc, jstc.stream, subjectDot, subjectDot); + filterMatchSubscribeOk(jstc, jstc.stream, ">", ">"); // will not work because mismatch / stream has more than 1 subject - filterMatchSubscribeEx(js, jsm, tsc.stream, subjectDot, ""); - filterMatchSubscribeEx(js, jsm, tsc.stream, subjectDot, ">"); - filterMatchSubscribeEx(js, jsm, tsc.stream, subjectDot, subjectStar); + filterMatchSubscribeEx(jstc, jstc.stream, subjectDot, ""); + filterMatchSubscribeEx(jstc, jstc.stream, subjectDot, ">"); + filterMatchSubscribeEx(jstc, jstc.stream, subjectDot, subjectStar); }); } - private void filterMatchSubscribeOk(JetStream js, JetStreamManagement jsm, String stream, String subscribeSubject, String... filterSubjects) throws IOException, JetStreamApiException { - int i = RandomUtils.PRAND.nextInt(); // just want a unique number - filterMatchSetupConsumer(jsm, i, stream, filterSubjects); - unsubscribeEnsureNotBound(js.subscribe(subscribeSubject, builder().durable(durable(i)).buildPushSubscribeOptions())); + private void filterMatchSubscribeOk(JetStreamTestingContext jstc, String stream, String subscribeSubject, String... filterSubjects) throws IOException, JetStreamApiException { + String deliver = random(); + String dur = random(); + filterMatchSetupConsumer(jstc, deliver, dur, stream, filterSubjects); + unsubscribeEnsureNotBound(jstc.js.subscribe(subscribeSubject, builder().durable(dur).buildPushSubscribeOptions())); } - private void filterMatchSubscribeEx(JetStream js, JetStreamManagement jsm, String stream, String subscribeSubject, String... filterSubjects) throws IOException, JetStreamApiException { - int i = RandomUtils.PRAND.nextInt(); // just want a unique number - filterMatchSetupConsumer(jsm, i, stream, filterSubjects); + private void filterMatchSubscribeEx(JetStreamTestingContext jstc, String stream, String subscribeSubject, String... filterSubjects) throws IOException, JetStreamApiException { + String deliver = random(); + String dur = random(); + filterMatchSetupConsumer(jstc, deliver, dur, stream, filterSubjects); IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, - () -> js.subscribe(subscribeSubject, builder().durable(durable(i)).buildPushSubscribeOptions())); + () -> jstc.js.subscribe(subscribeSubject, builder().durable(dur).buildPushSubscribeOptions())); assertTrue(iae.getMessage().contains(JsSubSubjectDoesNotMatchFilter.id())); } - private void filterMatchSetupConsumer(JetStreamManagement jsm, int i, String stream, String... fs) throws IOException, JetStreamApiException { - jsm.addOrUpdateConsumer(stream, - builder().deliverSubject(deliver(i)).durable(durable(i)).filterSubjects(fs).build()); + private void filterMatchSetupConsumer(JetStreamTestingContext jstc, String deliver, String dur, String stream, String... fs) throws IOException, JetStreamApiException { + jstc.jsm.addOrUpdateConsumer(stream, + builder().deliverSubject(deliver).durable(dur).filterSubjects(fs).build()); } @Test public void testBindDurableDeliverSubject() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - + runInLrServer((nc, jstc) -> { // create a durable push subscriber - has a deliver subject + String dur1 = random(); + String dur2 = random(); + String deliver = random(); ConsumerConfiguration ccDurPush = builder() - .durable(durable(1)) - .deliverSubject(deliver(1)) - .filterSubject(tsc.subject()) + .durable(dur1) + .deliverSubject(deliver) + .filterSubject(jstc.subject()) .build(); - jsm.addOrUpdateConsumer(tsc.stream, ccDurPush); + jstc.jsm.addOrUpdateConsumer(jstc.stream, ccDurPush); // create a durable pull subscriber - notice no deliver subject ConsumerConfiguration ccDurPull = builder() - .durable(durable(2)) - .filterSubject(tsc.subject()) + .durable(dur2) + .filterSubject(jstc.subject()) .build(); - jsm.addOrUpdateConsumer(tsc.stream, ccDurPull); + jstc.jsm.addOrUpdateConsumer(jstc.stream, ccDurPull); // try to pull subscribe against a push durable IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, - () -> js.subscribe(tsc.subject(), PullSubscribeOptions.builder().durable(durable(1)).build()) + () -> jstc.js.subscribe(jstc.subject(), PullSubscribeOptions.builder().durable(dur1).build()) ); assertTrue(iae.getMessage().contains(JsSubConsumerAlreadyConfiguredAsPush.id())); // try to pull bind against a push durable iae = assertThrows(IllegalArgumentException.class, - () -> js.subscribe(tsc.subject(), PullSubscribeOptions.bind(tsc.stream, durable(1))) + () -> jstc.js.subscribe(jstc.subject(), PullSubscribeOptions.bind(jstc.stream, dur1)) ); assertTrue(iae.getMessage().contains(JsSubConsumerAlreadyConfiguredAsPush.id())); // try to push subscribe against a pull durable iae = assertThrows(IllegalArgumentException.class, - () -> js.subscribe(tsc.subject(), PushSubscribeOptions.builder().durable(durable(2)).build()) + () -> jstc.js.subscribe(jstc.subject(), PushSubscribeOptions.builder().durable(dur2).build()) ); assertTrue(iae.getMessage().contains(JsSubConsumerAlreadyConfiguredAsPull.id())); // try to push bind against a pull durable iae = assertThrows(IllegalArgumentException.class, - () -> js.subscribe(tsc.subject(), PushSubscribeOptions.bind(tsc.stream, durable(2))) + () -> jstc.js.subscribe(jstc.subject(), PushSubscribeOptions.bind(jstc.stream, dur2)) ); assertTrue(iae.getMessage().contains(JsSubConsumerAlreadyConfiguredAsPull.id())); // this one is okay - js.subscribe(tsc.subject(), PushSubscribeOptions.builder().durable(durable(1)).build()); + jstc.js.subscribe(jstc.subject(), PushSubscribeOptions.builder().durable(dur1).build()); }); } @Test public void testConsumerIsNotModified() throws Exception { - jsServer.run(nc -> { - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - + runInLrServer((nc, jstc) -> { // test with config in issue 105 + String dur = random(); + String q = random(); ConsumerConfiguration cc = builder() .description("desc") .ackPolicy(AckPolicy.Explicit) .deliverPolicy(DeliverPolicy.All) - .deliverSubject(deliver(1)) - .deliverGroup(queue(1)) - .durable(durable(1)) + .deliverSubject(random()) + .deliverGroup(q) + .durable(dur) .maxAckPending(65000) .maxDeliver(5) .maxBatch(10) .maxBytes(11) .replayPolicy(ReplayPolicy.Instant) - .filterSubject(tsc.subject()) + .filterSubject(jstc.subject()) .build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - PushSubscribeOptions pushOpts = PushSubscribeOptions.bind(tsc.stream, durable(1)); - js.subscribe(tsc.subject(), queue(1), pushOpts); // should not throw an error + PushSubscribeOptions pushOpts = PushSubscribeOptions.bind(jstc.stream, dur); + jstc.js.subscribe(jstc.subject(), q, pushOpts); // should not throw an error // testing numerics + dur = random(); cc = builder() .deliverPolicy(DeliverPolicy.ByStartSequence) - .deliverSubject(deliver(21)) - .durable(durable(21)) + .deliverSubject(random()) + .durable(dur) .startSequence(42) .maxDeliver(43) .maxBatch(47) .maxBytes(48) .rateLimit(44) .maxAckPending(45) - .filterSubject(tsc.subject()) + .filterSubject(jstc.subject()) .build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - pushOpts = PushSubscribeOptions.bind(tsc.stream, durable(21)); - js.subscribe(tsc.subject(), pushOpts); // should not throw an error + pushOpts = PushSubscribeOptions.bind(jstc.stream, dur); + jstc.js.subscribe(jstc.subject(), pushOpts); // should not throw an error + dur = random(); cc = builder() - .durable(durable(22)) + .durable(dur) .maxPullWaiting(46) - .filterSubject(tsc.subject()) + .filterSubject(jstc.subject()) .build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - PullSubscribeOptions pullOpts = PullSubscribeOptions.bind(tsc.stream, durable(22)); - js.subscribe(tsc.subject(), pullOpts); // should not throw an error + PullSubscribeOptions pullOpts = PullSubscribeOptions.bind(jstc.stream, dur); + jstc.js.subscribe(jstc.subject(), pullOpts); // should not throw an error // testing DateTime + dur = random(); cc = builder() .deliverPolicy(DeliverPolicy.ByStartTime) - .deliverSubject(deliver(3)) - .durable(durable(3)) + .deliverSubject(random()) + .durable(dur) .startTime(ZonedDateTime.now().plusHours(1)) - .filterSubject(tsc.subject()) + .filterSubject(jstc.subject()) .build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - pushOpts = PushSubscribeOptions.bind(tsc.stream, durable(3)); - js.subscribe(tsc.subject(), pushOpts); // should not throw an error + pushOpts = PushSubscribeOptions.bind(jstc.stream, dur); + jstc.js.subscribe(jstc.subject(), pushOpts); // should not throw an error // testing boolean and duration + dur = random(); cc = builder() - .deliverSubject(deliver(4)) - .durable(durable(4)) + .deliverSubject(random()) + .durable(dur) .flowControl(1000) .headersOnly(true) .maxExpires(30000) .ackWait(2000) - .filterSubject(tsc.subject()) + .filterSubject(jstc.subject()) .build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - pushOpts = PushSubscribeOptions.bind(tsc.stream, durable(4)); - js.subscribe(tsc.subject(), pushOpts); // should not throw an error + pushOpts = PushSubscribeOptions.bind(jstc.stream, dur); + jstc.js.subscribe(jstc.subject(), pushOpts); // should not throw an error // testing enums + dur = random(); cc = builder() - .deliverSubject(deliver(5)) - .durable(durable(5)) + .deliverSubject(random()) + .durable(dur) .deliverPolicy(DeliverPolicy.Last) .ackPolicy(AckPolicy.None) .replayPolicy(ReplayPolicy.Original) - .filterSubject(tsc.subject()) + .filterSubject(jstc.subject()) .build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - pushOpts = PushSubscribeOptions.bind(tsc.stream, durable(5)); - js.subscribe(tsc.subject(), pushOpts); // should not throw an error + pushOpts = PushSubscribeOptions.bind(jstc.stream, dur); + jstc.js.subscribe(jstc.subject(), pushOpts); // should not throw an error }); } @Test public void testSubscribeDurableConsumerMustMatch() throws Exception { - jsServer.run(nc -> { - JetStream js = nc.jetStream(); - - String stream = stream(); - String subject = subject(); - createMemoryStream(nc, stream, subject); + runInLrServer((nc, jstc) -> { + String stream = jstc.stream; + String subject = jstc.subject(); // push - String uname = durable(); - String deliver = deliver(); + String uname = random(); + String deliver = random(); nc.jetStreamManagement().addOrUpdateConsumer(stream, pushDurableBuilder(subject, uname, deliver).build()); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).deliverPolicy(DeliverPolicy.Last), "deliverPolicy"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).deliverPolicy(DeliverPolicy.New), "deliverPolicy"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).ackPolicy(AckPolicy.None), "ackPolicy"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).ackPolicy(AckPolicy.All), "ackPolicy"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).replayPolicy(ReplayPolicy.Original), "replayPolicy"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).deliverPolicy(DeliverPolicy.Last), "deliverPolicy"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).deliverPolicy(DeliverPolicy.New), "deliverPolicy"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).ackPolicy(AckPolicy.None), "ackPolicy"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).ackPolicy(AckPolicy.All), "ackPolicy"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).replayPolicy(ReplayPolicy.Original), "replayPolicy"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).flowControl(10000), "flowControl"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).headersOnly(true), "headersOnly"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).flowControl(10000), "flowControl"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).headersOnly(true), "headersOnly"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).startTime(ZonedDateTime.now()), "startTime"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).ackWait(Duration.ofMillis(1)), "ackWait"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).description("x"), "description"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).sampleFrequency("x"), "sampleFrequency"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).idleHeartbeat(Duration.ofMillis(1000)), "idleHeartbeat"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).maxExpires(Duration.ofMillis(1000)), "maxExpires"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).inactiveThreshold(Duration.ofMillis(1000)), "inactiveThreshold"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).startTime(ZonedDateTime.now()), "startTime"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).ackWait(Duration.ofMillis(1)), "ackWait"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).description("x"), "description"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).sampleFrequency("x"), "sampleFrequency"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).idleHeartbeat(Duration.ofMillis(1000)), "idleHeartbeat"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).maxExpires(Duration.ofMillis(1000)), "maxExpires"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).inactiveThreshold(Duration.ofMillis(1000)), "inactiveThreshold"); // value - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).maxDeliver(MAX_DELIVER_MIN), "maxDeliver"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).maxAckPending(0), "maxAckPending"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).ackWait(0), "ackWait"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).maxDeliver(MAX_DELIVER_MIN), "maxDeliver"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).maxAckPending(0), "maxAckPending"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).ackWait(0), "ackWait"); // value unsigned - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).startSequence(1), "startSequence"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).rateLimit(1), "rateLimit"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).startSequence(1), "startSequence"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).rateLimit(1), "rateLimit"); // unset doesn't fail because the server provides a value equal to the unset - changeOkPush(js, subject, pushDurableBuilder(subject, uname, deliver).maxDeliver(INTEGER_UNSET)); + changeOkPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).maxDeliver(INTEGER_UNSET)); // unset doesn't fail because the server does not provide a value // negatives are considered the unset - changeOkPush(js, subject, pushDurableBuilder(subject, uname, deliver).startSequence(ULONG_UNSET)); - changeOkPush(js, subject, pushDurableBuilder(subject, uname, deliver).startSequence(-1)); - changeOkPush(js, subject, pushDurableBuilder(subject, uname, deliver).rateLimit(ULONG_UNSET)); - changeOkPush(js, subject, pushDurableBuilder(subject, uname, deliver).rateLimit(-1)); + changeOkPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).startSequence(ULONG_UNSET)); + changeOkPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).startSequence(-1)); + changeOkPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).rateLimit(ULONG_UNSET)); + changeOkPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).rateLimit(-1)); // unset fail b/c the server does set a value that is not equal to the unset or the minimum - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).maxAckPending(LONG_UNSET), "maxAckPending"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).maxAckPending(0), "maxAckPending"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).ackWait(LONG_UNSET), "ackWait"); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).ackWait(0), "ackWait"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).maxAckPending(LONG_UNSET), "maxAckPending"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).maxAckPending(0), "maxAckPending"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).ackWait(LONG_UNSET), "ackWait"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).ackWait(0), "ackWait"); // pull - String lname = durable(); + String lname = random(); nc.jetStreamManagement().addOrUpdateConsumer(stream, pullDurableBuilder(subject, lname).build()); // value - changeExPull(js, subject, pullDurableBuilder(subject, lname).maxPullWaiting(0), "maxPullWaiting"); - changeExPull(js, subject, pullDurableBuilder(subject, lname).maxBatch(0), "maxBatch"); - changeExPull(js, subject, pullDurableBuilder(subject, lname).maxBytes(0), "maxBytes"); + changeExPull(jstc.js, subject, pullDurableBuilder(subject, lname).maxPullWaiting(0), "maxPullWaiting"); + changeExPull(jstc.js, subject, pullDurableBuilder(subject, lname).maxBatch(0), "maxBatch"); + changeExPull(jstc.js, subject, pullDurableBuilder(subject, lname).maxBytes(0), "maxBytes"); // unsets fail b/c the server does set a value - changeExPull(js, subject, pullDurableBuilder(subject, lname).maxPullWaiting(-1), "maxPullWaiting"); + changeExPull(jstc.js, subject, pullDurableBuilder(subject, lname).maxPullWaiting(-1), "maxPullWaiting"); // unset - changeOkPull(js, subject, pullDurableBuilder(subject, lname).maxBatch(-1)); - changeOkPull(js, subject, pullDurableBuilder(subject, lname).maxBytes(-1)); + changeOkPull(jstc.js, subject, pullDurableBuilder(subject, lname).maxBatch(-1)); + changeOkPull(jstc.js, subject, pullDurableBuilder(subject, lname).maxBytes(-1)); // metadata Map metadataA = new HashMap<>(); metadataA.put("a", "A"); @@ -882,18 +873,18 @@ public void testSubscribeDurableConsumerMustMatch() throws Exception { if (atLeast2_10()) { // metadata server null versus new not null nc.jetStreamManagement().addOrUpdateConsumer(stream, pushDurableBuilder(subject, uname, deliver).build()); - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).metadata(metadataA), "metadata"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).metadata(metadataA), "metadata"); // metadata server not null versus new null nc.jetStreamManagement().addOrUpdateConsumer(stream, pushDurableBuilder(subject, uname, deliver).metadata(metadataA).build()); - changeOkPush(js, subject, pushDurableBuilder(subject, uname, deliver)); + changeOkPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver)); // metadata server not null versus new not null but different - changeExPush(js, subject, pushDurableBuilder(subject, uname, deliver).metadata(metadataB), "metadata"); + changeExPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).metadata(metadataB), "metadata"); if (before2_11()) { // metadata server not null versus new not null and same - changeOkPush(js, subject, pushDurableBuilder(subject, uname, deliver).metadata(metadataA)); + changeOkPush(jstc.js, subject, pushDurableBuilder(subject, uname, deliver).metadata(metadataA)); } } }); @@ -935,35 +926,24 @@ private Builder pullDurableBuilder(String subject, String durable) { @Test public void testGetConsumerInfoFromSubscription() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - - JetStreamSubscription sub = js.subscribe(tsc.subject()); + runInLrServer((nc, jstc) -> { + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject()); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server ConsumerInfo ci = sub.getConsumerInfo(); - assertEquals(tsc.stream, ci.getStreamName()); + assertEquals(jstc.stream, ci.getStreamName()); }); } @Test public void testInternalLookupConsumerInfoCoverage() throws Exception { - jsServer.run(nc -> { - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { // - consumer not found // - stream does not exist - JetStreamSubscription sub = js.subscribe(tsc.subject()); - assertNull(((NatsJetStream)js).lookupConsumerInfo(tsc.stream, tsc.consumerName())); + jstc.js.subscribe(jstc.subject()); + assertNull(jstc.js.lookupConsumerInfo(jstc.stream, random())); assertThrows(JetStreamApiException.class, - () -> ((NatsJetStream)js).lookupConsumerInfo(stream(999), tsc.consumerName())); + () -> jstc.js.lookupConsumerInfo(random(), random())); }); } @@ -999,6 +979,7 @@ public void testNatsConnectionTimeCheckLogic() { Options options = Options.builder() .timeTraceLogger(l) .build(); + //noinspection resource NatsConnection nc = new NatsConnection(options); nc.traceTimeCheck("zero", 0); @@ -1025,88 +1006,93 @@ public void testNatsConnectionTimeCheckLogic() { @Test public void testMoreCreateSubscriptionErrors() throws Exception { - jsServer.run(nc -> { - JetStream js = nc.jetStream(); - - IllegalStateException ise = assertThrows(IllegalStateException.class, () -> js.subscribe(SUBJECT)); + runInLrServer((nc, jstc) -> { + IllegalStateException ise = assertThrows(IllegalStateException.class, () -> jstc.js.subscribe(random())); assertTrue(ise.getMessage().contains(JsSubNoMatchingStreamForSubject.id())); - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - // general pull push validation - ConsumerConfiguration ccCantHave = builder().durable("pulldur").deliverGroup("cantHave").build(); + + String pulldur = random(); + String dgCantHave = random(); + ConsumerConfiguration ccCantHave = builder().durable(pulldur).deliverGroup(dgCantHave).build(); PullSubscribeOptions pullCantHaveDlvrGrp = PullSubscribeOptions.builder().configuration(ccCantHave).build(); - IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), pullCantHaveDlvrGrp)); + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), pullCantHaveDlvrGrp)); assertTrue(iae.getMessage().contains(JsSubPullCantHaveDeliverGroup.id())); - ccCantHave = builder().durable("pulldur").deliverSubject("cantHave").build(); + ccCantHave = builder().durable(pulldur).deliverSubject(dgCantHave).build(); PullSubscribeOptions pullCantHaveDlvrSub = PullSubscribeOptions.builder().configuration(ccCantHave).build(); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), pullCantHaveDlvrSub)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), pullCantHaveDlvrSub)); assertTrue(iae.getMessage().contains(JsSubPullCantHaveDeliverSubject.id())); ccCantHave = builder().maxPullWaiting(1L).build(); PushSubscribeOptions pushCantHaveMpw = PushSubscribeOptions.builder().configuration(ccCantHave).build(); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), pushCantHaveMpw)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), pushCantHaveMpw)); assertTrue(iae.getMessage().contains(JsSubPushCantHaveMaxPullWaiting.id())); ccCantHave = builder().maxBatch(1L).build(); PushSubscribeOptions pushCantHaveMb = PushSubscribeOptions.builder().configuration(ccCantHave).build(); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), pushCantHaveMb)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), pushCantHaveMb)); assertTrue(iae.getMessage().contains(JsSubPushCantHaveMaxBatch.id())); ccCantHave = builder().maxBytes(1L).build(); PushSubscribeOptions pushCantHaveMby = PushSubscribeOptions.builder().configuration(ccCantHave).build(); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), pushCantHaveMby)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), pushCantHaveMby)); assertTrue(iae.getMessage().contains(JsSubPushCantHaveMaxBytes.id())); // create some consumers - PushSubscribeOptions psoDurNoQ = PushSubscribeOptions.builder().durable("durNoQ").build(); - js.subscribe(tsc.subject(), psoDurNoQ); + String durNoQ = random(); + String durYesQ = random(); - PushSubscribeOptions psoDurYesQ = PushSubscribeOptions.builder().durable("durYesQ").build(); - js.subscribe(tsc.subject(), "yesQ", psoDurYesQ); + PushSubscribeOptions psoDurNoQ = PushSubscribeOptions.builder().durable(durNoQ).build(); + jstc.js.subscribe(jstc.subject(), psoDurNoQ); + + PushSubscribeOptions psoDurYesQ = PushSubscribeOptions.builder().durable(durYesQ).build(); + jstc.js.subscribe(jstc.subject(), "yesQ", psoDurYesQ); // already bound - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), psoDurNoQ)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), psoDurNoQ)); assertTrue(iae.getMessage().contains(JsSubConsumerAlreadyBound.id())); // queue match - PushSubscribeOptions qmatch = PushSubscribeOptions.builder() - .durable("qmatchdur").deliverGroup("qmatchq").build(); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), "qnotmatch", qmatch)); + String qmatchdur = random(); + String qmatchq = random(); + PushSubscribeOptions qmatch = PushSubscribeOptions.builder().durable(qmatchdur).deliverGroup(qmatchq).build(); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), "qnotmatch", qmatch)); assertTrue(iae.getMessage().contains(JsSubQueueDeliverGroupMismatch.id())); // queue vs config - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), "notConfigured", psoDurNoQ)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), "notConfigured", psoDurNoQ)); assertTrue(iae.getMessage().contains(JsSubExistingConsumerNotQueue.id())); - PushSubscribeOptions psoNoVsYes = PushSubscribeOptions.builder().durable("durYesQ").build(); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), psoNoVsYes)); + PushSubscribeOptions psoNoVsYes = PushSubscribeOptions.builder().durable(durYesQ).build(); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), psoNoVsYes)); assertTrue(iae.getMessage().contains(JsSubExistingConsumerIsQueue.id())); - PushSubscribeOptions psoYesVsNo = PushSubscribeOptions.builder().durable("durYesQ").build(); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), "qnotmatch", psoYesVsNo)); + PushSubscribeOptions psoYesVsNo = PushSubscribeOptions.builder().durable(durYesQ).build(); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), "qnotmatch", psoYesVsNo)); assertTrue(iae.getMessage().contains(JsSubExistingQueueDoesNotMatchRequestedQueue.id())); // flow control heartbeat push / pull - ConsumerConfiguration ccFc = builder().durable("ccFcDur").flowControl(1000).build(); - ConsumerConfiguration ccHb = builder().durable("ccHbDur").idleHeartbeat(1000).build(); + String ccFcDur = random(); + String ccHbDur = random(); + ConsumerConfiguration ccFc = builder().durable(ccFcDur).flowControl(1000).build(); + ConsumerConfiguration ccHb = builder().durable(ccHbDur).idleHeartbeat(1000).build(); PullSubscribeOptions psoPullCcFc = PullSubscribeOptions.builder().configuration(ccFc).build(); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), psoPullCcFc)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), psoPullCcFc)); assertTrue(iae.getMessage().contains(JsSubFcHbNotValidPull.id())); PullSubscribeOptions psoPullCcHb = PullSubscribeOptions.builder().configuration(ccHb).build(); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), psoPullCcHb)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), psoPullCcHb)); assertTrue(iae.getMessage().contains(JsSubFcHbNotValidPull.id())); PushSubscribeOptions psoPushCcFc = PushSubscribeOptions.builder().configuration(ccFc).build(); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), "cantHaveQ", psoPushCcFc)); + String cantHaveQ = random(); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), cantHaveQ, psoPushCcFc)); assertTrue(iae.getMessage().contains(JsSubFcHbNotValidQueue.id())); PushSubscribeOptions psoPushCcHb = PushSubscribeOptions.builder().configuration(ccHb).build(); - iae = assertThrows(IllegalArgumentException.class, () -> js.subscribe(tsc.subject(), "cantHaveQ", psoPushCcHb)); + iae = assertThrows(IllegalArgumentException.class, () -> jstc.js.subscribe(jstc.subject(), cantHaveQ, psoPushCcHb)); assertTrue(iae.getMessage().contains(JsSubFcHbNotValidQueue.id())); }); } @@ -1118,4 +1104,32 @@ public void testNatsJetStreamUtil() { assertNotNull(gen); assertTrue(gen.startsWith("prefix-")); } + + @Test + public void testRequestNoResponder() throws Exception { + runInLrServer((ncCancel, jsm, js) -> { + Options optReport = optionsBuilder(LongRunningServer.server()).reportNoResponders().build(); + try (Connection ncReport = standardConnectionWait(optReport)) { + assertThrows(CancellationException.class, () -> ncCancel.request(random(), null).get()); + ExecutionException ee = assertThrows(ExecutionException.class, () -> ncReport.request(random(), null).get()); + assertInstanceOf(JetStreamStatusException.class, ee.getCause()); + assertTrue(ee.getMessage().contains("503 No Responders Available For Request")); + + String stream = random(); + String subject = random(); + ncCancel.jetStreamManagement().addStream( + StreamConfiguration.builder() + .name(stream).subjects(subject).storageType(StorageType.Memory) + .build()); + + JetStream jsCancel = ncCancel.jetStream(); + JetStream jsReport = ncReport.jetStream(); + + IOException ioe = assertThrows(IOException.class, () -> jsCancel.publish("not-exist", null)); + assertTrue(ioe.getMessage().contains("503")); + ioe = assertThrows(IOException.class, () -> jsReport.publish("trnrNotExist", null)); + assertTrue(ioe.getMessage().contains("503")); + } + }); + } } \ No newline at end of file diff --git a/src/test/java/io/nats/client/impl/JetStreamManagementTests.java b/src/test/java/io/nats/client/impl/JetStreamManagementTests.java index f1b83689f..dae5375fd 100644 --- a/src/test/java/io/nats/client/impl/JetStreamManagementTests.java +++ b/src/test/java/io/nats/client/impl/JetStreamManagementTests.java @@ -16,7 +16,7 @@ import io.nats.client.*; import io.nats.client.api.*; import io.nats.client.support.DateTimeUtils; -import io.nats.client.utils.TestBase; +import io.nats.client.utils.VersionUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; @@ -34,21 +34,24 @@ import static io.nats.client.support.DateTimeUtils.ZONE_ID_GMT; import static io.nats.client.support.NatsJetStreamConstants.*; import static io.nats.client.utils.ResourceUtils.dataAsString; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; public class JetStreamManagementTests extends JetStreamTestBase { @Test public void testStreamCreate() throws Exception { - runInJsServer(nc -> { + runInLrServer((nc, jsm, js) -> { long now = ZonedDateTime.now().toEpochSecond(); - JetStreamManagement jsm = nc.jetStreamManagement(); + String stream = random(); + String subject0 = random(); + String subject1 = random(); StreamConfiguration sc = StreamConfiguration.builder() - .name(STREAM) + .name(stream) .storageType(StorageType.Memory) - .subjects(subject(0), subject(1)) + .subjects(subject0, subject1) .build(); StreamInfo si = jsm.addStream(sc); @@ -57,12 +60,12 @@ public void testStreamCreate() throws Exception { assertNotNull(si.getConfiguration()); sc = si.getConfiguration(); - assertEquals(STREAM, sc.getName()); + assertEquals(stream, sc.getName()); assertEquals(2, sc.getSubjects().size()); - assertEquals(subject(0), sc.getSubjects().get(0)); - assertEquals(subject(1), sc.getSubjects().get(1)); - assertTrue(subject(0).compareTo(subject(1)) != 0); // coverage + assertEquals(subject0, sc.getSubjects().get(0)); + assertEquals(subject1, sc.getSubjects().get(1)); + assertTrue(subject0.compareTo(subject1) != 0); // coverage assertEquals(RetentionPolicy.Limits, sc.getRetentionPolicy()); assertEquals(DiscardPolicy.Old, sc.getDiscardPolicy()); @@ -72,6 +75,7 @@ public void testStreamCreate() throws Exception { assertEquals(-1, sc.getMaxConsumers()); assertEquals(-1, sc.getMaxMsgs()); assertEquals(-1, sc.getMaxBytes()); + //noinspection deprecation assertEquals(-1, sc.getMaxMsgSize()); // COVERAGE for deprecated assertEquals(-1, sc.getMaximumMessageSize()); assertEquals(1, sc.getReplicas()); @@ -89,8 +93,7 @@ public void testStreamCreate() throws Exception { assertEquals(0, ss.getConsumerCount()); if (nc.getServerInfo().isSameOrNewerThanVersion("2.10")) { - JetStream js = jsm.jetStream(); - String stream = stream(); + stream = random(); sc = StreamConfiguration.builder() .name(stream) .storageType(StorageType.Memory) @@ -107,14 +110,12 @@ public void testStreamCreate() throws Exception { @Test public void testStreamMetadata() throws Exception { - jsServer.run(TestBase::atLeast2_9_0, nc -> { + runInLrServer(VersionUtils::atLeast2_9_0, (nc, jsm, js) -> { Map metaData = new HashMap<>(); metaData.put(META_KEY, META_VALUE); - JetStreamManagement jsm = nc.jetStreamManagement(); - StreamConfiguration sc = StreamConfiguration.builder() - .name(stream()) + .name(random()) .storageType(StorageType.Memory) - .subjects(subject()) + .subjects(random()) .metadata(metaData) .build(); @@ -126,12 +127,10 @@ public void testStreamMetadata() throws Exception { @Test public void testStreamCreateWithNoSubject() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { long now = ZonedDateTime.now().toEpochSecond(); - JetStreamManagement jsm = nc.jetStreamManagement(); - - String stream = stream(); + String stream = random(); StreamConfiguration sc = StreamConfiguration.builder() .name(stream) .storageType(StorageType.Memory) @@ -174,16 +173,19 @@ public void testStreamCreateWithNoSubject() throws Exception { @Test public void testUpdateStream() throws Exception { - runInJsServer(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - StreamInfo si = addTestStream(jsm); + runInLrServer((nc, jsm, js) -> { + String stream = random(); + String subject0 = random(); + String subject1 = random(); + String subject2 = random(); + StreamInfo si = jsm.addStream(getTestStreamConfiguration(stream, new String[]{subject0, subject1})); StreamConfiguration sc = si.getConfiguration(); assertNotNull(sc); - assertEquals(STREAM, sc.getName()); + assertEquals(stream, sc.getName()); assertNotNull(sc.getSubjects()); assertEquals(2, sc.getSubjects().size()); - assertEquals(subject(0), sc.getSubjects().get(0)); - assertEquals(subject(1), sc.getSubjects().get(1)); + assertEquals(subject0, sc.getSubjects().get(0)); + assertEquals(subject1, sc.getSubjects().get(1)); assertEquals(-1, sc.getMaxMsgs()); assertEquals(-1, sc.getMaxBytes()); assertEquals(-1, sc.getMaximumMessageSize()); @@ -196,9 +198,9 @@ public void testUpdateStream() throws Exception { assertNull(sc.getTemplateOwner()); sc = StreamConfiguration.builder() - .name(STREAM) + .name(stream) .storageType(StorageType.Memory) // File is default, this ensures it's not a change - .subjects(subject(0), subject(1), subject(2)) + .subjects(subject0, subject1, subject2) .maxMessages(42) .maxBytes(43) .maximumMessageSize(44) @@ -213,12 +215,12 @@ public void testUpdateStream() throws Exception { sc = si.getConfiguration(); assertNotNull(sc); - assertEquals(STREAM, sc.getName()); + assertEquals(stream, sc.getName()); assertNotNull(sc.getSubjects()); assertEquals(3, sc.getSubjects().size()); - assertEquals(subject(0), sc.getSubjects().get(0)); - assertEquals(subject(1), sc.getSubjects().get(1)); - assertEquals(subject(2), sc.getSubjects().get(2)); + assertEquals(subject0, sc.getSubjects().get(0)); + assertEquals(subject1, sc.getSubjects().get(1)); + assertEquals(subject2, sc.getSubjects().get(2)); assertEquals(42, sc.getMaxMsgs()); assertEquals(43, sc.getMaxBytes()); assertEquals(44, sc.getMaximumMessageSize()); @@ -232,38 +234,36 @@ public void testUpdateStream() throws Exception { assertNull(sc.getTemplateOwner()); // allowed to change Allow Direct - jsm.deleteStream(STREAM); - jsm.addStream(getTestStreamConfigurationBuilder().allowDirect(false).build()); - jsm.updateStream(getTestStreamConfigurationBuilder().allowDirect(true).build()); - jsm.updateStream(getTestStreamConfigurationBuilder().allowDirect(false).build()); + jsm.deleteStream(stream); + jsm.addStream(getTestStreamConfigurationBuilder(stream, subject0).allowDirect(false).build()); + jsm.updateStream(getTestStreamConfigurationBuilder(stream, subject0).allowDirect(true).build()); + jsm.updateStream(getTestStreamConfigurationBuilder(stream, subject0).allowDirect(false).build()); // allowed to change Mirror Direct - jsm.deleteStream(STREAM); - jsm.addStream(getTestStreamConfigurationBuilder().mirrorDirect(false).build()); - jsm.updateStream(getTestStreamConfigurationBuilder().mirrorDirect(true).build()); - jsm.updateStream(getTestStreamConfigurationBuilder().mirrorDirect(false).build()); + jsm.deleteStream(stream); + jsm.addStream(getTestStreamConfigurationBuilder(stream, subject0).mirrorDirect(false).build()); + jsm.updateStream(getTestStreamConfigurationBuilder(stream, subject0).mirrorDirect(true).build()); + jsm.updateStream(getTestStreamConfigurationBuilder(stream, subject0).mirrorDirect(false).build()); }); } @Test public void testAddStreamInvalids() throws Exception { - jsServer.run(TestBase::atLeast2_10, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - + runInLrServer(VersionUtils::atLeast2_10, (nc, jsm, js) -> { assertThrows(IllegalArgumentException.class, () -> jsm.addStream(null)); - String stream = stream(); + String stream = random(); StreamConfiguration sc = StreamConfiguration.builder() .name(stream) - .description(variant()) + .description(random()) .storageType(StorageType.Memory) - .subjects(subject()) + .subjects(random()) .build(); jsm.addStream(sc); - assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).subjects(subject()).build())); - assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).description(variant()).build())); + assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).subjects(random()).build())); + assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).description(random()).build())); assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).retentionPolicy(RetentionPolicy.Interest).build())); assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).retentionPolicy(RetentionPolicy.WorkQueue).build())); assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).compressionOption(CompressionOption.S2).build())); @@ -271,6 +271,7 @@ public void testAddStreamInvalids() throws Exception { assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).maxMessages(1).build())); assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).maxMessagesPerSubject(1).build())); assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).maxAge(Duration.ofSeconds(1L)).build())); + //noinspection deprecation assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).maxMsgSize(1).build())); // COVERAGE for deprecated assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).maximumMessageSize(1).build())); assert10058(() -> jsm.addStream(StreamConfiguration.builder(sc).storageType(StorageType.File).build())); @@ -293,13 +294,11 @@ private void assert10058(Executable executable) { @Test public void testUpdateStreamInvalids() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - + runInLrServer((nc, jsm, js) -> { assertThrows(IllegalArgumentException.class, () -> jsm.updateStream(null)); - String stream = stream(); - String[] subjects = new String[]{subject(), subject()}; + String stream = random(); + String[] subjects = new String[]{random(), random()}; // cannot update non existent stream StreamConfiguration sc = getTestStreamConfiguration(stream, subjects); @@ -316,7 +315,7 @@ public void testUpdateStreamInvalids() throws Exception { assertThrows(JetStreamApiException.class, () -> jsm.updateStream(scMemToFile)); // cannot change MaxConsumers - StreamConfiguration scMaxCon = getTestStreamConfigurationBuilder() + StreamConfiguration scMaxCon = getTestStreamConfigurationBuilder(stream, subjects) .maxConsumers(2) .build(); assertThrows(JetStreamApiException.class, () -> jsm.updateStream(scMaxCon)); @@ -339,29 +338,18 @@ public void testUpdateStreamInvalids() throws Exception { }); } - private static StreamInfo addTestStream(JetStreamManagement jsm) throws IOException, JetStreamApiException { - StreamInfo si = jsm.addStream(getTestStreamConfiguration()); - assertNotNull(si); - return si; - } - - private static StreamConfiguration getTestStreamConfiguration() { - return getTestStreamConfigurationBuilder().build(); + private static StreamConfiguration.Builder getTestStreamConfigurationBuilder(String stream, String subject) { + return StreamConfiguration.builder() + .name(stream) + .storageType(StorageType.Memory) + .subjects(subject); } - private static StreamConfiguration getTestStreamConfiguration(String stream, String... subjects) { + private static StreamConfiguration getTestStreamConfiguration(String stream, String[] subjects) { return getTestStreamConfigurationBuilder(stream, subjects).build(); } - private static StreamConfiguration.Builder getTestStreamConfigurationBuilder() { - return getTestStreamConfigurationBuilder(STREAM); - } - - private static StreamConfiguration.Builder getTestStreamConfigurationBuilder(String stream, String... subjects) { - if (subjects == null || subjects.length == 0) { - subjects = new String[]{subject(0), subject(1)}; - } - + private static StreamConfiguration.Builder getTestStreamConfigurationBuilder(String stream, String[] subjects) { return StreamConfiguration.builder() .name(stream) .storageType(StorageType.Memory) @@ -370,18 +358,14 @@ private static StreamConfiguration.Builder getTestStreamConfigurationBuilder(Str @Test public void testGetStreamInfo() throws Exception { - jsServer.run(nc -> { - String stream = stream(); - - JetStreamManagement jsm = nc.jetStreamManagement(); + runInLrServer((nc, jsm, js) -> { + String stream = random(); assertThrows(JetStreamApiException.class, () -> jsm.getStreamInfo(stream)); - JetStream js = nc.jetStream(); - String[] subjects = new String[6]; - String subjectIx5 = subject(); + String subjectIx5 = random(); for (int x = 0; x < 5; x++) { - subjects[x] = subject() + x + 1; + subjects[x] = random() + x + 1; } subjects[5] = subjectIx5 + ".>"; @@ -475,105 +459,14 @@ public void testGetStreamInfo() throws Exception { }); } - @Test - public void testGetStreamInfoSubjectPagination() throws Exception { - try (NatsTestServer ts = new NatsTestServer("src/test/resources/pagination.conf", false, true)) { - try (Connection nc = standardConnection(ts.getURI())) { - if (nc.getServerInfo().isNewerVersionThan("2.8.4")) { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - - long rounds = 101; - long size = 1000; - long count = rounds * size; - jsm.addStream(StreamConfiguration.builder() - .name(stream(1)) - .storageType(StorageType.Memory) - .subjects("s.*.*") - .build()); - - jsm.addStream(StreamConfiguration.builder() - .name(stream(2)) - .storageType(StorageType.Memory) - .subjects("t.*.*") - .build()); - - for (int x = 1; x <= rounds; x++) { - for (int y = 1; y <= size; y++) { - js.publish("s." + x + "." + y, null); - } - } - - for (int y = 1; y <= size; y++) { - js.publish("t.7." + y, null); - } - - StreamInfo si = jsm.getStreamInfo(stream(1)); - validateStreamInfo(si.getStreamState(), 0, 0, count); - - si = jsm.getStreamInfo(stream(1), StreamInfoOptions.allSubjects()); - validateStreamInfo(si.getStreamState(), count, count, count); - - si = jsm.getStreamInfo(stream(1), StreamInfoOptions.filterSubjects("s.7.*")); - validateStreamInfo(si.getStreamState(), size, size, count); - - si = jsm.getStreamInfo(stream(1), StreamInfoOptions.filterSubjects("s.7.1")); - validateStreamInfo(si.getStreamState(), 1L, 1, count); - - si = jsm.getStreamInfo(stream(2), StreamInfoOptions.filterSubjects("t.7.*")); - validateStreamInfo(si.getStreamState(), size, size, size); - - si = jsm.getStreamInfo(stream(2), StreamInfoOptions.filterSubjects("t.7.1")); - validateStreamInfo(si.getStreamState(), 1L, 1, size); - - List infos = jsm.getStreams(); - assertEquals(2, infos.size()); - si = infos.get(0); - if (si.getConfiguration().getSubjects().get(0).equals("s.*.*")) { - validateStreamInfo(si.getStreamState(), 0, 0, count); - validateStreamInfo(infos.get(1).getStreamState(), 0, 0, size); - } - else { - validateStreamInfo(si.getStreamState(), 0, 0, size); - validateStreamInfo(infos.get(1).getStreamState(), 0, 0, count); - } - - infos = jsm.getStreams(">"); - assertEquals(2, infos.size()); - - infos = jsm.getStreams("*.7.*"); - assertEquals(2, infos.size()); - - infos = jsm.getStreams("*.7.1"); - assertEquals(2, infos.size()); - - infos = jsm.getStreams("s.7.*"); - assertEquals(1, infos.size()); - assertEquals("s.*.*", infos.get(0).getConfiguration().getSubjects().get(0)); - - infos = jsm.getStreams("t.7.1"); - assertEquals(1, infos.size()); - assertEquals("t.*.*", infos.get(0).getConfiguration().getSubjects().get(0)); - } - } - } - } - - private void validateStreamInfo(StreamState streamState, long subjectsList, long filteredCount, long subjectCount) { - assertEquals(subjectsList, streamState.getSubjects().size()); - assertEquals(filteredCount, streamState.getSubjects().size()); - assertEquals(subjectCount, streamState.getSubjectCount()); - } - @Test public void testGetStreamInfoOrNamesPaginationFilter() throws Exception { - runInJsServer(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - + runInLrServer((nc, jsm, js) -> { // getStreams pages at 256 // getStreamNames pages at 1024 - addStreams(jsm, 300, 0, "x256"); + String prefix = random(); + addStreams(jsm, prefix, 300, 0, "x256"); List list = jsm.getStreams(); assertEquals(300, list.size()); @@ -581,7 +474,7 @@ public void testGetStreamInfoOrNamesPaginationFilter() throws Exception { List names = jsm.getStreamNames(); assertEquals(300, names.size()); - addStreams(jsm, 1100, 300, "x1024"); + addStreams(jsm, prefix, 1100, 300, "x1024"); list = jsm.getStreams(); assertEquals(1400, list.size()); @@ -603,209 +496,221 @@ public void testGetStreamInfoOrNamesPaginationFilter() throws Exception { }); } - private void addStreams(JetStreamManagement jsm, int count, int adj, String div) throws IOException, JetStreamApiException { + private void addStreams(JetStreamManagement jsm, String prefix, int count, int adj, String div) throws IOException, JetStreamApiException { for (int x = 0; x < count; x++) { - createMemoryStream(jsm, "stream-" + (x + adj), "sub" + (x + adj) + "." + div + ".*"); + createMemoryStream(jsm, prefix + "-" + (x + adj), "sub" + (x + adj) + "." + div + ".*"); } } @Test public void testGetStreamNamesBySubjectFilter() throws Exception { - runInJsServer(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - - createMemoryStream(jsm, stream(1), "foo"); - createMemoryStream(jsm, stream(2), "bar"); - createMemoryStream(jsm, stream(3), "a.a"); - createMemoryStream(jsm, stream(4), "a.b"); + runInLrServer((nc, jsm, js) -> { + String stream1 = random(); + String stream2 = random(); + String stream3 = random(); + String stream4 = random(); + createMemoryStream(jsm, stream1, "foo"); + createMemoryStream(jsm, stream2, "bar"); + createMemoryStream(jsm, stream3, "a.a"); + createMemoryStream(jsm, stream4, "a.b"); List list = jsm.getStreamNames("*"); - assertStreamNameList(list, 1, 2); + assertStreamNameList(list, stream1, stream2); list = jsm.getStreamNames(">"); - assertStreamNameList(list, 1, 2, 3, 4); + assertStreamNameList(list, stream1, stream2, stream3, stream4); list = jsm.getStreamNames("*.*"); - assertStreamNameList(list, 3, 4); + assertStreamNameList(list, stream3, stream4); list = jsm.getStreamNames("a.>"); - assertStreamNameList(list, 3, 4); + assertStreamNameList(list, stream3, stream4); list = jsm.getStreamNames("a.*"); - assertStreamNameList(list, 3, 4); + assertStreamNameList(list, stream3, stream4); list = jsm.getStreamNames("foo"); - assertStreamNameList(list, 1); + assertStreamNameList(list, stream1); list = jsm.getStreamNames("a.a"); - assertStreamNameList(list, 3); + assertStreamNameList(list, stream3); list = jsm.getStreamNames("nomatch"); assertStreamNameList(list); }); } - private void assertStreamNameList(List list, int... ids) { + private void assertStreamNameList(List list, String... streams) { assertNotNull(list); - assertEquals(ids.length, list.size()); - for (int id : ids) { - assertTrue(list.contains(stream(id))); + assertEquals(streams.length, list.size()); + for (String s : streams) { + assertTrue(list.contains(s)); } } @Test public void testDeleteStream() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); + runInLrServer((nc, jstc) -> { JetStreamApiException jsapiEx = - assertThrows(JetStreamApiException.class, () -> jsm.deleteStream(stream())); + assertThrows(JetStreamApiException.class, () -> jstc.jsm.deleteStream(random())); assertEquals(10059, jsapiEx.getApiErrorCode()); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - assertNotNull(jsm.getStreamInfo(tsc.stream)); - assertTrue(jsm.deleteStream(tsc.stream)); + assertNotNull(jstc.jsm.getStreamInfo(jstc.stream)); + assertTrue(jstc.jsm.deleteStream(jstc.stream)); - jsapiEx = assertThrows(JetStreamApiException.class, () -> jsm.getStreamInfo(tsc.stream)); + jsapiEx = assertThrows(JetStreamApiException.class, () -> jstc.jsm.getStreamInfo(jstc.stream)); assertEquals(10059, jsapiEx.getApiErrorCode()); - jsapiEx = assertThrows(JetStreamApiException.class, () -> jsm.deleteStream(tsc.stream)); + jsapiEx = assertThrows(JetStreamApiException.class, () -> jstc.jsm.deleteStream(jstc.stream)); assertEquals(10059, jsapiEx.getApiErrorCode()); }); } @Test public void testPurgeStreamAndOptions() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { // invalid to have both keep and seq assertThrows(IllegalArgumentException.class, () -> PurgeOptions.builder().keep(1).sequence(1).build()); - JetStreamManagement jsm = nc.jetStreamManagement(); - // error to purge a stream that does not exist - assertThrows(JetStreamApiException.class, () -> jsm.purgeStream(stream())); + assertThrows(JetStreamApiException.class, () -> jsm.purgeStream(random())); - TestingStreamContainer tsc = new TestingStreamContainer(nc, 2); - createMemoryStream(jsm, tsc.stream, tsc.subject(0), tsc.subject(1)); + JetStreamTestingContext jstc = new JetStreamTestingContext(nc, 2); + createMemoryStream(jsm, jstc.stream, jstc.subject(0), jstc.subject(1)); - StreamInfo si = jsm.getStreamInfo(tsc.stream); + StreamInfo si = jsm.getStreamInfo(jstc.stream); assertEquals(0, si.getStreamState().getMsgCount()); - jsPublish(nc, tsc.subject(0), 10); - si = jsm.getStreamInfo(tsc.stream); + jsPublish(nc, jstc.subject(0), 10); + si = jsm.getStreamInfo(jstc.stream); assertEquals(10, si.getStreamState().getMsgCount()); PurgeOptions options = PurgeOptions.builder().keep(7).build(); - PurgeResponse pr = jsm.purgeStream(tsc.stream, options); + PurgeResponse pr = jsm.purgeStream(jstc.stream, options); assertTrue(pr.isSuccess()); assertEquals(3, pr.getPurged()); options = PurgeOptions.builder().sequence(9).build(); - pr = jsm.purgeStream(tsc.stream, options); + pr = jsm.purgeStream(jstc.stream, options); assertTrue(pr.isSuccess()); assertEquals(5, pr.getPurged()); - si = jsm.getStreamInfo(tsc.stream); + si = jsm.getStreamInfo(jstc.stream); assertEquals(2, si.getStreamState().getMsgCount()); - pr = jsm.purgeStream(tsc.stream); + pr = jsm.purgeStream(jstc.stream); assertTrue(pr.isSuccess()); assertEquals(2, pr.getPurged()); - si = jsm.getStreamInfo(tsc.stream); + si = jsm.getStreamInfo(jstc.stream); assertEquals(0, si.getStreamState().getMsgCount()); - jsPublish(nc, tsc.subject(0), 10); - jsPublish(nc, tsc.subject(1), 10); - si = jsm.getStreamInfo(tsc.stream); + jsPublish(nc, jstc.subject(0), 10); + jsPublish(nc, jstc.subject(1), 10); + si = jsm.getStreamInfo(jstc.stream); assertEquals(20, si.getStreamState().getMsgCount()); - jsm.purgeStream(tsc.stream, PurgeOptions.subject(tsc.subject(0))); - si = jsm.getStreamInfo(tsc.stream); + jsm.purgeStream(jstc.stream, PurgeOptions.subject(jstc.subject(0))); + si = jsm.getStreamInfo(jstc.stream); assertEquals(10, si.getStreamState().getMsgCount()); - options = PurgeOptions.builder().subject(tsc.subject(0)).sequence(1).build(); - assertEquals(tsc.subject(0), options.getSubject()); + options = PurgeOptions.builder().subject(jstc.subject(0)).sequence(1).build(); + assertEquals(jstc.subject(0), options.getSubject()); assertEquals(1, options.getSequence()); - options = PurgeOptions.builder().subject(tsc.subject(0)).keep(2).build(); + options = PurgeOptions.builder().subject(jstc.subject(0)).keep(2).build(); assertEquals(2, options.getKeep()); }); } @Test public void testAddDeleteConsumer() throws Exception { - runInJsServer(nc -> { + runInLrServer((nc, jsm, js) -> { boolean atLeast2dot9 = nc.getServerInfo().isSameOrNewerThanVersion("2.9"); - JetStreamManagement jsm = nc.jetStreamManagement(); - - createMemoryStream(jsm, STREAM, subjectDot(">")); + String stream = random(); + String subject = random(); + createMemoryStream(jsm, stream, subjectGt(subject)); - List list = jsm.getConsumers(STREAM); + List list = jsm.getConsumers(stream); assertEquals(0, list.size()); final ConsumerConfiguration cc = ConsumerConfiguration.builder().build(); IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> jsm.addOrUpdateConsumer(null, cc)); assertTrue(iae.getMessage().contains("Stream cannot be null or empty")); - iae = assertThrows(IllegalArgumentException.class, () -> jsm.addOrUpdateConsumer(STREAM, null)); + iae = assertThrows(IllegalArgumentException.class, () -> jsm.addOrUpdateConsumer(stream, null)); assertTrue(iae.getMessage().contains("Config cannot be null")); // durable and name can both be null - ConsumerInfo ci = jsm.addOrUpdateConsumer(STREAM, cc); + ConsumerInfo ci = jsm.addOrUpdateConsumer(stream, cc); assertNotNull(ci.getName()); // threshold can be set for durable - final ConsumerConfiguration cc2 = ConsumerConfiguration.builder().durable(DURABLE).inactiveThreshold(10000).build(); - ci = jsm.addOrUpdateConsumer(STREAM, cc2); - assertEquals(10000, ci.getConsumerConfiguration().getInactiveThreshold().toMillis()); + final ConsumerConfiguration cc2 = ConsumerConfiguration.builder().durable(random()).inactiveThreshold(10000).build(); + ci = jsm.addOrUpdateConsumer(stream, cc2); + assertNotNull(ci.getName()); + ConsumerConfiguration cc2cc = ci.getConsumerConfiguration(); + assertNotNull(cc2cc); + Duration duration = ci.getConsumerConfiguration().getInactiveThreshold(); + assertNotNull(duration); + assertEquals(10000, duration.toMillis()); // prep for next part of test - jsm.deleteStream(STREAM); - createMemoryStream(jsm, STREAM, subjectDot(">")); + jsm.deleteStream(stream); + createMemoryStream(jsm, stream, subjectGt(subject)); // with and w/o deliver subject for push/pull - addConsumer(jsm, atLeast2dot9, 1, false, null, ConsumerConfiguration.builder() - .durable(durable(1)) + String dur0 = random(); + addConsumer(jsm, stream, atLeast2dot9, dur0, null, null, ConsumerConfiguration.builder() + .durable(dur0) .build()); - addConsumer(jsm, atLeast2dot9, 2, true, null, ConsumerConfiguration.builder() - .durable(durable(2)) - .deliverSubject(deliver(2)) + String dur = random(); + String deliver = random(); + addConsumer(jsm, stream, atLeast2dot9, dur, deliver, null, ConsumerConfiguration.builder() + .durable(dur) + .deliverSubject(deliver) .build()); // test delete here - List consumers = jsm.getConsumerNames(STREAM); + List consumers = jsm.getConsumerNames(stream); assertEquals(2, consumers.size()); - assertTrue(jsm.deleteConsumer(STREAM, durable(1))); - consumers = jsm.getConsumerNames(STREAM); + assertTrue(jsm.deleteConsumer(stream, dur0)); + consumers = jsm.getConsumerNames(stream); assertEquals(1, consumers.size()); - assertThrows(JetStreamApiException.class, () -> jsm.deleteConsumer(STREAM, durable(1))); + assertThrows(JetStreamApiException.class, () -> jsm.deleteConsumer(stream, dur0)); // some testing of new name if (atLeast2dot9) { - addConsumer(jsm, true, 3, false, null, ConsumerConfiguration.builder() - .durable(durable(3)) - .name(durable(3)) + dur = random(); + addConsumer(jsm, stream, true, dur, null, null, ConsumerConfiguration.builder() + .durable(dur) + .name(dur) .build()); - addConsumer(jsm, true, 4, true, null, ConsumerConfiguration.builder() - .durable(durable(4)) - .name(durable(4)) - .deliverSubject(deliver(4)) + dur = random(); + deliver = random(); + addConsumer(jsm, stream, true, dur, deliver, null, ConsumerConfiguration.builder() + .durable(dur) + .name(dur) + .deliverSubject(deliver) .build()); - addConsumer(jsm, true, 5, false, ">", ConsumerConfiguration.builder() - .durable(durable(5)) + dur = random(); + addConsumer(jsm, stream, true, dur, null, ">", ConsumerConfiguration.builder() + .durable(dur) .filterSubject(">") .build()); - addConsumer(jsm, true, 6, false, subjectDot(">"), ConsumerConfiguration.builder() - .durable(durable(6)) - .filterSubject(subjectDot(">")) + dur = random(); + addConsumer(jsm, stream, true, dur, null, subjectGt(subject), ConsumerConfiguration.builder() + .durable(dur) + .filterSubject(subjectGt(subject)) .build()); - addConsumer(jsm, true, 7, false, subjectDot("foo"), ConsumerConfiguration.builder() - .durable(durable(7)) - .filterSubject(subjectDot("foo")) + dur = random(); + addConsumer(jsm, stream, true, dur, null, subjectDot(subject, "foo"), ConsumerConfiguration.builder() + .durable(dur) + .filterSubject(subjectDot(subject, "foo")) .build()); } }); @@ -813,22 +718,20 @@ public void testAddDeleteConsumer() throws Exception { @Test public void testAddPausedConsumer() throws Exception { - jsServer.run(TestBase::atLeast2_11, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - - List list = jsm.getConsumers(tsc.stream); + runInLrServer(VersionUtils::atLeast2_11, (nc, jstc) -> { + List list = jstc.jsm.getConsumers(jstc.stream); assertEquals(0, list.size()); ZonedDateTime pauseUntil = ZonedDateTime.now(ZONE_ID_GMT).plusMinutes(2); ConsumerConfiguration cc = ConsumerConfiguration.builder() - .durable(tsc.consumerName()) + .durable(jstc.consumerName()) .pauseUntil(pauseUntil) .build(); // Consumer should be paused on creation. - ConsumerInfo ci = jsm.addOrUpdateConsumer(tsc.stream, cc); + ConsumerInfo ci = jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); assertTrue(ci.getPaused()); + assertNotNull(ci.getPauseRemaining()); assertTrue(ci.getPauseRemaining().toMillis() > 60_000); assertEquals(pauseUntil, ci.getConsumerConfiguration().getPauseUntil()); }); @@ -836,154 +739,157 @@ public void testAddPausedConsumer() throws Exception { @Test public void testPauseResumeConsumer() throws Exception { - jsServer.run(TestBase::atLeast2_11, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - - List list = jsm.getConsumers(tsc.stream); + runInLrServer(VersionUtils::atLeast2_11, (nc, jstc) -> { + List list = jstc.jsm.getConsumers(jstc.stream); assertEquals(0, list.size()); ConsumerConfiguration cc = ConsumerConfiguration.builder() - .durable(tsc.consumerName()) + .durable(jstc.consumerName()) .build(); // durable and name can both be null - ConsumerInfo ci = jsm.addOrUpdateConsumer(tsc.stream, cc); + ConsumerInfo ci = jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); assertNotNull(ci.getName()); // pause consumer ZonedDateTime pauseUntil = ZonedDateTime.now(ZONE_ID_GMT).plusMinutes(2); - ConsumerPauseResponse pauseResponse = jsm.pauseConsumer(tsc.stream, ci.getName(), pauseUntil); + ConsumerPauseResponse pauseResponse = jstc.jsm.pauseConsumer(jstc.stream, ci.getName(), pauseUntil); assertTrue(pauseResponse.isPaused()); assertEquals(pauseUntil, pauseResponse.getPauseUntil()); - ci = jsm.getConsumerInfo(tsc.stream, ci.getName()); + ci = jstc.jsm.getConsumerInfo(jstc.stream, ci.getName()); assertTrue(ci.getPaused()); + assertNotNull(ci.getPauseRemaining()); assertTrue(ci.getPauseRemaining().toMillis() > 60_000); // resume consumer - boolean isResumed = jsm.resumeConsumer(tsc.stream, ci.getName()); + boolean isResumed = jstc.jsm.resumeConsumer(jstc.stream, ci.getName()); assertTrue(isResumed); - ci = jsm.getConsumerInfo(tsc.stream, ci.getName()); + ci = jstc.jsm.getConsumerInfo(jstc.stream, ci.getName()); assertFalse(ci.getPaused()); // pause again - pauseResponse = jsm.pauseConsumer(tsc.stream, ci.getName(), pauseUntil); + pauseResponse = jstc.jsm.pauseConsumer(jstc.stream, ci.getName(), pauseUntil); assertTrue(pauseResponse.isPaused()); assertEquals(pauseUntil, pauseResponse.getPauseUntil()); // resume via pause with no date - pauseResponse = jsm.pauseConsumer(tsc.stream, ci.getName(), null); + pauseResponse = jstc.jsm.pauseConsumer(jstc.stream, ci.getName(), null); assertFalse(pauseResponse.isPaused()); assertEquals(DEFAULT_TIME, pauseResponse.getPauseUntil()); - ci = jsm.getConsumerInfo(tsc.stream, ci.getName()); + ci = jstc.jsm.getConsumerInfo(jstc.stream, ci.getName()); assertFalse(ci.getPaused()); - assertThrows(JetStreamApiException.class, () -> jsm.pauseConsumer(stream(), tsc.consumerName(), pauseUntil)); - assertThrows(JetStreamApiException.class, () -> jsm.pauseConsumer(tsc.stream, name(), pauseUntil)); - assertThrows(JetStreamApiException.class, () -> jsm.resumeConsumer(stream(), tsc.consumerName())); - assertThrows(JetStreamApiException.class, () -> jsm.resumeConsumer(tsc.stream, name())); + assertThrows(JetStreamApiException.class, () -> jstc.jsm.pauseConsumer(random(), jstc.consumerName(), pauseUntil)); + assertThrows(JetStreamApiException.class, () -> jstc.jsm.pauseConsumer(jstc.stream, random(), pauseUntil)); + assertThrows(JetStreamApiException.class, () -> jstc.jsm.resumeConsumer(random(), jstc.consumerName())); + assertThrows(JetStreamApiException.class, () -> jstc.jsm.resumeConsumer(jstc.stream, random())); }); } - private static void addConsumer(JetStreamManagement jsm, boolean atLeast2dot9, int id, boolean deliver, String fs, ConsumerConfiguration cc) throws IOException, JetStreamApiException { - ConsumerInfo ci = jsm.addOrUpdateConsumer(STREAM, cc); - assertEquals(durable(id), ci.getName()); + private static void addConsumer(JetStreamManagement jsm, String stream, boolean atLeast2dot9, String name, String deliver, String fs, ConsumerConfiguration cc) throws IOException, JetStreamApiException { + ConsumerInfo ci = jsm.addOrUpdateConsumer(stream, cc); + assertEquals(name, ci.getName()); if (atLeast2dot9) { - assertEquals(durable(id), ci.getConsumerConfiguration().getName()); + assertEquals(name, ci.getConsumerConfiguration().getName()); } - assertEquals(durable(id), ci.getConsumerConfiguration().getDurable()); + assertEquals(name, ci.getConsumerConfiguration().getDurable()); if (fs == null) { assertNull(ci.getConsumerConfiguration().getFilterSubject()); } - if (deliver) { - assertEquals(deliver(id), ci.getConsumerConfiguration().getDeliverSubject()); + if (deliver != null) { + assertEquals(deliver, ci.getConsumerConfiguration().getDeliverSubject()); } } @Test public void testValidConsumerUpdates() throws Exception { - runInJsServer(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - createMemoryStream(jsm, STREAM, SUBJECT_GT); + runInLrServer((nc, jsm, js) -> { + String stream = random(); + String subject = random(); + String subjectGt = subjectGt(subject); + createMemoryStream(jsm, stream, subjectGt); - ConsumerConfiguration cc = prepForUpdateTest(jsm); - cc = ConsumerConfiguration.builder(cc).deliverSubject(deliver(2)).build(); - assertValidAddOrUpdate(jsm, cc); + ConsumerConfiguration cc = prepForUpdateTest(jsm, stream, subjectGt, null); + cc = ConsumerConfiguration.builder(cc).deliverSubject(random()).build(); + assertValidAddOrUpdate(jsm, cc, stream); - cc = prepForUpdateTest(jsm); + cc = prepForUpdateTest(jsm, stream, subjectGt, cc.getDurable()); cc = ConsumerConfiguration.builder(cc).ackWait(Duration.ofSeconds(5)).build(); - assertValidAddOrUpdate(jsm, cc); + assertValidAddOrUpdate(jsm, cc, stream); - cc = prepForUpdateTest(jsm); + cc = prepForUpdateTest(jsm, stream, subjectGt, cc.getDurable()); cc = ConsumerConfiguration.builder(cc).rateLimit(100).build(); - assertValidAddOrUpdate(jsm, cc); + assertValidAddOrUpdate(jsm, cc, stream); - cc = prepForUpdateTest(jsm); + cc = prepForUpdateTest(jsm, stream, subjectGt, cc.getDurable()); cc = ConsumerConfiguration.builder(cc).maxAckPending(100).build(); - assertValidAddOrUpdate(jsm, cc); + assertValidAddOrUpdate(jsm, cc, stream); - cc = prepForUpdateTest(jsm); + cc = prepForUpdateTest(jsm, stream, subjectGt, cc.getDurable()); cc = ConsumerConfiguration.builder(cc).maxDeliver(4).build(); - assertValidAddOrUpdate(jsm, cc); + assertValidAddOrUpdate(jsm, cc, stream); if (nc.getServerInfo().isNewerVersionThan("2.8.4")) { - cc = prepForUpdateTest(jsm); - cc = ConsumerConfiguration.builder(cc).filterSubject(SUBJECT_STAR).build(); - assertValidAddOrUpdate(jsm, cc); + cc = prepForUpdateTest(jsm, stream, subjectGt, cc.getDurable()); + cc = ConsumerConfiguration.builder(cc).filterSubject(subjectStar(subject)).build(); + assertValidAddOrUpdate(jsm, cc, stream); } }); } @Test public void testInvalidConsumerUpdates() throws Exception { - runInJsServer(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - createMemoryStream(jsm, STREAM, SUBJECT_GT); + runInLrServer((nc, jsm, js) -> { + String stream = random(); + String subject = random(); + String subjectGt = subjectGt(subject); + createMemoryStream(jsm, stream, subjectGt); - ConsumerConfiguration cc = prepForUpdateTest(jsm); + ConsumerConfiguration cc = prepForUpdateTest(jsm, stream, subjectGt, null); cc = ConsumerConfiguration.builder(cc).deliverPolicy(DeliverPolicy.New).build(); - assertInvalidConsumerUpdate(jsm, cc); + assertInvalidConsumerUpdate(jsm, cc, stream); if (nc.getServerInfo().isSameOrOlderThanVersion("2.8.4")) { - cc = prepForUpdateTest(jsm); - cc = ConsumerConfiguration.builder(cc).filterSubject(SUBJECT_STAR).build(); - assertInvalidConsumerUpdate(jsm, cc); + cc = prepForUpdateTest(jsm, stream, subjectGt, cc.getDurable()); + cc = ConsumerConfiguration.builder(cc).filterSubject(subjectStar(subject)).build(); + assertInvalidConsumerUpdate(jsm, cc, stream); } - cc = prepForUpdateTest(jsm); + cc = prepForUpdateTest(jsm, stream, subjectGt, cc.getDurable()); cc = ConsumerConfiguration.builder(cc).idleHeartbeat(Duration.ofMillis(111)).build(); - assertInvalidConsumerUpdate(jsm, cc); + assertInvalidConsumerUpdate(jsm, cc, stream); }); } - private ConsumerConfiguration prepForUpdateTest(JetStreamManagement jsm) throws IOException, JetStreamApiException { + private ConsumerConfiguration prepForUpdateTest(JetStreamManagement jsm, String stream, String subjectGt, String durableToDelete) throws IOException, JetStreamApiException { try { - jsm.deleteConsumer(STREAM, durable(1)); + if (durableToDelete != null) { + jsm.deleteConsumer(stream, durableToDelete); + } } catch (Exception e) { /* ignore */ } - ConsumerConfiguration cc = ConsumerConfiguration.builder() - .durable(durable(1)) + .durable(random()) .ackPolicy(AckPolicy.Explicit) - .deliverSubject(deliver(1)) + .deliverSubject(random()) .maxDeliver(3) - .filterSubject(SUBJECT_GT) + .filterSubject(subjectGt) .build(); - assertValidAddOrUpdate(jsm, cc); + assertValidAddOrUpdate(jsm, cc, stream); return cc; } - private void assertInvalidConsumerUpdate(JetStreamManagement jsm, ConsumerConfiguration cc) { - JetStreamApiException e = assertThrows(JetStreamApiException.class, () -> jsm.addOrUpdateConsumer(STREAM, cc)); + private void assertInvalidConsumerUpdate(JetStreamManagement jsm, ConsumerConfiguration cc, String stream) { + JetStreamApiException e = assertThrows(JetStreamApiException.class, () -> jsm.addOrUpdateConsumer(stream, cc)); assertEquals(10012, e.getApiErrorCode()); assertEquals(500, e.getErrorCode()); } - private void assertValidAddOrUpdate(JetStreamManagement jsm, ConsumerConfiguration cc) throws IOException, JetStreamApiException { - ConsumerInfo ci = jsm.addOrUpdateConsumer(STREAM, cc); + private void assertValidAddOrUpdate(JetStreamManagement jsm, ConsumerConfiguration cc, String stream) throws IOException, JetStreamApiException { + ConsumerInfo ci = jsm.addOrUpdateConsumer(stream, cc); ConsumerConfiguration cicc = ci.getConsumerConfiguration(); assertEquals(cc.getDurable(), ci.getName()); assertEquals(cc.getDurable(), cicc.getDurable()); @@ -991,84 +897,81 @@ private void assertValidAddOrUpdate(JetStreamManagement jsm, ConsumerConfigurati assertEquals(cc.getMaxDeliver(), cicc.getMaxDeliver()); assertEquals(cc.getDeliverPolicy(), cicc.getDeliverPolicy()); - List consumers = jsm.getConsumerNames(STREAM); + List consumers = jsm.getConsumerNames(stream); assertEquals(1, consumers.size()); assertEquals(cc.getDurable(), consumers.get(0)); } @Test public void testConsumerMetadata() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jstc) -> { Map metaData = new HashMap<>(); metaData.put(META_KEY, META_VALUE); - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); ConsumerConfiguration cc = ConsumerConfiguration.builder() - .durable(tsc.consumerName()) + .durable(random()) .metadata(metaData) .build(); - ConsumerInfo ci = jsm.addOrUpdateConsumer(tsc.stream, cc); + ConsumerInfo ci = jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); assertMetaData(ci.getConsumerConfiguration().getMetadata()); }); } @Test public void testCreateConsumersWithFilters() throws Exception { - runInJsServer(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - - createDefaultTestStream(jsm); + runInLrServer((nc, jsm, js) -> { + String stream = random(); + String subject = random(); + createMemoryStream(jsm, stream, subject); // plain subject - ConsumerConfiguration.Builder builder = ConsumerConfiguration.builder().durable(DURABLE); - jsm.addOrUpdateConsumer(STREAM, builder.filterSubject(SUBJECT).build()); - List cis = jsm.getConsumers(STREAM); - assertEquals(SUBJECT, cis.get(0).getConsumerConfiguration().getFilterSubject()); + ConsumerConfiguration.Builder builder = ConsumerConfiguration.builder().durable(random()); + jsm.addOrUpdateConsumer(stream, builder.filterSubject(subject).build()); + List cis = jsm.getConsumers(stream); + assertEquals(subject, cis.get(0).getConsumerConfiguration().getFilterSubject()); if (nc.getServerInfo().isSameOrNewerThanVersion("2.10")) { // 2.10 and later you can set the filter to something that does not match - jsm.addOrUpdateConsumer(STREAM, builder.filterSubject(subjectDot("two-ten-allows-not-matching")).build()); - cis = jsm.getConsumers(STREAM); - assertEquals(subjectDot("two-ten-allows-not-matching"), cis.get(0).getConsumerConfiguration().getFilterSubject()); + jsm.addOrUpdateConsumer(stream, builder.filterSubject(subjectDot(subject, "two-ten-allows-not-matching")).build()); + cis = jsm.getConsumers(stream); + assertEquals(subjectDot(subject, "two-ten-allows-not-matching"), cis.get(0).getConsumerConfiguration().getFilterSubject()); } else { assertThrows(JetStreamApiException.class, - () -> jsm.addOrUpdateConsumer(STREAM, builder.filterSubject(subjectDot("not-match")).build())); + () -> jsm.addOrUpdateConsumer(stream, builder.filterSubject(subjectDot(subject, "not-match")).build())); } // wildcard subject - jsm.deleteStream(STREAM); - createMemoryStream(jsm, STREAM, SUBJECT_STAR); + jsm.deleteStream(stream); + createMemoryStream(jsm, stream, subjectStar(subject)); - jsm.addOrUpdateConsumer(STREAM, builder.filterSubject(subjectDot("A")).build()); - cis = jsm.getConsumers(STREAM); - assertEquals(subjectDot("A"), cis.get(0).getConsumerConfiguration().getFilterSubject()); + String subjectA = subjectDot(subject, "A"); + jsm.addOrUpdateConsumer(stream, builder.filterSubject(subjectA).build()); + cis = jsm.getConsumers(stream); + assertEquals(subjectA, cis.get(0).getConsumerConfiguration().getFilterSubject()); // gt subject - jsm.deleteStream(STREAM); - createMemoryStream(jsm, STREAM, SUBJECT_GT); + jsm.deleteStream(stream); + createMemoryStream(jsm, stream, subjectGt(subject)); - jsm.addOrUpdateConsumer(STREAM, builder.filterSubject(subjectDot("A")).build()); - cis = jsm.getConsumers(STREAM); - assertEquals(subjectDot("A"), cis.get(0).getConsumerConfiguration().getFilterSubject()); + jsm.addOrUpdateConsumer(stream, builder.filterSubject(subjectA).build()); + cis = jsm.getConsumers(stream); + assertEquals(subjectA, cis.get(0).getConsumerConfiguration().getFilterSubject()); }); } @Test public void testGetConsumerInfo() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - assertThrows(JetStreamApiException.class, () -> jsm.getConsumerInfo(tsc.stream, tsc.consumerName())); - ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(tsc.consumerName()).build(); - ConsumerInfo ci = jsm.addOrUpdateConsumer(tsc.stream, cc); - assertEquals(tsc.stream, ci.getStreamName()); - assertEquals(tsc.consumerName(), ci.getName()); - ci = jsm.getConsumerInfo(tsc.stream, tsc.consumerName()); - assertEquals(tsc.stream, ci.getStreamName()); - assertEquals(tsc.consumerName(), ci.getName()); - assertThrows(JetStreamApiException.class, () -> jsm.getConsumerInfo(tsc.stream, durable(999))); + runInLrServer((nc, jstc) -> { + assertThrows(JetStreamApiException.class, () -> jstc.jsm.getConsumerInfo(jstc.stream, jstc.consumerName())); + ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(jstc.consumerName()).build(); + ConsumerInfo ci = jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); + assertEquals(jstc.stream, ci.getStreamName()); + assertEquals(jstc.consumerName(), ci.getName()); + ci = jstc.jsm.getConsumerInfo(jstc.stream, jstc.consumerName()); + assertEquals(jstc.stream, ci.getStreamName()); + assertEquals(jstc.consumerName(), ci.getName()); + assertThrows(JetStreamApiException.class, () -> jstc.jsm.getConsumerInfo(jstc.stream, random())); if (nc.getServerInfo().isSameOrNewerThanVersion("2.10")) { assertNotNull(ci.getTimestamp()); } @@ -1080,24 +983,22 @@ public void testGetConsumerInfo() throws Exception { @Test public void testGetConsumers() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - - addConsumers(jsm, tsc.stream, 600, "A"); // getConsumers pages at 256 + runInLrServer((nc, jstc) -> { + addConsumers(jstc.jsm, jstc.stream, 600); // getConsumers pages at 256 - List list = jsm.getConsumers(tsc.stream); + List list = jstc.jsm.getConsumers(jstc.stream); assertEquals(600, list.size()); - addConsumers(jsm, tsc.stream, 500, "B"); // getConsumerNames pages at 1024 - List names = jsm.getConsumerNames(tsc.stream); + addConsumers(jstc.jsm, jstc.stream, 500); // getConsumerNames pages at 1024 + List names = jstc.jsm.getConsumerNames(jstc.stream); assertEquals(1100, names.size()); }); } - private void addConsumers(JetStreamManagement jsm, String stream, int count, String durableVary) throws IOException, JetStreamApiException { + private void addConsumers(JetStreamManagement jsm, String stream, int count) throws IOException, JetStreamApiException { + String base = random() ; for (int x = 1; x <= count; x++) { - String dur = durable(durableVary, x); + String dur = base + "-" + x; ConsumerConfiguration cc = ConsumerConfiguration.builder() .durable(dur) .build(); @@ -1122,88 +1023,59 @@ public void testDeleteMessage() throws Exception { assertFalse(mdr.isErase()); assertTrue(mdr.isNoErase()); - runInJsServer(nc -> { - createDefaultTestStream(nc); - JetStream js = nc.jetStream(); - + runInLrServer((nc, jstc) -> { Headers h = new Headers(); h.add("foo", "bar"); ZonedDateTime timeBeforeCreated = ZonedDateTime.now(); - js.publish(NatsMessage.builder().subject(SUBJECT).headers(h).data(dataBytes(1)).build()); - js.publish(NatsMessage.builder().subject(SUBJECT).build()); + jstc.js.publish(NatsMessage.builder().subject(jstc.subject()).headers(h).data(dataBytes(1)).build()); + jstc.js.publish(NatsMessage.builder().subject(jstc.subject()).build()); - JetStreamManagement jsm = nc.jetStreamManagement(); - - MessageInfo mi = jsm.getMessage(STREAM, 1); + MessageInfo mi = jstc.jsm.getMessage(jstc.stream, 1); assertNotNull(mi.toString()); - assertEquals(SUBJECT, mi.getSubject()); + assertEquals(jstc.subject(), mi.getSubject()); + assertNotNull(mi.getData()); assertEquals(data(1), new String(mi.getData())); assertEquals(1, mi.getSeq()); + assertNotNull(mi.getTime()); assertTrue(mi.getTime().toEpochSecond() >= timeBeforeCreated.toEpochSecond()); assertNotNull(mi.getHeaders()); - assertEquals("bar", mi.getHeaders().get("foo").get(0)); + List foos = mi.getHeaders().get("foo"); + assertNotNull(foos); + assertEquals("bar", foos.get(0)); - mi = jsm.getMessage(STREAM, 2); + mi = jstc.jsm.getMessage(jstc.stream, 2); assertNotNull(mi.toString()); - assertEquals(SUBJECT, mi.getSubject()); + assertEquals(jstc.subject(), mi.getSubject()); assertNull(mi.getData()); assertEquals(2, mi.getSeq()); + assertNotNull(mi.getTime()); assertTrue(mi.getTime().toEpochSecond() >= timeBeforeCreated.toEpochSecond()); assertTrue(mi.getHeaders() == null || mi.getHeaders().isEmpty()); - assertTrue(jsm.deleteMessage(STREAM, 1, false)); // added coverage for use of erase (no_erase) flag. - assertThrows(JetStreamApiException.class, () -> jsm.deleteMessage(STREAM, 1)); - assertThrows(JetStreamApiException.class, () -> jsm.getMessage(STREAM, 1)); - assertThrows(JetStreamApiException.class, () -> jsm.getMessage(STREAM, 3)); - assertThrows(JetStreamApiException.class, () -> jsm.deleteMessage(stream(999), 1)); - assertThrows(JetStreamApiException.class, () -> jsm.getMessage(stream(999), 1)); + assertTrue(jstc.jsm.deleteMessage(jstc.stream, 1, false)); // added coverage for use of erase (no_erase) flag. + assertThrows(JetStreamApiException.class, () -> jstc.jsm.deleteMessage(jstc.stream, 1)); + assertThrows(JetStreamApiException.class, () -> jstc.jsm.getMessage(jstc.stream, 1)); + assertThrows(JetStreamApiException.class, () -> jstc.jsm.getMessage(jstc.stream, 3)); + assertThrows(JetStreamApiException.class, () -> jstc.jsm.deleteMessage(random(), 1)); + assertThrows(JetStreamApiException.class, () -> jstc.jsm.getMessage(random(), 1)); }); } - @Test - public void testAuthCreateUpdateStream() throws Exception { - try (NatsTestServer ts = new NatsTestServer("src/test/resources/js_authorization.conf", false)) { - Options optionsSrc = new Options.Builder().server(ts.getURI()) - .userInfo("serviceup".toCharArray(), "uppass".toCharArray()).build(); - - try (Connection nc = Nats.connect(optionsSrc)) { - JetStreamManagement jsm = nc.jetStreamManagement(); - - // add streams with both account - StreamConfiguration sc = StreamConfiguration.builder() - .name(STREAM) - .storageType(StorageType.Memory) - .subjects(subject(1)) - .build(); - StreamInfo si = jsm.addStream(sc); - - sc = StreamConfiguration.builder(si.getConfiguration()) - .addSubjects(subject(2)) - .build(); - - jsm.updateStream(sc); - } - } - } - @Test public void testSealed() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); + runInLrServer((nc, jstc) -> { + assertFalse(jstc.si.getConfiguration().getSealed()); - TestingStreamContainer tsc = new TestingStreamContainer(nc); - assertFalse(tsc.si.getConfiguration().getSealed()); + jstc.js.publish(jstc.subject(), "data1".getBytes()); - JetStream js = nc.jetStream(); - js.publish(tsc.subject(), "data1".getBytes()); - - StreamConfiguration sc = new StreamConfiguration.Builder(tsc.si.getConfiguration()) - .seal().build(); - StreamInfo si = jsm.updateStream(sc); + StreamConfiguration sc = new StreamConfiguration.Builder(jstc.si.getConfiguration()) + .seal() + .build(); + StreamInfo si = jstc.jsm.updateStream(sc); assertTrue(si.getConfiguration().getSealed()); - assertThrows(JetStreamApiException.class, () -> js.publish(tsc.subject(), "data2".getBytes())); + assertThrows(JetStreamApiException.class, () -> jstc.js.publish(jstc.subject(), "data2".getBytes())); }); } @@ -1218,113 +1090,107 @@ public void testStorageTypeCoverage() { @Test public void testConsumerReplica() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { final ConsumerConfiguration cc0 = ConsumerConfiguration.builder() - .durable(tsc.consumerName()) + .durable(jstc.consumerName()) .build(); - ConsumerInfo ci = jsm.addOrUpdateConsumer(tsc.stream, cc0); + ConsumerInfo ci = jstc.jsm.addOrUpdateConsumer(jstc.stream, cc0); // server returns 0 when value is not set assertEquals(0, ci.getConsumerConfiguration().getNumReplicas()); final ConsumerConfiguration cc1 = ConsumerConfiguration.builder() - .durable(tsc.consumerName()) + .durable(jstc.consumerName()) .numReplicas(1) .build(); - ci = jsm.addOrUpdateConsumer(tsc.stream, cc1); + ci = jstc.jsm.addOrUpdateConsumer(jstc.stream, cc1); assertEquals(1, ci.getConsumerConfiguration().getNumReplicas()); }); } @Test public void testGetMessage() throws Exception { - jsServer.run(nc -> { - if (nc.getServerInfo().isNewerVersionThan("2.8.4")) { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - - TestingStreamContainer tsc = new TestingStreamContainer(nc, 2); - assertFalse(tsc.si.getConfiguration().getAllowDirect()); - - ZonedDateTime timeBeforeCreated = ZonedDateTime.now(); - sleep(100); - js.publish(buildTestGetMessage(tsc, 0, 1)); - js.publish(buildTestGetMessage(tsc, 1, 2)); - js.publish(buildTestGetMessage(tsc, 0, 3)); - js.publish(buildTestGetMessage(tsc, 1, 4)); - js.publish(buildTestGetMessage(tsc, 0, 5)); - js.publish(buildTestGetMessage(tsc, 1, 6)); - - validateGetMessage(jsm, tsc, timeBeforeCreated); - - StreamConfiguration sc = StreamConfiguration.builder(tsc.si.getConfiguration()).allowDirect(true).build(); - StreamInfo si = jsm.updateStream(sc); - assertTrue(si.getConfiguration().getAllowDirect()); - validateGetMessage(jsm, tsc, timeBeforeCreated); - - // error case stream doesn't exist - assertThrows(JetStreamApiException.class, () -> jsm.getMessage(stream(999), 1)); - } + runInLrServer((nc, jsm, js) -> { + JetStreamTestingContext jstc = new JetStreamTestingContext(nc, 2); + assertFalse(jstc.si.getConfiguration().getAllowDirect()); + + ZonedDateTime timeBeforeCreated = ZonedDateTime.now(); + sleep(100); + jstc.js.publish(buildTestGetMessage(jstc, 0, 1)); + jstc.js.publish(buildTestGetMessage(jstc, 1, 2)); + jstc.js.publish(buildTestGetMessage(jstc, 0, 3)); + jstc.js.publish(buildTestGetMessage(jstc, 1, 4)); + jstc.js.publish(buildTestGetMessage(jstc, 0, 5)); + jstc.js.publish(buildTestGetMessage(jstc, 1, 6)); + + validateGetMessage(jstc.jsm, jstc, timeBeforeCreated); + + StreamConfiguration sc = StreamConfiguration.builder(jstc.si.getConfiguration()).allowDirect(true).build(); + StreamInfo si = jstc.jsm.updateStream(sc); + assertTrue(si.getConfiguration().getAllowDirect()); + validateGetMessage(jstc.jsm, jstc, timeBeforeCreated); + + // error case stream doesn't exist + assertThrows(JetStreamApiException.class, () -> jstc.jsm.getMessage(random(), 1)); }); } - private static NatsMessage buildTestGetMessage(TestingStreamContainer tsc, int s, int q) { - String data = "s" + s + "-q" + q; + private static NatsMessage buildTestGetMessage(JetStreamTestingContext jstc, int n, int q) { + String data = "s" + n + "-q" + q; return NatsMessage.builder() - .subject(tsc.subject(s)) + .subject(jstc.subject(n)) .data("d-" + data) .headers(new Headers().put("h", "h-" + data)) .build(); } - private void validateGetMessage(JetStreamManagement jsm, TestingStreamContainer tsc, ZonedDateTime timeBeforeCreated) throws IOException, JetStreamApiException { - - assertMessageInfo(tsc, 0, 1, jsm.getMessage(tsc.stream, 1), timeBeforeCreated); - assertMessageInfo(tsc, 0, 5, jsm.getLastMessage(tsc.stream, tsc.subject(0)), timeBeforeCreated); - assertMessageInfo(tsc, 1, 6, jsm.getLastMessage(tsc.stream, tsc.subject(1)), timeBeforeCreated); - - assertMessageInfo(tsc, 0, 1, jsm.getNextMessage(tsc.stream, -1, tsc.subject(0)), timeBeforeCreated); - assertMessageInfo(tsc, 1, 2, jsm.getNextMessage(tsc.stream, -1, tsc.subject(1)), timeBeforeCreated); - assertMessageInfo(tsc, 0, 1, jsm.getNextMessage(tsc.stream, 0, tsc.subject(0)), timeBeforeCreated); - assertMessageInfo(tsc, 1, 2, jsm.getNextMessage(tsc.stream, 0, tsc.subject(1)), timeBeforeCreated); - assertMessageInfo(tsc, 0, 1, jsm.getFirstMessage(tsc.stream, tsc.subject(0)), timeBeforeCreated); - assertMessageInfo(tsc, 1, 2, jsm.getFirstMessage(tsc.stream, tsc.subject(1)), timeBeforeCreated); - assertMessageInfo(tsc, 0, 1, jsm.getFirstMessage(tsc.stream, timeBeforeCreated), timeBeforeCreated); - assertMessageInfo(tsc, 0, 1, jsm.getFirstMessage(tsc.stream, timeBeforeCreated, tsc.subject(0)), timeBeforeCreated); - assertMessageInfo(tsc, 1, 2, jsm.getFirstMessage(tsc.stream, timeBeforeCreated, tsc.subject(1)), timeBeforeCreated); - - assertMessageInfo(tsc, 0, 1, jsm.getNextMessage(tsc.stream, 1, tsc.subject(0)), timeBeforeCreated); - assertMessageInfo(tsc, 1, 2, jsm.getNextMessage(tsc.stream, 1, tsc.subject(1)), timeBeforeCreated); - - assertMessageInfo(tsc, 0, 3, jsm.getNextMessage(tsc.stream, 2, tsc.subject(0)), timeBeforeCreated); - assertMessageInfo(tsc, 1, 2, jsm.getNextMessage(tsc.stream, 2, tsc.subject(1)), timeBeforeCreated); - - assertMessageInfo(tsc, 0, 5, jsm.getNextMessage(tsc.stream, 5, tsc.subject(0)), timeBeforeCreated); - assertMessageInfo(tsc, 1, 6, jsm.getNextMessage(tsc.stream, 5, tsc.subject(1)), timeBeforeCreated); - - assertStatus(10003, assertThrows(JetStreamApiException.class, () -> jsm.getMessage(tsc.stream, -1))); - assertStatus(10003, assertThrows(JetStreamApiException.class, () -> jsm.getMessage(tsc.stream, 0))); - assertStatus(10037, assertThrows(JetStreamApiException.class, () -> jsm.getMessage(tsc.stream, 9))); - assertStatus(10037, assertThrows(JetStreamApiException.class, () -> jsm.getLastMessage(tsc.stream, "not-a-subject"))); - assertStatus(10037, assertThrows(JetStreamApiException.class, () -> jsm.getFirstMessage(tsc.stream, "not-a-subject"))); - assertStatus(10037, assertThrows(JetStreamApiException.class, () -> jsm.getNextMessage(tsc.stream, 9, tsc.subject(0)))); - assertStatus(10037, assertThrows(JetStreamApiException.class, () -> jsm.getNextMessage(tsc.stream, 1, "not-a-subject"))); + private void validateGetMessage(JetStreamManagement jsm, JetStreamTestingContext jstc, ZonedDateTime timeBeforeCreated) throws IOException, JetStreamApiException { + + assertMessageInfo(jstc, 0, 1, jsm.getMessage(jstc.stream, 1), timeBeforeCreated); + assertMessageInfo(jstc, 0, 5, jsm.getLastMessage(jstc.stream, jstc.subject(0)), timeBeforeCreated); + assertMessageInfo(jstc, 1, 6, jsm.getLastMessage(jstc.stream, jstc.subject(1)), timeBeforeCreated); + + assertMessageInfo(jstc, 0, 1, jsm.getNextMessage(jstc.stream, -1, jstc.subject(0)), timeBeforeCreated); + assertMessageInfo(jstc, 1, 2, jsm.getNextMessage(jstc.stream, -1, jstc.subject(1)), timeBeforeCreated); + assertMessageInfo(jstc, 0, 1, jsm.getNextMessage(jstc.stream, 0, jstc.subject(0)), timeBeforeCreated); + assertMessageInfo(jstc, 1, 2, jsm.getNextMessage(jstc.stream, 0, jstc.subject(1)), timeBeforeCreated); + assertMessageInfo(jstc, 0, 1, jsm.getFirstMessage(jstc.stream, jstc.subject(0)), timeBeforeCreated); + assertMessageInfo(jstc, 1, 2, jsm.getFirstMessage(jstc.stream, jstc.subject(1)), timeBeforeCreated); + assertMessageInfo(jstc, 0, 1, jsm.getFirstMessage(jstc.stream, timeBeforeCreated), timeBeforeCreated); + assertMessageInfo(jstc, 0, 1, jsm.getFirstMessage(jstc.stream, timeBeforeCreated, jstc.subject(0)), timeBeforeCreated); + assertMessageInfo(jstc, 1, 2, jsm.getFirstMessage(jstc.stream, timeBeforeCreated, jstc.subject(1)), timeBeforeCreated); + + assertMessageInfo(jstc, 0, 1, jsm.getNextMessage(jstc.stream, 1, jstc.subject(0)), timeBeforeCreated); + assertMessageInfo(jstc, 1, 2, jsm.getNextMessage(jstc.stream, 1, jstc.subject(1)), timeBeforeCreated); + + assertMessageInfo(jstc, 0, 3, jsm.getNextMessage(jstc.stream, 2, jstc.subject(0)), timeBeforeCreated); + assertMessageInfo(jstc, 1, 2, jsm.getNextMessage(jstc.stream, 2, jstc.subject(1)), timeBeforeCreated); + + assertMessageInfo(jstc, 0, 5, jsm.getNextMessage(jstc.stream, 5, jstc.subject(0)), timeBeforeCreated); + assertMessageInfo(jstc, 1, 6, jsm.getNextMessage(jstc.stream, 5, jstc.subject(1)), timeBeforeCreated); + + assertStatus(10003, assertThrows(JetStreamApiException.class, () -> jsm.getMessage(jstc.stream, -1))); + assertStatus(10003, assertThrows(JetStreamApiException.class, () -> jsm.getMessage(jstc.stream, 0))); + assertStatus(10037, assertThrows(JetStreamApiException.class, () -> jsm.getMessage(jstc.stream, 9))); + assertStatus(10037, assertThrows(JetStreamApiException.class, () -> jsm.getLastMessage(jstc.stream, "not-a-subject"))); + assertStatus(10037, assertThrows(JetStreamApiException.class, () -> jsm.getFirstMessage(jstc.stream, "not-a-subject"))); + assertStatus(10037, assertThrows(JetStreamApiException.class, () -> jsm.getNextMessage(jstc.stream, 9, jstc.subject(0)))); + assertStatus(10037, assertThrows(JetStreamApiException.class, () -> jsm.getNextMessage(jstc.stream, 1, "not-a-subject"))); } private void assertStatus(int apiErrorCode, JetStreamApiException jsae) { assertEquals(apiErrorCode, jsae.getApiErrorCode()); } - private void assertMessageInfo(TestingStreamContainer tsc, int subj, long seq, MessageInfo mi, ZonedDateTime timeBeforeCreated) { - assertEquals(tsc.stream, mi.getStream()); - assertEquals(tsc.subject(subj), mi.getSubject()); + private void assertMessageInfo(JetStreamTestingContext jstc, int subj, long seq, MessageInfo mi, ZonedDateTime timeBeforeCreated) { + assertEquals(jstc.stream, mi.getStream()); + assertEquals(jstc.subject(subj), mi.getSubject()); assertEquals(seq, mi.getSeq()); assertNotNull(mi.getTime()); assertTrue(mi.getTime().toEpochSecond() >= timeBeforeCreated.toEpochSecond()); String expectedData = "s" + subj + "-q" + seq; + assertNotNull(mi.getData()); assertEquals("d-" + expectedData, new String(mi.getData())); + assertNotNull(mi.getHeaders()); assertEquals("h-" + expectedData, mi.getHeaders().getFirst("h")); assertNull(mi.getHeaders().getFirst(NATS_SUBJECT)); assertNull(mi.getHeaders().getFirst(NATS_SEQUENCE)); @@ -1370,14 +1236,19 @@ private void validateMessageGetRequestObject( @Test public void testMessageGetRequestObjectDeprecatedMethods() { // coverage for deprecated methods + //noinspection deprecation MessageGetRequest.seqBytes(1); - MessageGetRequest.lastBySubjectBytes(SUBJECT); + //noinspection deprecation + MessageGetRequest.lastBySubjectBytes(random()); + //noinspection deprecation new MessageGetRequest(1); - new MessageGetRequest(SUBJECT); + //noinspection deprecation + new MessageGetRequest(random()); // coverage for MessageInfo, has error String json = dataAsString("GenericErrorResponse.json"); NatsMessage m = new NatsMessage("sub", null, json.getBytes(StandardCharsets.US_ASCII)); + //noinspection deprecation MessageInfo mi = new MessageInfo(m); assertTrue(mi.hasError()); assertEquals(-1, mi.getLastSeq()); @@ -1386,10 +1257,9 @@ public void testMessageGetRequestObjectDeprecatedMethods() { @Test public void testDirectMessageRepublishedSubject() throws Exception { - jsServer.run(TestBase::atLeast2_9_0, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - String streamBucketName = "sb-" + variant(null); - String subject = subject(); + runInLrServer(VersionUtils::atLeast2_9_0, (nc, jsm, js) -> { + String streamBucketName = "sb-" + random(); + String subject = random(); String streamSubject = subject + ".>"; String publishSubject1 = subject + ".one"; String publishSubject2 = subject + ".two"; @@ -1431,8 +1301,8 @@ public void testDirectMessageRepublishedSubject() throws Exception { @Test public void testCreateConsumerUpdateConsumer() throws Exception { - jsServer.run(TestBase::atLeast2_9_0, nc -> { - String streamPrefix = variant(); + runInLrServer(VersionUtils::atLeast2_9_0, (nc, jsm, js) -> { + String streamPrefix = random(); JetStreamManagement jsmNew = nc.jetStreamManagement(); JetStreamManagement jsmPre290 = nc.jetStreamManagement(JetStreamOptions.builder().optOut290ConsumerCreate(true).build()); @@ -1440,8 +1310,8 @@ public void testCreateConsumerUpdateConsumer() throws Exception { // New without filter // -------------------------------------------------------- String stream1 = streamPrefix + "-new"; - String name = name(); - String subject = name(); + String name = random(); + String subject = random(); createMemoryStream(jsmNew, stream1, subject + ".*"); ConsumerConfiguration cc11 = ConsumerConfiguration.builder().name(name).build(); @@ -1460,7 +1330,7 @@ public void testCreateConsumerUpdateConsumer() throws Exception { assertEquals(10148, e.getApiErrorCode()); // update ok when exists - ConsumerConfiguration cc12 = ConsumerConfiguration.builder().name(name).description(variant()).build(); + ConsumerConfiguration cc12 = ConsumerConfiguration.builder().name(name).description(random()).build(); ci = jsmNew.updateConsumer(stream1, cc12); assertEquals(name, ci.getName()); assertNull(ci.getConsumerConfiguration().getFilterSubject()); @@ -1469,8 +1339,8 @@ public void testCreateConsumerUpdateConsumer() throws Exception { // New with filter subject // -------------------------------------------------------- String stream2 = streamPrefix + "-new-fs"; - name = name(); - subject = name(); + name = random(); + subject = random(); String fs1 = subject + ".A"; String fs2 = subject + ".B"; createMemoryStream(jsmNew, stream2, subject + ".*"); @@ -1500,8 +1370,8 @@ public void testCreateConsumerUpdateConsumer() throws Exception { // Pre 290 durable pathway // -------------------------------------------------------- String stream3 = streamPrefix + "-old-durable"; - name = name(); - subject = name(); + name = random(); + subject = random(); fs1 = subject + ".A"; fs2 = subject + ".B"; String fs3 = subject + ".C"; @@ -1535,7 +1405,7 @@ public void testCreateConsumerUpdateConsumer() throws Exception { // -------------------------------------------------------- // Pre 290 ephemeral pathway // -------------------------------------------------------- - subject = name(); + subject = random(); String stream4 = streamPrefix + "-old-ephemeral"; fs1 = subject + ".A"; @@ -1556,12 +1426,9 @@ public void testCreateConsumerUpdateConsumer() throws Exception { @Test public void testNoRespondersWhenConsumerDeleted() throws Exception { ListenerForTesting listener = new ListenerForTesting(); - jsServer.run(new Options.Builder().errorListener(listener), TestBase::atLeast2_10_26, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - - String stream = stream(); - String subject = subject(); + runInLrServer(listener, VersionUtils::atLeast2_10_26, (nc, jsm, js) -> { + String stream = random(); + String subject = random(); assertThrows(JetStreamApiException.class, () -> jsm.getMessage(stream, 1)); @@ -1620,7 +1487,7 @@ public void testNoRespondersWhenConsumerDeleted() throws Exception { private static void validate1026(Message m, ListenerForTesting listener, boolean empty) { assertNull(m); - sleep(100); // give time for the message to get there + sleep(250); // give time for the message to get there assertEquals(empty, listener.getPullStatusWarnings().isEmpty()); } @@ -1633,7 +1500,7 @@ private static ConsumerContext setupFor1026Simplification(Connection nc, JetStre } private static String create1026Consumer(JetStreamManagement jsm, String stream, String subject) throws IOException, JetStreamApiException { - String consumer = name(); + String consumer = random(); jsm.addOrUpdateConsumer(stream, ConsumerConfiguration.builder() .durable(consumer) .filterSubject(subject) @@ -1688,22 +1555,20 @@ public void testMessageDeleteRequest() { @Test public void testStreamPersistMode() throws Exception { - jsServer.run(TestBase::atLeast2_12, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - + runInLrServer(VersionUtils::atLeast2_12, (nc, jsm, js) -> { StreamConfiguration sc = StreamConfiguration.builder() - .name(stream()) + .name(random()) .storageType(StorageType.File) - .subjects(subject()) + .subjects(random()) .build(); StreamInfo si = jsm.addStream(sc); assertTrue(si.getConfiguration().getPersistMode() == null || si.getConfiguration().getPersistMode() == PersistMode.Default); sc = StreamConfiguration.builder() - .name(stream()) + .name(random()) .storageType(StorageType.File) - .subjects(subject()) + .subjects(random()) .persistMode(PersistMode.Default) .build(); @@ -1711,9 +1576,9 @@ public void testStreamPersistMode() throws Exception { assertTrue(si.getConfiguration().getPersistMode() == null || si.getConfiguration().getPersistMode() == PersistMode.Default); sc = StreamConfiguration.builder() - .name(stream()) + .name(random()) .storageType(StorageType.File) - .subjects(subject()) + .subjects(random()) .persistMode(PersistMode.Async) .build(); diff --git a/src/test/java/io/nats/client/impl/JetStreamManagementWithConfTests.java b/src/test/java/io/nats/client/impl/JetStreamManagementWithConfTests.java new file mode 100644 index 000000000..4614dfc89 --- /dev/null +++ b/src/test/java/io/nats/client/impl/JetStreamManagementWithConfTests.java @@ -0,0 +1,149 @@ +// Copyright 2020-2025 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client.impl; + +import io.nats.client.*; +import io.nats.client.api.*; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static io.nats.client.NatsTestServer.configuredJsServer; +import static io.nats.client.utils.ConnectionUtils.standardConnectionWait; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class JetStreamManagementWithConfTests extends JetStreamTestBase { + + @Test + public void testGetStreamInfoSubjectPagination() throws Exception { + try (NatsTestServer ts = configuredJsServer("pagination.conf")) { + try (Connection nc = standardConnectionWait(ts.getURI())) { + if (nc.getServerInfo().isNewerVersionThan("2.8.4")) { + JetStreamManagement jsm = nc.jetStreamManagement(); + JetStream js = nc.jetStream(); + + String stream1 = random(); + String stream2 = random(); + long rounds = 101; + long size = 1000; + long count = rounds * size; + jsm.addStream(StreamConfiguration.builder() + .name(stream1) + .storageType(StorageType.Memory) + .subjects("s.*.*") + .build()); + + jsm.addStream(StreamConfiguration.builder() + .name(stream2) + .storageType(StorageType.Memory) + .subjects("t.*.*") + .build()); + + for (int x = 1; x <= rounds; x++) { + for (int y = 1; y <= size; y++) { + js.publish("s." + x + "." + y, null); + } + } + + for (int y = 1; y <= size; y++) { + js.publish("t.7." + y, null); + } + + StreamInfo si = jsm.getStreamInfo(stream1); + validateStreamInfo(si.getStreamState(), 0, 0, count); + + si = jsm.getStreamInfo(stream1, StreamInfoOptions.allSubjects()); + validateStreamInfo(si.getStreamState(), count, count, count); + + si = jsm.getStreamInfo(stream1, StreamInfoOptions.filterSubjects("s.7.*")); + validateStreamInfo(si.getStreamState(), size, size, count); + + si = jsm.getStreamInfo(stream1, StreamInfoOptions.filterSubjects("s.7.1")); + validateStreamInfo(si.getStreamState(), 1L, 1, count); + + si = jsm.getStreamInfo(stream2, StreamInfoOptions.filterSubjects("t.7.*")); + validateStreamInfo(si.getStreamState(), size, size, size); + + si = jsm.getStreamInfo(stream2, StreamInfoOptions.filterSubjects("t.7.1")); + validateStreamInfo(si.getStreamState(), 1L, 1, size); + + List infos = jsm.getStreams(); + assertEquals(2, infos.size()); + si = infos.get(0); + if (si.getConfiguration().getSubjects().get(0).equals("s.*.*")) { + validateStreamInfo(si.getStreamState(), 0, 0, count); + validateStreamInfo(infos.get(1).getStreamState(), 0, 0, size); + } + else { + validateStreamInfo(si.getStreamState(), 0, 0, size); + validateStreamInfo(infos.get(1).getStreamState(), 0, 0, count); + } + + infos = jsm.getStreams(">"); + assertEquals(2, infos.size()); + + infos = jsm.getStreams("*.7.*"); + assertEquals(2, infos.size()); + + infos = jsm.getStreams("*.7.1"); + assertEquals(2, infos.size()); + + infos = jsm.getStreams("s.7.*"); + assertEquals(1, infos.size()); + assertEquals("s.*.*", infos.get(0).getConfiguration().getSubjects().get(0)); + + infos = jsm.getStreams("t.7.1"); + assertEquals(1, infos.size()); + assertEquals("t.*.*", infos.get(0).getConfiguration().getSubjects().get(0)); + } + } + } + } + + private void validateStreamInfo(StreamState streamState, long subjectsList, long filteredCount, long subjectCount) { + assertEquals(subjectsList, streamState.getSubjects().size()); + assertEquals(filteredCount, streamState.getSubjects().size()); + assertEquals(subjectCount, streamState.getSubjectCount()); + } + + @Test + public void testAuthCreateUpdateStream() throws Exception { + try (NatsTestServer ts = configuredJsServer("js_authorization.conf")) { + Options optionsSrc = optionsBuilder(ts) + .userInfo("serviceup".toCharArray(), "uppass".toCharArray()).build(); + + try (Connection nc = standardConnectionWait(optionsSrc)) { + JetStreamManagement jsm = nc.jetStreamManagement(); + + // add streams with both account + String stream = random(); + String subject1 = random(); + String subject2 = random(); + StreamConfiguration sc = StreamConfiguration.builder() + .name(stream) + .storageType(StorageType.Memory) + .subjects(subject1) + .build(); + StreamInfo si = jsm.addStream(sc); + + sc = StreamConfiguration.builder(si.getConfiguration()) + .addSubjects(subject2) + .build(); + + jsm.updateStream(sc); + } + } + } +} diff --git a/src/test/java/io/nats/client/impl/JetStreamMirrorAndSourcesTests.java b/src/test/java/io/nats/client/impl/JetStreamMirrorAndSourcesTests.java index 3f8c0cb31..0380edb0e 100644 --- a/src/test/java/io/nats/client/impl/JetStreamMirrorAndSourcesTests.java +++ b/src/test/java/io/nats/client/impl/JetStreamMirrorAndSourcesTests.java @@ -13,9 +13,13 @@ package io.nats.client.impl; -import io.nats.client.*; +import io.nats.client.JetStreamApiException; +import io.nats.client.JetStreamSubscription; +import io.nats.client.Message; +import io.nats.client.PushSubscribeOptions; import io.nats.client.api.*; import io.nats.client.support.DateTimeUtils; +import io.nats.client.utils.VersionUtils; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -29,16 +33,16 @@ public class JetStreamMirrorAndSourcesTests extends JetStreamTestBase { @Test public void testMirrorBasics() throws Exception { - String S1 = stream(); - String U1 = subject(); - String U2 = subject(); - String U3 = subject(); - String M1 = mirror(); - - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - + String S1 = random(); + String S2 = random(); + String S3 = random(); + String S4 = random(); + String U1 = random(); + String U2 = random(); + String U3 = random(); + String M1 = random(); + + runInLrServer((nc, jsm, js) -> { Mirror mirror = Mirror.builder().sourceName(S1).build(); // Create source stream @@ -74,53 +78,50 @@ public void testMirrorBasics() throws Exception { // Create second mirror sc = StreamConfiguration.builder() - .name(mirror(2)) + .name(S2) .storageType(StorageType.Memory) .mirror(mirror) .build(); jsm.addStream(sc); // Check the state - assertMirror(jsm, mirror(2), S1, 50L, 101L); + assertMirror(jsm, S2, S1, 50L, 101L); jsPublish(js, U3, 100); // third mirror checks start seq sc = StreamConfiguration.builder() - .name(mirror(3)) + .name(S3) .storageType(StorageType.Memory) .mirror(Mirror.builder().sourceName(S1).startSeq(150).build()) .build(); jsm.addStream(sc); // Check the state - assertMirror(jsm, mirror(3), S1, 101L, 150L); + assertMirror(jsm, S3, S1, 101L, 150L); // third mirror checks start seq ZonedDateTime zdt = DateTimeUtils.fromNow(Duration.ofHours(-2)); sc = StreamConfiguration.builder() - .name(mirror(4)) + .name(S4) .storageType(StorageType.Memory) .mirror(Mirror.builder().sourceName(S1).startTime(zdt).build()) .build(); jsm.addStream(sc); // Check the state - assertMirror(jsm, mirror(4), S1, 150L, 101L); + assertMirror(jsm, S4, S1, 150L, 101L); }); } @Test public void testMirrorReading() throws Exception { - String S1 = stream(); - String U1 = subject(); - String U2 = subject(); - String M1 = mirror(); - - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); + String S1 = random(); + String U1 = random(); + String U2 = random(); + String M1 = random(); + runInLrServer((nc, jsm, js) -> { // Create source stream StreamConfiguration sc = StreamConfiguration.builder() .name(S1) @@ -184,14 +185,12 @@ public void testMirrorReading() throws Exception { @Test public void testMirrorExceptions() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - - Mirror mirror = Mirror.builder().sourceName(STREAM).build(); + runInLrServer((nc, jsm, js) -> { + Mirror mirror = Mirror.builder().sourceName(random()).build(); StreamConfiguration scEx = StreamConfiguration.builder() - .name(mirror()) - .subjects(subject()) + .name(random()) + .subjects(random()) .mirror(mirror) .build(); assertThrows(JetStreamApiException.class, () -> jsm.addStream(scEx)); @@ -200,49 +199,47 @@ public void testMirrorExceptions() throws Exception { @Test public void testSourceBasics() throws Exception { - String S1 = stream(); - String S2 = stream(); - String S3 = stream(); - String S4 = stream(); - String S5 = stream(); - String S99 = stream(); - String R1 = source(); - String R2 = source(); - - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - + String N1 = random(); + String N2 = random(); + String N3 = random(); + String N4 = random(); + String N5 = random(); + String N6 = random(); + String U1 = random(); + String R1 = random(); + String R2 = random(); + + runInLrServer((nc, jsm, js) -> { // Create streams StreamInfo si = jsm.addStream(StreamConfiguration.builder() - .name(S1).storageType(StorageType.Memory).build()); + .name(N1).storageType(StorageType.Memory).build()); StreamConfiguration sc = si.getConfiguration(); assertNotNull(sc); - assertEquals(S1, sc.getName()); + assertEquals(N1, sc.getName()); si = jsm.addStream(StreamConfiguration.builder() - .name(S2).storageType(StorageType.Memory).build()); + .name(N2).storageType(StorageType.Memory).build()); sc = si.getConfiguration(); assertNotNull(sc); - assertEquals(S2, sc.getName()); + assertEquals(N2, sc.getName()); si = jsm.addStream(StreamConfiguration.builder() - .name(S3).storageType(StorageType.Memory).build()); + .name(N3).storageType(StorageType.Memory).build()); sc = si.getConfiguration(); assertNotNull(sc); - assertEquals(S3, sc.getName()); + assertEquals(N3, sc.getName()); // Populate each one. - jsPublish(js, S1, 10); - jsPublish(js, S2, 15); - jsPublish(js, S3, 25); + jsPublish(js, N1, 10); + jsPublish(js, N2, 15); + jsPublish(js, N3, 25); sc = StreamConfiguration.builder() .name(R1) .storageType(StorageType.Memory) - .sources(Source.builder().sourceName(S1).build(), - Source.builder().sourceName(S2).build(), - Source.builder().sourceName(S3).build()) + .sources(Source.builder().sourceName(N1).build(), + Source.builder().sourceName(N2).build(), + Source.builder().sourceName(N3).build()) .build(); jsm.addStream(sc); @@ -252,53 +249,52 @@ public void testSourceBasics() throws Exception { sc = StreamConfiguration.builder() .name(R1) .storageType(StorageType.Memory) - .sources(Source.builder().sourceName(S1).build(), - Source.builder().sourceName(S2).build(), - Source.builder().sourceName(S4).build()) + .sources(Source.builder().sourceName(N1).build(), + Source.builder().sourceName(N2).build(), + Source.builder().sourceName(N4).build()) .build(); jsm.updateStream(sc); sc = StreamConfiguration.builder() - .name(S99) + .name(N5) .storageType(StorageType.Memory) - .subjects(S4, S5) + .subjects(N4, U1) .build(); jsm.addStream(sc); - jsPublish(js, S4, 20); - jsPublish(js, S5, 20); - jsPublish(js, S4, 10); + jsPublish(js, N4, 20); + jsPublish(js, U1, 20); + jsPublish(js, N4, 10); sc = StreamConfiguration.builder() .name(R2) .storageType(StorageType.Memory) - .sources(Source.builder().sourceName(S99).startSeq(26).build()) + .sources(Source.builder().sourceName(N5).startSeq(26).build()) .build(); jsm.addStream(sc); assertSource(jsm, R2, 25L, null); MessageInfo info = jsm.getMessage(R2, 1); - assertStreamSource(info, S99, 26); + assertStreamSource(info, N5, 26); sc = StreamConfiguration.builder() - .name(source(3)) + .name(N6) .storageType(StorageType.Memory) - .sources(Source.builder().sourceName(S99).startSeq(11).filterSubject(S4).build()) + .sources(Source.builder().sourceName(N5).startSeq(11).filterSubject(N4).build()) .build(); jsm.addStream(sc); - assertSource(jsm, source(3), 20L, null); + assertSource(jsm, N6, 20L, null); - info = jsm.getMessage(source(3), 1); - assertStreamSource(info, S99, 11); + info = jsm.getMessage(N6, 1); + assertStreamSource(info, N5, 11); }); } @Test @Disabled("This used to work.") public void testSourceAndTransformsRoundTrips() throws Exception { - jsServer.run(si -> atLeast2_10(), nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); + runInLrServer(VersionUtils::atLeast2_10, (nc, jsm, js) -> { StreamConfiguration scSource = StreamConfigurationTests.getStreamConfigurationFromJson( "StreamConfigurationSourcedSubjectTransform.json"); @@ -340,8 +336,7 @@ public void testSourceAndTransformsRoundTrips() throws Exception { @Test public void testMirror() throws Exception { - jsServer.run(si -> atLeast2_10(), nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); + runInLrServer(VersionUtils::atLeast2_10, (nc, jsm, js) -> { StreamConfiguration scMirror = StreamConfigurationTests.getStreamConfigurationFromJson( "StreamConfigurationMirrorSubjectTransform.json"); diff --git a/src/test/java/io/nats/client/impl/JetStreamPubTests.java b/src/test/java/io/nats/client/impl/JetStreamPubTests.java index 52f0a89e7..a5085aee1 100644 --- a/src/test/java/io/nats/client/impl/JetStreamPubTests.java +++ b/src/test/java/io/nats/client/impl/JetStreamPubTests.java @@ -28,55 +28,55 @@ import static io.nats.client.support.NatsJetStreamConstants.MSG_TTL_HDR; import static io.nats.client.support.NatsJetStreamConstants.NATS_MARKER_REASON_HDR; +import static io.nats.client.utils.ConnectionUtils.standardConnectionWait; +import static io.nats.client.utils.ConnectionUtils.standardOptionsBuilder; +import static io.nats.client.utils.ThreadUtils.sleep; +import static io.nats.client.utils.VersionUtils.atLeast2_12; import static org.junit.jupiter.api.Assertions.*; public class JetStreamPubTests extends JetStreamTestBase { @Test public void testPublishVarieties() throws Exception { - jsServer.run(nc -> { - TestingStreamContainer tsc = new TestingStreamContainer(nc); + runInLrServer((nc, jstc) -> { + PublishAck pa = jstc.js.publish(jstc.subject(), dataBytes(1)); + assertPublishAck(pa, jstc.stream, 1); - JetStream js = nc.jetStream(); - - PublishAck pa = js.publish(tsc.subject(), dataBytes(1)); - assertPublishAck(pa, tsc.stream, 1); - - Message msg = NatsMessage.builder().subject(tsc.subject()).data(dataBytes(2)).build(); - pa = js.publish(msg); - assertPublishAck(pa, tsc.stream, 2); + Message msg = NatsMessage.builder().subject(jstc.subject()).data(dataBytes(2)).build(); + pa = jstc.js.publish(msg); + assertPublishAck(pa, jstc.stream, 2); PublishOptions po = PublishOptions.builder().build(); - pa = js.publish(tsc.subject(), dataBytes(3), po); - assertPublishAck(pa, tsc.stream, 3); + pa = jstc.js.publish(jstc.subject(), dataBytes(3), po); + assertPublishAck(pa, jstc.stream, 3); - msg = NatsMessage.builder().subject(tsc.subject()).data(dataBytes(4)).build(); - pa = js.publish(msg, po); - assertPublishAck(pa, tsc.stream, 4); + msg = NatsMessage.builder().subject(jstc.subject()).data(dataBytes(4)).build(); + pa = jstc.js.publish(msg, po); + assertPublishAck(pa, jstc.stream, 4); - pa = js.publish(tsc.subject(), null); - assertPublishAck(pa, tsc.stream, 5); + pa = jstc.js.publish(jstc.subject(), null); + assertPublishAck(pa, jstc.stream, 5); - msg = NatsMessage.builder().subject(tsc.subject()).build(); - pa = js.publish(msg); - assertPublishAck(pa, tsc.stream, 6); + msg = NatsMessage.builder().subject(jstc.subject()).build(); + pa = jstc.js.publish(msg); + assertPublishAck(pa, jstc.stream, 6); - pa = js.publish(tsc.subject(), null, po); - assertPublishAck(pa, tsc.stream, 7); + pa = jstc.js.publish(jstc.subject(), null, po); + assertPublishAck(pa, jstc.stream, 7); - msg = NatsMessage.builder().subject(tsc.subject()).build(); - pa = js.publish(msg, po); - assertPublishAck(pa, tsc.stream, 8); + msg = NatsMessage.builder().subject(jstc.subject()).build(); + pa = jstc.js.publish(msg, po); + assertPublishAck(pa, jstc.stream, 8); Headers h = new Headers().put("foo", "bar9"); - pa = js.publish(tsc.subject(), h, dataBytes(9)); - assertPublishAck(pa, tsc.stream, 9); + pa = jstc.js.publish(jstc.subject(), h, dataBytes(9)); + assertPublishAck(pa, jstc.stream, 9); h = new Headers().put("foo", "bar10"); - pa = js.publish(tsc.subject(), h, dataBytes(10), po); - assertPublishAck(pa, tsc.stream, 10); + pa = jstc.js.publish(jstc.subject(), h, dataBytes(10), po); + assertPublishAck(pa, jstc.stream, 10); - Subscription s = js.subscribe(tsc.subject()); + Subscription s = jstc.js.subscribe(jstc.subject()); assertNextMessage(s, data(1), null); assertNextMessage(s, data(2), null); assertNextMessage(s, data(3), null); @@ -89,7 +89,7 @@ public void testPublishVarieties() throws Exception { assertNextMessage(s, data(10), "bar10"); // 503 - assertThrows(IOException.class, () -> js.publish(subject(999), null)); + assertThrows(IOException.class, () -> jstc.js.publish(random(), null)); }); } @@ -119,40 +119,37 @@ private void assertPublishAck(PublishAck pa, String stream, long seqno) { @Test public void testPublishAsyncVarieties() throws Exception { - jsServer.run(nc -> { - TestingStreamContainer tsc = new TestingStreamContainer(nc); - JetStream js = nc.jetStream(); - + runInLrServer((nc, jstc) -> { List> futures = new ArrayList<>(); - futures.add(js.publishAsync(tsc.subject(), dataBytes(1))); + futures.add(jstc.js.publishAsync(jstc.subject(), dataBytes(1))); - Message msg = NatsMessage.builder().subject(tsc.subject()).data(dataBytes(2)).build(); - futures.add(js.publishAsync(msg)); + Message msg = NatsMessage.builder().subject(jstc.subject()).data(dataBytes(2)).build(); + futures.add(jstc.js.publishAsync(msg)); PublishOptions po = PublishOptions.builder().build(); - futures.add(js.publishAsync(tsc.subject(), dataBytes(3), po)); + futures.add(jstc.js.publishAsync(jstc.subject(), dataBytes(3), po)); - msg = NatsMessage.builder().subject(tsc.subject()).data(dataBytes(4)).build(); - futures.add(js.publishAsync(msg, po)); + msg = NatsMessage.builder().subject(jstc.subject()).data(dataBytes(4)).build(); + futures.add(jstc.js.publishAsync(msg, po)); Headers h = new Headers().put("foo", "bar5"); - futures.add(js.publishAsync(tsc.subject(), h, dataBytes(5))); + futures.add(jstc.js.publishAsync(jstc.subject(), h, dataBytes(5))); h = new Headers().put("foo", "bar6"); - futures.add(js.publishAsync(tsc.subject(), h, dataBytes(6), po)); + futures.add(jstc.js.publishAsync(jstc.subject(), h, dataBytes(6), po)); sleep(100); // just make sure all the publish complete for (int i = 1; i <= 6; i++) { CompletableFuture future = futures.get(i-1); PublishAck pa = future.get(); - assertEquals(tsc.stream, pa.getStream()); + assertEquals(jstc.stream, pa.getStream()); assertFalse(pa.isDuplicate()); assertEquals(i, pa.getSeqno()); } - Subscription s = js.subscribe(tsc.subject()); + Subscription s = jstc.js.subscribe(jstc.subject()); for (int x = 1; x <= 6; x++) { Message m = s.nextMessage(DEFAULT_TIMEOUT); assertNotNull(m); @@ -164,50 +161,50 @@ public void testPublishAsyncVarieties() throws Exception { } } - assertFutureIOException(js.publishAsync(subject(999), null)); + assertFutureIOException(jstc.js.publishAsync(random(), null)); - msg = NatsMessage.builder().subject(subject(999)).build(); - assertFutureIOException(js.publishAsync(msg)); + msg = NatsMessage.builder().subject(random()).build(); + assertFutureIOException(jstc.js.publishAsync(msg)); PublishOptions pox1 = PublishOptions.builder().build(); - assertFutureIOException(js.publishAsync(subject(999), null, pox1)); + assertFutureIOException(jstc.js.publishAsync(random(), null, pox1)); - msg = NatsMessage.builder().subject(subject(999)).build(); - assertFutureIOException(js.publishAsync(msg, pox1)); + msg = NatsMessage.builder().subject(random()).build(); + assertFutureIOException(jstc.js.publishAsync(msg, pox1)); - PublishOptions pox2 = PublishOptions.builder().expectedLastMsgId(messageId(999)).build(); + PublishOptions pox2 = PublishOptions.builder().expectedLastMsgId(random()).build(); - assertFutureJetStreamApiException(js.publishAsync(tsc.subject(), null, pox2)); + assertFutureJetStreamApiException(jstc.js.publishAsync(jstc.subject(), null, pox2)); - msg = NatsMessage.builder().subject(tsc.subject()).build(); - assertFutureJetStreamApiException(js.publishAsync(msg, pox2)); + msg = NatsMessage.builder().subject(jstc.subject()).build(); + assertFutureJetStreamApiException(jstc.js.publishAsync(msg, pox2)); }); } @Test public void testMultithreadedPublishAsync() throws Exception { + //noinspection resource final ExecutorService executorService = Executors.newFixedThreadPool(3); try { - jsServer.run(nc -> { - TestingStreamContainer tsc = new TestingStreamContainer(nc); + runInLrServer((nc, jstc) -> { final int messagesToPublish = 6; // create a new connection that does not have the inbox dispatcher set try (NatsConnection nc2 = new NatsConnection(nc.getOptions())){ nc2.connect(true); - JetStream js = nc2.jetStream(); + JetStream js2 = nc2.jetStream(); List>> futures = new ArrayList<>(); for (int i = 0; i < messagesToPublish; i++) { final Future> submitFuture = executorService.submit(() -> - js.publishAsync(tsc.subject(), dataBytes(1))); + js2.publishAsync(jstc.subject(), dataBytes(1))); futures.add(submitFuture); } // verify all messages were published for (int i = 0; i < messagesToPublish; i++) { CompletableFuture future = futures.get(i).get(200, TimeUnit.MILLISECONDS); PublishAck pa = future.get(200, TimeUnit.MILLISECONDS); - assertEquals(tsc.stream, pa.getStream()); + assertEquals(jstc.stream, pa.getStream()); assertFalse(pa.isDuplicate()); } } @@ -231,146 +228,149 @@ private void assertFutureJetStreamApiException(CompletableFuture fut @Test public void testPublishExpectations() throws Exception { - jsServer.run(nc -> { - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); - - String subjectPrefix = variant(); + runInLrServer((nc, jsm, js) -> { + String stream1 = random(); + String subjectPrefix = random(); String streamSubject = subjectPrefix + ".>"; String sub1 = subjectPrefix + ".foo.1"; String sub2 = subjectPrefix + ".foo.2"; String sub3 = subjectPrefix + ".bar.3"; - TestingStreamContainer tsc = new TestingStreamContainer(nc, streamSubject); - String stream1 = tsc.stream; createMemoryStream(jsm, stream1, streamSubject); + String mid = random(); PublishOptions po = PublishOptions.builder() - .expectedStream(tsc.stream) - .messageId(messageId(1)) + .expectedStream(stream1) + .messageId(mid) .build(); PublishAck pa = js.publish(sub1, dataBytes(1), po); - assertPublishAck(pa, tsc.stream, 1); + assertPublishAck(pa, stream1, 1); + String lastId = mid; + mid = random(); po = PublishOptions.builder() - .expectedLastMsgId(messageId(1)) - .messageId(messageId(2)) + .expectedLastMsgId(lastId) + .messageId(mid) .build(); pa = js.publish(sub1, dataBytes(2), po); - assertPublishAck(pa, tsc.stream, 2); + assertPublishAck(pa, stream1, 2); + mid = random(); po = PublishOptions.builder() .expectedLastSequence(2) - .messageId(messageId(3)) + .messageId(mid) .build(); pa = js.publish(sub1, dataBytes(3), po); - assertPublishAck(pa, tsc.stream, 3); + assertPublishAck(pa, stream1, 3); + mid = random(); po = PublishOptions.builder() .expectedLastSequence(3) - .messageId(messageId(4)) + .messageId(mid) .build(); pa = js.publish(sub2, dataBytes(4), po); - assertPublishAck(pa, tsc.stream, 4); + assertPublishAck(pa, stream1, 4); + mid = random(); po = PublishOptions.builder() .expectedLastSubjectSequence(3) - .messageId(messageId(5)) + .messageId(mid) .build(); pa = js.publish(sub1, dataBytes(5), po); - assertPublishAck(pa, tsc.stream, 5); + assertPublishAck(pa, stream1, 5); + mid = random(); po = PublishOptions.builder() .expectedLastSubjectSequence(4) - .messageId(messageId(6)) + .messageId(mid) .build(); pa = js.publish(sub2, dataBytes(6), po); - assertPublishAck(pa, tsc.stream, 6); + assertPublishAck(pa, stream1, 6); - PublishOptions po1 = PublishOptions.builder().expectedStream(stream(999)).build(); - JetStreamApiException e = assertThrows(JetStreamApiException.class, () -> js.publish(sub1, dataBytes(999), po1)); + PublishOptions po1 = PublishOptions.builder().expectedStream(random()).build(); + JetStreamApiException e = assertThrows(JetStreamApiException.class, () -> js.publish(sub1, dataBytes(), po1)); assertEquals(10060, e.getApiErrorCode()); - PublishOptions po2 = PublishOptions.builder().expectedLastMsgId(messageId(999)).build(); - e = assertThrows(JetStreamApiException.class, () -> js.publish(sub1, dataBytes(999), po2)); + PublishOptions po2 = PublishOptions.builder().expectedLastMsgId(random()).build(); + e = assertThrows(JetStreamApiException.class, () -> js.publish(sub1, dataBytes(), po2)); assertEquals(10070, e.getApiErrorCode()); PublishOptions po3 = PublishOptions.builder().expectedLastSequence(999).build(); - e = assertThrows(JetStreamApiException.class, () -> js.publish(sub1, dataBytes(999), po3)); + e = assertThrows(JetStreamApiException.class, () -> js.publish(sub1, dataBytes(), po3)); assertEquals(10071, e.getApiErrorCode()); PublishOptions po4 = PublishOptions.builder().expectedLastSubjectSequence(999).build(); - e = assertThrows(JetStreamApiException.class, () -> js.publish(sub1, dataBytes(999), po4)); + e = assertThrows(JetStreamApiException.class, () -> js.publish(sub1, dataBytes(), po4)); assertEquals(10071, e.getApiErrorCode()); // 0 has meaning to expectedLastSubjectSequence - tsc = new TestingStreamContainer(nc); - createMemoryStream(jsm, tsc.stream, tsc.subject()); + JetStreamTestingContext tsc2 = new JetStreamTestingContext(nc); + createMemoryStream(jsm, tsc2.stream, tsc2.subject()); PublishOptions poLss = PublishOptions.builder().expectedLastSubjectSequence(0).build(); - pa = js.publish(tsc.subject(), dataBytes(22), poLss); - assertPublishAck(pa, tsc.stream, 1); + pa = tsc2.js.publish(tsc2.subject(), dataBytes(22), poLss); + assertPublishAck(pa, tsc2.stream, 1); - final String fSubject = tsc.subject(); - e = assertThrows(JetStreamApiException.class, () -> js.publish(fSubject, dataBytes(999), poLss)); + final String fSubject = tsc2.subject(); + e = assertThrows(JetStreamApiException.class, () -> tsc2.js.publish(fSubject, dataBytes(), poLss)); assertEquals(10071, e.getApiErrorCode()); // 0 has meaning - tsc = new TestingStreamContainer(nc); + JetStreamTestingContext tsc3 = new JetStreamTestingContext(nc); PublishOptions poLs = PublishOptions.builder().expectedLastSequence(0).build(); - pa = js.publish(tsc.subject(), dataBytes(331), poLs); - assertPublishAck(pa, tsc.stream, 1); + pa = tsc3.js.publish(tsc3.subject(), dataBytes(331), poLs); + assertPublishAck(pa, tsc3.stream, 1); - tsc = new TestingStreamContainer(nc); + JetStreamTestingContext tsc4 = new JetStreamTestingContext(nc); poLs = PublishOptions.builder().expectedLastSubjectSequence(0).build(); - pa = js.publish(tsc.subject(), dataBytes(441), poLs); - assertPublishAck(pa, tsc.stream, 1); + pa = tsc4.js.publish(tsc4.subject(), dataBytes(441), poLs); + assertPublishAck(pa, tsc4.stream, 1); // expectedLastSubjectSequenceSubject - pa = js.publish(sub3, dataBytes(500)); + pa = tsc4.js.publish(sub3, dataBytes(500)); assertPublishAck(pa, stream1, 7); PublishOptions poLsss = PublishOptions.builder() .expectedLastSubjectSequence(5) .build(); - pa = js.publish(sub1, dataBytes(501), poLsss); + pa = tsc4.js.publish(sub1, dataBytes(501), poLsss); assertPublishAck(pa, stream1, 8); poLsss = PublishOptions.builder() .expectedLastSubjectSequence(6) .build(); - pa = js.publish(sub2, dataBytes(502), poLsss); + pa = tsc4.js.publish(sub2, dataBytes(502), poLsss); assertPublishAck(pa, stream1, 9); poLsss = PublishOptions.builder() .expectedLastSubjectSequence(9) .expectedLastSubjectSequenceSubject(streamSubject) .build(); - pa = js.publish(sub2, dataBytes(503), poLsss); + pa = tsc4.js.publish(sub2, dataBytes(503), poLsss); assertPublishAck(pa, stream1, 10); poLsss = PublishOptions.builder() .expectedLastSubjectSequence(10) .expectedLastSubjectSequenceSubject(subjectPrefix + ".foo.*") .build(); - pa = js.publish(sub2, dataBytes(504), poLsss); + pa = tsc4.js.publish(sub2, dataBytes(504), poLsss); assertPublishAck(pa, stream1, 11); PublishOptions final1 = poLsss; - assertThrows(JetStreamApiException.class, () -> js.publish(sub2, dataBytes(505), final1)); + assertThrows(JetStreamApiException.class, () -> tsc4.js.publish(sub2, dataBytes(505), final1)); poLsss = PublishOptions.builder() .expectedLastSubjectSequence(7) .expectedLastSubjectSequenceSubject(subjectPrefix + ".bar.*") .build(); - pa = js.publish(sub3, dataBytes(506), poLsss); + pa = tsc4.js.publish(sub3, dataBytes(506), poLsss); assertPublishAck(pa, stream1, 12); poLsss = PublishOptions.builder() .expectedLastSubjectSequence(12) .expectedLastSubjectSequenceSubject(streamSubject) .build(); - pa = js.publish(sub3, dataBytes(507), poLsss); + pa = tsc4.js.publish(sub3, dataBytes(507), poLsss); assertPublishAck(pa, stream1, 13); poLsss = PublishOptions.builder() @@ -378,10 +378,10 @@ public void testPublishExpectations() throws Exception { .build(); if (atLeast2_12()) { PublishOptions fpoLsss = poLsss; - assertThrows(JetStreamApiException.class, () -> js.publish(sub3, dataBytes(508), fpoLsss)); + assertThrows(JetStreamApiException.class, () -> tsc4.js.publish(sub3, dataBytes(508), fpoLsss)); } else { - pa = js.publish(sub3, dataBytes(508), poLsss); + pa = tsc4.js.publish(sub3, dataBytes(508), poLsss); assertPublishAck(pa, stream1, 14); } @@ -391,10 +391,10 @@ public void testPublishExpectations() throws Exception { .build(); if (atLeast2_12()) { PublishOptions fpoLsss = poLsss; - assertThrows(JetStreamApiException.class, () -> js.publish(sub3, dataBytes(509), fpoLsss)); + assertThrows(JetStreamApiException.class, () -> tsc4.js.publish(sub3, dataBytes(509), fpoLsss)); } else { - pa = js.publish(sub3, dataBytes(509), poLsss); + pa = tsc4.js.publish(sub3, dataBytes(509), poLsss); assertPublishAck(pa, stream1, 15); } @@ -404,28 +404,25 @@ public void testPublishExpectations() throws Exception { .build(); PublishOptions final2 = poLsss; // JetStreamApiException: wrong last sequence: 0 [10071] - assertThrows(JetStreamApiException.class, () -> js.publish(sub3, dataBytes(510), final2)); + assertThrows(JetStreamApiException.class, () -> tsc4.js.publish(sub3, dataBytes(510), final2)); }); } @Test public void testPublishMiscExceptions() throws Exception { - jsServer.run(nc -> { - TestingStreamContainer tsc = new TestingStreamContainer(nc); - JetStream js = nc.jetStream(); - + runInLrServer((nc, jstc) -> { // stream supplied and matches //noinspection deprecation - PublishOptions po = PublishOptions.builder().stream(tsc.stream).build(); - js.publish(tsc.subject(), dataBytes(9), po); + PublishOptions po = PublishOptions.builder().stream(jstc.stream).build(); + jstc.js.publish(jstc.subject(), dataBytes(9), po); // mismatch stream to PO stream //noinspection deprecation - PublishOptions pox = PublishOptions.builder().stream(stream()).build(); - assertThrows(IOException.class, () -> js.publish(tsc.subject(), dataBytes(99), pox)); + PublishOptions pox = PublishOptions.builder().stream(random()).build(); + assertThrows(IOException.class, () -> jstc.js.publish(jstc.subject(), dataBytes(), pox)); // invalid subject - assertThrows(IOException.class, () -> js.publish(subject(), dataBytes(999))); + assertThrows(IOException.class, () -> jstc.js.publish(random(), dataBytes())); }); } @@ -441,22 +438,20 @@ public void testPublishAckJson() throws IOException, JetStreamApiException { @Test public void testPublishNoAck() throws Exception { - jsServer.run(nc -> { - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { JetStreamOptions jso = JetStreamOptions.builder().publishNoAck(true).build(); - JetStream js = nc.jetStream(jso); + JetStream customJs = nc.jetStream(jso); String data1 = "noackdata1"; String data2 = "noackdata2"; - PublishAck pa = js.publish(tsc.subject(), data1.getBytes()); + PublishAck pa = customJs.publish(jstc.subject(), data1.getBytes()); assertNull(pa); - CompletableFuture f = js.publishAsync(tsc.subject(), data2.getBytes()); + CompletableFuture f = customJs.publishAsync(jstc.subject(), data2.getBytes()); assertNull(f); - JetStreamSubscription sub = js.subscribe(tsc.subject()); + JetStreamSubscription sub = customJs.subscribe(jstc.subject()); Message m = sub.nextMessage(Duration.ofSeconds(2)); assertNotNull(m); assertEquals(data1, new String(m.getData())); @@ -476,7 +471,7 @@ public void testMaxPayloadJs() throws Exception { { Options options = standardOptionsBuilder().noReconnect().server(ts.getURI()).build(); long expectedSeq = 0; - try (Connection nc = standardConnection(options)){ + try (Connection nc = standardConnectionWait(options)){ JetStreamManagement jsm = nc.jetStreamManagement(); try { jsm.deleteStream(streamName); } catch (JetStreamApiException ignore) {} jsm.addStream(StreamConfiguration.builder() @@ -504,7 +499,7 @@ public void testMaxPayloadJs() throws Exception { } } - try (Connection nc = standardConnection(options)){ + try (Connection nc = standardConnectionWait(options)){ JetStream js = nc.jetStream(); for (int x = 1; x <= 3; x++) { @@ -528,51 +523,49 @@ public void testMaxPayloadJs() throws Exception { @Test public void testPublishWithTTL() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - - String stream = stream(); - String subject = subject(); + runInLrServer((nc, jstc) -> { + String stream = random(); + String subject = random(); StreamConfiguration sc = StreamConfiguration.builder() .name(stream) .storageType(StorageType.Memory) .allowMessageTtl() .subjects(subject).build(); - jsm.addStream(sc); + jstc.jsm.addStream(sc); PublishOptions opts = PublishOptions.builder().messageTtlSeconds(1).build(); - PublishAck pa1 = js.publish(subject, null, opts); + PublishAck pa1 = jstc.js.publish(subject, null, opts); assertNotNull(pa1); opts = PublishOptions.builder().messageTtlNever().build(); - PublishAck paNever = js.publish(subject, null, opts); + PublishAck paNever = jstc.js.publish(subject, null, opts); assertNotNull(paNever); - MessageInfo mi1 = jsm.getMessage(stream, pa1.getSeqno()); - assertEquals("1s", mi1.getHeaders().getFirst(MSG_TTL_HDR)); + MessageInfo mi1 = jstc.jsm.getMessage(stream, pa1.getSeqno()); + Headers h = mi1.getHeaders(); + assertNotNull(h); + assertEquals("1s",h.getFirst(MSG_TTL_HDR)); - MessageInfo miNever = jsm.getMessage(stream, paNever.getSeqno()); - assertEquals("never", miNever.getHeaders().getFirst(MSG_TTL_HDR)); + MessageInfo miNever = jstc.jsm.getMessage(stream, paNever.getSeqno()); + h = miNever.getHeaders(); + assertNotNull(h); + assertEquals("never",h.getFirst(MSG_TTL_HDR)); sleep(1200); - JetStreamApiException e = assertThrows(JetStreamApiException.class, () -> jsm.getMessage(stream, pa1.getSeqno())); + JetStreamApiException e = assertThrows(JetStreamApiException.class, () -> jstc.jsm.getMessage(stream, pa1.getSeqno())); assertEquals(10037, e.getApiErrorCode()); - assertNotNull((jsm.getMessage(stream, paNever.getSeqno()))); + assertNotNull((jstc.jsm.getMessage(stream, paNever.getSeqno()))); }); } @Test public void testMsgDeleteMarkerMaxAge() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - - String stream = stream(); - String subject = subject(); + runInLrServer((nc, jsm, js) -> { + String stream = random(); + String subject = random(); StreamConfiguration sc = StreamConfiguration.builder() .name(stream) .storageType(StorageType.Memory) @@ -590,8 +583,10 @@ public void testMsgDeleteMarkerMaxAge() throws Exception { sleep(1200); MessageInfo mi = jsm.getLastMessage(stream, subject); - assertEquals("MaxAge", mi.getHeaders().getFirst(NATS_MARKER_REASON_HDR)); - assertEquals("50s", mi.getHeaders().getFirst(MSG_TTL_HDR)); + Headers h = mi.getHeaders(); + assertNotNull(h); + assertEquals("MaxAge", h.getFirst(NATS_MARKER_REASON_HDR)); + assertEquals("50s", h.getFirst(MSG_TTL_HDR)); assertThrows(IllegalArgumentException.class, () -> StreamConfiguration.builder() .name(stream) diff --git a/src/test/java/io/nats/client/impl/JetStreamPullTests.java b/src/test/java/io/nats/client/impl/JetStreamPullTests.java index e26d6255d..93af7f8a7 100644 --- a/src/test/java/io/nats/client/impl/JetStreamPullTests.java +++ b/src/test/java/io/nats/client/impl/JetStreamPullTests.java @@ -19,7 +19,7 @@ import io.nats.client.api.PriorityPolicy; import io.nats.client.support.JsonUtils; import io.nats.client.support.Status; -import io.nats.client.utils.TestBase; +import io.nats.client.utils.VersionUtils; import org.jspecify.annotations.NonNull; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -41,6 +41,8 @@ import static io.nats.client.support.ApiConstants.*; import static io.nats.client.support.NatsJetStreamConstants.NATS_PIN_ID_HDR; import static io.nats.client.support.Status.*; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; public class JetStreamPullTests extends JetStreamTestBase { @@ -51,18 +53,12 @@ public void pullStatusWarning(Connection conn, JetStreamSubscription sub, Status } private Options.Builder noPullWarnings() { - return Options.builder().errorListener(new ErrorListenerPullImpl()); + return optionsBuilder().errorListener(new ErrorListenerPullImpl()); } @Test public void testFetch() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { long fetchMs = 3000; Duration fetchDur = Duration.ofMillis(fetchMs); Duration ackWaitDur = Duration.ofMillis(fetchMs * 2); @@ -72,12 +68,12 @@ public void testFetch() throws Exception { .build(); PullSubscribeOptions options = PullSubscribeOptions.builder() - .durable(tsc.consumerName()) + .durable(jstc.consumerName()) .configuration(cc) .build(); - JetStreamSubscription sub = js.subscribe(tsc.subject(), options); - assertSubscription(sub, tsc.stream, tsc.consumerName(), null, true); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), options); + assertSubscription(sub, jstc.stream, jstc.consumerName(), null, true); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server List messages = sub.fetch(10, fetchDur); @@ -85,12 +81,12 @@ public void testFetch() throws Exception { messages.forEach(Message::ack); sleep(ackWaitDur.toMillis()); // let the pull expire - jsPublish(js, tsc.subject(), "A", 10); + jsPublish(jstc.js, jstc.subject(), "A", 10); messages = sub.fetch(10, fetchDur); validateRead(10, messages.size()); messages.forEach(Message::ack); - jsPublish(js, tsc.subject(), "B", 20); + jsPublish(jstc.js, jstc.subject(), "B", 20); messages = sub.fetch(10, fetchDur); validateRead(10, messages.size()); messages.forEach(Message::ack); @@ -99,13 +95,13 @@ public void testFetch() throws Exception { validateRead(10, messages.size()); messages.forEach(Message::ack); - jsPublish(js, tsc.subject(), "C", 5); + jsPublish(jstc.js, jstc.subject(), "C", 5); messages = sub.fetch(10, fetchDur); validateRead(5, messages.size()); messages.forEach(Message::ack); sleep(fetchMs); // let the pull expire - jsPublish(js, tsc.subject(), "D", 15); + jsPublish(jstc.js, jstc.subject(), "D", 15); messages = sub.fetch(10, fetchDur); validateRead(10, messages.size()); messages.forEach(Message::ack); @@ -114,7 +110,7 @@ public void testFetch() throws Exception { validateRead(5, messages.size()); messages.forEach(Message::ack); - jsPublish(js, tsc.subject(), "E", 10); + jsPublish(jstc.js, jstc.subject(), "E", 10); messages = sub.fetch(10, fetchDur); validateRead(10, messages.size()); sleep(ackWaitDur.toMillis()); // let the acks wait expire, pull will also expire it's shorter @@ -131,13 +127,7 @@ public void testFetch() throws Exception { @Test public void testIterate() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { long fetchMs = 5000; Duration fetchDur = Duration.ofMillis(fetchMs); Duration ackWaitDur = Duration.ofMillis(fetchMs * 2); @@ -147,12 +137,12 @@ public void testIterate() throws Exception { .build(); PullSubscribeOptions options = PullSubscribeOptions.builder() - .durable(tsc.consumerName()) + .durable(jstc.consumerName()) .configuration(cc) .build(); - JetStreamSubscription sub = js.subscribe(tsc.subject(), options); - assertSubscription(sub, tsc.stream, tsc.consumerName(), null, true); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), options); + assertSubscription(sub, jstc.stream, jstc.consumerName(), null, true); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server Iterator iterator = sub.iterate(10, fetchDur); @@ -160,13 +150,13 @@ public void testIterate() throws Exception { validateRead(0, messages.size()); messages.forEach(Message::ack); - jsPublish(js, tsc.subject(), "A", 10); + jsPublish(jstc.js, jstc.subject(), "A", 10); iterator = sub.iterate(10, fetchDur); messages = readMessages(iterator); validateRead(10, messages.size()); messages.forEach(Message::ack); - jsPublish(js, tsc.subject(), "B", 20); + jsPublish(jstc.js, jstc.subject(), "B", 20); iterator = sub.iterate(10, fetchDur); messages = readMessages(iterator); validateRead(10, messages.size()); @@ -177,14 +167,14 @@ public void testIterate() throws Exception { validateRead(10, messages.size()); messages.forEach(Message::ack); - jsPublish(js, tsc.subject(), "C", 5); + jsPublish(jstc.js, jstc.subject(), "C", 5); iterator = sub.iterate(10, fetchDur); messages = readMessages(iterator); validateRead(5, messages.size()); messages.forEach(Message::ack); sleep(fetchMs); // give time for the pull to expire - jsPublish(js, tsc.subject(), "D", 15); + jsPublish(jstc.js, jstc.subject(), "D", 15); iterator = sub.iterate(10, fetchDur); messages = readMessages(iterator); validateRead(10, messages.size()); @@ -196,7 +186,7 @@ public void testIterate() throws Exception { messages.forEach(Message::ack); sleep(fetchMs); // give time for the pull to expire - jsPublish(js, tsc.subject(), "E", 10); + jsPublish(jstc.js, jstc.subject(), "E", 10); iterator = sub.iterate(10, fetchDur); messages = readMessages(iterator); validateRead(10, messages.size()); @@ -207,7 +197,7 @@ public void testIterate() throws Exception { validateRead(10, messages.size()); messages.forEach(Message::ack); - jsPublish(js, tsc.subject(), "F", 1); + jsPublish(jstc.js, jstc.subject(), "F", 1); iterator = sub.iterate(1, fetchDur); //noinspection ResultOfMethodCallIgnored iterator.hasNext(); // calling hasNext twice in a row is for coverage @@ -218,23 +208,17 @@ public void testIterate() throws Exception { @Test public void testBasic() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { // Build our subscription options. - PullSubscribeOptions options = PullSubscribeOptions.builder().durable(tsc.consumerName()).build(); + PullSubscribeOptions options = PullSubscribeOptions.builder().durable(jstc.consumerName()).build(); // Subscribe synchronously. - JetStreamSubscription sub = js.subscribe(tsc.subject(), options); - assertSubscription(sub, tsc.stream, tsc.consumerName(), null, true); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), options); + assertSubscription(sub, jstc.stream, jstc.consumerName(), null, true); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server // publish some amount of messages, but not entire pull size - jsPublish(js, tsc.subject(), "A", 4); + jsPublish(jstc.js, jstc.subject(), "A", 4); // start the pull sub.pull(10); @@ -245,7 +229,7 @@ public void testBasic() throws Exception { validateRedAndTotal(4, messages.size(), 4, total); // publish some more covering our initial pull and more - jsPublish(js, tsc.subject(), "B", 10); + jsPublish(jstc.js, jstc.subject(), "B", 10); // read what is available, expect 6 more messages = readMessagesAck(sub); @@ -266,7 +250,7 @@ public void testBasic() throws Exception { validateRedAndTotal(4, messages.size(), 14, total); // publish some more - jsPublish(js, tsc.subject(), "C", 10); + jsPublish(jstc.js, jstc.subject(), "C", 10); // read what is available, should be 6 since we didn't finish the last batch messages = readMessagesAck(sub); @@ -298,16 +282,16 @@ public void testBasic() throws Exception { validateRedAndTotal(0, messages.size(), 24, total); // publish some more to test null timeout - jsPublish(js, tsc.subject(), "D", 10); - sub = js.subscribe(tsc.subject(), PullSubscribeOptions.builder().durable(durable(2)).build()); + jsPublish(jstc.js, jstc.subject(), "D", 10); + sub = jstc.js.subscribe(jstc.subject(), PullSubscribeOptions.builder().durable(random()).build()); sub.pull(10); sleep(500); messages = readMessagesAck(sub, null); validateRedAndTotal(10, messages.size(), 10, messages.size()); // publish some more to test never timeout - jsPublish(js, tsc.subject(), "E", 10); - sub = js.subscribe(tsc.subject(), PullSubscribeOptions.builder().durable(durable(2)).build()); + jsPublish(jstc.js, jstc.subject(), "E", 10); + sub = jstc.js.subscribe(jstc.subject(), PullSubscribeOptions.builder().durable(random()).build()); sub.pull(10); sleep(500); messages = readMessagesAck(sub, Duration.ZERO, 10); @@ -317,24 +301,18 @@ public void testBasic() throws Exception { @Test public void testNoWait() throws Exception { - runInJsServer(noPullWarnings(), nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer(noPullWarnings(), (nc, jstc) -> { // Build our subscription options. - PullSubscribeOptions options = PullSubscribeOptions.builder().durable(tsc.consumerName()).build(); + PullSubscribeOptions options = PullSubscribeOptions.builder().durable(jstc.consumerName()).build(); // Subscribe synchronously. - JetStreamSubscription sub = js.subscribe(tsc.subject(), options); - assertSubscription(sub, tsc.stream, tsc.consumerName(), null, true); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), options); + assertSubscription(sub, jstc.stream, jstc.consumerName(), null, true); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server // publish 10 messages // no wait, batch size 10, there are 10 messages, we will read them all and not trip nowait - jsPublish(js, tsc.subject(), "A", 10); + jsPublish(jstc.js, jstc.subject(), "A", 10); sub.pullNoWait(10); List messages = readMessagesAck(sub); assertEquals(10, messages.size()); @@ -342,7 +320,7 @@ public void testNoWait() throws Exception { // publish 20 messages // no wait, batch size 10, there are 20 messages, we will read 10 - jsPublish(js, tsc.subject(), "B", 20); + jsPublish(jstc.js, jstc.subject(), "B", 20); sub.pullNoWait(10); messages = readMessagesAck(sub); assertEquals(10, messages.size()); @@ -355,14 +333,14 @@ public void testNoWait() throws Exception { // publish 5 messages // no wait, batch size 10, there are 5 messages, we WILL trip nowait - jsPublish(js, tsc.subject(), "C", 5); + jsPublish(jstc.js, jstc.subject(), "C", 5); sub.pullNoWait(10); messages = readMessagesAck(sub); assertEquals(5, messages.size()); // publish 12 messages // no wait, batch size 10, there are more than batch messages we will read 10 - jsPublish(js, tsc.subject(), "D", 12); + jsPublish(jstc.js, jstc.subject(), "D", 12); sub.pullNoWait(10); messages = readMessagesAck(sub); assertEquals(10, messages.size()); @@ -376,7 +354,7 @@ public void testNoWait() throws Exception { // this is just coverage of the pullNoWait api + expires, not really validating server functionality // publish 12 messages // no wait, batch size 10, there are more than batch messages we will read 10 - jsPublish(js, tsc.subject(), "E", 12); + jsPublish(jstc.js, jstc.subject(), "E", 12); sub.pullNoWait(10, 10000); messages = readMessagesAck(sub); assertEquals(10, messages.size()); @@ -391,81 +369,75 @@ public void testNoWait() throws Exception { @Test public void testPullExpires() throws Exception { - runInJsServer(noPullWarnings(), nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer(noPullWarnings(), (nc, jstc) -> { // Build our subscription options. - PullSubscribeOptions options = PullSubscribeOptions.builder().durable(tsc.consumerName()).build(); + PullSubscribeOptions options = PullSubscribeOptions.builder().durable(jstc.consumerName()).build(); // Subscribe synchronously. - JetStreamSubscription sub = js.subscribe(tsc.subject(), options); - assertSubscription(sub, tsc.stream, tsc.consumerName(), null, true); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), options); + assertSubscription(sub, jstc.stream, jstc.consumerName(), null, true); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server long expires = 500; // millis // publish 10 messages - jsPublish(js, tsc.subject(), "A", 5); + jsPublish(jstc.js, jstc.subject(), "A", 5); sub.pullExpiresIn(10, Duration.ofMillis(expires)); // using Duration version here List messages = readMessagesAck(sub); assertEquals(5, messages.size()); assertAllJetStream(messages); sleep(expires); // make sure the pull actually expires - jsPublish(js, tsc.subject(), "B", 10); + jsPublish(jstc.js, jstc.subject(), "B", 10); sub.pullExpiresIn(10, Duration.ofMillis(expires)); // using Duration version here messages = readMessagesAck(sub); assertEquals(10, messages.size()); sleep(expires); // make sure the pull actually expires - jsPublish(js, tsc.subject(), "C", 5); + jsPublish(jstc.js, jstc.subject(), "C", 5); sub.pullExpiresIn(10, Duration.ofMillis(expires)); // using Duration version here messages = readMessagesAck(sub); assertEquals(5, messages.size()); assertAllJetStream(messages); sleep(expires); // make sure the pull actually expires - jsPublish(js, tsc.subject(), "D", 10); + jsPublish(jstc.js, jstc.subject(), "D", 10); sub.pull(10); messages = readMessagesAck(sub); assertEquals(10, messages.size()); - jsPublish(js, tsc.subject(), "E", 5); + jsPublish(jstc.js, jstc.subject(), "E", 5); sub.pullExpiresIn(10, expires); // using millis version here messages = readMessagesAck(sub); assertEquals(5, messages.size()); assertAllJetStream(messages); sleep(expires); // make sure the pull actually expires - jsPublish(js, tsc.subject(), "F", 10); + jsPublish(jstc.js, jstc.subject(), "F", 10); sub.pullNoWait(10); messages = readMessagesAck(sub); assertEquals(10, messages.size()); - jsPublish(js, tsc.subject(), "G", 5); + jsPublish(jstc.js, jstc.subject(), "G", 5); sub.pullExpiresIn(10, expires); // using millis version here messages = readMessagesAck(sub); assertEquals(5, messages.size()); assertAllJetStream(messages); sleep(expires); // make sure the pull actually expires - jsPublish(js, tsc.subject(), "H", 10); + jsPublish(jstc.js, jstc.subject(), "H", 10); messages = sub.fetch(10, expires); assertEquals(10, messages.size()); assertAllJetStream(messages); - jsPublish(js, tsc.subject(), "I", 5); + jsPublish(jstc.js, jstc.subject(), "I", 5); sub.pullExpiresIn(10, expires); messages = readMessagesAck(sub); assertEquals(5, messages.size()); assertAllJetStream(messages); sleep(expires); // make sure the pull actually expires - jsPublish(js, tsc.subject(), "J", 10); + jsPublish(jstc.js, jstc.subject(), "J", 10); Iterator i = sub.iterate(10, expires); int count = 0; while (i.hasNext()) { @@ -482,19 +454,13 @@ public void testPullExpires() throws Exception { @Test public void testAckNak() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - - PullSubscribeOptions pso = PullSubscribeOptions.builder().durable(DURABLE).build(); - JetStreamSubscription sub = js.subscribe(tsc.subject(), pso); + runInLrServer((nc, jstc) -> { + PullSubscribeOptions pso = PullSubscribeOptions.builder().durable(random()).build(); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), pso); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server // NAK - jsPublish(js, tsc.subject(), "NAK", 1); + jsPublish(jstc.js, jstc.subject(), "NAK", 1); sub.pull(1); @@ -518,19 +484,13 @@ public void testAckNak() throws Exception { @Test public void testAckTerm() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - - PullSubscribeOptions pso = PullSubscribeOptions.builder().durable(DURABLE).build(); - JetStreamSubscription sub = js.subscribe(tsc.subject(), pso); + runInLrServer((nc, jstc) -> { + PullSubscribeOptions pso = PullSubscribeOptions.builder().durable(random()).build(); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), pso); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server // TERM - jsPublish(js, tsc.subject(), "TERM", 1); + jsPublish(jstc.js, jstc.subject(), "TERM", 1); sub.pull(1); Message message = sub.nextMessage(Duration.ofSeconds(1)); @@ -546,51 +506,39 @@ public void testAckTerm() throws Exception { @Test public void testAckReplySyncCoverage() throws Exception { - jsServer.run(nc -> { - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - - // Create our JetStream context. - JetStream js = nc.jetStream(); - - JetStreamSubscription sub = js.subscribe(tsc.subject()); + runInLrServer((nc, jstc) -> { + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject()); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server - jsPublish(js, tsc.subject(), "COVERAGE", 1); + jsPublish(jstc.js, jstc.subject(), "COVERAGE", 1); Message message = sub.nextMessage(Duration.ofSeconds(1)); assertNotNull(message); - NatsJetStreamMessage njsm = (NatsJetStreamMessage)message; + NatsJetStreamMessage njsMsg = (NatsJetStreamMessage)message; - njsm.replyTo = "$JS.ACK.stream.LS0k4eeN.1.1.1.1627472530542070600.0"; + njsMsg.replyTo = "$tsc.js.ACK.stream.LS0k4eeN.1.1.1.1627472530542070600.0"; - assertThrows(TimeoutException.class, () -> njsm.ackSync(Duration.ofSeconds(1))); + assertThrows(TimeoutException.class, () -> njsMsg.ackSync(Duration.ofSeconds(1))); }); } @Test public void testAckWaitTimeout() throws Exception { - jsServer.run(nc -> { - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - - // Create our JetStream context. - JetStream js = nc.jetStream(); - + runInLrServer((nc, jstc) -> { ConsumerConfiguration cc = ConsumerConfiguration.builder() .ackWait(1500) .build(); PullSubscribeOptions pso = PullSubscribeOptions.builder() - .durable(tsc.consumerName()) + .durable(jstc.consumerName()) .configuration(cc) .build(); - JetStreamSubscription sub = js.subscribe(tsc.subject(), pso); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), pso); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server // Ack Wait timeout - jsPublish(js, tsc.subject(), "WAIT", 2); + jsPublish(jstc.js, jstc.subject(), "WAIT", 2); sub.pull(2); Message m = sub.nextMessage(1000); @@ -622,81 +570,72 @@ public void testAckWaitTimeout() throws Exception { @Test public void testDurable() throws Exception { - runInJsServer(noPullWarnings(), nc -> { - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - String durable = durable(); - - // Create our JetStream context. - JetStream js = nc.jetStream(); + runInLrServer(noPullWarnings(), (nc, jstc) -> { + String durable = random(); // Build our subscription options normally PullSubscribeOptions options1 = PullSubscribeOptions.builder().durable(durable).build(); - _testDurableOrNamed(js, tsc.subject(), () -> js.subscribe(tsc.subject(), options1)); + _testDurableOrNamed(jstc.js, jstc.subject(), () -> jstc.js.subscribe(jstc.subject(), options1)); // bind long form PullSubscribeOptions options2 = PullSubscribeOptions.builder() - .stream(tsc.stream) + .stream(jstc.stream) .durable(durable) .bind(true) .build(); - _testDurableOrNamed(js, tsc.subject(), () -> js.subscribe(null, options2)); + _testDurableOrNamed(jstc.js, jstc.subject(), () -> jstc.js.subscribe(null, options2)); // fast bind long form PullSubscribeOptions options3 = PullSubscribeOptions.builder() - .stream(tsc.stream) + .stream(jstc.stream) .durable(durable) .fastBind(true) .build(); - _testDurableOrNamed(js, tsc.subject(), () -> js.subscribe(null, options3)); + _testDurableOrNamed(jstc.js, jstc.subject(), () -> jstc.js.subscribe(null, options3)); // bind short form - PullSubscribeOptions options4 = PullSubscribeOptions.bind(tsc.stream, durable); - _testDurableOrNamed(js, tsc.subject(), () -> js.subscribe(null, options4)); + PullSubscribeOptions options4 = PullSubscribeOptions.bind(jstc.stream, durable); + _testDurableOrNamed(jstc.js, jstc.subject(), () -> jstc.js.subscribe(null, options4)); // fast bind short form - PullSubscribeOptions options5 = PullSubscribeOptions.fastBind(tsc.stream, durable); - _testDurableOrNamed(js, tsc.subject(), () -> js.subscribe(null, options5)); + PullSubscribeOptions options5 = PullSubscribeOptions.fastBind(jstc.stream, durable); + _testDurableOrNamed(jstc.js, jstc.subject(), () -> jstc.js.subscribe(null, options5)); }); } @Test public void testNamed() throws Exception { - runInJsServer(noPullWarnings(), TestBase::atLeast2_9_0, nc -> { - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - String name = name(); + runInLrServer(noPullWarnings(), VersionUtils::atLeast2_9_0, (nc, jstc) -> { + String name = random(); - jsm.addOrUpdateConsumer(tsc.stream, ConsumerConfiguration.builder() + jstc.jsm.addOrUpdateConsumer(jstc.stream, ConsumerConfiguration.builder() .name(name) .inactiveThreshold(10_000) .build()); // bind long form PullSubscribeOptions options2 = PullSubscribeOptions.builder() - .stream(tsc.stream) + .stream(jstc.stream) .name(name) .bind(true) .build(); - _testDurableOrNamed(js, tsc.subject(), () -> js.subscribe(null, options2)); + _testDurableOrNamed(jstc.js, jstc.subject(), () -> jstc.js.subscribe(null, options2)); // fast bind long form PullSubscribeOptions options3 = PullSubscribeOptions.builder() - .stream(tsc.stream) + .stream(jstc.stream) .name(name) .fastBind(true) .build(); - _testDurableOrNamed(js, tsc.subject(), () -> js.subscribe(null, options3)); + _testDurableOrNamed(jstc.js, jstc.subject(), () -> jstc.js.subscribe(null, options3)); // bind short form - PullSubscribeOptions options4 = PullSubscribeOptions.bind(tsc.stream, name); - _testDurableOrNamed(js, tsc.subject(), () -> js.subscribe(null, options4)); + PullSubscribeOptions options4 = PullSubscribeOptions.bind(jstc.stream, name); + _testDurableOrNamed(jstc.js, jstc.subject(), () -> jstc.js.subscribe(null, options4)); // fast bind short form - PullSubscribeOptions options5 = PullSubscribeOptions.fastBind(tsc.stream, name); - _testDurableOrNamed(js, tsc.subject(), () -> js.subscribe(null, options5)); + PullSubscribeOptions options5 = PullSubscribeOptions.fastBind(jstc.stream, name); + _testDurableOrNamed(jstc.js, jstc.subject(), () -> jstc.js.subscribe(null, options5)); }); } @@ -777,7 +716,7 @@ public void testPullRequestOptionsBuilder() { } interface ConflictSetup { - JetStreamSubscription setup(Connection nc, JetStreamManagement jsm, JetStream js, TestingStreamContainer tsc, ListenerForTesting listener) throws Exception; + JetStreamSubscription setup(Connection nc, JetStreamManagement jsm, JetStream js, JetStreamTestingContext jstc, ListenerForTesting listener) throws Exception; } private boolean versionIsBefore(Connection nc, String targetVersion) { @@ -801,15 +740,12 @@ private PullSubscribeOptions makePso(BuilderCustomizer c) { private void testConflictStatus(int statusCode, String statusText, int type, String targetVersion, ConflictSetup setup) throws Exception { ListenerForTesting listener = new ListenerForTesting(); AtomicBoolean skip = new AtomicBoolean(false); - runInJsServer(listener, nc -> { + runInLrServer(listener, (nc, jstc) -> { skip.set(versionIsBefore(nc, targetVersion)); if (skip.get()) { return; } - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - JetStreamSubscription sub = setup.setup(nc, jsm, js, tsc, listener); + JetStreamSubscription sub = setup.setup(nc, jstc.jsm, jstc.js, jstc, listener); if (sub.getDispatcher() == null) { if (type == TYPE_ERROR) { JetStreamStatusException jsse = assertThrows(JetStreamStatusException.class, () -> sub.nextMessage(NEXT_MESSAGE)); @@ -837,9 +773,9 @@ else if (type == TYPE_WARNING) { @Test public void testExceedsMaxWaitingSyncSub() throws Exception { - testConflictStatus(409, EXCEEDED_MAX_WAITING, TYPE_WARNING, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(409, EXCEEDED_MAX_WAITING, TYPE_WARNING, "2.9.0", (nc, jsm, js, jstc, handler) -> { PullSubscribeOptions so = makePso(b -> b.maxPullWaiting(1)); - JetStreamSubscription sub = js.subscribe(tsc.subject(), so); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), so); sub.pull(1); sub.pull(1); return sub; @@ -848,10 +784,10 @@ public void testExceedsMaxWaitingSyncSub() throws Exception { @Test public void testExceedsMaxWaitingAsyncSub() throws Exception { - testConflictStatus(409, EXCEEDED_MAX_WAITING, TYPE_WARNING, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(409, EXCEEDED_MAX_WAITING, TYPE_WARNING, "2.9.0", (nc, jsm, js, jstc, handler) -> { Dispatcher d = nc.createDispatcher(); PullSubscribeOptions so = makePso(b -> b.maxPullWaiting(1)); - JetStreamSubscription sub = js.subscribe(tsc.subject(), d, m -> {}, so); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), d, m -> {}, so); sub.pull(1); sub.pull(1); return sub; @@ -860,9 +796,9 @@ public void testExceedsMaxWaitingAsyncSub() throws Exception { @Test public void testExceedsMaxRequestBatchSyncSub() throws Exception { - testConflictStatus(409, EXCEEDED_MAX_REQUEST_BATCH, TYPE_WARNING, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(409, EXCEEDED_MAX_REQUEST_BATCH, TYPE_WARNING, "2.9.0", (nc, jsm, js, jstc, handler) -> { PullSubscribeOptions so = makePso(b -> b.maxBatch(1)); - JetStreamSubscription sub = js.subscribe(tsc.subject(), so); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), so); sub.pull(2); return sub; }); @@ -870,10 +806,10 @@ public void testExceedsMaxRequestBatchSyncSub() throws Exception { @Test public void testExceedsMaxRequestBatchAsyncSub() throws Exception { - testConflictStatus(409, EXCEEDED_MAX_REQUEST_BATCH, TYPE_WARNING, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(409, EXCEEDED_MAX_REQUEST_BATCH, TYPE_WARNING, "2.9.0", (nc, jsm, js, jstc, handler) -> { Dispatcher d = nc.createDispatcher(); PullSubscribeOptions so = makePso(b -> b.maxBatch(1)); - JetStreamSubscription sub = js.subscribe(tsc.subject(), d, m -> {}, so); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), d, m -> {}, so); sub.pull(2); return sub; }); @@ -881,10 +817,10 @@ public void testExceedsMaxRequestBatchAsyncSub() throws Exception { @Test public void testMessageSizeExceedsMaxBytesSyncSub() throws Exception { - testConflictStatus(409, MESSAGE_SIZE_EXCEEDS_MAX_BYTES, TYPE_NONE, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(409, MESSAGE_SIZE_EXCEEDS_MAX_BYTES, TYPE_NONE, "2.9.0", (nc, jsm, js, jstc, handler) -> { PullSubscribeOptions so = makePso(b -> b); - js.publish(tsc.subject(), new byte[1000]); - JetStreamSubscription sub = js.subscribe(tsc.subject(), so); + jstc.js.publish(jstc.subject(), new byte[1000]); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), so); sub.pull(PullRequestOptions.builder(1).maxBytes(100).build()); return sub; }); @@ -892,11 +828,11 @@ public void testMessageSizeExceedsMaxBytesSyncSub() throws Exception { @Test public void testMessageSizeExceedsMaxBytesAsyncSub() throws Exception { - testConflictStatus(409, MESSAGE_SIZE_EXCEEDS_MAX_BYTES, TYPE_NONE, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(409, MESSAGE_SIZE_EXCEEDS_MAX_BYTES, TYPE_NONE, "2.9.0", (nc, jsm, js, jstc, handler) -> { Dispatcher d = nc.createDispatcher(); PullSubscribeOptions so = makePso(b -> b); - js.publish(tsc.subject(), new byte[1000]); - JetStreamSubscription sub = js.subscribe(tsc.subject(), d, m -> {}, so); + jstc.js.publish(jstc.subject(), new byte[1000]); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), d, m -> {}, so); sub.pull(PullRequestOptions.builder(1).maxBytes(100).build()); return sub; }); @@ -904,9 +840,9 @@ public void testMessageSizeExceedsMaxBytesAsyncSub() throws Exception { @Test public void testExceedsMaxRequestExpiresSyncSub() throws Exception { - testConflictStatus(409, EXCEEDED_MAX_REQUEST_EXPIRES, TYPE_WARNING, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(409, EXCEEDED_MAX_REQUEST_EXPIRES, TYPE_WARNING, "2.9.0", (nc, jsm, js, jstc, handler) -> { PullSubscribeOptions so = makePso(b -> b.maxExpires(1000)); - JetStreamSubscription sub = js.subscribe(tsc.subject(), so); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), so); sub.pullExpiresIn(1, 2000); return sub; }); @@ -914,10 +850,10 @@ public void testExceedsMaxRequestExpiresSyncSub() throws Exception { @Test public void testExceedsMaxRequestExpiresAsyncSub() throws Exception { - testConflictStatus(409, EXCEEDED_MAX_REQUEST_EXPIRES, TYPE_WARNING, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(409, EXCEEDED_MAX_REQUEST_EXPIRES, TYPE_WARNING, "2.9.0", (nc, jsm, js, jstc, handler) -> { Dispatcher d = nc.createDispatcher(); PullSubscribeOptions so = makePso(b -> b.maxExpires(1000)); - JetStreamSubscription sub = js.subscribe(tsc.subject(), d, m -> {}, so); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), d, m -> {}, so); sub.pullExpiresIn(1, 2000); return sub; }); @@ -925,13 +861,14 @@ public void testExceedsMaxRequestExpiresAsyncSub() throws Exception { @Test public void testConsumerIsPushBasedSyncSub() throws Exception { - testConflictStatus(409, CONSUMER_IS_PUSH_BASED, TYPE_ERROR, "2.9.0", (nc, jsm, js, tsc, handler) -> { - jsm.addOrUpdateConsumer(tsc.stream, builder().durable(durable(1)).ackPolicy(AckPolicy.None).build()); - PullSubscribeOptions so = PullSubscribeOptions.bind(tsc.stream, durable(1)); - JetStreamSubscription sub = js.subscribe(null, so); - jsm.deleteConsumer(tsc.stream, durable(1)); + testConflictStatus(409, CONSUMER_IS_PUSH_BASED, TYPE_ERROR, "2.9.0", (nc, jsm, js, jstc, handler) -> { + String dur = random(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, builder().durable(dur).ackPolicy(AckPolicy.None).build()); + PullSubscribeOptions so = PullSubscribeOptions.bind(jstc.stream, dur); + JetStreamSubscription sub = jstc.js.subscribe(null, so); + jstc.jsm.deleteConsumer(jstc.stream, dur); // consumer with same name but is push now - jsm.addOrUpdateConsumer(tsc.stream, builder().durable(durable(1)).deliverSubject(deliver(1)).build()); + jstc.jsm.addOrUpdateConsumer(jstc.stream, builder().durable(dur).deliverSubject(dur).build()); sub.pull(1); return sub; }); @@ -939,14 +876,15 @@ public void testConsumerIsPushBasedSyncSub() throws Exception { @Test public void testConsumerIsPushBasedAsyncSub() throws Exception { - testConflictStatus(409, CONSUMER_IS_PUSH_BASED, TYPE_ERROR, "2.9.0", (nc, jsm, js, tsc, handler) -> { - jsm.addOrUpdateConsumer(tsc.stream, builder().durable(durable(1)).ackPolicy(AckPolicy.None).build()); + testConflictStatus(409, CONSUMER_IS_PUSH_BASED, TYPE_ERROR, "2.9.0", (nc, jsm, js, jstc, handler) -> { + String dur = random(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, builder().durable(dur).ackPolicy(AckPolicy.None).build()); Dispatcher d = nc.createDispatcher(); - PullSubscribeOptions so = PullSubscribeOptions.bind(tsc.stream, durable(1)); - JetStreamSubscription sub = js.subscribe(null, d, m -> {}, so); - jsm.deleteConsumer(tsc.stream, durable(1)); + PullSubscribeOptions so = PullSubscribeOptions.bind(jstc.stream, dur); + JetStreamSubscription sub = jstc.js.subscribe(null, d, m -> {}, so); + jstc.jsm.deleteConsumer(jstc.stream, dur); // consumer with same name but is push now - jsm.addOrUpdateConsumer(tsc.stream, builder().durable(durable(1)).deliverSubject(deliver(1)).build()); + jstc.jsm.addOrUpdateConsumer(jstc.stream, builder().durable(dur).deliverSubject(dur).build()); sub.pull(1); return sub; }); @@ -956,13 +894,14 @@ public void testConsumerIsPushBasedAsyncSub() throws Exception { @Test @Disabled public void testConsumerDeletedSyncSub() throws Exception { - testConflictStatus(409, CONSUMER_DELETED, TYPE_ERROR, "2.9.6", (nc, jsm, js, tsc, handler) -> { - jsm.addOrUpdateConsumer(tsc.stream, builder().durable(durable(1)).ackPolicy(AckPolicy.None).build()); - PullSubscribeOptions so = PullSubscribeOptions.bind(tsc.stream, durable(1)); - JetStreamSubscription sub = js.subscribe(null, so); + testConflictStatus(409, CONSUMER_DELETED, TYPE_ERROR, "2.9.6", (nc, jsm, js, jstc, handler) -> { + String dur = random(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, builder().durable(dur).ackPolicy(AckPolicy.None).build()); + PullSubscribeOptions so = PullSubscribeOptions.bind(jstc.stream, dur); + JetStreamSubscription sub = jstc.js.subscribe(null, so); sub.pullExpiresIn(1, 30000); - jsm.deleteConsumer(tsc.stream, durable(1)); - js.publish(tsc.subject(), null); + jstc.jsm.deleteConsumer(jstc.stream, dur); + jstc.js.publish(jstc.subject(), null); return sub; }); } @@ -971,14 +910,15 @@ public void testConsumerDeletedSyncSub() throws Exception { @Test @Disabled public void testConsumerDeletedAsyncSub() throws Exception { - testConflictStatus(409, CONSUMER_DELETED, TYPE_ERROR, "2.9.6", (nc, jsm, js, tsc, handler) -> { - jsm.addOrUpdateConsumer(tsc.stream, builder().durable(durable(1)).ackPolicy(AckPolicy.None).build()); + testConflictStatus(409, CONSUMER_DELETED, TYPE_ERROR, "2.9.6", (nc, jsm, js, jstc, handler) -> { + String dur = random(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, builder().durable(dur).ackPolicy(AckPolicy.None).build()); Dispatcher d = nc.createDispatcher(); - PullSubscribeOptions so = PullSubscribeOptions.bind(tsc.stream, durable(1)); - JetStreamSubscription sub = js.subscribe(null, d, m -> {}, so); + PullSubscribeOptions so = PullSubscribeOptions.bind(jstc.stream, dur); + JetStreamSubscription sub = jstc.js.subscribe(null, d, m -> {}, so); sub.pullExpiresIn(1, 30000); - jsm.deleteConsumer(tsc.stream, durable(1)); - js.publish(tsc.subject(), null); + jstc.jsm.deleteConsumer(jstc.stream, dur); + jstc.js.publish(jstc.subject(), null); return sub; }); } @@ -1001,9 +941,9 @@ public String toJson() { @Test public void testBadRequestSyncSub() throws Exception { - testConflictStatus(400, BAD_REQUEST, TYPE_ERROR, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(400, BAD_REQUEST, TYPE_ERROR, "2.9.0", (nc, jsm, js, jstc, handler) -> { PullSubscribeOptions so = makePso(b -> b); - JetStreamSubscription sub = js.subscribe(tsc.subject(), so); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), so); sub.pull(new BadPullRequestOptions()); return sub; }); @@ -1011,10 +951,10 @@ public void testBadRequestSyncSub() throws Exception { @Test public void testBadRequestAsyncSub() throws Exception { - testConflictStatus(400, BAD_REQUEST, TYPE_ERROR, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(400, BAD_REQUEST, TYPE_ERROR, "2.9.0", (nc, jsm, js, jstc, handler) -> { Dispatcher d = nc.createDispatcher(); PullSubscribeOptions so = makePso(b -> b); - JetStreamSubscription sub = js.subscribe(tsc.subject(), d, m -> {}, so); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), d, m -> {}, so); sub.pull(new BadPullRequestOptions()); return sub; }); @@ -1022,9 +962,9 @@ public void testBadRequestAsyncSub() throws Exception { @Test public void testNotFoundSyncSub() throws Exception { - testConflictStatus(404, NO_MESSAGES, TYPE_NONE, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(404, NO_MESSAGES, TYPE_NONE, "2.9.0", (nc, jsm, js, jstc, handler) -> { PullSubscribeOptions so = makePso(b -> b); - JetStreamSubscription sub = js.subscribe(tsc.subject(), so); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), so); sub.pullNoWait(1); return sub; }); @@ -1032,10 +972,10 @@ public void testNotFoundSyncSub() throws Exception { @Test public void testNotFoundAsyncSub() throws Exception { - testConflictStatus(404, NO_MESSAGES, TYPE_NONE, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(404, NO_MESSAGES, TYPE_NONE, "2.9.0", (nc, jsm, js, jstc, handler) -> { Dispatcher d = nc.createDispatcher(); PullSubscribeOptions so = makePso(b -> b); - JetStreamSubscription sub = js.subscribe(tsc.subject(), d, m -> {}, so); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), d, m -> {}, so); sub.pullNoWait(1); return sub; }); @@ -1043,9 +983,9 @@ public void testNotFoundAsyncSub() throws Exception { @Test public void testExceedsMaxRequestBytes1stMessageSyncSub() throws Exception { - testConflictStatus(409, EXCEEDED_MAX_REQUEST_MAX_BYTES, TYPE_WARNING, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(409, EXCEEDED_MAX_REQUEST_MAX_BYTES, TYPE_WARNING, "2.9.0", (nc, jsm, js, jstc, handler) -> { PullSubscribeOptions so = makePso(b -> b.maxBytes(1)); - JetStreamSubscription sub = js.subscribe(tsc.subject(), so); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), so); sub.pull(PullRequestOptions.builder(1).maxBytes(2).build()); return sub; }); @@ -1053,10 +993,10 @@ public void testExceedsMaxRequestBytes1stMessageSyncSub() throws Exception { @Test public void testExceedsMaxRequestBytes1stMessageAsyncSub() throws Exception { - testConflictStatus(409, EXCEEDED_MAX_REQUEST_MAX_BYTES, TYPE_WARNING, "2.9.0", (nc, jsm, js, tsc, handler) -> { + testConflictStatus(409, EXCEEDED_MAX_REQUEST_MAX_BYTES, TYPE_WARNING, "2.9.0", (nc, jsm, js, jstc, handler) -> { Dispatcher d = nc.createDispatcher(); PullSubscribeOptions so = makePso(b -> b.maxBytes(1)); - JetStreamSubscription sub = js.subscribe(tsc.subject(), d, m -> {}, so); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), d, m -> {}, so); sub.pull(PullRequestOptions.builder(1).maxBytes(2).build()); return sub; }); @@ -1065,22 +1005,19 @@ public void testExceedsMaxRequestBytes1stMessageAsyncSub() throws Exception { @Test public void testExceedsMaxRequestBytesNthMessageSyncSub() throws Exception { ListenerForTesting listener = new ListenerForTesting(); - runInJsServer(TestBase::atLeast2_9_1, listener, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - - jsm.addOrUpdateConsumer(tsc.stream, builder().durable(durable(1)).ackPolicy(AckPolicy.None).filterSubjects(tsc.subject()).build()); - PullSubscribeOptions so = PullSubscribeOptions.bind(tsc.stream, durable(1)); - JetStreamSubscription sub = js.subscribe(tsc.subject(), so); + runInLrServer(listener, VersionUtils::atLeast2_9_1, (nc, jstc) -> { + String dur = random(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, builder().durable(dur).ackPolicy(AckPolicy.None).filterSubjects(jstc.subject()).build()); + PullSubscribeOptions so = PullSubscribeOptions.bind(jstc.stream, dur); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), so); // subject 7 + reply 52 + bytes 100 = 159 // subject 7 + reply 52 + bytes 100 + headers 21 = 180 - js.publish(tsc.subject(), new byte[100]); - js.publish(tsc.subject(), new Headers().add("foo", "bar"), new byte[100]); + jstc.js.publish(jstc.subject(), new byte[100]); + jstc.js.publish(jstc.subject(), new Headers().add("foo", "bar"), new byte[100]); // 1000 - 159 - 180 = 661 // subject 7 + reply 52 + bytes 610 = 669 > 661 - js.publish(tsc.subject(), new byte[610]); + jstc.js.publish(jstc.subject(), new byte[610]); sub.pull(PullRequestOptions.builder(10).maxBytes(1000).expiresIn(1000).build()); assertNotNull(sub.nextMessage(500)); @@ -1093,24 +1030,22 @@ public void testExceedsMaxRequestBytesNthMessageSyncSub() throws Exception { @Test public void testExceedsMaxRequestBytesExactBytes() throws Exception { ListenerForTesting listener = new ListenerForTesting(); - runInJsServer(TestBase::atLeast2_9_1, listener, nc -> { - String stream = "sixsix"; // six letters so I can count - String subject = "seven"; // seven letters so I can count - String durable = durable(0); // short keeps under max bytes + runInLrServer(listener, VersionUtils::atLeast2_9_1, (nc, jstc) -> { + String stream = randomWide(6); // six letters so I can count + String subject = randomWide(5); // five letters so I can count + String durable = randomWide(10); // short keeps under max bytes createMemoryStream(nc, stream, subject); - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - jsm.addOrUpdateConsumer(stream, builder().durable(durable).ackPolicy(AckPolicy.None).filterSubjects(subject).build()); + jstc.jsm.addOrUpdateConsumer(stream, builder().durable(durable).ackPolicy(AckPolicy.None).filterSubjects(subject).build()); PullSubscribeOptions so = PullSubscribeOptions.bind(stream, durable); - JetStreamSubscription sub = js.subscribe(subject, so); + JetStreamSubscription sub = jstc.js.subscribe(subject, so); - // 159 + 180 + 661 = 1000 + // 159 + 180 + 661 = 1000 // subject includes crlf // subject 7 + reply 52 + bytes 100 = 159 // subject 7 + reply 52 + bytes 100 + headers 21 = 180 // subject 7 + reply 52 + bytes 602 = 661 - js.publish(subject, new byte[100]); - js.publish(subject, new Headers().add("foo", "bar"), new byte[100]); - js.publish(subject, new byte[602]); + jstc.js.publish(subject, new byte[100]); + jstc.js.publish(subject, new Headers().add("foo", "bar"), new byte[100]); + jstc.js.publish(subject, new byte[602]); sub.pull(PullRequestOptions.builder(10).maxBytes(1000).expiresIn(1000).build()); assertNotNull(sub.nextMessage(500)); @@ -1123,17 +1058,13 @@ public void testExceedsMaxRequestBytesExactBytes() throws Exception { @Test public void testReader() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - JetStream js = nc.jetStream(); - + runInLrServer((nc, jstc) -> { // Pre define a consumer - ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(tsc.consumerName()).filterSubjects(tsc.subject()).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(jstc.consumerName()).filterSubjects(jstc.subject()).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - PullSubscribeOptions so = PullSubscribeOptions.bind(tsc.stream, tsc.consumerName()); - JetStreamSubscription sub = js.subscribe(tsc.subject(), so); + PullSubscribeOptions so = PullSubscribeOptions.bind(jstc.stream, jstc.consumerName()); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), so); JetStreamReader reader = sub.reader(500, 125); int stopCount = 500; @@ -1142,7 +1073,7 @@ public void testReader() throws Exception { AtomicInteger count = new AtomicInteger(); Thread readerThread = getReaderThread(count, stopCount, reader); - Publisher publisher = new Publisher(js, tsc.subject(), 25); + Publisher publisher = new Publisher(jstc.js, jstc.subject(), 25); Thread pubThread = new Thread(publisher); pubThread.start(); @@ -1185,35 +1116,31 @@ private static Thread getReaderThread(AtomicInteger count, int stopCount, JetStr @Test public void testOverflow() throws Exception { - ListenerForTesting l = new ListenerForTesting(); - Options.Builder b = Options.builder().errorListener(l); - jsServer.run(b, TestBase::atLeast2_11, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - JetStream js = nc.jetStream(); - jsPublish(js, tsc.subject(), 100); + ListenerForTesting listener = new ListenerForTesting(); + runInLrServer(listener, VersionUtils::atLeast2_11, (nc, jstc) -> { + jsPublish(jstc.js, jstc.subject(), 100); // Setting PriorityPolicy requires at least one PriorityGroup to be set ConsumerConfiguration ccNoGroup = ConsumerConfiguration.builder() .priorityPolicy(PriorityPolicy.Overflow) .build(); JetStreamApiException jsae = assertThrows(JetStreamApiException.class, - () -> jsm.addOrUpdateConsumer(tsc.stream, ccNoGroup)); + () -> jstc.jsm.addOrUpdateConsumer(jstc.stream, ccNoGroup)); assertEquals(10159, jsae.getApiErrorCode()); // Testing errors - String group = variant(); - String consumer = variant(); + String group = random(); + String consumer = random(); ConsumerConfiguration cc = ConsumerConfiguration.builder() .name(consumer) .priorityPolicy(PriorityPolicy.Overflow) .priorityGroups(group) - .filterSubjects(tsc.subject()).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + .filterSubjects(jstc.subject()).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - PullSubscribeOptions so = PullSubscribeOptions.fastBind(tsc.stream, consumer); - JetStreamSubscription sub = js.subscribe(null, so); + PullSubscribeOptions so = PullSubscribeOptions.fastBind(jstc.stream, consumer); + JetStreamSubscription sub = jstc.js.subscribe(null, so); // 400 Bad Request - Priority Group missing sub.pull(1); @@ -1224,20 +1151,20 @@ public void testOverflow() throws Exception { assertThrows(JetStreamStatusException.class, () -> sub.nextMessage(1000)); // Testing min ack pending - group = variant(); - consumer = variant(); + group = random(); + consumer = random(); cc = ConsumerConfiguration.builder() .name(consumer) .priorityPolicy(PriorityPolicy.Overflow) .priorityGroups(group) .ackWait(60_000) - .filterSubjects(tsc.subject()).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + .filterSubjects(jstc.subject()).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - so = PullSubscribeOptions.fastBind(tsc.stream, consumer); - JetStreamSubscription subPrime = js.subscribe(null, so); - JetStreamSubscription subOver = js.subscribe(null, so); + so = PullSubscribeOptions.fastBind(jstc.stream, consumer); + JetStreamSubscription subPrime = jstc.js.subscribe(null, so); + JetStreamSubscription subOver = jstc.js.subscribe(null, so); PullRequestOptions proNoMin = PullRequestOptions.builder(5) .group(group) @@ -1261,19 +1188,19 @@ public void testOverflow() throws Exception { _overflowCheck(subOver, proOverB, true, 0); // Testing min pending - group = variant(); - consumer = variant(); + group = random(); + consumer = random(); cc = ConsumerConfiguration.builder() .name(consumer) .priorityPolicy(PriorityPolicy.Overflow) .priorityGroups(group) - .filterSubjects(tsc.subject()).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + .filterSubjects(jstc.subject()).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - so = PullSubscribeOptions.fastBind(tsc.stream, consumer); - subPrime = js.subscribe(null, so); - subOver = js.subscribe(null, so); + so = PullSubscribeOptions.fastBind(jstc.stream, consumer); + subPrime = jstc.js.subscribe(null, so); + subOver = jstc.js.subscribe(null, so); proNoMin = PullRequestOptions.builder(5) .group(group) @@ -1295,7 +1222,7 @@ public void testOverflow() throws Exception { }); } - private static void _overflowCheck(JetStreamSubscription sub, PullRequestOptions pro, boolean ack, int expected) throws InterruptedException, JetStreamApiException, IOException { + private static void _overflowCheck(JetStreamSubscription sub, PullRequestOptions pro, boolean ack, int expected) throws InterruptedException { sub.pull(pro); int count = 0; Message m = sub.nextMessage(1000); @@ -1315,24 +1242,19 @@ public void testPrioritized() throws Exception { // start a priority 1 (#1) and a priority 2 (#2) consumer, #1 should get messages, #2 should get none // close the #1, #2 should get messages // start another priority 1 (#3), #2 should stop getting messages #3 should get messages - ListenerForTesting l = new ListenerForTesting(); - Options.Builder b = Options.builder().errorListener(l); - jsServer.run(b, TestBase::atLeast2_12, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - JetStream js = nc.jetStream(); - - String consumer = name(); - String group = variant(); + ListenerForTesting listener = new ListenerForTesting(); + runInLrServer(listener, VersionUtils::atLeast2_12, (nc, jstc) -> { + String consumer = random(); + String group = random(); ConsumerConfiguration cc = ConsumerConfiguration.builder() - .filterSubject(tsc.subject()) + .filterSubject(jstc.subject()) .name(consumer) .priorityGroups(group) .priorityPolicy(PriorityPolicy.Prioritized) .build(); - StreamContext streamContext = nc.getStreamContext(tsc.stream); + StreamContext streamContext = nc.getStreamContext(jstc.stream); ConsumerContext consumerContext1 = streamContext.createOrUpdateConsumer(cc); ConsumerContext consumerContext2 = streamContext.getConsumerContext(consumer); @@ -1378,7 +1300,7 @@ public void testPrioritized() throws Exception { while (pub.get()) { ++count; try { - js.publish(tsc.subject(), ("x" + count).getBytes()); + jstc.js.publish(jstc.subject(), ("x" + count).getBytes()); sleep(20); } catch (Exception e) { @@ -1417,24 +1339,19 @@ public void testPinnedClient() throws Exception { // have 3 consumers in the same group all PriorityPolicy.PinnedClient // start consuming, tracking pin ids and counts // unpin 10 times and make sure that new pins are made - ListenerForTesting l = new ListenerForTesting(); - Options.Builder b = Options.builder().errorListener(l); - jsServer.run(b, TestBase::atLeast2_12, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - JetStream js = nc.jetStream(); - - String consumer = name(); - String group = variant(); + ListenerForTesting listener = new ListenerForTesting(); + runInLrServer(listener, VersionUtils::atLeast2_12, (nc, jstc) -> { + String consumer = random(); + String group = random(); ConsumerConfiguration cc = ConsumerConfiguration.builder() - .filterSubject(tsc.subject()) + .filterSubject(jstc.subject()) .name(consumer) .priorityGroups(group) .priorityPolicy(PriorityPolicy.PinnedClient) .build(); - StreamContext streamContext = nc.getStreamContext(tsc.stream); + StreamContext streamContext = nc.getStreamContext(jstc.stream); ConsumerContext consumerContext1 = streamContext.createOrUpdateConsumer(cc); ConsumerContext consumerContext2 = streamContext.getConsumerContext(consumer); ConsumerContext consumerContext3 = streamContext.getConsumerContext(consumer); @@ -1487,7 +1404,7 @@ public void testPinnedClient() throws Exception { while (pub.get()) { ++count; try { - js.publish(tsc.subject(), ("x" + count).getBytes()); + jstc.js.publish(jstc.subject(), ("x" + count).getBytes()); sleep(20); } catch (Exception e) { @@ -1512,7 +1429,7 @@ public void testPinnedClient() throws Exception { assertTrue(consumerContext3.unpin(group)); break; case 3: - assertTrue(jsm.unpinConsumer(tsc.stream, consumer, group)); + assertTrue(jstc.jsm.unpinConsumer(jstc.stream, consumer, group)); break; } assertTrue(consumerContext1.unpin(group)); diff --git a/src/test/java/io/nats/client/impl/JetStreamPushAsyncTests.java b/src/test/java/io/nats/client/impl/JetStreamPushAsyncTests.java index b49510f1c..93c9eaeb9 100644 --- a/src/test/java/io/nats/client/impl/JetStreamPushAsyncTests.java +++ b/src/test/java/io/nats/client/impl/JetStreamPushAsyncTests.java @@ -25,21 +25,16 @@ import static io.nats.client.support.NatsJetStreamConstants.*; import static io.nats.client.support.NatsKeyValueUtil.KV_OPERATION_HEADER_KEY; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; public class JetStreamPushAsyncTests extends JetStreamTestBase { @Test public void testHandlerSub() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { // publish some messages - jsPublish(js, tsc.subject(), 10); + jsPublish(jstc.js, jstc.subject(), 10); // create a dispatcher without a default handler. Dispatcher dispatcher = nc.createDispatcher(); @@ -55,7 +50,7 @@ public void testHandlerSub() throws Exception { }; // Subscribe using the handler - js.subscribe(tsc.subject(), dispatcher, handler, false); + jstc.js.subscribe(jstc.subject(), dispatcher, handler, false); // Wait for messages to arrive using the countdown latch. // make sure we don't wait forever @@ -67,15 +62,9 @@ public void testHandlerSub() throws Exception { @Test public void testHandlerAutoAck() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { // publish some messages - jsPublish(js, tsc.subject(), 10); + jsPublish(jstc.js, jstc.subject(), 10); // create a dispatcher without a default handler. Dispatcher dispatcher = nc.createDispatcher(); @@ -91,8 +80,8 @@ public void testHandlerAutoAck() throws Exception { }; // subscribe using the handler, auto ack true - PushSubscribeOptions pso1 = PushSubscribeOptions.builder().durable(durable(1)).build(); - JetStreamSubscription sub = js.subscribe(tsc.subject(), dispatcher, handler1, true, pso1); + PushSubscribeOptions pso1 = PushSubscribeOptions.builder().durable(random()).build(); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), dispatcher, handler1, true, pso1); // Wait for messages to arrive using the countdown latch. // make sure we don't wait forever @@ -102,7 +91,7 @@ public void testHandlerAutoAck() throws Exception { // check that all the messages were read by the durable dispatcher.unsubscribe(sub); - sub = js.subscribe(tsc.subject(), pso1); + sub = jstc.js.subscribe(jstc.subject(), pso1); assertNull(sub.nextMessage(Duration.ofSeconds(1))); // 2. auto ack false @@ -117,8 +106,8 @@ public void testHandlerAutoAck() throws Exception { // subscribe using the handler, auto ack false ConsumerConfiguration cc = ConsumerConfiguration.builder().ackWait(Duration.ofMillis(500)).build(); - PushSubscribeOptions pso2 = PushSubscribeOptions.builder().durable(durable(2)).configuration(cc).build(); - sub = js.subscribe(tsc.subject(), dispatcher, handler2, false, pso2); + PushSubscribeOptions pso2 = PushSubscribeOptions.builder().durable(random()).configuration(cc).build(); + sub = jstc.js.subscribe(jstc.subject(), dispatcher, handler2, false, pso2); // Wait for messages to arrive using the countdown latch. // make sure we don't wait forever @@ -131,7 +120,7 @@ public void testHandlerAutoAck() throws Exception { sleep(1000); // just give it time for the server to realize the messages are not ack'ed dispatcher.unsubscribe(sub); - sub = js.subscribe(tsc.subject(), pso2); + sub = jstc.js.subscribe(jstc.subject(), pso2); List list = readMessagesAck(sub, false); assertEquals(10, list.size()); }); @@ -139,14 +128,8 @@ public void testHandlerAutoAck() throws Exception { @Test public void testCantNextMessageOnAsyncPushSub() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - - JetStreamSubscription sub = js.subscribe(tsc.subject(), nc.createDispatcher(), msg -> {}, false); + runInLrServer((nc, jstc) -> { + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), nc.createDispatcher(), msg -> {}, false); // this should exception, can't next message on an async push sub assertThrows(IllegalStateException.class, () -> sub.nextMessage(Duration.ofMillis(1000))); @@ -156,16 +139,8 @@ public void testCantNextMessageOnAsyncPushSub() throws Exception { @Test public void testPushAsyncFlowControl() throws Exception { - ListenerForTesting listenerForTesting = new ListenerForTesting(); - Options.Builder ob = new Options.Builder().errorListener(listenerForTesting); - - runInJsServer(ob, nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + ListenerForTesting listener = new ListenerForTesting(); + runInLrServer(listener, (nc, jstc) -> { byte[] data = new byte[8192]; int MSG_COUNT = 1000; @@ -174,7 +149,7 @@ public void testPushAsyncFlowControl() throws Exception { for (int x = 100_000; x < MSG_COUNT + 100_000; x++) { byte[] fill = (""+ x).getBytes(); System.arraycopy(fill, 0, data, 0, 6); - js.publish(NatsMessage.builder().subject(tsc.subject()).data(data).build()); + jstc.js.publish(NatsMessage.builder().subject(jstc.subject()).data(data).build()); } // create a dispatcher without a default handler. @@ -197,29 +172,26 @@ public void testPushAsyncFlowControl() throws Exception { ConsumerConfiguration cc = ConsumerConfiguration.builder().flowControl(1000).build(); PushSubscribeOptions pso = PushSubscribeOptions.builder().configuration(cc).build(); - js.subscribe(tsc.subject(), dispatcher, handler, false, pso); + jstc.js.subscribe(jstc.subject(), dispatcher, handler, false, pso); // Wait for messages to arrive using the countdown latch. // make sure we don't wait forever awaitAndAssert(msgLatch); assertEquals(MSG_COUNT, count.get()); - assertFalse(listenerForTesting.getFlowControlProcessedEvents().isEmpty()); + assertFalse(listener.getFlowControlProcessedEvents().isEmpty()); }); } @Test - public void testDontAutoAckSituations() throws Exception { - String mockAckReply = "mock-ack-reply."; + public void testDoNotAutoAckSituations() throws Exception { + String mockAckReply = random(); // "mock-ack-reply."; - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { // create the stream. - String stream = stream(); - String subject = subject(); - createMemoryStream(nc, stream, subject, mockAckReply + "*"); - - // Create our JetStream context. - JetStream js = nc.jetStream(); + String stream = random(); + String subject = random(); + createMemoryStream(nc, stream, subject, subjectStar(mockAckReply)); int pubCount = 5; @@ -243,23 +215,23 @@ public void testDontAutoAckSituations() throws Exception { int f = count.incrementAndGet(); if (f == 1) { - m.replyTo = mockAckReply + "ack"; + m.replyTo = subjectDot(mockAckReply, "ack"); m.ack(); } else if (f == 2) { - m.replyTo = mockAckReply + "nak"; + m.replyTo = subjectDot(mockAckReply, "nak"); m.nak(); } else if (f == 3) { - m.replyTo = mockAckReply + "term"; + m.replyTo = subjectDot(mockAckReply, "term"); m.term(); } else if (f == 4) { - m.replyTo = mockAckReply + "progress"; + m.replyTo = subjectDot(mockAckReply, "progress"); m.inProgress(); } else { - m.replyTo = mockAckReply + "auto"; + m.replyTo = subjectDot(mockAckReply, "auto"); } msgLatchRef.get().countDown(); }; @@ -273,30 +245,30 @@ else if (f == 4) { assertEquals(0, msgLatchRef.get().getCount()); dispatcher.unsubscribe(async); - JetStreamSubscription mockAckReplySub = js.subscribe(mockAckReply + "*"); + JetStreamSubscription mockAckReplySub = js.subscribe(subjectStar(mockAckReply)); Message msg = mockAckReplySub.nextMessage(2000); - assertEquals(mockAckReply + "ack", msg.getSubject()); + assertEquals(subjectDot(mockAckReply, "ack"), msg.getSubject()); assertEquals("+ACK", new String(msg.getData())); msg = mockAckReplySub.nextMessage(500); - assertEquals(mockAckReply + "nak", msg.getSubject()); + assertEquals(subjectDot(mockAckReply, "nak"), msg.getSubject()); assertEquals("-NAK", new String(msg.getData())); msg = mockAckReplySub.nextMessage(500); - assertEquals(mockAckReply + "term", msg.getSubject()); + assertEquals(subjectDot(mockAckReply, "term"), msg.getSubject()); assertEquals("+TERM", new String(msg.getData())); msg = mockAckReplySub.nextMessage(500); - assertEquals(mockAckReply + "progress", msg.getSubject()); + assertEquals(subjectDot(mockAckReply, "progress"), msg.getSubject()); assertEquals("+WPI", new String(msg.getData())); // because it was in progress which is not a terminal ack, the auto ack acks msg = mockAckReplySub.nextMessage(500); - assertEquals(mockAckReply + "progress", msg.getSubject()); + assertEquals(subjectDot(mockAckReply, "progress"), msg.getSubject()); assertEquals("+ACK", new String(msg.getData())); msg = mockAckReplySub.nextMessage(500); - assertEquals(mockAckReply + "auto", msg.getSubject()); + assertEquals(subjectDot(mockAckReply, "auto"), msg.getSubject()); assertEquals("+ACK", new String(msg.getData())); // coverage explicit no ack flag @@ -334,10 +306,11 @@ public void onMessage(Message msg) throws InterruptedException { @Test public void testMemoryStorageServerBugPR2719() throws Exception { - String stream = stream(); - String sub = "msbsub.>"; - String key1 = "msbsub.key1"; - String key2 = "msbsub.key2"; + String stream = random(); + String subBase = random(); + String sub = subjectGt(subBase); + String key1 = subjectDot(subBase, "key1"); + String key2 = subjectDot(subBase, "key2"); Headers deleteHeaders = new Headers() .put(KV_OPERATION_HEADER_KEY, KeyValueOperation.DELETE.name()); @@ -345,7 +318,7 @@ public void testMemoryStorageServerBugPR2719() throws Exception { .put(KV_OPERATION_HEADER_KEY, KeyValueOperation.PURGE.name()) .put(ROLLUP_HDR, ROLLUP_HDR_SUBJECT); - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { StreamConfiguration sc = StreamConfiguration.builder() .name(stream) .storageType(StorageType.Memory) @@ -354,9 +327,7 @@ public void testMemoryStorageServerBugPR2719() throws Exception { .denyDelete(true) .build(); - nc.jetStreamManagement().addStream(sc); - - JetStream js = nc.jetStream(); + jsm.addStream(sc); MemStorBugHandler fullHandler = new MemStorBugHandler(); MemStorBugHandler onlyHandler = new MemStorBugHandler(); diff --git a/src/test/java/io/nats/client/impl/JetStreamPushQueueTests.java b/src/test/java/io/nats/client/impl/JetStreamPushQueueTests.java index 47064559b..2323ed069 100644 --- a/src/test/java/io/nats/client/impl/JetStreamPushQueueTests.java +++ b/src/test/java/io/nats/client/impl/JetStreamPushQueueTests.java @@ -32,26 +32,21 @@ public class JetStreamPushQueueTests extends JetStreamTestBase { @Test public void testQueueSubWorkflow() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { // Set up the subscribers // - the PushSubscribeOptions can be re-used since all the subscribers are the same // - use a concurrent integer to track all the messages received // - have a list of subscribers and threads so I can track them - PushSubscribeOptions pso = PushSubscribeOptions.builder().durable(tsc.consumerName()).build(); + PushSubscribeOptions pso = PushSubscribeOptions.builder().durable(jstc.consumerName()).build(); AtomicInteger allReceived = new AtomicInteger(); List subscribers = new ArrayList<>(); + String queue = random(); List subThreads = new ArrayList<>(); for (int id = 1; id <= 3; id++) { // set up the subscription - JetStreamSubscription sub = js.subscribe(tsc.subject(), QUEUE, pso); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), queue, pso); // create and track the runnable - JsQueueSubscriber qs = new JsQueueSubscriber(100, js, sub, allReceived); + JsQueueSubscriber qs = new JsQueueSubscriber(100, jstc.js, sub, allReceived); subscribers.add(qs); // create, track and start the thread Thread t = new Thread(qs); @@ -61,7 +56,7 @@ public void testQueueSubWorkflow() throws Exception { nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server // create and start the publishing - Thread pubThread = new Thread(new JsPublisher(js, tsc.subject(), 100)); + Thread pubThread = new Thread(new JsPublisher(jstc.js, jstc.subject(), 100)); pubThread.start(); // wait for all threads to finish diff --git a/src/test/java/io/nats/client/impl/JetStreamPushTests.java b/src/test/java/io/nats/client/impl/JetStreamPushTests.java index cda2f9c7a..5b9785b07 100644 --- a/src/test/java/io/nats/client/impl/JetStreamPushTests.java +++ b/src/test/java/io/nats/client/impl/JetStreamPushTests.java @@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static io.nats.client.support.NatsJetStreamClientError.JsSubPushAsyncCantSetPending; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; public class JetStreamPushTests extends JetStreamTestBase { @@ -41,26 +42,21 @@ public void testPushEphemeralNullDeliver() throws Exception { @Test public void testPushEphemeralWithDeliver() throws Exception { - _testPushEphemeral(DELIVER); + _testPushEphemeral(random()); } private void _testPushEphemeral(String deliverSubject) throws Exception { - jsServer.run(nc -> { - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - - // Create our JetStream context. - JetStream js = nc.jetStream(); - + runInJsServer(nc -> { + JetStreamTestingContext jstc = new JetStreamTestingContext(nc); // publish some messages - jsPublish(js, tsc.subject(), 1, 5); + jsPublish(jstc.js, jstc.subject(), 1, 5); // Build our subscription options. PushSubscribeOptions options = PushSubscribeOptions.builder().deliverSubject(deliverSubject).build(); // Subscription 1 - JetStreamSubscription sub1 = js.subscribe(tsc.subject(), options); - assertSubscription(sub1, tsc.stream, null, deliverSubject, false); + JetStreamSubscription sub1 = jstc.js.subscribe(jstc.subject(), options); + assertSubscription(sub1, jstc.stream, null, deliverSubject, false); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server // read what is available @@ -79,7 +75,7 @@ private void _testPushEphemeral(String deliverSubject) throws Exception { unsubscribeEnsureNotBound(sub1); // Subscription 2 - JetStreamSubscription sub2 = js.subscribe(tsc.subject(), options); + JetStreamSubscription sub2 = jstc.js.subscribe(jstc.subject(), options); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server // read what is available, same messages @@ -97,7 +93,7 @@ private void _testPushEphemeral(String deliverSubject) throws Exception { unsubscribeEnsureNotBound(sub2); // Subscription 3 testing null timeout - JetStreamSubscription sub3 = js.subscribe(tsc.subject(), options); + JetStreamSubscription sub3 = jstc.js.subscribe(jstc.subject(), options); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server sleep(1000); // give time to make sure the messages get to the client @@ -105,7 +101,7 @@ private void _testPushEphemeral(String deliverSubject) throws Exception { validateRedAndTotal(5, messages0.size(), 5, 5); // Subscription 4 testing timeout <= 0 duration / millis - JetStreamSubscription sub4 = js.subscribe(tsc.subject(), options); + JetStreamSubscription sub4 = jstc.js.subscribe(jstc.subject(), options); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server sleep(1000); // give time to make sure the messages get to the client assertNotNull(sub4.nextMessage(Duration.ZERO)); @@ -128,68 +124,64 @@ public void testPushDurableWithDeliver() throws Exception { } private void _testPushDurable(boolean useDeliverSubject) throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jstc) -> { // create the stream. - String stream = stream(); - String subjectDotGt = subject() + ".>"; + String stream = random(); + String subjectDotGt = random() + ".>"; createMemoryStream(nc, stream, subjectDotGt); - // Create our JetStream context. - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - // For async, create a dispatcher without a default handler. Dispatcher dispatcher = nc.createDispatcher(); // normal, no bind - _testPushDurableSubSync(jsm, js, stream, subjectDotGt, useDeliverSubject, false, (s, cc) -> { + _testPushDurableSubSync(jstc, stream, subjectDotGt, useDeliverSubject, false, (s, cc) -> { PushSubscribeOptions options = PushSubscribeOptions.builder() .durable(cc.getDurable()) .deliverSubject(cc.getDeliverSubject()) .build(); - return js.subscribe(s, options); + return jstc.js.subscribe(s, options); }); - _testPushDurableSubAsync(jsm, js, dispatcher, stream, subjectDotGt, useDeliverSubject, false, (s, d, h, cc) -> { + _testPushDurableSubAsync(jstc, dispatcher, stream, subjectDotGt, useDeliverSubject, false, (s, d, h, cc) -> { PushSubscribeOptions options = PushSubscribeOptions.builder() .durable(cc.getDurable()) .deliverSubject(cc.getDeliverSubject()) .build(); - return js.subscribe(s, d, h, false, options); + return jstc.js.subscribe(s, d, h, false, options); }); // use configuration, no bind - _testPushDurableSubSync(jsm, js, stream, subjectDotGt, useDeliverSubject, false, (s, cc) -> { + _testPushDurableSubSync(jstc, stream, subjectDotGt, useDeliverSubject, false, (s, cc) -> { PushSubscribeOptions options = PushSubscribeOptions.builder().configuration(cc).build(); - return js.subscribe(s, options); + return jstc.js.subscribe(s, options); }); - _testPushDurableSubAsync(jsm, js, dispatcher, stream, subjectDotGt, useDeliverSubject, false, (s, d, h, cc) -> { + _testPushDurableSubAsync(jstc, dispatcher, stream, subjectDotGt, useDeliverSubject, false, (s, d, h, cc) -> { PushSubscribeOptions options = PushSubscribeOptions.builder().configuration(cc).build(); - return js.subscribe(s, d, h, false, options); + return jstc.js.subscribe(s, d, h, false, options); }); if (useDeliverSubject) { // bind long form - _testPushDurableSubSync(jsm, js, stream, subjectDotGt, true, true, (s, cc) -> { + _testPushDurableSubSync(jstc, stream, subjectDotGt, true, true, (s, cc) -> { PushSubscribeOptions options = PushSubscribeOptions.builder().stream(stream).durable(cc.getDurable()).bind(true).build(); - return js.subscribe(s, options); + return jstc.js.subscribe(s, options); }); - _testPushDurableSubAsync(jsm, js, dispatcher, stream, subjectDotGt, true, true, (s, d, h, cc) -> { + _testPushDurableSubAsync(jstc, dispatcher, stream, subjectDotGt, true, true, (s, d, h, cc) -> { PushSubscribeOptions options = PushSubscribeOptions.builder().stream(stream).durable(cc.getDurable()).bind(true).build(); - return js.subscribe(s, d, h, false, options); + return jstc.js.subscribe(s, d, h, false, options); }); // bind short form - _testPushDurableSubSync(jsm, js, stream, subjectDotGt, true, true, (s, cc) -> { + _testPushDurableSubSync(jstc, stream, subjectDotGt, true, true, (s, cc) -> { PushSubscribeOptions options = PushSubscribeOptions.bind(stream, cc.getDurable()); - return js.subscribe(s, options); + return jstc.js.subscribe(s, options); }); - _testPushDurableSubAsync(jsm, js, dispatcher, stream, subjectDotGt, true, true, (s, d, h, cc) -> { + _testPushDurableSubAsync(jstc, dispatcher, stream, subjectDotGt, true, true, (s, d, h, cc) -> { PushSubscribeOptions options = PushSubscribeOptions.bind(stream, cc.getDurable()); - return js.subscribe(s, d, h, false, options); + return jstc.js.subscribe(s, d, h, false, options); }); } }); @@ -203,10 +195,10 @@ private interface SubscriptionSupplierAsync { JetStreamSubscription get(String subject, Dispatcher dispatcher, MessageHandler handler, ConsumerConfiguration cc) throws IOException, JetStreamApiException; } - private void _testPushDurableSubSync(JetStreamManagement jsm, JetStream js, String stream, String subjectDotGt, boolean useDeliverSubject, boolean bind, SubscriptionSupplier supplier) throws Exception { - String subject = subjectDotGt.replace(">", subject()); - String durable = durable(); - String deliverSubject = useDeliverSubject ? deliver() : null; + private void _testPushDurableSubSync(JetStreamTestingContext jstc, String stream, String subjectDotGt, boolean useDeliverSubject, boolean bind, SubscriptionSupplier supplier) throws Exception { + String subject = subjectDotGt.replace(">", random()); + String durable = random(); + String deliverSubject = useDeliverSubject ? random() : null; ConsumerConfiguration cc = ConsumerConfiguration.builder() .durable(durable) .deliverSubject(deliverSubject) @@ -214,11 +206,11 @@ private void _testPushDurableSubSync(JetStreamManagement jsm, JetStream js, Stri .build(); if (bind) { - jsm.addOrUpdateConsumer(stream, cc); + jstc.jsm.addOrUpdateConsumer(stream, cc); } // publish some messages - jsPublish(js, subject, 1, 5); + jsPublish(jstc.js, subject, 1, 5); JetStreamSubscription sub = supplier.get(subject, cc); assertSubscription(sub, stream, durable, deliverSubject, false); @@ -246,20 +238,20 @@ private void _testPushDurableSubSync(JetStreamManagement jsm, JetStream js, Stri unsubscribeEnsureNotBound(sub); } - private void _testPushDurableSubAsync(JetStreamManagement jsm, JetStream js, Dispatcher dispatcher, String stream, String subjectDotGt, boolean useDeliverSubject, boolean bind, SubscriptionSupplierAsync supplier) throws IOException, JetStreamApiException, InterruptedException { - String subject = subjectDotGt.replace(">", subject()); - String deliverSubject = useDeliverSubject ? deliver() : null; + private void _testPushDurableSubAsync(JetStreamTestingContext jstc, Dispatcher dispatcher, String stream, String subjectDotGt, boolean useDeliverSubject, boolean bind, SubscriptionSupplierAsync supplier) throws IOException, JetStreamApiException, InterruptedException { + String subject = subjectDotGt.replace(">", random()); + String deliverSubject = useDeliverSubject ? random() : null; ConsumerConfiguration cc = ConsumerConfiguration.builder() - .durable(durable()) + .durable(random()) .deliverSubject(deliverSubject) .filterSubject(subject) .build(); if (bind) { - jsm.addOrUpdateConsumer(stream, cc); + jstc.jsm.addOrUpdateConsumer(stream, cc); } // publish some messages - jsPublish(js, subject, 5); + jsPublish(jstc.js, subject, 5); CountDownLatch msgLatch = new CountDownLatch(5); AtomicInteger received = new AtomicInteger(); @@ -283,22 +275,16 @@ private void _testPushDurableSubAsync(JetStreamManagement jsm, JetStream js, Dis @Test public void testCantPullOnPushSub() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - - JetStreamSubscription sub = js.subscribe(tsc.subject()); - assertSubscription(sub, tsc.stream, null, null, false); + runInLrServer((nc, jstc) -> { + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject()); + assertSubscription(sub, jstc.stream, null, null, false); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server assertCantPullOnPushSub(sub); unsubscribeEnsureNotBound(sub); PushSubscribeOptions pso = PushSubscribeOptions.builder().ordered(true).build(); - sub = js.subscribe(tsc.subject(), pso); + sub = jstc.js.subscribe(jstc.subject(), pso); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server assertCantPullOnPushSub(sub); @@ -322,17 +308,12 @@ private void assertCantPullOnPushSub(JetStreamSubscription sub) { @Test public void testHeadersOnly() throws Exception { - jsServer.run(nc -> { - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { PushSubscribeOptions pso = ConsumerConfiguration.builder().headersOnly(true).buildPushSubscribeOptions(); - JetStreamSubscription sub = js.subscribe(tsc.subject(), pso); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), pso); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server - jsPublish(js, tsc.subject(), 5); + jsPublish(jstc.js, jstc.subject(), 5); List messages = readMessagesAck(sub, Duration.ZERO, 5); assertEquals(5, messages.size()); @@ -344,20 +325,14 @@ public void testHeadersOnly() throws Exception { @Test public void testAcks() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { ConsumerConfiguration cc = ConsumerConfiguration.builder().ackWait(Duration.ofMillis(1500)).build(); PushSubscribeOptions pso = PushSubscribeOptions.builder().configuration(cc).build(); - JetStreamSubscription sub = js.subscribe(tsc.subject(), pso); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), pso); nc.flush(Duration.ofSeconds(1)); // flush outgoing communication with/to the server // TERM - jsPublish(js, tsc.subject(), "TERM", 1); + jsPublish(jstc.js, jstc.subject(), "TERM", 1); Message message = sub.nextMessage(Duration.ofSeconds(1)); assertNotNull(message); @@ -369,7 +344,7 @@ public void testAcks() throws Exception { assertNull(sub.nextMessage(Duration.ofMillis(500))); // Ack Wait timeout - jsPublish(js, tsc.subject(), "WAIT", 1); + jsPublish(jstc.js, jstc.subject(), "WAIT", 1); message = sub.nextMessage(Duration.ofSeconds(1)); assertNotNull(message); @@ -385,7 +360,7 @@ public void testAcks() throws Exception { assertEquals("WAIT1", data); // In Progress - jsPublish(js, tsc.subject(), "PRO", 1); + jsPublish(jstc.js, jstc.subject(), "PRO", 1); message = sub.nextMessage(Duration.ofSeconds(1)); assertNotNull(message); @@ -409,7 +384,7 @@ public void testAcks() throws Exception { assertNull(sub.nextMessage(Duration.ofMillis(500))); // ACK Sync - jsPublish(js, tsc.subject(), "ACKSYNC", 1); + jsPublish(jstc.js, jstc.subject(), "ACKSYNC", 1); message = sub.nextMessage(Duration.ofSeconds(1)); assertNotNull(message); @@ -421,7 +396,7 @@ public void testAcks() throws Exception { assertNull(sub.nextMessage(Duration.ofMillis(500))); // NAK - jsPublish(js, tsc.subject(), "NAK", 1, 1); + jsPublish(jstc.js, jstc.subject(), "NAK", 1, 1); message = sub.nextMessage(Duration.ofSeconds(1)); assertNotNull(message); @@ -439,7 +414,7 @@ public void testAcks() throws Exception { assertNull(sub.nextMessage(Duration.ofMillis(500))); - jsPublish(js, tsc.subject(), "NAK", 2, 1); + jsPublish(jstc.js, jstc.subject(), "NAK", 2, 1); message = sub.nextMessage(Duration.ofSeconds(1)); assertNotNull(message); @@ -459,7 +434,7 @@ public void testAcks() throws Exception { assertNull(sub.nextMessage(Duration.ofMillis(500))); - jsPublish(js, tsc.subject(), "NAK", 3, 1); + jsPublish(jstc.js, jstc.subject(), "NAK", 3, 1); message = sub.nextMessage(Duration.ofSeconds(1)); assertNotNull(message); @@ -483,16 +458,15 @@ public void testAcks() throws Exception { @Test public void testDeliveryPolicy() throws Exception { - jsServer.run(nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - + runInLrServer((nc, jsm, js) -> { // create the stream. - String stream = stream(); - createMemoryStream(jsm, stream, SUBJECT_STAR); + String stream = random(); + String subject = random(); + String subjectStar = subjectStar(subject); + createMemoryStream(jsm, stream, subjectStar); - String subjectA = subjectDot("A"); - String subjectB = subjectDot("B"); + String subjectA = subjectDot(subject, "A"); + String subjectB = subjectDot(subject, "B"); js.publish(subjectA, dataBytes(1)); js.publish(subjectA, dataBytes(2)); @@ -601,24 +575,17 @@ private void assertMessage(Message m, int i) { @Test public void testPushSyncFlowControl() throws Exception { ListenerForTesting listener = new ListenerForTesting(); - Options.Builder ob = new Options.Builder().errorListener(listener); - - runInJsServer(ob, nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); + runInJsServer(listener, nc -> { + JetStreamTestingContext jstc = new JetStreamTestingContext(nc); byte[] data = new byte[1024*10]; - int MSG_COUNT = 1000; // publish some messages for (int x = 100_000; x < MSG_COUNT + 100_000; x++) { byte[] fill = ("" + x).getBytes(); System.arraycopy(fill, 0, data, 0, 6); - js.publish(NatsMessage.builder().subject(tsc.subject()).data(data).build()); + jstc.js.publish(NatsMessage.builder().subject(jstc.subject()).data(data).build()); } // reset the counters @@ -626,7 +593,7 @@ public void testPushSyncFlowControl() throws Exception { ConsumerConfiguration cc = ConsumerConfiguration.builder().flowControl(1000).build(); PushSubscribeOptions pso = PushSubscribeOptions.builder().configuration(cc).build(); - JetStreamSubscription sub = js.subscribe(tsc.subject(), pso); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject(), pso); for (int x = 0; x < MSG_COUNT; x++) { Message msg = sub.nextMessage(1000); set.add(new String(Arrays.copyOf(msg.getData(), 6))); @@ -640,19 +607,13 @@ public void testPushSyncFlowControl() throws Exception { // coverage for subscribe options heartbeat directly cc = ConsumerConfiguration.builder().idleHeartbeat(100).build(); pso = PushSubscribeOptions.builder().configuration(cc).build(); - js.subscribe(tsc.subject(), pso); + jstc.js.subscribe(jstc.subject(), pso); }); } @Test public void testPendingLimits() throws Exception { - jsServer.run(nc -> { - // Create our JetStream context. - JetStream js = nc.jetStream(); - - // create the stream. - TestingStreamContainer tsc = new TestingStreamContainer(nc); - + runInLrServer((nc, jstc) -> { int customMessageLimit = 1000; int customByteLimit = 1024 * 1024; @@ -674,19 +635,19 @@ public void testPendingLimits() throws Exception { .pendingByteLimit(-1) .build(); - JetStreamSubscription syncSub = js.subscribe(tsc.subject(), psoDefaultSync); + JetStreamSubscription syncSub = jstc.js.subscribe(jstc.subject(), psoDefaultSync); assertEquals(Consumer.DEFAULT_MAX_MESSAGES, syncSub.getPendingMessageLimit()); assertEquals(Consumer.DEFAULT_MAX_BYTES, syncSub.getPendingByteLimit()); - syncSub = js.subscribe(tsc.subject(), psoCustomSync); + syncSub = jstc.js.subscribe(jstc.subject(), psoCustomSync); assertEquals(customMessageLimit, syncSub.getPendingMessageLimit()); assertEquals(customByteLimit, syncSub.getPendingByteLimit()); - syncSub = js.subscribe(tsc.subject(), psoCustomSyncUnlimited0); + syncSub = jstc.js.subscribe(jstc.subject(), psoCustomSyncUnlimited0); assertEquals(0, syncSub.getPendingMessageLimit()); assertEquals(0, syncSub.getPendingByteLimit()); - syncSub = js.subscribe(tsc.subject(), psoCustomSyncUnlimitedUnlimitedNegative); + syncSub = jstc.js.subscribe(jstc.subject(), psoCustomSyncUnlimitedUnlimitedNegative); assertEquals(0, syncSub.getPendingMessageLimit()); assertEquals(0, syncSub.getPendingByteLimit()); @@ -701,11 +662,11 @@ public void testPendingLimits() throws Exception { .pendingByteLimit(Consumer.DEFAULT_MAX_BYTES) .build(); - JetStreamSubscription subAsync = js.subscribe(tsc.subject(), d, m -> {}, false, psoAsyncDefault); + JetStreamSubscription subAsync = jstc.js.subscribe(jstc.subject(), d, m -> {}, false, psoAsyncDefault); assertEquals(Consumer.DEFAULT_MAX_MESSAGES, subAsync.getPendingMessageLimit()); assertEquals(Consumer.DEFAULT_MAX_BYTES, subAsync.getPendingByteLimit()); - subAsync = js.subscribe(tsc.subject(), d, m -> {}, false, psoAsyncNonDefaultValid); + subAsync = jstc.js.subscribe(jstc.subject(), d, m -> {}, false, psoAsyncNonDefaultValid); assertEquals(Consumer.DEFAULT_MAX_MESSAGES, subAsync.getPendingMessageLimit()); assertEquals(Consumer.DEFAULT_MAX_BYTES, subAsync.getPendingByteLimit()); @@ -725,10 +686,10 @@ public void testPendingLimits() throws Exception { .pendingByteLimit(0) .build(); - assertClientError(JsSubPushAsyncCantSetPending, () -> js.subscribe(SUBJECT, d, m ->{}, false, psoAsyncNopeMessages)); - assertClientError(JsSubPushAsyncCantSetPending, () -> js.subscribe(SUBJECT, d, m ->{}, false, psoAsyncNopeBytes)); - assertClientError(JsSubPushAsyncCantSetPending, () -> js.subscribe(SUBJECT, d, m ->{}, false, psoAsyncNope2Messages)); - assertClientError(JsSubPushAsyncCantSetPending, () -> js.subscribe(SUBJECT, d, m ->{}, false, psoAsyncNope2Bytes)); + assertClientError(JsSubPushAsyncCantSetPending, () -> jstc.js.subscribe(random(), d, m ->{}, false, psoAsyncNopeMessages)); + assertClientError(JsSubPushAsyncCantSetPending, () -> jstc.js.subscribe(random(), d, m ->{}, false, psoAsyncNopeBytes)); + assertClientError(JsSubPushAsyncCantSetPending, () -> jstc.js.subscribe(random(), d, m ->{}, false, psoAsyncNope2Messages)); + assertClientError(JsSubPushAsyncCantSetPending, () -> jstc.js.subscribe(random(), d, m ->{}, false, psoAsyncNope2Bytes)); }); } } diff --git a/src/test/java/io/nats/client/impl/JetStreamTestBase.java b/src/test/java/io/nats/client/impl/JetStreamTestBase.java index da4333189..77d9b9385 100644 --- a/src/test/java/io/nats/client/impl/JetStreamTestBase.java +++ b/src/test/java/io/nats/client/impl/JetStreamTestBase.java @@ -16,14 +16,13 @@ import io.nats.client.*; import io.nats.client.api.*; import io.nats.client.utils.TestBase; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.function.Executable; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.time.Duration; -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -31,6 +30,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; public class JetStreamTestBase extends TestBase { @@ -43,20 +43,6 @@ public class JetStreamTestBase extends TestBase { public static final String InvalidMeta10Tokens = "$JS.ACK.v2Domain.v2Hash.test-stream.test-consumer.1.2.3.1605139610113260000"; public static final String InvalidMetaData = "$JS.ACK.v2Domain.v2Hash.test-stream.test-consumer.1.2.3.1605139610113260000.not-a-number"; - public static LongRunningNatsTestServer jsServer; - - @BeforeAll - public static void beforeAll() throws IOException, InterruptedException { - jsServer = new LongRunningNatsTestServer(false, true, null); - } - - @AfterAll - public static void afterAll() throws Exception { - if (jsServer != null) { - jsServer.close(); - } - } - public static final Duration DEFAULT_TIMEOUT = Duration.ofMillis(1000); private static final AtomicInteger MOCK_SID_HOLDER = new AtomicInteger(4273); @@ -88,106 +74,6 @@ public NatsMessage getTestMessage(String replyTo, String sid) { return new IncomingMessageFactory(sid, "subj", replyTo, 0, false).getMessage(); } - // ---------------------------------------------------------------------------------------------------- - // Management - // ---------------------------------------------------------------------------------------------------- - public static class TestingStreamContainer { - private String defaultSubjectVariant; - private final String defaultNameVariant = TestBase.name(); - public final StreamInfo si; - public final String stream = stream(); - private final Map subjects = new HashMap<>(); - private final Map names = new HashMap<>(); - - public TestingStreamContainer(Connection nc) throws JetStreamApiException, IOException { - this(nc.jetStreamManagement(), (String[])null); - } - - public TestingStreamContainer(Connection nc, int subjectCount) throws JetStreamApiException, IOException { - this(nc.jetStreamManagement(), subjectCount); - } - - public TestingStreamContainer(Connection nc, String... subjects) throws JetStreamApiException, IOException { - this(nc.jetStreamManagement(), subjects); - } - - public TestingStreamContainer(JetStreamManagement jsm) throws JetStreamApiException, IOException { - this(jsm, (String[])null); - } - - public TestingStreamContainer(JetStreamManagement jsm, String... subjects) throws JetStreamApiException, IOException { - if (subjects == null) { - this.si = createMemoryStream(jsm, stream, subject()); - } - else { - this.si = createMemoryStream(jsm, stream, subjects); - } - } - - public TestingStreamContainer(JetStreamManagement jsm, int subjectCount) throws JetStreamApiException, IOException { - String[] subjects = new String[subjectCount]; - for (int x = 0; x < subjectCount; x++) { - subjects[x] = subject(x); - } - this.si = createMemoryStream(jsm, stream, subjects); - } - - public String subject() { - if (defaultSubjectVariant == null) { - defaultSubjectVariant = TestBase.variant(null); - } - return subject(defaultSubjectVariant); - } - - public String subject(Object variant) { - return subjects.computeIfAbsent(variant, TestBase::subject); - } - - public String consumerName() { - return consumerName(defaultNameVariant); - } - - public String consumerName(Object variant) { - return names.computeIfAbsent(variant, TestBase::name); - } - } - - public static StreamInfo createMemoryStream(JetStreamManagement jsm, String streamName, String... subjects) throws IOException, JetStreamApiException { - if (streamName == null) { - streamName = stream(); - } - - if (subjects == null || subjects.length == 0) { - subjects = new String[]{subject()}; - } - - StreamConfiguration sc = StreamConfiguration.builder() - .name(streamName) - .storageType(StorageType.Memory) - .subjects(subjects).build(); - - return jsm.addStream(sc); - } - - public static StreamInfo createMemoryStream(Connection nc, String streamName, String... subjects) - throws IOException, JetStreamApiException { - return createMemoryStream(nc.jetStreamManagement(), streamName, subjects); - } - - public static StreamInfo createDefaultTestStream(Connection nc) throws IOException, JetStreamApiException { - return createMemoryStream(nc, STREAM, SUBJECT); - } - - public static StreamInfo createDefaultTestStream(JetStreamManagement jsm) throws IOException, JetStreamApiException { - return createMemoryStream(jsm, STREAM, SUBJECT); - } - - public static T assertThrowsPrint(Class expectedType, Executable executable) { - T t = org.junit.jupiter.api.Assertions.assertThrows(expectedType, executable); - t.printStackTrace(); - return t; - } - // ---------------------------------------------------------------------------------------------------- // Publish / Read // ---------------------------------------------------------------------------------------------------- @@ -242,10 +128,6 @@ public static PublishAck jsPublish(JetStream js, String subject, String data) th return js.publish(NatsMessage.builder().subject(subject).data(data.getBytes(StandardCharsets.US_ASCII)).build()); } - public static PublishAck jsPublish(JetStream js) throws IOException, JetStreamApiException { - return jsPublish(js, SUBJECT, DATA); - } - public static PublishAck jsPublish(JetStream js, String subject) throws IOException, JetStreamApiException { return jsPublish(js, subject, DATA); } @@ -323,7 +205,7 @@ public void run() { try { while (keepGoing.get()) { if (jitter > 0) { - Thread.sleep(ThreadLocalRandom.current().nextLong(jitter)); + sleep(ThreadLocalRandom.current().nextLong(jitter)); } js.publish(subject, dataBytes(++dataId)); } @@ -457,7 +339,10 @@ public static void assertConfig(String stream, Long msgCount, Long firstSeq, Str } public static void assertStreamSource(MessageInfo info, String stream, int i) { - String hval = info.getHeaders().get("Nats-Stream-Source").get(0); + assertNotNull(info.getHeaders()); + List nss = info.getHeaders().get("Nats-Stream-Source"); + assertNotNull(nss); + String hval = nss.get(0); assertTrue(hval.contains(stream)); assertTrue(hval.contains("" + i)); } diff --git a/src/test/java/io/nats/client/impl/JetStreamTestingContext.java b/src/test/java/io/nats/client/impl/JetStreamTestingContext.java new file mode 100644 index 000000000..e66b0e4e8 --- /dev/null +++ b/src/test/java/io/nats/client/impl/JetStreamTestingContext.java @@ -0,0 +1,81 @@ +// Copyright 2025 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client.impl; + +import io.nats.client.Connection; +import io.nats.client.JetStreamApiException; +import io.nats.client.api.StreamInfo; +import io.nats.client.utils.TestBase; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class JetStreamTestingContext implements AutoCloseable { + public final NatsJetStreamManagement jsm; + public final NatsJetStream js; + public final String stream; + public StreamInfo si; + + private final String subjectBase; + private final Map subjects; + private final String consumerNameBase; + private final Map consumerNames; + + public JetStreamTestingContext(Connection nc) throws JetStreamApiException, IOException { + this(nc, 1); + } + + public JetStreamTestingContext(Connection nc, int subjectCount) throws JetStreamApiException, IOException { + this.jsm = (NatsJetStreamManagement)nc.jetStreamManagement(); + this.js = (NatsJetStream)nc.jetStream(); + stream = TestBase.random(); + subjectBase = TestBase.random(); + this.subjects = new HashMap<>(); + consumerNameBase = TestBase.random(); + this.consumerNames = new HashMap<>(); + + subjectCount = Math.max(subjectCount, 1); + String[] subjects = new String[subjectCount]; + for (int x = 0; x < subjectCount; x++) { + subjects[x] = subject(x); + } + this.si = TestBase.createMemoryStream(jsm, stream, subjects); + } + + @Override + public void close() throws Exception { + try { + jsm.deleteStream(stream); + } + catch (Exception ignore) { + } + } + + public String subject() { + return subject(0); + } + + public String subject(Object variant) { + return subjects.computeIfAbsent(variant, v -> subjectBase + "-" + v); + } + + public String consumerName() { + return consumerNameBase; + } + + public String consumerName(Object variant) { + return consumerNames.computeIfAbsent(variant, v -> consumerNameBase + "-" + v); + } +} diff --git a/src/test/java/io/nats/client/impl/KeyValueTests.java b/src/test/java/io/nats/client/impl/KeyValueTests.java index d0313e4fc..7947d7c97 100644 --- a/src/test/java/io/nats/client/impl/KeyValueTests.java +++ b/src/test/java/io/nats/client/impl/KeyValueTests.java @@ -15,7 +15,7 @@ import io.nats.client.*; import io.nats.client.api.*; import io.nats.client.support.NatsKeyValueUtil; -import io.nats.client.utils.TestBase; +import io.nats.client.utils.VersionUtils; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -34,6 +34,9 @@ import static io.nats.client.api.KeyValueWatchOption.*; import static io.nats.client.support.NatsConstants.DOT; import static io.nats.client.support.NatsJetStreamConstants.SERVER_DEFAULT_DUPLICATE_WINDOW_MS; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.ThreadUtils.sleep; +import static io.nats.client.utils.VersionUtils.atLeast2_10; import static org.junit.jupiter.api.Assertions.*; public class KeyValueTests extends JetStreamTestBase { @@ -42,16 +45,16 @@ public class KeyValueTests extends JetStreamTestBase { public void testWorkflow() throws Exception { long now = ZonedDateTime.now().toEpochSecond(); - String byteKey = "key.byte" + variant(); - String stringKey = "key.string" + variant(); - String longKey = "key.long" + variant(); - String notFoundKey = "notFound" + variant(); + String byteKey = "key.byte" + random(); + String stringKey = "key.string" + random(); + String longKey = "key.long" + random(); + String notFoundKey = "notFound" + random(); String byteValue1 = "Byte Value 1"; String byteValue2 = "Byte Value 2"; String stringValue1 = "String Value 1"; String stringValue2 = "String Value 2"; - jsServer.run(TestBase::atLeast2_10, nc -> { + runInLrServer(VersionUtils::atLeast2_10, (nc, jsm, js) -> { // get the kv management context KeyValueManagement kvm = nc.keyValueManagement(); nc.keyValueManagement(KeyValueOptions.builder(DEFAULT_JS_OPTIONS).build()); // coverage @@ -60,8 +63,8 @@ public void testWorkflow() throws Exception { metadata.put(META_KEY, META_VALUE); // create the bucket - String bucket = bucket(); - String desc = variant(); + String bucket = random(); + String desc = random(); KeyValueConfiguration kvc = KeyValueConfiguration.builder() .name(bucket) .description(desc) @@ -91,9 +94,20 @@ public void testWorkflow() throws Exception { // retrieve the values. all types are stored as bytes // so you can always get the bytes directly - assertEquals(byteValue1, new String(kv.get(byteKey).getValue())); - assertEquals(stringValue1, new String(kv.get(stringKey).getValue())); - assertEquals("1", new String(kv.get(longKey).getValue())); + KeyValueEntry entry = kv.get(byteKey); + assertNotNull(entry); + assertNotNull(entry.getValue()); + assertEquals(byteValue1, new String(entry.getValue())); + + entry = kv.get(stringKey); + assertNotNull(entry); + assertNotNull(entry.getValue()); + assertEquals(stringValue1, new String(entry.getValue())); + + entry = kv.get(longKey); + assertNotNull(entry); + assertNotNull(entry.getValue()); + assertEquals("1", new String(entry.getValue())); // if you know the value is not binary and can safely be read // as a UTF-8 string, the getStringValue method is ok to use @@ -130,6 +144,7 @@ public void testWorkflow() throws Exception { // let's check the bucket info status = kvm.getStatus(bucket); assertState(status, 3, 3); + //noinspection deprecation status = kvm.getBucketInfo(bucket); // coverage for deprecated assertState(status, 3, 3); @@ -159,7 +174,10 @@ public void testWorkflow() throws Exception { assertEquals(7, kv.put(longKey, 2)); // values after updates - assertEquals(byteValue2, new String(kv.get(byteKey).getValue())); + entry = kv.get(byteKey); + assertNotNull(entry); + assertNotNull(entry.getValue()); + assertEquals(byteValue2, new String(entry.getValue())); assertEquals(stringValue2, kv.get(stringKey).getValueAsString()); assertEquals(2, kv.get(longKey).getValueAsLong()); @@ -321,7 +339,9 @@ private void assertInitialStatus(KeyValueStatus status, String bucket, String de assertEquals(3, kvc.getMaxHistoryPerKey()); assertEquals(-1, status.getMaxBucketSize()); assertEquals(-1, kvc.getMaxBucketSize()); + //noinspection deprecation assertEquals(-1, status.getMaxValueSize()); // COVERAGE for deprecated + //noinspection deprecation assertEquals(-1, kvc.getMaxValueSize()); assertEquals(-1, status.getMaximumValueSize()); assertEquals(-1, kvc.getMaximumValueSize()); @@ -346,17 +366,17 @@ private void assertInitialStatus(KeyValueStatus status, String bucket, String de @Test public void testGetRevision() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); - String bucket = bucket(); + String bucket = random(); kvm.create(KeyValueConfiguration.builder() .name(bucket) .storageType(StorageType.Memory) .maxHistoryPerKey(2) .build()); - String key = key(); + String key = random(); KeyValue kv = nc.keyValue(bucket); long seq1 = kv.put(key, 1); long seq2 = kv.put(key, 2); @@ -384,11 +404,11 @@ public void testGetRevision() throws Exception { @Test public void testKeys() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); // create bucket - String bucket = bucket(); + String bucket = random(); kvm.create(KeyValueConfiguration.builder() .name(bucket) .storageType(StorageType.Memory) @@ -474,11 +494,11 @@ private static List getKeysFromQueue(LinkedBlockingQueue q) { @Test public void testMaxHistoryPerKey() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); - String bucket1 = bucket(); - String bucket2 = bucket(); + String bucket1 = random(); + String bucket2 = random(); // default maxHistoryPerKey is 1 kvm.create(KeyValueConfiguration.builder() .name(bucket1) @@ -486,7 +506,7 @@ public void testMaxHistoryPerKey() throws Exception { .build()); KeyValue kv = nc.keyValue(bucket1); - String key = key(); + String key = random(); kv.put(key, 1); kv.put(key, 2); @@ -500,7 +520,7 @@ public void testMaxHistoryPerKey() throws Exception { .storageType(StorageType.Memory) .build()); - key = key(); + key = random(); kv = nc.keyValue(bucket2); kv.put(key, 1); kv.put(key, 2); @@ -515,10 +535,10 @@ public void testMaxHistoryPerKey() throws Exception { @Test public void testCreateUpdate() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); - String bucket = bucket(); + String bucket = random(); // doesn't exist yet assertThrows(JetStreamApiException.class, () -> kvm.getStatus(bucket)); @@ -539,7 +559,7 @@ public void testCreateUpdate() throws Exception { assertEquals(0, kvs.getEntryCount()); assertEquals("JetStream", kvs.getBackingStore()); - String key = key(); + String key = random(); KeyValue kv = nc.keyValue(bucket); kv.put(key, 1); kv.put(key, 2); @@ -548,8 +568,8 @@ public void testCreateUpdate() throws Exception { assertEquals(1, history.size()); assertEquals(2, history.get(0).getValueAsLong()); - boolean compression = atLeast2_10(ensureRunServerInfo()); - String desc = variant(); + boolean compression = atLeast2_10(); + String desc = random(); KeyValueConfiguration kvc = KeyValueConfiguration.builder(kvs.getConfiguration()) .description(desc) .maxHistoryPerKey(3) @@ -565,6 +585,7 @@ public void testCreateUpdate() throws Exception { assertEquals(desc, kvs.getDescription()); assertEquals(3, kvs.getMaxHistoryPerKey()); assertEquals(10_000, kvs.getMaxBucketSize()); + //noinspection deprecation assertEquals(100, kvs.getMaxValueSize()); // COVERAGE for deprecated assertEquals(100, kvs.getMaximumValueSize()); assertEquals(Duration.ofHours(1), kvs.getTtl()); @@ -587,11 +608,11 @@ public void testCreateUpdate() throws Exception { @Test public void testHistoryDeletePurge() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); // create bucket - String bucket = bucket(); + String bucket = random(); kvm.create(KeyValueConfiguration.builder() .name(bucket) .storageType(StorageType.Memory) @@ -599,7 +620,7 @@ public void testHistoryDeletePurge() throws Exception { .build()); KeyValue kv = nc.keyValue(bucket); - String key = key(); + String key = random(); kv.put(key, "a"); kv.put(key, "b"); kv.put(key, "c"); @@ -618,11 +639,11 @@ public void testHistoryDeletePurge() throws Exception { @Test public void testAtomicDeleteAtomicPurge() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); // create bucket - String bucket = bucket(); + String bucket = random(); kvm.create(KeyValueConfiguration.builder() .name(bucket) .storageType(StorageType.Memory) @@ -630,7 +651,7 @@ public void testAtomicDeleteAtomicPurge() throws Exception { .build()); KeyValue kv = nc.keyValue(bucket); - String key = key(); + String key = random(); kv.put(key, "a"); kv.put(key, "b"); kv.put(key, "c"); @@ -669,17 +690,17 @@ public void testAtomicDeleteAtomicPurge() throws Exception { // Correct revision writes roll-up purge tombstone kv.purge(key, 5); - assertHistory(Arrays.asList(KeyValueOperation.PURGE), kv.history(key)); + assertHistory(Collections.singletonList(KeyValueOperation.PURGE), kv.history(key)); }); } @Test public void testPurgeDeletes() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); // create bucket - String bucket = bucket(); + String bucket = random(); kvm.create(KeyValueConfiguration.builder() .name(bucket) .storageType(StorageType.Memory) @@ -687,14 +708,15 @@ public void testPurgeDeletes() throws Exception { .build()); KeyValue kv = nc.keyValue(bucket); - kv.put(key(1), "a"); - kv.delete(key(1)); - kv.put(key(2), "b"); - kv.put(key(3), "c"); - kv.put(key(4), "d"); - kv.purge(key(4)); - - JetStream js = nc.jetStream(); + String keyA = random(); + String keyD = random(); + kv.put(keyA, "a"); + kv.delete(keyA); + kv.put(random(), "b"); + kv.put(random(), "c"); + kv.put(keyD, "d"); + kv.purge(keyD); + assertPurgeDeleteEntries(js, bucket, new String[]{"a", null, "b", "c", null}); // default purge deletes uses the default threshold @@ -734,11 +756,11 @@ private void assertPurgeDeleteEntries(JetStream js, String bucket, String[] expe @Test public void testCreateAndUpdate() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); // create bucket - String bucket = bucket(); + String bucket = random(); kvm.create(KeyValueConfiguration.builder() .name(bucket) .storageType(StorageType.Memory) @@ -747,7 +769,7 @@ public void testCreateAndUpdate() throws Exception { KeyValue kv = nc.keyValue(bucket); - String key = key(); + String key = random(); // 1. allowed to create something that does not exist long rev1 = kv.create(key, "a".getBytes()); @@ -800,7 +822,7 @@ private void assertHistory(List manualHistory, List apiHi for (int x = 0; x < apiHistory.size(); x++) { Object o = manualHistory.get(x); if (o instanceof KeyValueOperation) { - assertEquals((KeyValueOperation)o, apiHistory.get(x).getOperation()); + assertEquals(o, apiHistory.get(x).getOperation()); } else { assertKvEquals((KeyValueEntry)o, apiHistory.get(x)); @@ -816,6 +838,7 @@ private KeyValueEntry assertEntry(String bucket, String key, KeyValueOperation o assertEquals(seq, entry.getRevision()); assertEquals(0, entry.getDelta()); if (op == KeyValueOperation.PUT) { + assertNotNull(entry.getValue()); assertEquals(value, new String(entry.getValue())); } else { @@ -841,25 +864,25 @@ private void assertKvEquals(KeyValueEntry kv1, KeyValueEntry kv2) { @Test public void testManageGetBucketNamesStatuses() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); // create bucket 1 - String bucket1 = bucket(); + String bucket1 = random(); kvm.create(KeyValueConfiguration.builder() .name(bucket1) .storageType(StorageType.Memory) .build()); // create bucket 2 - String bucket2 = bucket(); + String bucket2 = random(); kvm.create(KeyValueConfiguration.builder() .name(bucket2) .storageType(StorageType.Memory) .build()); - createMemoryStream(nc, stream(1)); - createMemoryStream(nc, stream(2)); + createMemoryStream(nc, random()); + createMemoryStream(nc, random()); List infos = kvm.getStatuses(); assertEquals(2, infos.size()); @@ -1000,7 +1023,7 @@ public void testWatch() throws Exception { List allKeys = Arrays.asList(TEST_WATCH_KEY_1, TEST_WATCH_KEY_2, TEST_WATCH_KEY_NULL); - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { _testWatch(nc, key1FullWatcher, key1AllExpecteds, -1, kv -> kv.watch(TEST_WATCH_KEY_1, key1FullWatcher, key1FullWatcher.watchOptions)); _testWatch(nc, key1MetaWatcher, key1AllExpecteds, -1, kv -> kv.watch(TEST_WATCH_KEY_1, key1MetaWatcher, key1MetaWatcher.watchOptions)); _testWatch(nc, key1StartNewWatcher, key1AllExpecteds, -1, kv -> kv.watch(TEST_WATCH_KEY_1, key1StartNewWatcher, key1StartNewWatcher.watchOptions)); @@ -1037,7 +1060,7 @@ public void testWatch() throws Exception { private void _testWatch(Connection nc, TestKeyValueWatcher watcher, Object[] expectedKves, long fromRevision, TestWatchSubSupplier supplier) throws Exception { KeyValueManagement kvm = nc.keyValueManagement(); - String bucket = variant() + watcher.name + "Bucket"; + String bucket = random() + watcher.name + "Bucket"; kvm.create(KeyValueConfiguration.builder() .name(bucket) .maxHistoryPerKey(10) @@ -1085,6 +1108,7 @@ private void _testWatch(Connection nc, TestKeyValueWatcher watcher, Object[] exp if (!watcher.metaOnly) { List names = nc.jetStreamManagement().getConsumerNames("KV_" + bucket); assertEquals(1, names.size()); + assertNotNull(watcher.getConsumerNamePrefix()); assertTrue(names.get(0).startsWith(watcher.getConsumerNamePrefix())); } @@ -1145,8 +1169,8 @@ else if (expected instanceof String) { @Test public void testWithAccount() throws Exception { try (NatsTestServer ts = new NatsTestServer("src/test/resources/kv_account.conf", false)) { - Options acctA = new Options.Builder().server(ts.getURI()).userInfo("a", "a").build(); - Options acctI = new Options.Builder().server(ts.getURI()).userInfo("i", "i").inboxPrefix("ForI").build(); + Options acctA = optionsBuilder(ts).userInfo("a", "a").build(); + Options acctI = optionsBuilder(ts).userInfo("i", "i").inboxPrefix("ForI").build(); try (Connection connUserA = Nats.connect(acctA); Connection connUserI = Nats.connect(acctI)) { @@ -1165,11 +1189,11 @@ public void testWithAccount() throws Exception { KeyValueManagement kvmUserIBcktA = connUserI.keyValueManagement(jsOpt_UserI_BucketA_WithPrefix); KeyValueManagement kvmUserIBcktI = connUserI.keyValueManagement(jsOpt_UserI_BucketI_WithPrefix); - String bucketA = bucket(); + String bucketA = random(); KeyValueConfiguration kvcA = KeyValueConfiguration.builder() .name(bucketA).storageType(StorageType.Memory).maxHistoryPerKey(64).build(); - String bucketI = bucket(); + String bucketI = random(); KeyValueConfiguration kvcI = KeyValueConfiguration.builder() .name(bucketI).storageType(StorageType.Memory).maxHistoryPerKey(64).build(); @@ -1207,23 +1231,27 @@ public void testWithAccount() throws Exception { kv_connI_bucketA.watchAll(watcher_connI_BucketA); kv_connI_bucketI.watchAll(watcher_connI_BucketI); + String key11 = random(); + String key12 = random(); + String key21 = random(); + String key22 = random(); // bucket a from user a: AA, check AA, IA - assertKveAccount(kv_connA_bucketA, key(11), kv_connA_bucketA, kv_connI_bucketA); + assertKveAccount(kv_connA_bucketA, key11, kv_connA_bucketA, kv_connI_bucketA); // bucket a from user i: IA, check AA, IA - assertKveAccount(kv_connI_bucketA, key(12), kv_connA_bucketA, kv_connI_bucketA); + assertKveAccount(kv_connI_bucketA, key12, kv_connA_bucketA, kv_connI_bucketA); // bucket i from user a: AI, check AI, II - assertKveAccount(kv_connA_bucketI, key(21), kv_connA_bucketI, kv_connI_bucketI); + assertKveAccount(kv_connA_bucketI, key21, kv_connA_bucketI, kv_connI_bucketI); // bucket i from user i: II, check AI, II - assertKveAccount(kv_connI_bucketI, key(22), kv_connA_bucketI, kv_connI_bucketI); + assertKveAccount(kv_connI_bucketI, key22, kv_connA_bucketI, kv_connI_bucketI); // check keys from each kv - assertKvAccountKeys(kv_connA_bucketA.keys(), key(11), key(12)); - assertKvAccountKeys(kv_connI_bucketA.keys(), key(11), key(12)); - assertKvAccountKeys(kv_connA_bucketI.keys(), key(21), key(22)); - assertKvAccountKeys(kv_connI_bucketI.keys(), key(21), key(22)); + assertKvAccountKeys(kv_connA_bucketA.keys(), key11, key12); + assertKvAccountKeys(kv_connI_bucketA.keys(), key11, key12); + assertKvAccountKeys(kv_connA_bucketI.keys(), key21, key22); + assertKvAccountKeys(kv_connI_bucketI.keys(), key21, key22); Object[] expecteds = new Object[]{ data(0), data(1), KeyValueOperation.DELETE, KeyValueOperation.PURGE, data(2), @@ -1300,13 +1328,15 @@ private void assertKveAccountGet(KeyValue kvUserA, KeyValue kvUserI, String key, @SuppressWarnings({"SimplifiableAssertion", "ConstantConditions", "EqualsWithItself"}) @Test public void testCoverBucketAndKey() { - NatsKeyValueUtil.BucketAndKey bak1 = new NatsKeyValueUtil.BucketAndKey(DOT + BUCKET + DOT + KEY); - NatsKeyValueUtil.BucketAndKey bak2 = new NatsKeyValueUtil.BucketAndKey(DOT + BUCKET + DOT + KEY); - NatsKeyValueUtil.BucketAndKey bak3 = new NatsKeyValueUtil.BucketAndKey(DOT + bucket(1) + DOT + KEY); - NatsKeyValueUtil.BucketAndKey bak4 = new NatsKeyValueUtil.BucketAndKey(DOT + BUCKET + DOT + key(1)); - - assertEquals(BUCKET, bak1.bucket); - assertEquals(KEY, bak1.key); + String bucket = random(); + String key = random(); + NatsKeyValueUtil.BucketAndKey bak1 = new NatsKeyValueUtil.BucketAndKey(DOT + bucket + DOT + key); + NatsKeyValueUtil.BucketAndKey bak2 = new NatsKeyValueUtil.BucketAndKey(DOT + bucket + DOT + key); + NatsKeyValueUtil.BucketAndKey bak3 = new NatsKeyValueUtil.BucketAndKey(DOT + random() + DOT + key); + NatsKeyValueUtil.BucketAndKey bak4 = new NatsKeyValueUtil.BucketAndKey(DOT + bucket + DOT + random()); + + assertEquals(bucket, bak1.bucket); + assertEquals(key, bak1.key); assertEquals(bak1, bak1); assertEquals(bak1, bak2); assertEquals(bak2, bak1); @@ -1330,18 +1360,18 @@ public void testCoverPrefix() { @Test public void testKeyValueEntryEqualsImpl() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); // create bucket 1 - String bucket1 = bucket(); + String bucket1 = random(); kvm.create(KeyValueConfiguration.builder() .name(bucket1) .storageType(StorageType.Memory) .build()); // create bucket 2 - String bucket2 = bucket(); + String bucket2 = random(); kvm.create(KeyValueConfiguration.builder() .name(bucket2) .storageType(StorageType.Memory) @@ -1349,37 +1379,42 @@ public void testKeyValueEntryEqualsImpl() throws Exception { KeyValue kv1 = nc.keyValue(bucket1); KeyValue kv2 = nc.keyValue(bucket2); - kv1.put(key(1), "ONE"); - kv1.put(key(2), "TWO"); - kv2.put(key(1), "ONE"); + String key1 = random(); + String key2 = random(); + String key3 = random(); + kv1.put(key1, "ONE"); + kv1.put(key2, "TWO"); + kv2.put(key1, "ONE"); - KeyValueEntry kve1_1 = kv1.get(key(1)); - KeyValueEntry kve1_2 = kv1.get(key(2)); - KeyValueEntry kve2_1 = kv2.get(key(1)); + KeyValueEntry kve1_1 = kv1.get(key1); + KeyValueEntry kve1_2 = kv1.get(key2); + KeyValueEntry kve2_1 = kv2.get(key1); //noinspection EqualsWithItself assertEquals(kve1_1, kve1_1); - assertEquals(kve1_1, kv1.get(key(1))); + assertEquals(kve1_1, kv1.get(key1)); assertNotEquals(kve1_1, kve1_2); assertNotEquals(kve1_1, kve2_1); - kv1.put(key(1), "ONE-PRIME"); - assertNotEquals(kve1_1, kv1.get(key(1))); + kv1.put(key1, "ONE-PRIME"); + assertNotEquals(kve1_1, kv1.get(key1)); - kv1.put(key(9), (byte[]) null); - KeyValueEntry kve9 = kv1.get(key(9)); + kv1.put(key3, (byte[]) null); + KeyValueEntry kve9 = kv1.get(key3); assertNull(kve9.getValue()); assertNull(kve9.getValueAsString()); assertNull(kve9.getValueAsLong()); - kv1.put(key(9), new byte[0]); - kve9 = kv1.get(key(9)); + kv1.put(key3, new byte[0]); + kve9 = kv1.get(key3); assertNull(kve9.getValue()); assertNull(kve9.getValueAsString()); assertNull(kve9.getValueAsLong()); // coverage + //noinspection MisorderedAssertEqualsArguments assertNotEquals(kve1_1, null); + //noinspection MisorderedAssertEqualsArguments assertNotEquals(kve1_1, new Object()); }); } @@ -1449,11 +1484,11 @@ public void testKeyValuePurgeOptionsBuilderCoverage() { @Test public void testCreateDiscardPolicy() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); // create bucket - String bucket1 = bucket(); + String bucket1 = random(); KeyValueStatus status = kvm.create(KeyValueConfiguration.builder() .name(bucket1) .storageType(StorageType.Memory) @@ -1471,11 +1506,11 @@ public void testCreateDiscardPolicy() throws Exception { @Test public void testEntryCoercion() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); // create bucket - String bucket = bucket(); + String bucket = random(); kvm.create(KeyValueConfiguration.builder() .name(bucket) .storageType(StorageType.Memory) @@ -1484,7 +1519,18 @@ public void testEntryCoercion() throws Exception { KeyValue kv = nc.keyValue(bucket); kv.put("a", "a"); KeyValueEntry kve = kv.get("a"); - assertThrows(NumberFormatException.class, kve::getValueAsLong); + assertNotNull(kve); + assertNotNull(kve.getValue()); + try { + kve.getValueAsLong(); + fail(); + } + catch (NumberFormatException nfe) { + // correct! + } + catch (Exception e) { + fail(e); + } kv.delete("a"); List list = kv.history("a"); @@ -1494,7 +1540,7 @@ public void testEntryCoercion() throws Exception { } @Test - public void testKeyResultConstruction() throws Exception { + public void testKeyResultConstruction() { KeyResult r = new KeyResult(); assertNull(r.getKey()); assertNull(r.getException()); @@ -1519,21 +1565,29 @@ public void testKeyResultConstruction() throws Exception { } @Test - public void testMirrorSourceBuilderPrefixConversion() throws Exception { - String bucket = bucket(); - String name = variant(); + public void testMirrorSourceBuilderPrefixConversion() { + String bucket = random(); + String name = random(); String kvName = "KV_" + name; KeyValueConfiguration kvc = KeyValueConfiguration.builder() .name(bucket) .mirror(Mirror.builder().name(name).build()) .build(); - assertEquals(kvName, kvc.getBackingConfig().getMirror().getName()); + StreamConfiguration sc = kvc.getBackingConfig(); + assertNotNull(sc); + Mirror mirror = sc.getMirror(); + assertNotNull(mirror); + assertEquals(kvName, mirror.getName()); kvc = KeyValueConfiguration.builder() .name(bucket) .mirror(Mirror.builder().name(kvName).build()) .build(); - assertEquals(kvName, kvc.getBackingConfig().getMirror().getName()); + sc = kvc.getBackingConfig(); + assertNotNull(sc); + mirror = sc.getMirror(); + assertNotNull(mirror); + assertEquals(kvName, mirror.getName()); Source s1 = Source.builder().name("s1").build(); Source s2 = Source.builder().name("s2").build(); @@ -1557,9 +1611,13 @@ public void testMirrorSourceBuilderPrefixConversion() throws Exception { .addSources((Collection)null) .build(); - assertEquals(6, kvc.getBackingConfig().getSources().size()); + sc = kvc.getBackingConfig(); + assertNotNull(sc); + List sources = sc.getSources(); + assertNotNull(sources); + assertEquals(6, sources.size()); List names = new ArrayList<>(); - for (Source source : kvc.getBackingConfig().getSources()) { + for (Source source : sources) { names.add(source.getName()); } assertTrue(names.contains("KV_s1")); @@ -1577,8 +1635,8 @@ public void testKeyValueMirrorCrossDomains() throws Exception { KeyValueManagement leafKvm = leaf.keyValueManagement(); // Create main KV on HUB - String hubBucket = variant(); - KeyValueStatus hubStatus = hubKvm.create(KeyValueConfiguration.builder() + String hubBucket = random(); + hubKvm.create(KeyValueConfiguration.builder() .name(hubBucket) .storageType(StorageType.Memory) .build()); @@ -1589,7 +1647,7 @@ public void testKeyValueMirrorCrossDomains() throws Exception { hubKv.put("key3", "c0"); hubKv.delete("key3"); - String leafBucket = variant(); + String leafBucket = random(); String leafStream = "KV_" + leafBucket; leafKvm.create(KeyValueConfiguration.builder() .name(leafBucket) @@ -1634,6 +1692,7 @@ private void _testMirror(KeyValue okv, KeyValue mkv, int num) throws Exception { // Make sure we can create a watcher on the mirror KV. TestKeyValueWatcher mWatcher = new TestKeyValueWatcher("mirrorWatcher" + num, false); + //noinspection unused try (NatsKeyValueWatchSubscription mWatchSub = mkv.watchAll(mWatcher)) { sleep(200); // give the messages time to propagate } @@ -1642,6 +1701,7 @@ private void _testMirror(KeyValue okv, KeyValue mkv, int num) throws Exception { // Does the origin data match? if (okv != null) { TestKeyValueWatcher oWatcher = new TestKeyValueWatcher("originWatcher" + num, false); + //noinspection unused try (NatsKeyValueWatchSubscription oWatchSub = okv.watchAll(oWatcher)) { sleep(200); // give the messages time to propagate } @@ -1651,10 +1711,10 @@ private void _testMirror(KeyValue okv, KeyValue mkv, int num) throws Exception { @Test public void testKeyValueTransform() throws Exception { - jsServer.run(TestBase::atLeast2_10_3, nc -> { + runInLrServer(VersionUtils::atLeast2_10_3, (nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); - String kvName1 = variant(); + String kvName1 = random(); String kvName2 = kvName1 + "-mir"; String mirrorSegment = "MirrorMe"; String dontMirrorSegment = "DontMirrorMe"; @@ -1709,10 +1769,10 @@ public void testKeyValueTransform() throws Exception { @Test public void testSubjectFiltersAgainst209OptOut() throws Exception { - jsServer.run(TestBase::atLeast2_10, nc -> { + runInLrServer(VersionUtils::atLeast2_10, (nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); - String bucket = bucket(); + String bucket = random(); kvm.create(KeyValueConfiguration.builder() .name(bucket) .storageType(StorageType.Memory) @@ -1729,9 +1789,9 @@ public void testSubjectFiltersAgainst209OptOut() throws Exception { @Test public void testTtlAndDuplicateWindowRoundTrip() throws Exception { - jsServer.run(TestBase::atLeast2_10, nc -> { + runInLrServer(VersionUtils::atLeast2_10, (nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); - String bucket = bucket(); + String bucket = random(); KeyValueConfiguration config = KeyValueConfiguration.builder() .name(bucket) .storageType(StorageType.Memory) @@ -1750,7 +1810,7 @@ public void testTtlAndDuplicateWindowRoundTrip() throws Exception { assertNotNull(sc.getDuplicateWindow()); assertEquals(10_000, sc.getDuplicateWindow().toMillis()); - bucket = bucket(); + bucket = random(); config = KeyValueConfiguration.builder() .name(bucket) .storageType(StorageType.Memory) @@ -1768,9 +1828,9 @@ public void testTtlAndDuplicateWindowRoundTrip() throws Exception { @Test public void testConsumeKeys() throws Exception { int count = 10000; - jsServer.run(TestBase::atLeast2_10, nc -> { + runInLrServer(VersionUtils::atLeast2_10, (nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); - String bucket = bucket(); + String bucket = random(); KeyValueConfiguration config = KeyValueConfiguration.builder() .name(bucket) .storageType(StorageType.Memory) @@ -1803,9 +1863,9 @@ public void testConsumeKeys() throws Exception { @Test public void testLimitMarkerCoverage() throws Exception { - jsServer.run(TestBase::atLeast2_12, nc -> { + runInLrServer(VersionUtils::atLeast2_12, (nc, jsm, js) -> { KeyValueManagement kvm = nc.keyValueManagement(); - String bucket = bucket(); + String bucket = random(); KeyValueConfiguration config = KeyValueConfiguration.builder() .name(bucket) .storageType(StorageType.Memory) @@ -1815,7 +1875,7 @@ public void testLimitMarkerCoverage() throws Exception { assertNotNull(status.getLimitMarkerTtl()); assertEquals(1000, status.getLimitMarkerTtl().toMillis()); - String key = key(); + String key = random(); KeyValue kv = nc.keyValue(bucket); kv.create(key, dataBytes(), MessageTtl.seconds(1)); @@ -1828,7 +1888,7 @@ public void testLimitMarkerCoverage() throws Exception { assertNull(kve); config = KeyValueConfiguration.builder() - .name(bucket()) + .name(random()) .storageType(StorageType.Memory) .limitMarker(Duration.ofSeconds(2)) // coverage of duration api vs ms api .build(); @@ -1852,11 +1912,11 @@ public void testLimitMarkerCoverage() throws Exception { @Test public void testLimitMarkerBehavior() throws Exception { - jsServer.run(TestBase::atLeast2_12, nc -> { - String bucket = bucket(); - String key1 = key(); - String key2 = key(); - String key3 = key(); + runInLrServer(VersionUtils::atLeast2_12, (nc, jsm, js) -> { + String bucket = random(); + String key1 = random(); + String key2 = random(); + String key3 = random(); KeyValueManagement kvm = nc.keyValueManagement(); KeyValueConfiguration config = KeyValueConfiguration.builder() @@ -1893,6 +1953,7 @@ public void endOfData() { } }; + //noinspection unused NatsKeyValueWatchSubscription watch = kv.watchAll(watcher); AtomicInteger rMessages = new AtomicInteger(); @@ -1925,6 +1986,7 @@ public void endOfData() { }; Dispatcher d = nc.createDispatcher(); + //noinspection unused JetStreamSubscription sub = nc.jetStream().subscribe(null, d, rawHandler, true, PushSubscribeOptions.builder() .stream("KV_" + bucket) @@ -1967,13 +2029,11 @@ public void endOfData() { @Test public void testJustLimitMarkerCreatePurge() throws Exception { - jsServer.run(TestBase::atLeast2_12, nc -> { - - String bucket = bucket(); + runInLrServer(VersionUtils::atLeast2_12, (nc, jsm, js) -> { + String bucket = random(); String rawStream = "KV_" + bucket; - String key = key(); + String key = random(); - JetStreamManagement jsm = nc.jetStreamManagement(); KeyValueManagement kvm = nc.keyValueManagement(); KeyValueConfiguration config = KeyValueConfiguration.builder() .name(bucket) @@ -2023,6 +2083,7 @@ else if (mcount == 4) { } }; + //noinspection unused JetStreamSubscription sub = nc.jetStream().subscribe(null, d, rawHandler, true, PushSubscribeOptions.builder() .stream(rawStream) @@ -2077,13 +2138,11 @@ else if (mcount == 4) { @Test public void testJustTtlForDeletePurge() throws Exception { - jsServer.run(TestBase::atLeast2_12, nc -> { - - String bucket = bucket(); + runInLrServer(VersionUtils::atLeast2_12, (nc, jsm, js) -> { + String bucket = random(); String rawStream = "KV_" + bucket; - String key = key(); + String key = random(); - JetStreamManagement jsm = nc.jetStreamManagement(); KeyValueManagement kvm = nc.keyValueManagement(); KeyValueConfiguration config = KeyValueConfiguration.builder() .name(bucket) @@ -2130,6 +2189,7 @@ else if (mcount == 4) { } }; + //noinspection unused JetStreamSubscription sub = nc.jetStream().subscribe(null, d, rawHandler, true, PushSubscribeOptions.builder() .stream(rawStream) diff --git a/src/test/java/io/nats/client/impl/ListenerForTesting.java b/src/test/java/io/nats/client/impl/ListenerForTesting.java index e29a26af0..325fbf5a6 100644 --- a/src/test/java/io/nats/client/impl/ListenerForTesting.java +++ b/src/test/java/io/nats/client/impl/ListenerForTesting.java @@ -69,6 +69,10 @@ public ListenerForTesting() { this(false, false); } + public ListenerForTesting(boolean verbose) { + this(false, verbose); + } + public ListenerForTesting(boolean printExceptions, boolean verbose) { this.printExceptions = printExceptions; this.verbose = verbose; diff --git a/src/test/java/io/nats/client/impl/MessageContentTests.java b/src/test/java/io/nats/client/impl/MessageContentTests.java index 9da663992..4aad6146e 100644 --- a/src/test/java/io/nats/client/impl/MessageContentTests.java +++ b/src/test/java/io/nats/client/impl/MessageContentTests.java @@ -23,13 +23,14 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; import static org.junit.jupiter.api.Assertions.*; public class MessageContentTests { @Test public void testSimpleString() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); Connection nc = Nats.connect(ts.getURI())) { assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); @@ -51,7 +52,7 @@ public void testSimpleString() throws Exception { @Test public void testUTF8String() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); Connection nc = Nats.connect(ts.getURI())) { assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); @@ -73,7 +74,7 @@ public void testUTF8String() throws Exception { @Test public void testDifferentSizes() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); Connection nc = Nats.connect(ts.getURI())) { assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); @@ -100,7 +101,7 @@ public void testDifferentSizes() throws Exception { @Test public void testZeros() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); Connection nc = Nats.connect(ts.getURI())) { assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); @@ -202,7 +203,7 @@ void runBadContentTest(NatsServerProtocolMock.Customizer badServer, CompletableF ListenerForTesting listener = new ListenerForTesting(); try (NatsServerProtocolMock ts = new NatsServerProtocolMock(badServer, null)) { - Options options = new Options.Builder(). + Options options = optionsBuilder(). server(ts.getURI()). maxReconnects(0). errorListener(listener). diff --git a/src/test/java/io/nats/client/impl/MessageManagerTests.java b/src/test/java/io/nats/client/impl/MessageManagerTests.java index 96e8e7eff..d450d6cf0 100644 --- a/src/test/java/io/nats/client/impl/MessageManagerTests.java +++ b/src/test/java/io/nats/client/impl/MessageManagerTests.java @@ -30,6 +30,8 @@ import static io.nats.client.support.NatsConstants.NANOS_PER_MILLI; import static io.nats.client.support.NatsJetStreamConstants.CONSUMER_STALLED_HDR; import static io.nats.client.support.Status.*; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; @SuppressWarnings("SameParameterValue") @@ -37,7 +39,7 @@ public class MessageManagerTests extends JetStreamTestBase { @Test public void testConstruction() throws Exception { - runInJsServer(nc -> { + runInLrServer((nc, jsm, js) -> { NatsJetStreamSubscription sub = genericPushSub(nc); _pushConstruction(nc, true, true, push_hb_fc(), sub); _pushConstruction(nc, true, false, push_hb_xfc(), sub); @@ -51,9 +53,9 @@ private void tf(Consumer c) { } } - private void _pushConstruction(Connection conn, boolean hb, boolean fc, SubscribeOptions so, NatsJetStreamSubscription sub) { + private void _pushConstruction(Connection nc, boolean hb, boolean fc, SubscribeOptions so, NatsJetStreamSubscription sub) { tf(ordered -> tf(syncMode -> tf(queueMode -> { - PushMessageManager manager = getPushManager(conn, so, sub, ordered, syncMode, queueMode); + PushMessageManager manager = getPushManager(nc, so, sub, ordered, syncMode, queueMode); assertEquals(syncMode, manager.isSyncMode()); assertEquals(queueMode, manager.isQueueMode()); if (queueMode) { @@ -136,12 +138,13 @@ public void testPullBeforeQueueProcessorAndManage() throws Exception { runInJsServer(listener, nc -> { NatsJetStreamSubscription sub = genericPullSub(nc); + String pullSubject = random(); PullMessageManager pullMgr = getPullManager(nc, sub, true); - pullMgr.startPullRequest("pullSubject", PullRequestOptions.builder(1).build(), true, null); + pullMgr.startPullRequest(pullSubject, PullRequestOptions.builder(1).build(), true, null); testPullBqpAndManage(sub, listener, pullMgr); pullMgr = getPullManager(nc, sub, true); - pullMgr.startPullRequest("pullSubject", PullRequestOptions.builder(1).expiresIn(10000).idleHeartbeat(100).build(), true, null); + pullMgr.startPullRequest(pullSubject, PullRequestOptions.builder(1).expiresIn(10000).idleHeartbeat(100).build(), true, null); testPullBqpAndManage(sub, listener, pullMgr); }); } @@ -386,14 +389,10 @@ private void _push_xfc(SubscribeOptions so) { @Test public void test_received_time() throws Exception { - runInJsServer(nc -> { - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - - _received_time_yes(push_hb_fc(), js, tsc.subject()); - _received_time_yes(push_hb_xfc(), js, tsc.subject()); - _received_time_no(js, jsm, tsc.stream, tsc.subject(), js.subscribe(tsc.subject(), push_xhb_xfc())); + runInLrServer((nc, jstc) -> { + _received_time_yes(push_hb_fc(), jstc.js, jstc.subject()); + _received_time_yes(push_hb_xfc(), jstc.js, jstc.subject()); + _received_time_no(jstc.js, jstc.jsm, jstc.stream, jstc.subject(), jstc.js.subscribe(jstc.subject(), push_xhb_xfc())); }); } @@ -429,7 +428,7 @@ private void _received_time_no(JetStream js, JetStreamManagement jsm, String str @Test public void test_hb_yes_settings() throws Exception { - runInJsServer(nc -> { + runInLrServer((nc, jsm, js) -> { NatsJetStreamSubscription sub = genericPushSub(nc); ConsumerConfiguration cc = ConsumerConfiguration.builder().idleHeartbeat(1000).build(); @@ -462,7 +461,7 @@ public void test_hb_yes_settings() throws Exception { @Test public void test_hb_no_settings() throws Exception { - runInJsServer(nc -> { + runInLrServer((nc, jsm, js) -> { NatsJetStreamSubscription sub = genericPushSub(nc); SubscribeOptions so = push_xhb_xfc(); PushMessageManager manager = getPushManager(nc, so, sub, false); @@ -576,7 +575,7 @@ static class MockPublishInternal extends NatsConnection { String fcSubject; public MockPublishInternal() { - this(new Options.Builder().errorListener(new ErrorListener() {}).build()); + this(optionsBuilder().build()); } public MockPublishInternal(Options options) { @@ -605,8 +604,8 @@ private static NatsJetStreamSubscription genericPullSub(Connection nc) throws IO private static String genericSub(Connection nc) throws IOException, JetStreamApiException { String id = "-" + ID.incrementAndGet() + "-" + System.currentTimeMillis(); - String stream = STREAM + id; - String subject = STREAM + id; + String stream = random() + id; + String subject = random() + id; createMemoryStream(nc, stream, subject); return subject; } @@ -637,6 +636,7 @@ protected void shutdown() {} @Test public void testMessageManagerInterfaceDefaultImplCoverage() { // make a dummy connection so we can make a subscription + // notice we don't nc.connect Options options = Options.builder().build(); NatsConnection nc = new NatsConnection(options); diff --git a/src/test/java/io/nats/client/impl/MessageQueueTests.java b/src/test/java/io/nats/client/impl/MessageQueueTests.java index 6585a6cd2..de0789b52 100644 --- a/src/test/java/io/nats/client/impl/MessageQueueTests.java +++ b/src/test/java/io/nats/client/impl/MessageQueueTests.java @@ -23,7 +23,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static io.nats.client.support.NatsConstants.OUTPUT_QUEUE_IS_FULL; -import static io.nats.client.utils.TestBase.sleep; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; public class MessageQueueTests { diff --git a/src/test/java/io/nats/client/impl/NatsConnectionImplTests.java b/src/test/java/io/nats/client/impl/NatsConnectionImplTests.java index e12af837f..56ee3b61e 100644 --- a/src/test/java/io/nats/client/impl/NatsConnectionImplTests.java +++ b/src/test/java/io/nats/client/impl/NatsConnectionImplTests.java @@ -21,7 +21,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import static io.nats.client.utils.TestBase.*; +import static io.nats.client.utils.ConnectionUtils.*; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -31,16 +31,16 @@ public class NatsConnectionImplTests { public void testConnectionClosedProperly() throws Exception { try (NatsTestServer server = new NatsTestServer()) { Options options = standardOptions(server.getNatsLocalhostUri()); - verifyInternalExecutors(options, (NatsConnection)standardConnection(options)); + verifyInternalExecutors(options, (NatsConnection) standardConnectionWait(options)); // using the same options to demonstrate the executors came // from the internal factory and were not reused - verifyInternalExecutors(options, (NatsConnection)standardConnection(options)); + verifyInternalExecutors(options, (NatsConnection) standardConnectionWait(options)); // using options copied from options to demonstrate the executors // came from the internal factory and were not reused options = new Options.Builder(options).build(); - verifyInternalExecutors(options, (NatsConnection)standardConnection(options)); + verifyInternalExecutors(options, (NatsConnection) standardConnectionWait(options)); ExecutorService es = Executors.newFixedThreadPool(3); ScheduledExecutorService ses = Executors.newScheduledThreadPool(3); @@ -51,10 +51,10 @@ public void testConnectionClosedProperly() throws Exception { .executor(es) .scheduledExecutor(ses) .build(); - verifyExternalExecutors(options, (NatsConnection)standardConnection(options)); + verifyExternalExecutors(options, (NatsConnection) standardConnectionWait(options)); // same options just because - verifyExternalExecutors(options, (NatsConnection)standardConnection(options)); + verifyExternalExecutors(options, (NatsConnection) standardConnectionWait(options)); es.shutdown(); ses.shutdown(); diff --git a/src/test/java/io/nats/client/impl/NatsMessageTests.java b/src/test/java/io/nats/client/impl/NatsMessageTests.java index fe924de3f..0d0ecac52 100644 --- a/src/test/java/io/nats/client/impl/NatsMessageTests.java +++ b/src/test/java/io/nats/client/impl/NatsMessageTests.java @@ -1,392 +1,390 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.nats.client.impl; - -import io.nats.client.*; -import io.nats.client.NatsServerProtocolMock.ExitAt; -import io.nats.client.support.IncomingHeadersProcessor; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.List; - -import static io.nats.client.support.NatsConstants.OP_PING; -import static io.nats.client.support.NatsConstants.OP_PING_BYTES; -import static io.nats.client.utils.ResourceUtils.dataAsLines; -import static org.junit.jupiter.api.Assertions.*; - -public class NatsMessageTests extends JetStreamTestBase { - @Test - public void testProtocolMessage() { - NatsMessage msg = new ProtocolMessage(OP_PING_BYTES); - assertEquals(msg.getProtocolBytes().length + 2, msg.getSizeInBytes(), "Size is set, with CRLF"); - assertEquals(OP_PING_BYTES.length + 2, msg.getSizeInBytes(), "Size is correct"); - assertTrue(msg.toString().endsWith(OP_PING)); // toString COVERAGE - assertEquals(0, msg.copyNotEmptyHeaders(0, new byte[0])); // coverage for copyNotEmptyHeaders which is a no-op - } - - @Test - public void testSizeOnPublishMessage() { - byte[] body = new byte[10]; - String subject = "subj"; - String replyTo = "reply"; - String protocol = "PUB " + subject + " " + replyTo + " " + body.length; - - NatsMessage msg = new NatsMessage(subject, replyTo, body); - - assertEquals(msg.getProtocolBytes().length + body.length + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); - assertEquals(protocol.getBytes(StandardCharsets.US_ASCII).length + body.length + 4, msg.getSizeInBytes(), "Size is correct"); - - msg = new NatsMessage(subject, replyTo, body); - - assertEquals(msg.getProtocolBytes().length + body.length + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); - assertEquals(protocol.getBytes(StandardCharsets.UTF_8).length + body.length + 4, msg.getSizeInBytes(), "Size is correct"); - } - - @Test - public void testSizeOnPublishMessageWithHeaders() { - Headers headers = new Headers().add("Content-Type", "text/plain"); - byte[] body = new byte[10]; - String subject = "subj"; - String replyTo = "reply"; - String protocol = "HPUB " + subject + " " + replyTo + " " + headers.serializedLength() + " " + (headers.serializedLength() + body.length); - - NatsMessage msg = new NatsMessage(subject, replyTo, headers, body); - - assertEquals(msg.getProtocolBytes().length + headers.serializedLength() + body.length + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); - assertEquals(protocol.getBytes(StandardCharsets.US_ASCII).length + headers.serializedLength() + body.length + 4, msg.getSizeInBytes(), "Size is correct"); - - msg = new NatsMessage(subject, replyTo, headers, body); - - assertEquals(msg.getProtocolBytes().length + headers.serializedLength() + body.length + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); - assertEquals(protocol.getBytes(StandardCharsets.UTF_8).length + headers.serializedLength() + body.length + 4, msg.getSizeInBytes(), "Size is correct"); - } - - @Test - public void testSizeOnPublishMessageOnlyHeaders() { - Headers headers = new Headers().add("Content-Type", "text/plain"); - String subject = "subj"; - String replyTo = "reply"; - String protocol = "HPUB " + subject + " " + replyTo + " " + headers.serializedLength() + " " + headers.serializedLength(); - - NatsMessage msg = new NatsMessage(subject, replyTo, headers, null); - - assertEquals(msg.getProtocolBytes().length + headers.serializedLength() + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); - assertEquals(protocol.getBytes(StandardCharsets.US_ASCII).length + headers.serializedLength() + 4, msg.getSizeInBytes(), "Size is correct"); - - msg = new NatsMessage(subject, replyTo, headers, null); - - assertEquals(msg.getProtocolBytes().length + headers.serializedLength() + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); - assertEquals(protocol.getBytes(StandardCharsets.UTF_8).length + headers.serializedLength() + 4, msg.getSizeInBytes(), "Size is correct"); - } - - @Test - public void testSizeOnPublishMessageOnlySubject() { - String subject = "subj"; - String replyTo = "reply"; - String protocol = "PUB " + subject + " " + replyTo + " " + 0; - - NatsMessage msg = new NatsMessage(subject, replyTo, null, null); - - assertEquals(msg.getProtocolBytes().length + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); - assertEquals(protocol.getBytes(StandardCharsets.US_ASCII).length + 4, msg.getSizeInBytes(), "Size is correct"); - - msg = new NatsMessage(subject, replyTo, null, null); - - assertEquals(msg.getProtocolBytes().length + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); - assertEquals(protocol.getBytes(StandardCharsets.UTF_8).length + 4, msg.getSizeInBytes(), "Size is correct"); - } - - @Test - public void testCustomMaxControlLine() { - assertThrows(IllegalArgumentException.class, () -> { - byte[] body = new byte[10]; - String subject = "subject"; - int maxControlLine = 1024; - - while (subject.length() <= maxControlLine) { - subject += subject; - } - - try (NatsTestServer ts = new NatsTestServer()) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(0). - maxControlLine(maxControlLine). - build(); - Connection nc = Nats.connect(options); - standardConnectionWait(nc); - nc.request(subject, body); - } - }); - } - - @Test - public void testBigProtocolLineWithoutBody() { - assertThrows(IllegalArgumentException.class, () -> { - String subject = "subject"; - - while (subject.length() <= Options.DEFAULT_MAX_CONTROL_LINE) { - subject += subject; - } - - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT); - NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - nc.subscribe(subject); - } - }); - } - - @Test - public void testBigProtocolLineWithBody() { - assertThrows(IllegalArgumentException.class, () -> { - byte[] body = new byte[10]; - String subject = "subject"; - String replyTo = "reply"; - - while (subject.length() <= Options.DEFAULT_MAX_CONTROL_LINE) { - subject += subject; - } - - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT); - NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { - standardConnectionWait(nc); - nc.publish(subject, replyTo, body); - } - }); - } - - - @Test - public void notJetStream() throws Exception { - NatsMessage m = testMessage(); - m.ack(); - m.ackSync(Duration.ZERO); - m.nak(); - m.nakWithDelay(Duration.ZERO); - m.nakWithDelay(0); - m.inProgress(); - m.term(); - assertThrows(IllegalStateException.class, m::metaData); - } - - @Test - public void miscCoverage() { - //noinspection deprecation - NatsMessage m = NatsMessage.builder() - .subject("test").replyTo("reply").utf8mode(true) - .data("data", StandardCharsets.US_ASCII) - .build(); - assertFalse(m.hasHeaders()); - assertFalse(m.isJetStream()); - assertFalse(m.isStatusMessage()); - assertNotNull(m.toString()); - assertNotNull(m.toDetailString()); - assertFalse(m.isProtocol()); - assertFalse(m.isProtocolFilterOnStop()); - - m = NatsMessage.builder() - .subject("test").replyTo("reply") - .data("very long data to string truncates with dot dot dot", StandardCharsets.US_ASCII) - .build(); - assertNotNull(m.toString()); - assertTrue(m.toString().contains("...")); - - // no reply to, no data - m = NatsMessage.builder().subject("test").build(); - assertNotNull(m.toString()); - assertNotNull(m.toDetailString()); - - // no reply to, no empty data - m = NatsMessage.builder().subject("test").data(new byte[0]).build(); - assertNotNull(m.toString()); - assertNotNull(m.toDetailString()); - - // no reply to, no empty data - m = NatsMessage.builder().subject("test").data((byte[])null).build(); - assertNotNull(m.toString()); - assertNotNull(m.toDetailString()); - - // no reply to, no empty data - m = NatsMessage.builder().subject("test").data((String)null).build(); - assertNotNull(m.toString()); - assertNotNull(m.toDetailString()); - - List data = dataAsLines("utf8-test-strings.txt"); - for (String d : data) { - Message m1 = NatsMessage.builder().subject("test").data(d).build(); - Message m2 = NatsMessage.builder().subject("test").data(d, StandardCharsets.UTF_8).build(); - assertByteArraysEqual(m1.getData(), m2.getData()); - } - - m = testMessage(); - assertTrue(m.hasHeaders()); - assertNotNull(m.getHeaders()); - assertFalse(m.isUtf8mode()); // coverage, ALWAYS FALSE SINCE DISUSED - assertFalse(m.getHeaders().isEmpty()); - assertNull(m.getSubscription()); - assertNull(m.getNatsSubscription()); - assertNull(m.getConnection()); - assertEquals(23, m.getControlLineLength()); - assertNotNull(m.toDetailString()); // COVERAGE - - m.headers = null; // we can do this because we have package access - assertFalse(m.hasHeaders()); - assertNull(m.getHeaders()); - assertNotNull(m.toString()); // COVERAGE - - ProtocolMessage pmFilterOnStop = new ProtocolMessage(new byte[0]); - ProtocolMessage pmNotFilterOnStop = new ProtocolMessage(pmFilterOnStop.getProtocolBab(), false); - - validateProto(pmFilterOnStop, true); - validateProto(pmNotFilterOnStop, false); - - // retains filter on stop - validateProto(new ProtocolMessage(pmFilterOnStop), true); - validateProto(new ProtocolMessage(pmNotFilterOnStop), false); - - // sets filter on stop - validateProto(new ProtocolMessage(pmFilterOnStop.getProtocolBab(), true), true); - validateProto(new ProtocolMessage(pmFilterOnStop.getProtocolBab(), false), false); - validateProto(new ProtocolMessage(pmNotFilterOnStop.getProtocolBab(), true), true); - validateProto(new ProtocolMessage(pmNotFilterOnStop.getProtocolBab(), false), false); - - IncomingMessage scm = new IncomingMessage() {}; - assertEquals(0, scm.getSizeInBytes()); - assertThrows(IllegalStateException.class, scm::getProtocolBab); - assertThrows(IllegalStateException.class, scm::getProtocolBytes); - assertThrows(IllegalStateException.class, scm::getControlLineLength); - assertFalse(scm.isProtocol()); - assertFalse(scm.isProtocolFilterOnStop()); - - // coverage coverage coverage - //noinspection deprecation - NatsMessage nmCov = new NatsMessage("sub", "reply", null, true); - nmCov.calculate(); - - assertTrue(nmCov.toDetailString().contains("PUB sub reply 0")); - } - - private static void validateProto(ProtocolMessage pm, boolean isProtocolFilterOnStop) { - assertNotNull(pm.getProtocolBab()); - assertEquals(0, pm.getProtocolBab().length()); - assertEquals(2, pm.getSizeInBytes()); - assertEquals(2, pm.getControlLineLength()); - assertTrue(pm.isProtocol()); - assertEquals(isProtocolFilterOnStop, pm.isProtocolFilterOnStop()); - } - - @Test - public void constructorWithMessage() { - NatsMessage m = testMessage(); - - NatsMessage copy = new NatsMessage(m); - assertEquals(m.getSubject(), copy.getSubject()); - assertEquals(m.getReplyTo(), copy.getReplyTo()); - assertEquals(m.getData(), copy.getData()); - assertEquals(m.getSubject(), copy.getSubject()); - assertEquals(m.getSubject(), copy.getSubject()); - } - - @Test - public void testFactoryProducesStatusMessage() { - IncomingHeadersProcessor incomingHeadersProcessor = - new IncomingHeadersProcessor("NATS/1.0 503 No Responders\r\n".getBytes()); - IncomingMessageFactory factory = - new IncomingMessageFactory("sid", "subj", "replyTo", 0, false); - factory.setHeaders(incomingHeadersProcessor); - factory.setData(null); // coverage - - Message m = factory.getMessage(); - assertTrue(m.isStatusMessage()); - assertNotNull(m.getStatus()); - assertEquals(503, m.getStatus().getCode()); - assertNotNull(m.getStatus().toString()); - StatusMessage sm = (StatusMessage)m; - assertNotNull(sm.toString()); - } - - private NatsMessage testMessage() { - Headers h = new Headers(); - h.add("key", "value"); - - return NatsMessage.builder() - .subject("test").replyTo("reply").headers(h) - .data("data", StandardCharsets.US_ASCII) - .build(); - } - - @Test - public void testHeadersMutableBeforePublish() throws Exception { - jsServer.run(connection -> { - String subject = subject(); - Subscription sub = connection.subscribe(subject); - - Headers h = new Headers(); - h.put("one", "A"); - Message m = new NatsMessage(subject, null, h, null); - connection.publish(m); - Message incoming = sub.nextMessage(1000); - assertEquals(1, incoming.getHeaders().size()); - - // headers are no longer copied, just referenced - // so this will affect the message which is the same - // as the local copy - h.put("two", "B"); - connection.publish(m); - incoming = sub.nextMessage(1000); - assertEquals(2, incoming.getHeaders().size()); - - // also if you get the headers from the message - m.getHeaders().put("three", "C"); - connection.publish(m); - incoming = sub.nextMessage(1000); - assertEquals(3, incoming.getHeaders().size()); - }); - } - - @Test - public void testNatsPublishableMessageHeadersCoverage() { - byte[] data = new byte[0]; - - NatsPublishableMessage npm = new NatsPublishableMessage("subject", "replyTo", null, data, false, false); - assertFalse(npm.hasHeaders); - assertNull(npm.getHeaders()); - - Headers h = new Headers(); - assertTrue(h.isEmpty()); - assertFalse(h.isReadOnly()); - npm = new NatsPublishableMessage("subject", "replyTo", h, data, false, false); - assertFalse(npm.hasHeaders); - assertNull(npm.getHeaders()); - - h.put("foo", "bar"); - assertFalse(h.isEmpty()); - assertFalse(h.isReadOnly()); - npm = new NatsPublishableMessage("subject", "replyTo", h, data, false, false); - assertTrue(npm.hasHeaders); - assertFalse(npm.getHeaders().isEmpty()); - assertTrue(npm.getHeaders().isReadOnly()); - - h = new Headers(h, true); - assertFalse(h.isEmpty()); - assertTrue(h.isReadOnly()); - npm = new NatsPublishableMessage("subject", "replyTo", h, data, false, false); - assertTrue(npm.hasHeaders); - assertFalse(npm.getHeaders().isEmpty()); - assertTrue(npm.getHeaders().isReadOnly()); - } +// Copyright 2015-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client.impl; + +import io.nats.client.*; +import io.nats.client.NatsServerProtocolMock.ExitAt; +import io.nats.client.support.IncomingHeadersProcessor; +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.List; + +import static io.nats.client.support.NatsConstants.OP_PING; +import static io.nats.client.support.NatsConstants.OP_PING_BYTES; +import static io.nats.client.utils.ConnectionUtils.standardConnectionWait; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.ResourceUtils.dataAsLines; +import static org.junit.jupiter.api.Assertions.*; + +public class NatsMessageTests extends JetStreamTestBase { + @Test + public void testProtocolMessage() { + NatsMessage msg = new ProtocolMessage(OP_PING_BYTES); + assertEquals(msg.getProtocolBytes().length + 2, msg.getSizeInBytes(), "Size is set, with CRLF"); + assertEquals(OP_PING_BYTES.length + 2, msg.getSizeInBytes(), "Size is correct"); + assertTrue(msg.toString().endsWith(OP_PING)); // toString COVERAGE + assertEquals(0, msg.copyNotEmptyHeaders(0, new byte[0])); // coverage for copyNotEmptyHeaders which is a no-op + } + + @Test + public void testSizeOnPublishMessage() { + byte[] body = new byte[10]; + String subject = "subj"; + String replyTo = "reply"; + String protocol = "PUB " + subject + " " + replyTo + " " + body.length; + + NatsMessage msg = new NatsMessage(subject, replyTo, body); + + assertEquals(msg.getProtocolBytes().length + body.length + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); + assertEquals(protocol.getBytes(StandardCharsets.US_ASCII).length + body.length + 4, msg.getSizeInBytes(), "Size is correct"); + + msg = new NatsMessage(subject, replyTo, body); + + assertEquals(msg.getProtocolBytes().length + body.length + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); + assertEquals(protocol.getBytes(StandardCharsets.UTF_8).length + body.length + 4, msg.getSizeInBytes(), "Size is correct"); + } + + @Test + public void testSizeOnPublishMessageWithHeaders() { + Headers headers = new Headers().add("Content-Type", "text/plain"); + byte[] body = new byte[10]; + String subject = "subj"; + String replyTo = "reply"; + String protocol = "HPUB " + subject + " " + replyTo + " " + headers.serializedLength() + " " + (headers.serializedLength() + body.length); + + NatsMessage msg = new NatsMessage(subject, replyTo, headers, body); + + assertEquals(msg.getProtocolBytes().length + headers.serializedLength() + body.length + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); + assertEquals(protocol.getBytes(StandardCharsets.US_ASCII).length + headers.serializedLength() + body.length + 4, msg.getSizeInBytes(), "Size is correct"); + + msg = new NatsMessage(subject, replyTo, headers, body); + + assertEquals(msg.getProtocolBytes().length + headers.serializedLength() + body.length + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); + assertEquals(protocol.getBytes(StandardCharsets.UTF_8).length + headers.serializedLength() + body.length + 4, msg.getSizeInBytes(), "Size is correct"); + } + + @Test + public void testSizeOnPublishMessageOnlyHeaders() { + Headers headers = new Headers().add("Content-Type", "text/plain"); + String subject = "subj"; + String replyTo = "reply"; + String protocol = "HPUB " + subject + " " + replyTo + " " + headers.serializedLength() + " " + headers.serializedLength(); + + NatsMessage msg = new NatsMessage(subject, replyTo, headers, null); + + assertEquals(msg.getProtocolBytes().length + headers.serializedLength() + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); + assertEquals(protocol.getBytes(StandardCharsets.US_ASCII).length + headers.serializedLength() + 4, msg.getSizeInBytes(), "Size is correct"); + + msg = new NatsMessage(subject, replyTo, headers, null); + + assertEquals(msg.getProtocolBytes().length + headers.serializedLength() + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); + assertEquals(protocol.getBytes(StandardCharsets.UTF_8).length + headers.serializedLength() + 4, msg.getSizeInBytes(), "Size is correct"); + } + + @Test + public void testSizeOnPublishMessageOnlySubject() { + String subject = "subj"; + String replyTo = "reply"; + String protocol = "PUB " + subject + " " + replyTo + " " + 0; + + NatsMessage msg = new NatsMessage(subject, replyTo, null, null); + + assertEquals(msg.getProtocolBytes().length + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); + assertEquals(protocol.getBytes(StandardCharsets.US_ASCII).length + 4, msg.getSizeInBytes(), "Size is correct"); + + msg = new NatsMessage(subject, replyTo, null, null); + + assertEquals(msg.getProtocolBytes().length + 4, msg.getSizeInBytes(), "Size is set, with CRLF"); + assertEquals(protocol.getBytes(StandardCharsets.UTF_8).length + 4, msg.getSizeInBytes(), "Size is correct"); + } + + @Test + public void testCustomMaxControlLine() { + assertThrows(IllegalArgumentException.class, () -> { + byte[] body = new byte[10]; + String subject = "subject"; + int maxControlLine = 1024; + + while (subject.length() <= maxControlLine) { + subject += subject; + } + + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts).maxReconnects(0).maxControlLine(maxControlLine).build(); + Connection nc = Nats.connect(options); + standardConnectionWait(nc); + nc.request(subject, body); + } + }); + } + + @Test + public void testBigProtocolLineWithoutBody() { + assertThrows(IllegalArgumentException.class, () -> { + String subject = "subject"; + + while (subject.length() <= Options.DEFAULT_MAX_CONTROL_LINE) { + subject += subject; + } + + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT); + NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { + standardConnectionWait(nc); + nc.subscribe(subject); + } + }); + } + + @Test + public void testBigProtocolLineWithBody() { + assertThrows(IllegalArgumentException.class, () -> { + byte[] body = new byte[10]; + String subject = "subject"; + String replyTo = "reply"; + + while (subject.length() <= Options.DEFAULT_MAX_CONTROL_LINE) { + subject += subject; + } + + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT); + NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { + standardConnectionWait(nc); + nc.publish(subject, replyTo, body); + } + }); + } + + + @Test + public void notJetStream() throws Exception { + NatsMessage m = testMessage(); + m.ack(); + m.ackSync(Duration.ZERO); + m.nak(); + m.nakWithDelay(Duration.ZERO); + m.nakWithDelay(0); + m.inProgress(); + m.term(); + assertThrows(IllegalStateException.class, m::metaData); + } + + @Test + public void miscCoverage() { + //noinspection deprecation + NatsMessage m = NatsMessage.builder() + .subject("test").replyTo("reply").utf8mode(true) + .data("data", StandardCharsets.US_ASCII) + .build(); + assertFalse(m.hasHeaders()); + assertFalse(m.isJetStream()); + assertFalse(m.isStatusMessage()); + assertNotNull(m.toString()); + assertNotNull(m.toDetailString()); + assertFalse(m.isProtocol()); + assertFalse(m.isProtocolFilterOnStop()); + + m = NatsMessage.builder() + .subject("test").replyTo("reply") + .data("very long data to string truncates with dot dot dot", StandardCharsets.US_ASCII) + .build(); + assertNotNull(m.toString()); + assertTrue(m.toString().contains("...")); + + // no reply to, no data + m = NatsMessage.builder().subject("test").build(); + assertNotNull(m.toString()); + assertNotNull(m.toDetailString()); + + // no reply to, no empty data + m = NatsMessage.builder().subject("test").data(new byte[0]).build(); + assertNotNull(m.toString()); + assertNotNull(m.toDetailString()); + + // no reply to, no empty data + m = NatsMessage.builder().subject("test").data((byte[])null).build(); + assertNotNull(m.toString()); + assertNotNull(m.toDetailString()); + + // no reply to, no empty data + m = NatsMessage.builder().subject("test").data((String)null).build(); + assertNotNull(m.toString()); + assertNotNull(m.toDetailString()); + + List data = dataAsLines("utf8-test-strings.txt"); + for (String d : data) { + Message m1 = NatsMessage.builder().subject("test").data(d).build(); + Message m2 = NatsMessage.builder().subject("test").data(d, StandardCharsets.UTF_8).build(); + assertByteArraysEqual(m1.getData(), m2.getData()); + } + + m = testMessage(); + assertTrue(m.hasHeaders()); + assertNotNull(m.getHeaders()); + assertFalse(m.isUtf8mode()); // coverage, ALWAYS FALSE SINCE DISUSED + assertFalse(m.getHeaders().isEmpty()); + assertNull(m.getSubscription()); + assertNull(m.getNatsSubscription()); + assertNull(m.getConnection()); + assertEquals(23, m.getControlLineLength()); + assertNotNull(m.toDetailString()); // COVERAGE + + m.headers = null; // we can do this because we have package access + assertFalse(m.hasHeaders()); + assertNull(m.getHeaders()); + assertNotNull(m.toString()); // COVERAGE + + ProtocolMessage pmFilterOnStop = new ProtocolMessage(new byte[0]); + ProtocolMessage pmNotFilterOnStop = new ProtocolMessage(pmFilterOnStop.getProtocolBab(), false); + + validateProto(pmFilterOnStop, true); + validateProto(pmNotFilterOnStop, false); + + // retains filter on stop + validateProto(new ProtocolMessage(pmFilterOnStop), true); + validateProto(new ProtocolMessage(pmNotFilterOnStop), false); + + // sets filter on stop + validateProto(new ProtocolMessage(pmFilterOnStop.getProtocolBab(), true), true); + validateProto(new ProtocolMessage(pmFilterOnStop.getProtocolBab(), false), false); + validateProto(new ProtocolMessage(pmNotFilterOnStop.getProtocolBab(), true), true); + validateProto(new ProtocolMessage(pmNotFilterOnStop.getProtocolBab(), false), false); + + IncomingMessage scm = new IncomingMessage() {}; + assertEquals(0, scm.getSizeInBytes()); + assertThrows(IllegalStateException.class, scm::getProtocolBab); + assertThrows(IllegalStateException.class, scm::getProtocolBytes); + assertThrows(IllegalStateException.class, scm::getControlLineLength); + assertFalse(scm.isProtocol()); + assertFalse(scm.isProtocolFilterOnStop()); + + // coverage coverage coverage + //noinspection deprecation + NatsMessage nmCov = new NatsMessage("sub", "reply", null, true); + nmCov.calculate(); + + assertTrue(nmCov.toDetailString().contains("PUB sub reply 0")); + } + + private static void validateProto(ProtocolMessage pm, boolean isProtocolFilterOnStop) { + assertNotNull(pm.getProtocolBab()); + assertEquals(0, pm.getProtocolBab().length()); + assertEquals(2, pm.getSizeInBytes()); + assertEquals(2, pm.getControlLineLength()); + assertTrue(pm.isProtocol()); + assertEquals(isProtocolFilterOnStop, pm.isProtocolFilterOnStop()); + } + + @Test + public void constructorWithMessage() { + NatsMessage m = testMessage(); + + NatsMessage copy = new NatsMessage(m); + assertEquals(m.getSubject(), copy.getSubject()); + assertEquals(m.getReplyTo(), copy.getReplyTo()); + assertEquals(m.getData(), copy.getData()); + assertEquals(m.getSubject(), copy.getSubject()); + assertEquals(m.getSubject(), copy.getSubject()); + } + + @Test + public void testFactoryProducesStatusMessage() { + IncomingHeadersProcessor incomingHeadersProcessor = + new IncomingHeadersProcessor("NATS/1.0 503 No Responders\r\n".getBytes()); + IncomingMessageFactory factory = + new IncomingMessageFactory("sid", "subj", "replyTo", 0, false); + factory.setHeaders(incomingHeadersProcessor); + factory.setData(null); // coverage + + Message m = factory.getMessage(); + assertTrue(m.isStatusMessage()); + assertNotNull(m.getStatus()); + assertEquals(503, m.getStatus().getCode()); + assertNotNull(m.getStatus().toString()); + StatusMessage sm = (StatusMessage)m; + assertNotNull(sm.toString()); + } + + private NatsMessage testMessage() { + Headers h = new Headers(); + h.add("key", "value"); + + return NatsMessage.builder() + .subject("test").replyTo("reply").headers(h) + .data("data", StandardCharsets.US_ASCII) + .build(); + } + + @Test + public void testHeadersMutableBeforePublish() throws Exception { + runInLrServer(nc -> { + String subject = random(); + Subscription sub = nc.subscribe(subject); + + Headers h = new Headers(); + h.put("one", "A"); + Message m = new NatsMessage(subject, null, h, null); + nc.publish(m); + Message incoming = sub.nextMessage(1000); + assertEquals(1, incoming.getHeaders().size()); + + // headers are no longer copied, just referenced + // so this will affect the message which is the same + // as the local copy + h.put("two", "B"); + nc.publish(m); + incoming = sub.nextMessage(1000); + assertEquals(2, incoming.getHeaders().size()); + + // also if you get the headers from the message + m.getHeaders().put("three", "C"); + nc.publish(m); + incoming = sub.nextMessage(1000); + assertEquals(3, incoming.getHeaders().size()); + }); + } + + @Test + public void testNatsPublishableMessageHeadersCoverage() { + byte[] data = new byte[0]; + + NatsPublishableMessage npm = new NatsPublishableMessage("subject", "replyTo", null, data, false, false); + assertFalse(npm.hasHeaders); + assertNull(npm.getHeaders()); + + Headers h = new Headers(); + assertTrue(h.isEmpty()); + assertFalse(h.isReadOnly()); + npm = new NatsPublishableMessage("subject", "replyTo", h, data, false, false); + assertFalse(npm.hasHeaders); + assertNull(npm.getHeaders()); + + h.put("foo", "bar"); + assertFalse(h.isEmpty()); + assertFalse(h.isReadOnly()); + npm = new NatsPublishableMessage("subject", "replyTo", h, data, false, false); + assertTrue(npm.hasHeaders); + assertFalse(npm.getHeaders().isEmpty()); + assertTrue(npm.getHeaders().isReadOnly()); + + h = new Headers(h, true); + assertFalse(h.isEmpty()); + assertTrue(h.isReadOnly()); + npm = new NatsPublishableMessage("subject", "replyTo", h, data, false, false); + assertTrue(npm.hasHeaders); + assertFalse(npm.getHeaders().isEmpty()); + assertTrue(npm.getHeaders().isReadOnly()); + } } \ No newline at end of file diff --git a/src/test/java/io/nats/client/impl/NatsStatisticsTests.java b/src/test/java/io/nats/client/impl/NatsStatisticsTests.java index f98115c4a..58dd82d6c 100644 --- a/src/test/java/io/nats/client/impl/NatsStatisticsTests.java +++ b/src/test/java/io/nats/client/impl/NatsStatisticsTests.java @@ -13,7 +13,11 @@ package io.nats.client.impl; -import io.nats.client.*; +import io.nats.client.Dispatcher; +import io.nats.client.Message; +import io.nats.client.MessageHandler; +import io.nats.client.Statistics; +import io.nats.client.utils.TestBase; import org.junit.jupiter.api.Test; import java.time.Duration; @@ -21,20 +25,14 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static io.nats.client.utils.TestBase.*; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; -public class NatsStatisticsTests { +public class NatsStatisticsTests extends TestBase { @Test public void testHumanReadableString() throws Exception { - // This test is purely for coverage, any test without a human is likely pedantic - try (NatsTestServer ts = new NatsTestServer(); - Connection nc = Nats.connect(new Options.Builder() - .server(ts.getURI()) - .turnOnAdvancedStats() - .build())) { - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - + runInLrServer(optionsBuilder().turnOnAdvancedStats(), nc -> { Dispatcher d = nc.createDispatcher((msg) -> { nc.publish(msg.getReplyTo(), new byte[16]); }); @@ -51,164 +49,135 @@ public void testHumanReadableString() throws Exception { assertTrue(str.length() > 0); assertTrue(str.contains("### Connection ###")); assertTrue(str.contains("Socket Writes")); - } + }); } @Test public void testInOutOKRequestStats() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()).verbose().build(); - Connection nc = Nats.connect(options); - StatisticsCollector stats = ((NatsConnection) nc).getStatisticsCollector(); - - try { - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - - Dispatcher d = nc.createDispatcher((msg) -> { - Message m = NatsMessage.builder() - .subject(msg.getReplyTo()) - .data(new byte[16]) - .headers(new Headers().put("header", "reply")) - .build(); - nc.publish(m); - }); - d.subscribe("subject"); - + runInLrServer(optionsBuilder().verbose(), nc -> { + Dispatcher d = nc.createDispatcher((msg) -> { Message m = NatsMessage.builder() - .subject("subject") - .data(new byte[8]) - .headers(new Headers().put("header", "request")) + .subject(msg.getReplyTo()) + .data(new byte[16]) + .headers(new Headers().put("header", "reply")) .build(); - Future incoming = nc.request(m); - Message msg = incoming.get(500, TimeUnit.MILLISECONDS); - - assertNotNull(msg); - assertEquals(0, stats.getOutstandingRequests(), "outstanding"); - assertTrue(stats.getInBytes() > 200, "bytes in"); - assertTrue(stats.getOutBytes() > 400, "bytes out"); - assertEquals(2, stats.getInMsgs(), "messages in"); // reply & request - assertEquals(6, stats.getOutMsgs(), "messages out"); // ping, sub, pub, msg, pub, msg - assertEquals(5, stats.getOKs(), "oks"); //sub, pub, msg, pub, msg - } finally { - nc.close(); - } - } + nc.publish(m); + }); + d.subscribe("subject"); + + Message m = NatsMessage.builder() + .subject("subject") + .data(new byte[8]) + .headers(new Headers().put("header", "request")) + .build(); + Future incoming = nc.request(m); + Message msg = incoming.get(500, TimeUnit.MILLISECONDS); + + assertNotNull(msg); + Statistics stats = nc.getStatistics(); + assertEquals(0, stats.getOutstandingRequests(), "outstanding"); + assertTrue(stats.getInBytes() > 200, "bytes in"); + assertTrue(stats.getOutBytes() > 400, "bytes out"); + assertEquals(2, stats.getInMsgs(), "messages in"); // reply & request + assertEquals(6, stats.getOutMsgs(), "messages out"); // ping, sub, pub, msg, pub, msg + assertEquals(5, stats.getOKs(), "oks"); //sub, pub, msg, pub, msg + }); } @Test public void testReadWriteAdvancedStatsEnabled() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()).verbose().turnOnAdvancedStats().build(); - Connection nc = Nats.connect(options); - StatisticsCollector stats = ((NatsConnection) nc).getStatisticsCollector(); - - try { - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - - Dispatcher d = nc.createDispatcher((msg) -> { - Message m = NatsMessage.builder() - .subject(msg.getReplyTo()) - .data(new byte[16]) - .headers(new Headers().put("header", "reply")) - .build(); - nc.publish(m); - }); - d.subscribe("subject"); - + runInLrServer(optionsBuilder().verbose().turnOnAdvancedStats(), nc -> { + Dispatcher d = nc.createDispatcher((msg) -> { Message m = NatsMessage.builder() - .subject("subject") - .data(new byte[8]) - .headers(new Headers().put("header", "request")) - .build(); - Future incoming = nc.request(m); - Message msg = incoming.get(500, TimeUnit.MILLISECONDS); - - assertNotNull(msg); - - // The read/write advanced stats are only exposed via toString, so assert on that - String stringStats = stats.toString(); - assertTrue(stringStats.contains("Socket Reads"), "readStats count"); - assertTrue(stringStats.contains("Average Bytes Per Read"), "readStats average bytes"); - assertTrue(stringStats.contains("Min Bytes Per Read"), "readStats min bytes"); - assertTrue(stringStats.contains("Max Bytes Per Read"), "readStats max bytes"); - - assertTrue(stringStats.contains("Socket Writes"), "writeStats count"); - assertTrue(stringStats.contains("Average Bytes Per Write"), "writeStats average bytes"); - assertTrue(stringStats.contains("Min Bytes Per Write"), "writeStats min bytes"); - assertTrue(stringStats.contains("Max Bytes Per Write"), "writeStats max bytes"); - } finally { - nc.close(); - } - } + .subject(msg.getReplyTo()) + .data(new byte[16]) + .headers(new Headers().put("header", "reply")) + .build(); + nc.publish(m); + }); + d.subscribe("subject"); + + Message m = NatsMessage.builder() + .subject("subject") + .data(new byte[8]) + .headers(new Headers().put("header", "request")) + .build(); + Future incoming = nc.request(m); + Message msg = incoming.get(500, TimeUnit.MILLISECONDS); + + assertNotNull(msg); + + // The read/write advanced stats are only exposed via toString, so assert on that + Statistics stats = nc.getStatistics(); + String stringStats = stats.toString(); + assertTrue(stringStats.contains("Socket Reads"), "readStats count"); + assertTrue(stringStats.contains("Average Bytes Per Read"), "readStats average bytes"); + assertTrue(stringStats.contains("Min Bytes Per Read"), "readStats min bytes"); + assertTrue(stringStats.contains("Max Bytes Per Read"), "readStats max bytes"); + + assertTrue(stringStats.contains("Socket Writes"), "writeStats count"); + assertTrue(stringStats.contains("Average Bytes Per Write"), "writeStats average bytes"); + assertTrue(stringStats.contains("Min Bytes Per Write"), "writeStats min bytes"); + assertTrue(stringStats.contains("Max Bytes Per Write"), "writeStats max bytes"); + }); } @Test public void testReadWriteAdvancedStatsDisabled() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()).verbose().build(); - Connection nc = Nats.connect(options); - StatisticsCollector stats = ((NatsConnection) nc).getStatisticsCollector(); - - try { - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - - Dispatcher d = nc.createDispatcher((msg) -> { - Message m = NatsMessage.builder() - .subject(msg.getReplyTo()) - .data(new byte[16]) - .headers(new Headers().put("header", "reply")) - .build(); - nc.publish(m); - }); - d.subscribe("subject"); - + runInLrServer(optionsBuilder().verbose(), nc -> { + Dispatcher d = nc.createDispatcher((msg) -> { Message m = NatsMessage.builder() - .subject("subject") - .data(new byte[8]) - .headers(new Headers().put("header", "request")) - .build(); - Future incoming = nc.request(m); - Message msg = incoming.get(500, TimeUnit.MILLISECONDS); + .subject(msg.getReplyTo()) + .data(new byte[16]) + .headers(new Headers().put("header", "reply")) + .build(); + nc.publish(m); + }); + d.subscribe("subject"); - assertNotNull(msg); + Message m = NatsMessage.builder() + .subject("subject") + .data(new byte[8]) + .headers(new Headers().put("header", "request")) + .build(); + Future incoming = nc.request(m); + Message msg = incoming.get(500, TimeUnit.MILLISECONDS); - incoming = nc.request(m); - incoming.get(500, TimeUnit.MILLISECONDS); + assertNotNull(msg); - assertNotNull(msg); + incoming = nc.request(m); + incoming.get(500, TimeUnit.MILLISECONDS); - incoming = nc.request(m); - incoming.get(500, TimeUnit.MILLISECONDS); + assertNotNull(msg); - assertNotNull(msg); + incoming = nc.request(m); + incoming.get(500, TimeUnit.MILLISECONDS); - incoming = nc.request(m); - incoming.get(500, TimeUnit.MILLISECONDS); + assertNotNull(msg); - assertNotNull(msg); + incoming = nc.request(m); + incoming.get(500, TimeUnit.MILLISECONDS); - // The read/write advanced stats are only exposed via toString, so assert on that - String stringStats = stats.toString(); - assertFalse(stringStats.contains("Socket Reads"), "readStats count"); - assertFalse(stringStats.contains("Average Bytes Per Read"), "readStats average bytes"); - assertFalse(stringStats.contains("Min Bytes Per Read"), "readStats min bytes"); - assertFalse(stringStats.contains("Max Bytes Per Read"), "readStats max bytes"); + assertNotNull(msg); - assertFalse(stringStats.contains("Socket Writes"), "writeStats count"); - assertFalse(stringStats.contains("Average Bytes Per Write"), "writeStats average bytes"); - assertFalse(stringStats.contains("Min Bytes Per Write"), "writeStats min bytes"); - assertFalse(stringStats.contains("Max Bytes Per Write"), "writeStats max bytes"); - } finally { - nc.close(); - } - } + // The read/write advanced stats are only exposed via toString, so assert on that + Statistics stats = nc.getStatistics(); + String stringStats = stats.toString(); + assertFalse(stringStats.contains("Socket Reads"), "readStats count"); + assertFalse(stringStats.contains("Average Bytes Per Read"), "readStats average bytes"); + assertFalse(stringStats.contains("Min Bytes Per Read"), "readStats min bytes"); + assertFalse(stringStats.contains("Max Bytes Per Read"), "readStats max bytes"); + + assertFalse(stringStats.contains("Socket Writes"), "writeStats count"); + assertFalse(stringStats.contains("Average Bytes Per Write"), "writeStats average bytes"); + assertFalse(stringStats.contains("Min Bytes Per Write"), "writeStats min bytes"); + assertFalse(stringStats.contains("Max Bytes Per Write"), "writeStats max bytes"); + }); } @Test public void testOrphanDuplicateRepliesAdvancedStatsEnabled() throws Exception { - Options.Builder builder = new Options.Builder().turnOnAdvancedStats(); - - runInServer(builder, nc -> { + runInLrServer(optionsBuilder().turnOnAdvancedStats(), nc -> { AtomicInteger requests = new AtomicInteger(); MessageHandler handler = (msg) -> { requests.incrementAndGet(); @@ -221,12 +190,13 @@ public void testOrphanDuplicateRepliesAdvancedStatsEnabled() throws Exception { sleep(5000); handler.onMessage(msg); }); - d1.subscribe(SUBJECT); - d2.subscribe(SUBJECT); - d3.subscribe(SUBJECT); - d4.subscribe(SUBJECT); + String subject = TestBase.random(); + d1.subscribe(subject); + d2.subscribe(subject); + d3.subscribe(subject); + d4.subscribe(subject); - Message reply = nc.request(SUBJECT, null, Duration.ofSeconds(2)); + Message reply = nc.request(subject, null, Duration.ofSeconds(2)); assertNotNull(reply); sleep(2000); assertEquals(3, requests.get()); @@ -250,9 +220,7 @@ public void testOrphanDuplicateRepliesAdvancedStatsEnabled() throws Exception { @Test public void testOrphanDuplicateRepliesAdvancedStatsDisabled() throws Exception { - Options.Builder builder = new Options.Builder(); - - runInServer(builder, nc -> { + runInServer(optionsBuilder(), nc -> { AtomicInteger requests = new AtomicInteger(); MessageHandler handler = (msg) -> { requests.incrementAndGet(); @@ -265,12 +233,13 @@ public void testOrphanDuplicateRepliesAdvancedStatsDisabled() throws Exception { sleep(5000); handler.onMessage(msg); }); - d1.subscribe(SUBJECT); - d2.subscribe(SUBJECT); - d3.subscribe(SUBJECT); - d4.subscribe(SUBJECT); + String subject = TestBase.random(); + d1.subscribe(subject); + d2.subscribe(subject); + d3.subscribe(subject); + d4.subscribe(subject); - Message reply = nc.request(SUBJECT, null, Duration.ofSeconds(2)); + Message reply = nc.request(subject, null, Duration.ofSeconds(2)); assertNotNull(reply); sleep(2000); assertEquals(3, requests.get()); diff --git a/src/test/java/io/nats/client/impl/ObjectStoreTests.java b/src/test/java/io/nats/client/impl/ObjectStoreTests.java index 9ac3a3c57..9d0f674fa 100644 --- a/src/test/java/io/nats/client/impl/ObjectStoreTests.java +++ b/src/test/java/io/nats/client/impl/ObjectStoreTests.java @@ -14,7 +14,7 @@ import io.nats.client.*; import io.nats.client.api.*; -import io.nats.client.utils.TestBase; +import io.nats.client.utils.VersionUtils; import org.junit.jupiter.api.Test; import java.io.*; @@ -30,55 +30,56 @@ import static io.nats.client.api.ObjectStoreWatchOption.IGNORE_DELETE; import static io.nats.client.support.NatsJetStreamClientError.*; import static io.nats.client.support.NatsObjectStoreUtil.DEFAULT_CHUNK_SIZE; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; public class ObjectStoreTests extends JetStreamTestBase { @Test public void testWorkflow() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { ObjectStoreManagement osm = nc.objectStoreManagement(); nc.objectStoreManagement(ObjectStoreOptions.builder(DEFAULT_JS_OPTIONS).build()); // coverage Map metadata = new HashMap<>(); metadata.put(META_KEY, META_VALUE); - String bucket = bucket(); - String desc = variant(); + String bucket = random(); + String objectDesc = random(); // create the bucket ObjectStoreConfiguration osc = ObjectStoreConfiguration.builder(bucket) - .description(desc) + .description(objectDesc) .ttl(Duration.ofHours(24)) .storageType(StorageType.Memory) .metadata(metadata) .build(); ObjectStoreStatus status = osm.create(osc); - validateStatus(status, bucket, desc); - validateStatus(osm.getStatus(bucket), bucket, desc); + validateStatus(status, bucket, objectDesc); + validateStatus(osm.getStatus(bucket), bucket, objectDesc); - JetStreamManagement jsm = nc.jetStreamManagement(); assertNotNull(jsm.getStreamInfo("OBJ_" + bucket)); List names = osm.getBucketNames(); - assertEquals(1, names.size()); assertTrue(names.contains(bucket)); // put some objects into the stores ObjectStore os = nc.objectStore(bucket); nc.objectStore(bucket, ObjectStoreOptions.builder(DEFAULT_JS_OPTIONS).build()); // coverage; - validateStatus(os.getStatus(), bucket, desc); + validateStatus(os.getStatus(), bucket, objectDesc); // object not found errors assertClientError(OsObjectNotFound, () -> os.get("notFound", new ByteArrayOutputStream())); assertClientError(OsObjectNotFound, () -> os.updateMeta("notFound", ObjectMeta.objectName("notFound"))); assertClientError(OsObjectNotFound, () -> os.delete("notFound")); - String objectName = name(); + String objectName = random(); + objectDesc = random(); + String[] keys = new String[]{random(), random()}; ObjectMeta meta = ObjectMeta.builder(objectName) - .description("object-desc") - .headers(new Headers().put(key(1), data(1)).put(key(2), data(21)).add(key(2), data(22))) + .description(objectDesc) + .headers(new Headers().put(keys[0], data(0)).put(keys[1], data(11)).add(keys[1], data(12))) .chunkSize(4096) .build(); @@ -90,14 +91,14 @@ public void testWorkflow() throws Exception { } File file = (File)input[1]; InputStream in = Files.newInputStream(file.toPath()); - ObjectInfo oi1 = validateObjectInfo(os.put(meta, in), bucket, objectName, len, expectedChunks, 4096); + ObjectInfo oi1 = validateObjectInfo(os.put(meta, in), bucket, objectName, objectDesc, len, expectedChunks, 4096, keys); - ByteArrayOutputStream baos = validateGet(os, bucket, objectName, len, expectedChunks, 4096); + ByteArrayOutputStream baos = validateGet(os, bucket, objectName, objectDesc, len, expectedChunks, 4096); byte[] bytes = baos.toByteArray(); byte[] bytes4k = Arrays.copyOf(bytes, 4096); - ObjectInfo oi2 = validateObjectInfo(os.put(meta, new ByteArrayInputStream(bytes4k)), bucket, objectName, 4096, 1, 4096); - validateGet(os, bucket, objectName, 4096, 1, 4096); + ObjectInfo oi2 = validateObjectInfo(os.put(meta, new ByteArrayInputStream(bytes4k)), bucket, objectName, objectDesc, 4096, 1, 4096, keys); + validateGet(os, bucket, objectName, objectDesc, 4096, 1, 4096); assertNotEquals(oi1.getNuid(), oi2.getNuid()); @@ -146,16 +147,16 @@ public void testWorkflow() throws Exception { os.updateMeta(oi.getObjectName(), ObjectMeta.objectName(oi3.getObjectName())); // alternate puts - String altName = name(); + String altName = random(); expectedChunks = len / DEFAULT_CHUNK_SIZE; if (expectedChunks * DEFAULT_CHUNK_SIZE < len) { expectedChunks++; } in = Files.newInputStream(file.toPath()); - validateObjectInfo(os.put(altName, in), bucket, altName, null, false, len, expectedChunks, DEFAULT_CHUNK_SIZE); + validateObjectInfo(os.put(altName, in), bucket, altName, null, len, expectedChunks, DEFAULT_CHUNK_SIZE, null); altName = file.getName(); - validateObjectInfo(os.put(file), bucket, altName, null, false, len, expectedChunks, DEFAULT_CHUNK_SIZE); + validateObjectInfo(os.put(file), bucket, altName, null, len, expectedChunks, DEFAULT_CHUNK_SIZE, null); }); } @@ -179,19 +180,15 @@ private static void validateStatus(ObjectStoreStatus status, String bucket, Stri } @SuppressWarnings("SameParameterValue") - private ByteArrayOutputStream validateGet(ObjectStore os, String bucket, String objectName, long len, long chunks, int chunkSize) throws IOException, JetStreamApiException, InterruptedException, NoSuchAlgorithmException { + private ByteArrayOutputStream validateGet(ObjectStore os, String bucket, String objectName, String objectDesc, long len, long chunks, int chunkSize) throws IOException, JetStreamApiException, InterruptedException, NoSuchAlgorithmException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectInfo oi = os.get(objectName, baos); assertEquals(len, baos.size()); - validateObjectInfo(oi, bucket, objectName, len, chunks, chunkSize); + validateObjectInfo(oi, bucket, objectName, objectDesc, len, chunks, chunkSize, null); return baos; } - private ObjectInfo validateObjectInfo(ObjectInfo oi, String bucket, String objectName, long size, long chunks, int chunkSize) { - return validateObjectInfo(oi, bucket, objectName, "object-desc", true, size, chunks, chunkSize); - } - - private ObjectInfo validateObjectInfo(ObjectInfo oi, String bucket, String objectName, String objectDesc, boolean headers, long size, long chunks, int chunkSize) { + private ObjectInfo validateObjectInfo(ObjectInfo oi, String bucket, String objectName, String objectDesc, long size, long chunks, int chunkSize, String[] keys) { assertEquals(bucket, oi.getBucket()); assertEquals(objectName, oi.getObjectName()); if (objectDesc == null) { @@ -211,18 +208,18 @@ private ObjectInfo validateObjectInfo(ObjectInfo oi, String bucket, String objec assertNotNull(oi.getObjectMeta().getObjectMetaOptions()); assertEquals(chunkSize, oi.getObjectMeta().getObjectMetaOptions().getChunkSize()); } - if (headers) { + if (keys != null) { assertNotNull(oi.getHeaders()); assertEquals(2, oi.getHeaders().size()); - List list = oi.getHeaders().get(key(1)); + List list = oi.getHeaders().get(keys[0]); assertNotNull(list); assertEquals(1, list.size()); - assertEquals(data(1), oi.getHeaders().getFirst(key(1))); - list = oi.getHeaders().get(key(2)); + assertEquals(data(0), oi.getHeaders().getFirst(keys[0])); + list = oi.getHeaders().get(keys[1]); assertNotNull(list); assertEquals(2, list.size()); - assertTrue(list.contains(data(21))); - assertTrue(list.contains(data(22))); + assertTrue(list.contains(data(11))); + assertTrue(list.contains(data(12))); } return oi; } @@ -252,18 +249,18 @@ private static Object[] getInput(int size) { @Test public void testManageGetBucketNamesStatuses() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { ObjectStoreManagement osm = nc.objectStoreManagement(); // create bucket 1 - String bucket1 = bucket(); + String bucket1 = random(); osm.create(ObjectStoreConfiguration.builder() .name(bucket1) // constructor variety .storageType(StorageType.Memory) .build()); // create bucket 2 - String bucket2 = bucket(); + String bucket2 = random(); osm.create(ObjectStoreConfiguration.builder(bucket2) .storageType(StorageType.Memory) .build()); @@ -314,7 +311,7 @@ private void assertOso(ObjectStoreOptions oso) { @Test public void testObjectLinks() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { ObjectStoreManagement osm = nc.objectStoreManagement(); String bucket1 = "b1"; // bucket(); @@ -399,7 +396,7 @@ public void testObjectLinks() throws Exception { assertClientError(OsObjectNotFound, () -> os2.get("targetWillBeDeleted", new ByteArrayOutputStream())); assertClientError(OsCantLinkToLink, () -> os2.addLink("willException", oiLink)); // can't link to link - String crossName = name(); + String crossName = random(); os2.addLink(crossName, info12); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectInfo crossInfo12 = os2.get(crossName, baos); @@ -435,15 +432,15 @@ private void validateLink(ObjectInfo oiLink, String linkName, ObjectInfo targetI @Test public void testList() throws Exception { - jsServer.run(nc -> { + runInLrServer((nc, jsm, js) -> { ObjectStoreManagement osm = nc.objectStoreManagement(); - String bucket = bucket(); + String bucket = random(); osm.create(ObjectStoreConfiguration.builder(bucket).storageType(StorageType.Memory).build()); ObjectStore os = nc.objectStore(bucket); - String[] names = new String[] {name(), name(), name(), name(), name()}; + String[] names = new String[] {random(), random(), random(), random(), random()}; os.put(names[0], dataBytes()); os.put(names[1], dataBytes()); os.put(names[2], dataBytes()); @@ -470,8 +467,8 @@ public void testList() throws Exception { @Test public void testSeal() throws Exception { - jsServer.run(nc -> { - String bucket = bucket(); + runInLrServer((nc, jsm, js) -> { + String bucket = random(); ObjectStoreManagement osm = nc.objectStoreManagement(); osm.create(ObjectStoreConfiguration.builder(bucket) .storageType(StorageType.Memory) @@ -493,8 +490,8 @@ public void testSeal() throws Exception { @Test public void testCompression() throws Exception { - jsServer.run(TestBase::atLeast2_10, nc -> { - String bucket = bucket(); + runInLrServer(VersionUtils::atLeast2_10, (nc, jsm, js) -> { + String bucket = random(); ObjectStoreManagement osm = nc.objectStoreManagement(); osm.create(ObjectStoreConfiguration.builder(bucket) .storageType(StorageType.Memory) @@ -558,7 +555,7 @@ public void testWatch() throws Exception { TestObjectStoreWatcher fullAfterWatcher = new TestObjectStoreWatcher("fullAfterWatcher", false); TestObjectStoreWatcher delAfterWatcher = new TestObjectStoreWatcher("delAfterWatcher", false, IGNORE_DELETE); - runInJsServer(nc -> { + runInLrServer((nc, jsm, js) -> { _testWatch(nc, fullB4Watcher, new Object[]{"A", "B", null}, os -> os.watch(fullB4Watcher, fullB4Watcher.watchOptions)); _testWatch(nc, delB4Watcher, new Object[]{"A", "B"}, os -> os.watch(delB4Watcher, delB4Watcher.watchOptions)); _testWatch(nc, fullAfterWatcher, new Object[]{"B", null}, os -> os.watch(fullAfterWatcher, fullAfterWatcher.watchOptions)); @@ -636,7 +633,7 @@ public void testObjectStoreDomains() throws Exception { ObjectStoreManagement leafOsm = leafNc.objectStoreManagement(ObjectStoreOptions.builder().jsDomain(HUB_DOMAIN).build()); // Create main OS on HUB - String bucketName = bucket(); + String bucketName = random(); ObjectStoreStatus hubStatus = hubOsm.create(ObjectStoreConfiguration.builder() .name(bucketName) .storageType(StorageType.Memory) @@ -648,7 +645,7 @@ public void testObjectStoreDomains() throws Exception { ObjectStore hubOs = hubNc.objectStore(bucketName); ObjectStore leafOs = leafNc.objectStore(bucketName, ObjectStoreOptions.builder().jsDomain(HUB_DOMAIN).build()); - String objectName = name(); + String objectName = random(); ObjectMeta meta = ObjectMeta.builder(objectName).chunkSize(8 * 1024).build(); Object[] input = getInput(4 * 8 * 1024); diff --git a/src/test/java/io/nats/client/impl/ParseTests.java b/src/test/java/io/nats/client/impl/ParseTests.java index 5e1008f03..2c4630fda 100644 --- a/src/test/java/io/nats/client/impl/ParseTests.java +++ b/src/test/java/io/nats/client/impl/ParseTests.java @@ -15,13 +15,13 @@ import io.nats.client.Nats; import io.nats.client.NatsTestServer; -import io.nats.client.Options; import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.charset.StandardCharsets; import static io.nats.client.support.NatsConstants.*; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -55,7 +55,7 @@ public void testTooBig() { @Test public void testLongProtocolOpThrows() { assertThrows(IOException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { NatsConnectionReader reader = nc.getReader(); byte[] bytes = ("thisistoolong\r\n").getBytes(StandardCharsets.US_ASCII); @@ -68,7 +68,7 @@ public void testLongProtocolOpThrows() { @Test public void testMissingLineFeed() { assertThrows(IOException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { NatsConnectionReader reader = nc.getReader(); byte[] bytes = ("PING\rPONG").getBytes(StandardCharsets.US_ASCII); @@ -81,7 +81,7 @@ public void testMissingLineFeed() { @Test public void testMissingSubject() { assertThrows(IOException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { NatsConnectionReader reader = nc.getReader(); byte[] bytes = ("MSG 1 1\r\n").getBytes(StandardCharsets.US_ASCII); @@ -96,7 +96,7 @@ public void testMissingSubject() { @Test public void testMissingSID() { assertThrows(IOException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { NatsConnectionReader reader = nc.getReader(); byte[] bytes = ("MSG subject 1\r\n").getBytes(StandardCharsets.US_ASCII); @@ -111,7 +111,7 @@ public void testMissingSID() { @Test public void testMissingLength() { assertThrows(IOException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { NatsConnectionReader reader = nc.getReader(); byte[] bytes = ("MSG subject 2 \r\n").getBytes(StandardCharsets.US_ASCII); @@ -126,7 +126,7 @@ public void testMissingLength() { @Test public void testBadLength() { assertThrows(IOException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { NatsConnectionReader reader = nc.getReader(); byte[] bytes = ("MSG subject 2 x\r\n").getBytes(StandardCharsets.US_ASCII); @@ -141,11 +141,10 @@ public void testBadLength() { @Test public void testMessageLineTooLong() { assertThrows(IOException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - NatsConnection nc = (NatsConnection) Nats.connect(new Options.Builder(). - server(ts.getURI()). - maxControlLine(16). - build())) { + try (NatsTestServer ts = new NatsTestServer(); + NatsConnection nc = (NatsConnection) Nats.connect( + optionsBuilder(ts.getURI()).maxControlLine(16).build()) + ) { NatsConnectionReader reader = nc.getReader(); byte[] bytes = ("MSG reallylongsubjectobreakthelength 1 1\r\n").getBytes(StandardCharsets.US_ASCII); reader.fakeReadForTest(bytes); @@ -159,11 +158,10 @@ public void testMessageLineTooLong() { @Test public void testProtocolLineTooLong() { assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - NatsConnection nc = (NatsConnection) Nats.connect(new Options.Builder(). - server(ts.getURI()). - maxControlLine(1024). - build())) { + try (NatsTestServer ts = new NatsTestServer(); + NatsConnection nc = (NatsConnection) Nats.connect( + optionsBuilder(ts.getURI()).maxControlLine(1024).build()) + ) { NatsConnectionReader reader = nc.getReader(); StringBuilder longString = new StringBuilder(); @@ -203,7 +201,7 @@ public void testProtocolStrings() throws Exception { OP_OK, OP_PONG, OP_PONG, OP_MSG }; - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { NatsConnectionReader reader = nc.getReader(); diff --git a/src/test/java/io/nats/client/impl/PingTests.java b/src/test/java/io/nats/client/impl/PingTests.java index bcd7cc96e..fb3cb9373 100644 --- a/src/test/java/io/nats/client/impl/PingTests.java +++ b/src/test/java/io/nats/client/impl/PingTests.java @@ -1,275 +1,275 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.nats.client.impl; - -import io.nats.client.*; -import io.nats.client.ConnectionListener.Events; -import io.nats.client.NatsServerProtocolMock.ExitAt; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.time.Duration; -import java.util.concurrent.*; - -import static io.nats.client.utils.TestBase.runInJsServer; -import static org.junit.jupiter.api.Assertions.*; - -public class PingTests { - @Test - public void testHandlingPing() throws Exception,ExecutionException { - CompletableFuture gotPong = new CompletableFuture<>(); - - NatsServerProtocolMock.Customizer pingPongCustomizer = (ts, r,w) -> { - - System.out.println("*** Mock Server @" + ts.getPort() + " sending PING ..."); - w.write("PING\r\n"); - w.flush(); - - String pong = ""; - - System.out.println("*** Mock Server @" + ts.getPort() + " waiting for PONG ..."); - try { - pong = r.readLine(); - } catch(Exception e) { - gotPong.cancel(true); - return; - } - - if (pong.startsWith("PONG")) { - System.out.println("*** Mock Server @" + ts.getPort() + " got PONG ..."); - gotPong.complete(Boolean.TRUE); - } else { - System.out.println("*** Mock Server @" + ts.getPort() + " got something else... " + pong); - gotPong.complete(Boolean.FALSE); - } - }; - - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(pingPongCustomizer)) { - Connection nc = Nats.connect(ts.getURI()); - try { - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - assertTrue(gotPong.get(), "Got pong."); - } finally { - nc.close(); - assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); - } - } - } - - @Test - public void testPingTimer() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()) - .pingInterval(Duration.ofMillis(5)) - .maxPingsOut(10000) // just don't want this to be what fails the test - .build(); - NatsConnection nc = (NatsConnection) Nats.connect(options); - StatisticsCollector stats = nc.getStatisticsCollector(); - - try { - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - try { Thread.sleep(200); } catch (Exception ignore) {} // 1200 / 100 ... should get 10+ pings - assertTrue(stats.getPings() > 10, "got pings"); - } finally { - nc.close(); - assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); - } - } - } - - @Test - public void testPingFailsWhenClosed() throws Exception { - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT)) { - Options options = new Options.Builder(). - server(ts.getURI()). - pingInterval(Duration.ofMillis(10)). - maxPingsOut(5). - maxReconnects(0). - build(); - NatsConnection nc = (NatsConnection) Nats.connect(options); - - try { - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - } finally { - nc.close(); - } - - Future pong = nc.sendPing(); - - assertFalse(pong.get(10,TimeUnit.MILLISECONDS)); - } - } - - @Test - public void testMaxPingsOut() throws Exception { - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT)) { - Options options = new Options.Builder(). - server(ts.getURI()). - pingInterval(Duration.ofSeconds(10)). // Avoid auto pings - maxPingsOut(2). - maxReconnects(0). - build(); - NatsConnection nc = (NatsConnection) Nats.connect(options); - - //noinspection TryFinallyCanBeTryWithResources - try { - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - nc.sendPing(); - nc.sendPing(); - assertNull(nc.sendPing(), "No future returned when past max"); - } finally { - nc.close(); - } - } - } - - @Test - public void testFlushTimeout() { - assertThrows(TimeoutException.class, () -> { - try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT)) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(0). - build(); - NatsConnection nc = (NatsConnection) Nats.connect(options); - - //noinspection TryFinallyCanBeTryWithResources - try { - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - // fake server so flush will time out - nc.flush(Duration.ofMillis(50)); - } finally { - nc.close(); - } - } - }); - } - - @Test - public void testFlushTimeoutDisconnected() { - assertThrows(TimeoutException.class, () -> { - ListenerForTesting listener = new ListenerForTesting(); - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().connectionListener(listener).server(ts.getURI()).build(); - NatsConnection nc = (NatsConnection) Nats.connect(options); - - try { - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - nc.flush(Duration.ofSeconds(2)); - listener.prepForStatusChange(Events.DISCONNECTED); - ts.close(); - listener.waitForStatusChange(2, TimeUnit.SECONDS); - nc.flush(Duration.ofSeconds(2)); - } finally { - nc.close(); - assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); - } - } - }); - } - - @Test - public void testPingTimerThroughReconnect() throws Exception { - ListenerForTesting listener = new ListenerForTesting(); - try (NatsTestServer ts = new NatsTestServer(false)) { - try (NatsTestServer ts2 = new NatsTestServer()) { - Options options = new Options.Builder() - .connectionListener(listener) - .server(ts.getURI()) - .server(ts2.getURI()) - .pingInterval(Duration.ofMillis(5)) - .maxPingsOut(10000) // just don't want this to be what fails the test - .build(); - NatsConnection nc = (NatsConnection) Nats.connect(options); - StatisticsCollector stats = nc.getStatisticsCollector(); - - try { - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - try { - Thread.sleep(200); // should get 10+ pings - } catch (Exception exp) - { - //Ignore - } - long pings = stats.getPings(); - assertTrue(pings > 10, "got pings"); - listener.prepForStatusChange(Events.RECONNECTED); - ts.close(); - listener.waitForStatusChange(5, TimeUnit.SECONDS); - pings = stats.getPings(); - Thread.sleep(250); // should get more pings - assertTrue(stats.getPings() > pings, "more pings"); - Thread.sleep(1000); - } finally { - nc.close(); - assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); - } - } - } - } - - - @Test - public void testMessagesDelayPings() throws Exception, ExecutionException, TimeoutException { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()). - pingInterval(Duration.ofMillis(200)).build(); - NatsConnection nc = (NatsConnection) Nats.connect(options); - StatisticsCollector stats = nc.getStatisticsCollector(); - - try { - final CompletableFuture done = new CompletableFuture<>(); - assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - - Dispatcher d = nc.createDispatcher((msg) -> { - if (msg.getSubject().equals("done")) { - done.complete(Boolean.TRUE); - } - }); - - d.subscribe("subject"); - d.subscribe("done"); - nc.flush(Duration.ofMillis(1000)); // wait for them to go through - - long b4 = stats.getPings(); - for (int i=0;i<10;i++) { - Thread.sleep(50); - nc.publish("subject", new byte[16]); - } - long after = stats.getPings(); - assertEquals(after, b4, "pings hidden"); - nc.publish("done", new byte[16]); - nc.flush(Duration.ofMillis(1000)); // wait for them to go through - done.get(500, TimeUnit.MILLISECONDS); - - // no more messages, pings should start to go through - b4 = stats.getPings(); - Thread.sleep(500); - after = stats.getPings(); - assertTrue(after > b4, "pings restarted"); - } finally { - nc.close(); - } - } - } - - @Test - public void testRtt() throws Exception { - runInJsServer(nc -> { - assertTrue(nc.RTT().toMillis() < 10); - nc.close(); - assertThrows(IOException.class, nc::RTT); - }); - } -} +// Copyright 2015-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client.impl; + +import io.nats.client.*; +import io.nats.client.ConnectionListener.Events; +import io.nats.client.NatsServerProtocolMock.ExitAt; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.time.Duration; +import java.util.concurrent.*; + +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.TestBase.runInServer; +import static org.junit.jupiter.api.Assertions.*; + +public class PingTests { + @Test + public void testHandlingPing() throws Exception,ExecutionException { + CompletableFuture gotPong = new CompletableFuture<>(); + + NatsServerProtocolMock.Customizer pingPongCustomizer = (ts, r,w) -> { + + System.out.println("*** Mock Server @" + ts.getPort() + " sending PING ..."); + w.write("PING\r\n"); + w.flush(); + + String pong = ""; + + System.out.println("*** Mock Server @" + ts.getPort() + " waiting for PONG ..."); + try { + pong = r.readLine(); + } catch(Exception e) { + gotPong.cancel(true); + return; + } + + if (pong.startsWith("PONG")) { + System.out.println("*** Mock Server @" + ts.getPort() + " got PONG ..."); + gotPong.complete(Boolean.TRUE); + } else { + System.out.println("*** Mock Server @" + ts.getPort() + " got something else... " + pong); + gotPong.complete(Boolean.FALSE); + } + }; + + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(pingPongCustomizer)) { + Connection nc = Nats.connect(ts.getURI()); + try { + assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); + assertTrue(gotPong.get(), "Got pong."); + } finally { + nc.close(); + assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); + } + } + } + + @Test + public void testPingTimer() throws Exception { + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts) + .pingInterval(Duration.ofMillis(5)) + .maxPingsOut(10000) // just don't want this to be what fails the test + .build(); + NatsConnection nc = (NatsConnection) Nats.connect(options); + StatisticsCollector stats = nc.getStatisticsCollector(); + + try { + assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); + try { Thread.sleep(200); } catch (Exception ignore) {} // 1200 / 100 ... should get 10+ pings + assertTrue(stats.getPings() > 10, "got pings"); + } finally { + nc.close(); + assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); + } + } + } + + @Test + public void testPingFailsWhenClosed() throws Exception { + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT)) { + Options options = optionsBuilder().server(ts.getURI()). + pingInterval(Duration.ofMillis(10)). + maxPingsOut(5). + maxReconnects(0). + build(); + NatsConnection nc = (NatsConnection) Nats.connect(options); + + try { + assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); + } finally { + nc.close(); + } + + Future pong = nc.sendPing(); + + assertFalse(pong.get(10,TimeUnit.MILLISECONDS)); + } + } + + @Test + public void testMaxPingsOut() throws Exception { + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT)) { + Options options = optionsBuilder(). + server(ts.getURI()). + pingInterval(Duration.ofSeconds(10)). // Avoid auto pings + maxPingsOut(2). + maxReconnects(0). + build(); + NatsConnection nc = (NatsConnection) Nats.connect(options); + + //noinspection TryFinallyCanBeTryWithResources + try { + assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); + nc.sendPing(); + nc.sendPing(); + assertNull(nc.sendPing(), "No future returned when past max"); + } finally { + nc.close(); + } + } + } + + @Test + public void testFlushTimeout() { + assertThrows(TimeoutException.class, () -> { + try (NatsServerProtocolMock ts = new NatsServerProtocolMock(ExitAt.NO_EXIT)) { + Options options = optionsBuilder(). + server(ts.getURI()). + maxReconnects(0). + build(); + NatsConnection nc = (NatsConnection) Nats.connect(options); + + //noinspection TryFinallyCanBeTryWithResources + try { + assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); + // fake server so flush will time out + nc.flush(Duration.ofMillis(50)); + } finally { + nc.close(); + } + } + }); + } + + @Test + public void testFlushTimeoutDisconnected() { + assertThrows(TimeoutException.class, () -> { + ListenerForTesting listener = new ListenerForTesting(); + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts).connectionListener(listener).build(); + NatsConnection nc = (NatsConnection) Nats.connect(options); + + try { + assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); + nc.flush(Duration.ofSeconds(2)); + listener.prepForStatusChange(Events.DISCONNECTED); + ts.close(); + listener.waitForStatusChange(2, TimeUnit.SECONDS); + nc.flush(Duration.ofSeconds(2)); + } finally { + nc.close(); + assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); + } + } + }); + } + + @Test + public void testPingTimerThroughReconnect() throws Exception { + ListenerForTesting listener = new ListenerForTesting(); + try (NatsTestServer ts = new NatsTestServer()) { + try (NatsTestServer ts2 = new NatsTestServer()) { + Options options = optionsBuilder() + .connectionListener(listener) + .server(ts.getURI()) + .server(ts2.getURI()) + .pingInterval(Duration.ofMillis(5)) + .maxPingsOut(10000) // just don't want this to be what fails the test + .build(); + NatsConnection nc = (NatsConnection) Nats.connect(options); + StatisticsCollector stats = nc.getStatisticsCollector(); + + try { + assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); + try { + Thread.sleep(200); // should get 10+ pings + } catch (Exception exp) + { + //Ignore + } + long pings = stats.getPings(); + assertTrue(pings > 10, "got pings"); + listener.prepForStatusChange(Events.RECONNECTED); + ts.close(); + listener.waitForStatusChange(5, TimeUnit.SECONDS); + pings = stats.getPings(); + Thread.sleep(250); // should get more pings + assertTrue(stats.getPings() > pings, "more pings"); + Thread.sleep(1000); + } finally { + nc.close(); + assertSame(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); + } + } + } + } + + + @Test + public void testMessagesDelayPings() throws Exception, ExecutionException, TimeoutException { + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts). + pingInterval(Duration.ofMillis(200)).build(); + NatsConnection nc = (NatsConnection) Nats.connect(options); + StatisticsCollector stats = nc.getStatisticsCollector(); + + try { + final CompletableFuture done = new CompletableFuture<>(); + assertSame(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); + + Dispatcher d = nc.createDispatcher((msg) -> { + if (msg.getSubject().equals("done")) { + done.complete(Boolean.TRUE); + } + }); + + d.subscribe("subject"); + d.subscribe("done"); + nc.flush(Duration.ofMillis(1000)); // wait for them to go through + + long b4 = stats.getPings(); + for (int i=0;i<10;i++) { + Thread.sleep(50); + nc.publish("subject", new byte[16]); + } + long after = stats.getPings(); + assertEquals(after, b4, "pings hidden"); + nc.publish("done", new byte[16]); + nc.flush(Duration.ofMillis(1000)); // wait for them to go through + done.get(500, TimeUnit.MILLISECONDS); + + // no more messages, pings should start to go through + b4 = stats.getPings(); + Thread.sleep(500); + after = stats.getPings(); + assertTrue(after > b4, "pings restarted"); + } finally { + nc.close(); + } + } + } + + @Test + public void testRtt() throws Exception { + runInServer(nc -> { + assertTrue(nc.RTT().toMillis() < 10); + nc.close(); + assertThrows(IOException.class, nc::RTT); + }); + } +} diff --git a/src/test/java/io/nats/client/impl/ReconnectTests.java b/src/test/java/io/nats/client/impl/ReconnectTests.java index dbcc608cd..152c5b842 100644 --- a/src/test/java/io/nats/client/impl/ReconnectTests.java +++ b/src/test/java/io/nats/client/impl/ReconnectTests.java @@ -13,9 +13,11 @@ package io.nats.client.impl; +import io.nats.NatsServerRunner; import io.nats.client.*; import io.nats.client.ConnectionListener.Events; import io.nats.client.api.ServerInfo; +import io.nats.client.utils.TestBase; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.Isolated; @@ -31,11 +33,14 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; -import java.util.function.Function; import static io.nats.client.NatsTestServer.getNatsLocalhostUri; import static io.nats.client.support.NatsConstants.OUTPUT_QUEUE_IS_FULL; +import static io.nats.client.utils.ConnectionUtils.*; +import static io.nats.client.utils.OptionsUtils.*; import static io.nats.client.utils.TestBase.*; +import static io.nats.client.utils.ThreadUtils.sleep; +import static io.nats.client.utils.VersionUtils.atLeast2_9_0; import static org.junit.jupiter.api.Assertions.*; @Isolated @@ -49,47 +54,35 @@ void checkReconnectingStatus(Connection nc) { @Test public void testSimpleReconnect() throws Exception { //Includes test for subscriptions and dispatchers across reconnect - Function ntsSupplier = port -> { - try { - return new NatsTestServer(port, false); - } - catch (Exception e) { - throw new RuntimeException(e); - } - }; - _testReconnect(ntsSupplier, (ts, builder) -> builder.server(ts.getURI())); + _testReconnect(NatsServerRunner.builder(), (ts, optionsBuilder) -> optionsBuilder.server(ts.getNatsLocalhostUri())); } @Test public void testWsReconnect() throws Exception { //Includes test for subscriptions and dispatchers across reconnect - Function ntsSupplier = port -> { - try { - return new NatsTestServer("src/test/resources/ws_operator.conf", port, false); - } - catch (Exception e) { - throw new RuntimeException(e); - } - }; - _testReconnect(ntsSupplier, (ts, builder) -> - builder.server(ts.getLocalhostUri("ws")).authHandler(Nats.credentials("src/test/resources/jwt_nkey/user.creds"))); + _testReconnect(NatsServerRunner.builder().configFilePath("src/test/resources/ws_operator.conf"), + (ts, optionsBuilder) -> + optionsBuilder + .server(ts.getLocalhostUri("ws")) + .authHandler(Nats.credentials("src/test/resources/jwt_nkey/user.creds"))); } - private void _testReconnect(Function ntsSupplier, BiConsumer optSetter) throws Exception { + private void _testReconnect(NatsServerRunner.Builder nsrb, BiConsumer optSetter) throws Exception { int port = NatsTestServer.nextPort(); + nsrb.port(port); // set the port into the builder ListenerForTesting listener = new ListenerForTesting(); NatsConnection nc; Subscription sub; long start; long end; - try (NatsTestServer ts = ntsSupplier.apply(port)) { - Options.Builder builder = new Options.Builder() + try (NatsTestServer ts = new NatsTestServer(nsrb)) { + Options.Builder builder = optionsBuilder() .maxReconnects(-1) .reconnectWait(Duration.ofMillis(1000)) .connectionListener(listener); optSetter.accept(ts, builder); Options options = builder.build(); - nc = (NatsConnection)standardConnection(options); + nc = (NatsConnection) standardConnectionWait(options); sub = nc.subscribe("subsubject"); @@ -115,7 +108,7 @@ private void _testReconnect(Function ntsSupplier, BiCon listener.prepForStatusChange(Events.RESUBSCRIBED); - try (NatsTestServer ignored = ntsSupplier.apply(port)) { + try (NatsTestServer ignored = new NatsTestServer(nsrb.port(port))) { listenerConnectionWait(nc, listener, LONG_CONNECTION_WAIT_MS); end = System.nanoTime(); @@ -146,14 +139,13 @@ public void testSubscribeDuringReconnect() throws Exception { Subscription sub; try (NatsTestServer ts = new NatsTestServer()) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(-1). - reconnectWait(Duration.ofMillis(20)). - connectionListener(listener). - build(); - port = ts.getPort(); - nc = (NatsConnection) standardConnection(options); + Options options = optionsBuilder(ts) + .maxReconnects(-1) + .reconnectWait(Duration.ofMillis(20)) + .connectionListener(listener) + .build(); + port = ts.getPort(); + nc = (NatsConnection) standardConnectionWait(options); listener.prepForStatusChange(Events.DISCONNECTED); } @@ -198,14 +190,13 @@ public void testReconnectBuffer() throws Exception { String[] customArgs = {"--user","stephen","--pass","password"}; try (NatsTestServer ts = new NatsTestServer(customArgs, port, false)) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(-1). - userInfo("stephen".toCharArray(), "password".toCharArray()). - reconnectWait(Duration.ofMillis(1000)). - connectionListener(listener). - build(); - nc = (NatsConnection) standardConnection(options); + Options options = optionsBuilder(ts) + .maxReconnects(-1) + .userInfo("stephen".toCharArray(), "password".toCharArray()) + .reconnectWait(Duration.ofMillis(1000)) + .connectionListener(listener) + .build(); + nc = (NatsConnection) standardConnectionWait(options); sub = nc.subscribe("subsubject"); @@ -268,13 +259,12 @@ public void testMaxReconnects() throws Exception { int port = NatsTestServer.nextPort(); try (NatsTestServer ts = new NatsTestServer(port, false)) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(1). - connectionListener(listener). - reconnectWait(Duration.ofMillis(10)). - build(); - nc = standardConnection(options); + Options options = optionsBuilder(ts) + .maxReconnects(1) + .connectionListener(listener) + .reconnectWait(Duration.ofMillis(10)) + .build(); + nc = standardConnectionWait(options); listener.prepForStatusChange(Events.CLOSED); } @@ -290,14 +280,14 @@ public void testReconnectToSecondServer() throws Exception { try (NatsTestServer ts = new NatsTestServer()) { try (NatsTestServer ts2 = new NatsTestServer()) { - Options options = new Options.Builder(). - server(ts2.getURI()). - server(ts.getURI()). - noRandomize(). - connectionListener(listener). - maxReconnects(-1). - build(); - nc = (NatsConnection) standardConnection(options); + Options options = optionsBuilder() + .server(ts2.getNatsLocalhostUri()) + .server(ts.getNatsLocalhostUri()) + .noRandomize() + .connectionListener(listener) + .maxReconnects(-1) + .build(); + nc = (NatsConnection) standardConnectionWait(options); assertEquals(ts2.getURI(), nc.getConnectedUrl()); listener.prepForStatusChange(Events.RECONNECTED); } @@ -317,22 +307,22 @@ public void testNoRandomizeReconnectToSecondServer() throws Exception { try (NatsTestServer ts = new NatsTestServer()) { try (NatsTestServer ts2 = new NatsTestServer()) { - Options options = new Options.Builder(). - server(ts2.getURI()). - server(ts.getURI()). - connectionListener(listener). - maxReconnects(-1). - noRandomize(). - build(); - nc = (NatsConnection) standardConnection(options); - assertEquals(nc.getConnectedUrl(), ts2.getURI()); + Options options = optionsBuilder() + .server(ts2.getNatsLocalhostUri()) + .server(ts.getNatsLocalhostUri()) + .noRandomize() + .connectionListener(listener) + .maxReconnects(-1) + .build(); + nc = (NatsConnection) standardConnectionWait(options); + assertEquals(nc.getConnectedUrl(), ts2.getNatsLocalhostUri()); listener.prepForStatusChange(Events.RECONNECTED); } flushAndWaitLong(nc, listener); assertConnected(nc); - assertEquals(ts.getURI(), nc.getConnectedUrl()); + assertEquals(ts.getNatsLocalhostUri(), nc.getConnectedUrl()); standardCloseConnection(nc); } } @@ -346,14 +336,14 @@ public void testReconnectToSecondServerFromInfo() throws Exception { String striped = ts.getURI().substring("nats://".length()); // info doesn't have protocol String customInfo = "{\"server_id\":\"myid\", \"version\":\"9.9.99\",\"connect_urls\": [\""+striped+"\"]}"; try (NatsServerProtocolMock ts2 = new NatsServerProtocolMock(null, customInfo)) { - Options options = new Options.Builder(). - server(ts2.getURI()). - connectionListener(listener). - maxReconnects(-1). - connectionTimeout(Duration.ofSeconds(5)). - reconnectWait(Duration.ofSeconds(1)). - build(); - nc = (NatsConnection) standardConnection(options); + Options options = optionsBuilder() + .server(ts2.getURI()) + .connectionListener(listener) + .maxReconnects(-1) + .connectionTimeout(Duration.ofSeconds(5)) + .reconnectWait(Duration.ofSeconds(1)) + .build(); + nc = (NatsConnection) standardConnectionWait(options); assertEquals(nc.getConnectedUrl(), ts2.getURI()); listener.prepForStatusChange(Events.RECONNECTED); } @@ -361,6 +351,7 @@ public void testReconnectToSecondServerFromInfo() throws Exception { flushAndWaitLong(nc, listener); assertConnected(nc); + assertNotNull(nc.getConnectedUrl()); assertTrue(ts.getURI().endsWith(nc.getConnectedUrl())); standardCloseConnection(nc); } @@ -373,14 +364,13 @@ public void testOverflowReconnectBuffer() { ListenerForTesting listener = new ListenerForTesting(); try (NatsTestServer ts = new NatsTestServer()) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(-1). - connectionListener(listener). - reconnectBufferSize(4*512). - reconnectWait(Duration.ofSeconds(480)). - build(); - nc = standardConnection(options); + Options options = optionsBuilder(ts) + .maxReconnects(-1) + .connectionListener(listener) + .reconnectBufferSize(4*512) + .reconnectWait(Duration.ofSeconds(480)) + .build(); + nc = standardConnectionWait(options); listener.prepForStatusChange(Events.DISCONNECTED); } @@ -398,14 +388,13 @@ public void testInfiniteReconnectBuffer() throws Exception { Connection nc; ListenerForTesting listener = new ListenerForTesting(); try (NatsTestServer ts = new NatsTestServer()) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(5). - connectionListener(listener). - reconnectBufferSize(-1). - reconnectWait(Duration.ofSeconds(30)). - build(); - nc = standardConnection(options); + Options options = optionsBuilder(ts) + .maxReconnects(5) + .connectionListener(listener) + .reconnectBufferSize(-1) + .reconnectWait(Duration.ofSeconds(30)) + .build(); + nc = standardConnectionWait(options); listener.prepForStatusChange(Events.DISCONNECTED); } @@ -435,7 +424,7 @@ public void testReconnectDropOnLineFeed() throws Exception { NatsServerProtocolMock.Customizer receiveMessageCustomizer = (ts, r,w) -> { String subLine; - System.out.println("*** Mock Server @" + ts.getPort() + " waiting for SUB ..."); + // System.out.println("*** Mock Server @" + ts.getPort() + " waiting for SUB ..."); try { subLine = r.readLine(); } catch(Exception e) { @@ -458,13 +447,13 @@ public void testReconnectDropOnLineFeed() throws Exception { }; try (NatsServerProtocolMock ts = new NatsServerProtocolMock(receiveMessageCustomizer, port, true)) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(-1). - reconnectWait(reconnectWait). - connectionListener(listener). - build(); - port = ts.getPort(); + Options options = optionsBuilder() + .server(ts.getURI()) + .maxReconnects(-1) + .reconnectWait(reconnectWait) + .connectionListener(listener) + .build(); + port = ts.getPort(); nc = (NatsConnection) Nats.connect(options); assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); nc.subscribe("test"); @@ -543,15 +532,14 @@ public void testReconnectNoIPTLSConnection() throws Exception { NatsTestServer ts2 = new NatsTestServer("src/test/resources/tls_noip.conf", ts2Inserts, ts2Port, false) ) { SslTestingHelper.setKeystoreSystemParameters(); - Options options = new Options.Builder(). - server(ts.getURI()). - secure(). - connectionListener(listener). - maxReconnects(20). // we get multiples for some, so need enough - reconnectWait(Duration.ofMillis(100)). - connectionTimeout(Duration.ofSeconds(5)). - noRandomize(). - build(); + Options options = optionsBuilder(ts) + .secure() + .connectionListener(listener) + .maxReconnects(20) + .reconnectWait(Duration.ofMillis(100)) + .connectionTimeout(Duration.ofSeconds(5)) + .noRandomize() + .build(); listener.prepForStatusChange(Events.DISCOVERED_SERVERS); nc = (NatsConnection) longConnectionWait(options); @@ -576,8 +564,7 @@ public void testURISchemeNoIPTLSConnection() throws Exception { //System.setProperty("javax.net.debug", "all"); SslTestingHelper.setKeystoreSystemParameters(); try (NatsTestServer ts = new NatsTestServer("src/test/resources/tls_noip.conf", false)) { - Options options = new Options.Builder() - .server("tls://localhost:"+ts.getPort()) + Options options = optionsBuilder("tls://localhost:"+ts.getPort()) .connectionTimeout(Duration.ofSeconds(5)) .maxReconnects(0) .build(); @@ -590,10 +577,9 @@ public void testURISchemeNoIPOpenTLSConnection() throws Exception { //System.setProperty("javax.net.debug", "all"); SslTestingHelper.setKeystoreSystemParameters(); try (NatsTestServer ts = new NatsTestServer("src/test/resources/tls_noip.conf", false)) { - Options options = new Options.Builder(). - server("opentls://localhost:"+ts.getPort()). - maxReconnects(0). - build(); + Options options = optionsBuilder("opentls://localhost:"+ts.getPort()) + .maxReconnects(0) + .build(); assertCanConnect(options); } } @@ -605,11 +591,10 @@ public void testWriterFilterTiming() throws Exception { int port = NatsTestServer.nextPort(); try (NatsTestServer ts = new NatsTestServer(port, false)) { - Options options = new Options.Builder(). - server(ts.getURI()). - noReconnect(). - connectionListener(listener). - build(); + Options options = optionsBuilder(ts) + .noReconnect() + .connectionListener(listener) + .build(); nc = (NatsConnection) Nats.connect(options); assertConnected(nc); @@ -652,13 +637,12 @@ public void testReconnectWait() throws Exception { TestReconnectWaitHandler trwh = new TestReconnectWaitHandler(); int port = NatsTestServer.nextPort(); - Options options = new Options.Builder(). - server("nats://localhost:"+port). - maxReconnects(-1). - connectionTimeout(Duration.ofSeconds(1)). - reconnectWait(Duration.ofMillis(250)). - connectionListener(trwh). - build(); + Options options = optionsBuilder("nats://localhost:"+port) + .maxReconnects(-1) + .connectionTimeout(Duration.ofSeconds(1)) + .reconnectWait(Duration.ofMillis(250)) + .connectionListener(trwh) + .build(); NatsTestServer ts = new NatsTestServer(port, false); Connection c = Nats.connect(options); @@ -673,7 +657,7 @@ public void testReconnectWait() throws Exception { @Test public void testReconnectOnConnect() throws Exception { int port = NatsTestServer.nextPort(); - Options options = Options.builder().server(getNatsLocalhostUri(port)).build(); + Options options = options(getNatsLocalhostUri(port)); CountDownLatch latch = new CountDownLatch(1); AtomicReference testConn = new AtomicReference<>(); @@ -717,7 +701,7 @@ private static Thread getReconnectOnConnectTestThread(AtomicReference runInServer(nc2 -> { + runInServer(nc1 -> runInServer(nc2 -> { int port1 = nc1.getServerInfo().getPort(); int port2 = nc2.getServerInfo().getPort(); @@ -962,8 +949,8 @@ public void testSocketDataPortTimeout() throws Exception { getNatsLocalhostUri(port1), getNatsLocalhostUri(port2) }; - Connection nc = standardConnection(builder.servers(servers).build()); - String subject = subject(); + Connection nc = standardConnectionWait(builder.servers(servers).build()); + String subject = TestBase.random(); int connectedPort = nc.getServerInfo().getPort(); AtomicInteger pubId = new AtomicInteger(); while (pubId.get() < 50000) { diff --git a/src/test/java/io/nats/client/impl/RequestTests.java b/src/test/java/io/nats/client/impl/RequestTests.java index ec030e617..24782dfd1 100644 --- a/src/test/java/io/nats/client/impl/RequestTests.java +++ b/src/test/java/io/nats/client/impl/RequestTests.java @@ -25,13 +25,16 @@ import java.util.concurrent.atomic.AtomicInteger; import static io.nats.client.support.NatsRequestCompletableFuture.CancelAction; +import static io.nats.client.utils.ConnectionUtils.standardConnectionWait; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; public class RequestTests extends TestBase { @Test public void testSimpleRequest() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); Dispatcher d = nc.createDispatcher((msg) -> { @@ -48,7 +51,7 @@ public void testSimpleRequest() throws Exception { Future incoming = nc.request("subject", null); Message msg = incoming.get(500, TimeUnit.MILLISECONDS); - assertEquals(0, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); assertNotNull(msg); assertEquals(0, msg.getData().length); assertTrue(msg.getSubject().indexOf('.') < msg.getSubject().lastIndexOf('.')); @@ -56,7 +59,7 @@ public void testSimpleRequest() throws Exception { incoming = nc.request("subject", new Headers().put("foo", "bar"), null); msg = incoming.get(500, TimeUnit.MILLISECONDS); - assertEquals(0, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); assertNotNull(msg); assertEquals(0, msg.getData().length); assertTrue(msg.getSubject().indexOf('.') < msg.getSubject().lastIndexOf('.')); @@ -76,38 +79,49 @@ public void testRequestVarieties() throws Exception { nc.publish(msg.getReplyTo(), msg.getData()); } }); - d.subscribe(SUBJECT); + String subject = random(); + d.subscribe(subject); - Future f = nc.request(SUBJECT, dataBytes(1)); + Future f = nc.request(subject, dataBytes(1)); Message msg = f.get(500, TimeUnit.MILLISECONDS); assertEquals(data(1), new String(msg.getData())); - NatsMessage outMsg = NatsMessage.builder().subject(SUBJECT).data(dataBytes(2)).build(); + NatsMessage outMsg = NatsMessage.builder().subject(subject).data(dataBytes(2)).build(); f = nc.request(outMsg); msg = f.get(500, TimeUnit.MILLISECONDS); + assertNotNull(msg); + assertNotNull(msg.getData()); assertEquals(data(2), new String(msg.getData())); - msg = nc.request(SUBJECT, dataBytes(3), Duration.ofSeconds(1)); + msg = nc.request(subject, dataBytes(3), Duration.ofSeconds(1)); + assertNotNull(msg); + assertNotNull(msg.getData()); assertEquals(data(3), new String(msg.getData())); - outMsg = NatsMessage.builder().subject(SUBJECT).data(dataBytes(4)).build(); + outMsg = NatsMessage.builder().subject(subject).data(dataBytes(4)).build(); msg = nc.request(outMsg, Duration.ofSeconds(1)); + assertNotNull(msg); + assertNotNull(msg.getData()); assertEquals(data(4), new String(msg.getData())); - msg = nc.request(SUBJECT, new Headers().put("foo", "bar"), dataBytes(5), Duration.ofSeconds(1)); + msg = nc.request(subject, new Headers().put("foo", "bar"), dataBytes(5), Duration.ofSeconds(1)); + assertNotNull(msg); + assertNotNull(msg.getData()); assertEquals(data(5), new String(msg.getData())); assertTrue(msg.hasHeaders()); assertEquals("bar", msg.getHeaders().getFirst("foo")); + //noinspection DataFlowIssue assertThrows(IllegalArgumentException.class, () -> nc.request(null)); + //noinspection DataFlowIssue assertThrows(IllegalArgumentException.class, () -> nc.request(null, Duration.ofSeconds(1))); }); } @Test public void testSimpleResponseMessageHasConnection() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); Dispatcher d = nc.createDispatcher((msg) -> { @@ -119,7 +133,7 @@ public void testSimpleResponseMessageHasConnection() throws Exception { Future incoming = nc.request("subject", null); Message msg = incoming.get(5000, TimeUnit.MILLISECONDS); - assertEquals(0, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); assertNotNull(msg); assertEquals(0, msg.getData().length); assertTrue(msg.getSubject().indexOf('.') < msg.getSubject().lastIndexOf('.')); @@ -129,8 +143,8 @@ public void testSimpleResponseMessageHasConnection() throws Exception { @Test public void testSafeRequest() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); Dispatcher d = nc.createDispatcher((msg) -> nc.publish(msg.getReplyTo(), null)); @@ -138,7 +152,7 @@ public void testSafeRequest() throws Exception { Message msg = nc.request("subject", null, Duration.ofMillis(1000)); - assertEquals(0, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); assertNotNull(msg); assertEquals(0, msg.getData().length); assertTrue(msg.getSubject().indexOf('.') < msg.getSubject().lastIndexOf('.')); @@ -147,8 +161,8 @@ public void testSafeRequest() throws Exception { @Test public void testMultipleRequest() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(new Options.Builder().server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = Nats.connect(optionsBuilder(ts).maxReconnects(0).build())) { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); Dispatcher d = nc.createDispatcher((msg) -> nc.publish(msg.getReplyTo(), new byte[7])); @@ -158,7 +172,7 @@ public void testMultipleRequest() throws Exception { Future incoming = nc.request("subject", new byte[11]); Message msg = incoming.get(500, TimeUnit.MILLISECONDS); - assertEquals(0, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); assertNotNull(msg); assertEquals(7, msg.getData().length); assertTrue(msg.getSubject().indexOf('.') < msg.getSubject().lastIndexOf('.')); @@ -168,8 +182,7 @@ public void testMultipleRequest() throws Exception { @Test public void testMultipleReplies() throws Exception { - Options.Builder builder = new Options.Builder().turnOnAdvancedStats(); - + Options.Builder builder = optionsBuilder().turnOnAdvancedStats(); runInServer(builder, nc -> { AtomicInteger requests = new AtomicInteger(); MessageHandler handler = (msg) -> { requests.incrementAndGet(); nc.publish(msg.getReplyTo(), null); }; @@ -177,12 +190,14 @@ public void testMultipleReplies() throws Exception { Dispatcher d2 = nc.createDispatcher(handler); Dispatcher d3 = nc.createDispatcher(handler); Dispatcher d4 = nc.createDispatcher(msg -> { sleep(5000); handler.onMessage(msg); }); - d1.subscribe(SUBJECT); - d2.subscribe(SUBJECT); - d3.subscribe(SUBJECT); - d4.subscribe(SUBJECT); - Message reply = nc.request(SUBJECT, null, Duration.ofSeconds(2)); + String subject = random(); + d1.subscribe(subject); + d2.subscribe(subject); + d3.subscribe(subject); + d4.subscribe(subject); + + Message reply = nc.request(subject, null, Duration.ofSeconds(2)); assertNotNull(reply); sleep(2000); assertEquals(3, requests.get()); @@ -202,7 +217,7 @@ public void testMultipleReplies() throws Exception { @Test public void testManualRequestReplyAndPublishSignatures() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); Connection nc = Nats.connect(ts.getURI())) { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); @@ -238,8 +253,8 @@ public void testManualRequestReplyAndPublishSignatures() throws Exception { @Test public void testRequestWithCustomInboxPrefix() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(new Options.Builder().inboxPrefix("myinbox").server(ts.getURI()).maxReconnects(0).build())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = Nats.connect(optionsBuilder(ts).inboxPrefix("myinbox").maxReconnects(0).build())) { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); Dispatcher d = nc.createDispatcher((msg) -> { @@ -251,7 +266,7 @@ public void testRequestWithCustomInboxPrefix() throws Exception { Future incoming = nc.request("subject", null); Message msg = incoming.get(500, TimeUnit.MILLISECONDS); - assertEquals(0, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); assertNotNull(msg); assertEquals(0, msg.getData().length); assertTrue(msg.getSubject().indexOf('.') < msg.getSubject().lastIndexOf('.')); @@ -260,8 +275,8 @@ public void testRequestWithCustomInboxPrefix() throws Exception { @Test public void testRequireCleanupOnTimeoutNoNoResponders() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()) + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts) .requestCleanupInterval(Duration.ofHours(1)) .noNoResponders().build(); @@ -272,7 +287,7 @@ public void testRequireCleanupOnTimeoutNoNoResponders() throws Exception { assertThrows(TimeoutException.class, () -> nc.request("subject", null).get(100, TimeUnit.MILLISECONDS)); - assertEquals(1, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(1, nc.getStatistics().getOutstandingRequests()); } finally { nc.close(); assertEquals(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); @@ -282,24 +297,24 @@ public void testRequireCleanupOnTimeoutNoNoResponders() throws Exception { @Test public void testRequireCleanupOnTimeoutCleanCompletable() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { + try (NatsTestServer ts = new NatsTestServer()) { long cleanupInterval = 100; - Options options = new Options.Builder().server(ts.getURI()) + Options options = optionsBuilder(ts) .requestCleanupInterval(Duration.ofMillis(cleanupInterval)) .noNoResponders().build(); NatsConnection nc = (NatsConnection) Nats.connect(options); try { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - NatsMessage nm = NatsMessage.builder().subject(SUBJECT).data(dataBytes(2)).build(); + NatsMessage nm = NatsMessage.builder().subject(random()).data(dataBytes(2)).build(); CompletableFuture future = nc.requestWithTimeout(nm, Duration.ofMillis(cleanupInterval)); Thread.sleep(2 * cleanupInterval + Options.DEFAULT_CONNECTION_TIMEOUT.toMillis()); assertTrue(future.isCompletedExceptionally()); - assertEquals(0, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); } finally { nc.close(); @@ -311,9 +326,9 @@ public void testRequireCleanupOnTimeoutCleanCompletable() throws Exception { @Test public void testSimpleRequestWithTimeout() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) + try (NatsTestServer ts = new NatsTestServer()) { - Options options = new Options.Builder().server(ts.getURI()).requestCleanupInterval(Duration.ofHours(1)).build(); + Options options = optionsBuilder(ts).requestCleanupInterval(Duration.ofHours(1)).build(); Connection nc = Nats.connect(options); try { @@ -329,23 +344,23 @@ public void testSimpleRequestWithTimeout() throws Exception { nc.publish(msg.getReplyTo(), null); } }); - d.subscribe(SUBJECT); + String subject = random(); + d.subscribe(subject); - NatsMessage outMsg = NatsMessage.builder().subject(SUBJECT).data(dataBytes(2)).build(); - CompletableFuture incoming = nc.requestWithTimeout("subject", null, Duration.ofMillis(100)); + CompletableFuture incoming = nc.requestWithTimeout(subject, null, Duration.ofMillis(100)); Message msg = incoming.get(500, TimeUnit.MILLISECONDS); - assertEquals(0, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); assertNotNull(msg); assertEquals(0, msg.getData().length); assertTrue(msg.getSubject().indexOf('.') < msg.getSubject().lastIndexOf('.')); - incoming = nc.requestWithTimeout("subject", new Headers().put("foo", "bar"), null, Duration.ofMillis(100)); + incoming = nc.requestWithTimeout(subject, new Headers().put("foo", "bar"), null, Duration.ofMillis(100)); msg = incoming.get(500, TimeUnit.MILLISECONDS); - assertEquals(0, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); assertNotNull(msg); assertEquals(0, msg.getData().length); assertTrue(msg.getSubject().indexOf('.') < msg.getSubject().lastIndexOf('.')); @@ -362,10 +377,10 @@ public void testSimpleRequestWithTimeout() throws Exception { @Test public void testSimpleRequestWithTimeoutSlowProducer() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) + try (NatsTestServer ts = new NatsTestServer()) { long cleanupInterval = 10; - Options options = new Options.Builder().server(ts.getURI()).requestCleanupInterval(Duration.ofMillis(cleanupInterval)).build(); + Options options = optionsBuilder(ts).requestCleanupInterval(Duration.ofMillis(cleanupInterval)).build(); NatsConnection nc = (NatsConnection) Nats.connect(options); try { @@ -380,9 +395,9 @@ public void testSimpleRequestWithTimeoutSlowProducer() throws Exception { Thread.sleep(delay); nc.publish(msg.getReplyTo(), null); }); - d.subscribe(SUBJECT); + String subject = random(); + d.subscribe(subject); - NatsMessage nm = NatsMessage.builder().subject(SUBJECT).data(dataBytes(2)).build(); CompletableFuture incoming = nc.requestWithTimeout("subject", null, Duration.ofMillis(cleanupInterval)); assertThrows(CancellationException.class, () -> incoming.get(delay, TimeUnit.MILLISECONDS)); @@ -396,8 +411,8 @@ public void testSimpleRequestWithTimeoutSlowProducer() throws Exception { @Test public void testRequireCleanupOnCancelFromNoResponders() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()) + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts) .requestCleanupInterval(Duration.ofHours(1)).build(); Connection nc = Nats.connect(options); @@ -405,7 +420,7 @@ public void testRequireCleanupOnCancelFromNoResponders() throws Exception { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); assertThrows(CancellationException.class, () -> nc.request("subject", null).get(100, TimeUnit.MILLISECONDS)); - assertEquals(0, ((NatsStatistics) nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); } finally { nc.close(); assertEquals(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); @@ -416,15 +431,15 @@ public void testRequireCleanupOnCancelFromNoResponders() throws Exception { @Test public void testRequireCleanupWithTimeoutNoResponders() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()) + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts) .requestCleanupInterval(Duration.ofHours(1)).build(); Connection nc = Nats.connect(options); try { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); assertThrows(CancellationException.class, () -> nc.requestWithTimeout("subject", null, Duration.ofMillis(100)).get(100, TimeUnit.MILLISECONDS)); - assertEquals(0, ((NatsStatistics) nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); } finally { nc.close(); assertEquals(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); @@ -435,8 +450,8 @@ public void testRequireCleanupWithTimeoutNoResponders() throws Exception { @Test public void testRequireCleanupWithTimeoutNoNoResponders() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()) + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts) .requestCleanupInterval(Duration.ofHours(1)) .noNoResponders().build(); @@ -446,7 +461,7 @@ public void testRequireCleanupWithTimeoutNoNoResponders() throws Exception { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); assertThrows(TimeoutException.class, () -> nc.requestWithTimeout("subject", null, Duration.ofMillis(100)).get(100, TimeUnit.MILLISECONDS)); - assertEquals(1, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(1, nc.getStatistics().getOutstandingRequests()); } finally { nc.close(); @@ -457,14 +472,14 @@ public void testRequireCleanupWithTimeoutNoNoResponders() throws Exception { @Test public void testRequireCleanupOnCancel() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()).requestCleanupInterval(Duration.ofHours(1)).build(); + try (NatsTestServer ts = new NatsTestServer()) { + Options options = optionsBuilder(ts).requestCleanupInterval(Duration.ofHours(1)).build(); Connection nc = Nats.connect(options); try { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); NatsRequestCompletableFuture incoming = (NatsRequestCompletableFuture)nc.request("subject", null); incoming.cancel(true); - NatsStatistics stats = ((NatsStatistics)nc.getStatistics()); + NatsStatistics stats = (NatsStatistics)nc.getStatistics(); // sometimes if the machine is very fast, the request gets a reply (even if it's no responders) // so there is either an outstanding or a received assertEquals(1, stats.getOutstandingRequests() + stats.getRepliesReceived()); @@ -478,9 +493,9 @@ public void testRequireCleanupOnCancel() throws Exception { @Test public void testCleanupTimerWorks() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { + try (NatsTestServer ts = new NatsTestServer()) { long cleanupInterval = 50; - Options options = new Options.Builder().server(ts.getURI()).requestCleanupInterval(Duration.ofMillis(cleanupInterval)).build(); + Options options = optionsBuilder(ts).requestCleanupInterval(Duration.ofMillis(cleanupInterval)).build(); Connection nc = Nats.connect(options); try { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); @@ -496,7 +511,7 @@ public void testCleanupTimerWorks() throws Exception { long timeout = 10 * cleanupInterval; sleep(sleep); - assertTrueByTimeout(timeout, () -> ((NatsStatistics)nc.getStatistics()).getOutstandingRequests() == 0); + assertTrueByTimeout(timeout, () -> nc.getStatistics().getOutstandingRequests() == 0); // Make sure it is still running incoming = nc.request("subject", null); @@ -507,7 +522,7 @@ public void testCleanupTimerWorks() throws Exception { incoming.cancel(true); sleep(sleep); - assertTrueByTimeout(timeout, () -> ((NatsStatistics)nc.getStatistics()).getOutstandingRequests() == 0); + assertTrueByTimeout(timeout, () -> nc.getStatistics().getOutstandingRequests() == 0); } finally { nc.close(); assertEquals(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); @@ -517,10 +532,10 @@ public void testCleanupTimerWorks() throws Exception { @Test public void testRequestsVsCleanup() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { + try (NatsTestServer ts = new NatsTestServer()) { long cleanupInterval = 50; int msgCount = 100; - Options options = new Options.Builder().server(ts.getURI()).requestCleanupInterval(Duration.ofMillis(cleanupInterval)).build(); + Options options = optionsBuilder(ts).requestCleanupInterval(Duration.ofMillis(cleanupInterval)).build(); Connection nc = Nats.connect(options); try { assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); @@ -542,7 +557,7 @@ public void testRequestsVsCleanup() throws Exception { } assertTrue((end-start) > 2 * cleanupInterval * 1_000_000); - assertTrue(0 >= ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertTrue(0 >= nc.getStatistics().getOutstandingRequests()); } finally { nc.close(); assertEquals(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); @@ -552,7 +567,7 @@ public void testRequestsVsCleanup() throws Exception { @Test public void testDelayInPickingUpFuture() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { + try (NatsTestServer ts = new NatsTestServer()) { int msgCount = 100; ArrayList> messages = new ArrayList<>(); Connection nc = Nats.connect(ts.getURI()); @@ -574,7 +589,7 @@ public void testDelayInPickingUpFuture() throws Exception { assertEquals(1, msg.getData().length); } - assertEquals(0, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); } finally { nc.close(); assertEquals(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); @@ -584,44 +599,28 @@ public void testDelayInPickingUpFuture() throws Exception { @Test public void testOldStyleRequest() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { - Options options = new Options.Builder().server(ts.getURI()).oldRequestStyle().build(); - Connection nc = Nats.connect(options); - try { - assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - - Dispatcher d = nc.createDispatcher((msg) -> nc.publish(msg.getReplyTo(), null)); - d.subscribe("subject"); + runInLrServer(Options.builder().oldRequestStyle(), nc -> { + String subject = random(); + Dispatcher d = nc.createDispatcher((msg) -> nc.publish(msg.getReplyTo(), null)); + d.subscribe(subject); - Future incoming = nc.request("subject", null); - Message msg = incoming.get(500, TimeUnit.MILLISECONDS); + Future incoming = nc.request(subject, null); + Message msg = incoming.get(500, TimeUnit.MILLISECONDS); - assertEquals(0, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); - assertNotNull(msg); - assertEquals(0, msg.getData().length); - assertEquals(msg.getSubject().indexOf('.'), msg.getSubject().lastIndexOf('.')); - } finally { - nc.close(); - assertEquals(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); - } - } + assertEquals(0, nc.getStatistics().getOutstandingRequests()); + assertNotNull(msg); + assertEquals(0, msg.getData().length); + assertEquals(msg.getSubject().indexOf('.'), msg.getSubject().lastIndexOf('.')); + }); } @Test public void testBuffersResize() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { + try (NatsTestServer ts = new NatsTestServer()) { int initialSize = 128; int messageSize = 1024; - Options options = new Options.Builder(). - server(ts.getURI()). - bufferSize(initialSize). - connectionTimeout(Duration.ofSeconds(10)). - build(); - - Connection nc = Nats.connect(options); - try { - assertEquals(Connection.Status.CONNECTED, nc.getStatus(), "Connected Status"); - + Options options = optionsBuilder(ts).bufferSize(initialSize).connectionTimeout(Duration.ofSeconds(10)).build(); + try (Connection nc = standardConnectionWait(options)) { Dispatcher d = nc.createDispatcher((msg) -> nc.publish(msg.getReplyTo(), msg.getData())); d.subscribe("subject"); @@ -634,12 +633,9 @@ public void testBuffersResize() throws Exception { exp.printStackTrace(); } - assertEquals(0, ((NatsStatistics)nc.getStatistics()).getOutstandingRequests()); + assertEquals(0, nc.getStatistics().getOutstandingRequests()); assertNotNull(msg); assertEquals(messageSize, msg.getData().length); - } finally { - nc.close(); - assertEquals(Connection.Status.CLOSED, nc.getStatus(), "Closed Status"); } } } @@ -647,7 +643,7 @@ public void testBuffersResize() throws Exception { @Test public void throwsIfClosed() { assertThrows(IllegalStateException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); Connection nc = Nats.connect(ts.getURI())) { nc.close(); nc.request("subject", null); @@ -659,8 +655,9 @@ public void throwsIfClosed() { @Test public void testThrowsWithoutSubject() { assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); - Connection nc = Nats.connect(ts.getURI())) { + try (NatsTestServer ts = new NatsTestServer(); + Connection nc = Nats.connect(ts.getURI())) { + //noinspection DataFlowIssue nc.request((String)null, null); fail(); } @@ -670,7 +667,7 @@ public void testThrowsWithoutSubject() { @Test public void testThrowsEmptySubject() { assertThrows(IllegalArgumentException.class, () -> { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); Connection nc = Nats.connect(ts.getURI())) { nc.request("", null); fail(); @@ -726,7 +723,7 @@ public void testNatsRequestCompletableFuture() throws Exception { assertTrue(ftot.useTimeoutException()); ftot.cancelTimedOut(); ExecutionException ee = assertThrows(ExecutionException.class, ftot::get); - assertTrue(ee.getCause() instanceof TimeoutException); + assertInstanceOf(TimeoutException.class, ee.getCause()); } @Test @@ -742,7 +739,7 @@ public void testNatsImplAndEmptyStatsCoverage() { @Test public void testCancelledFutureMustNotErrorOnCleanResponses() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false)) { + try (NatsTestServer ts = new NatsTestServer()) { Options options = Options.builder() .server(ts.getURI()) .noNoResponders() @@ -754,9 +751,7 @@ public void testCancelledFutureMustNotErrorOnCleanResponses() throws Exception { future.cancelClosing(); // Future is already cancelled, collecting it shouldn't result in an exception being thrown. - assertDoesNotThrow(() -> { - nc.cleanResponses(false); - }); + assertDoesNotThrow(() -> nc.cleanResponses(false)); } } } diff --git a/src/test/java/io/nats/client/impl/ServerPoolTests.java b/src/test/java/io/nats/client/impl/ServerPoolTests.java index 776aae337..a9c1fd912 100644 --- a/src/test/java/io/nats/client/impl/ServerPoolTests.java +++ b/src/test/java/io/nats/client/impl/ServerPoolTests.java @@ -21,6 +21,7 @@ import java.net.URISyntaxException; import java.util.*; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; import static org.junit.jupiter.api.Assertions.*; public class ServerPoolTests extends TestBase { @@ -41,7 +42,7 @@ public void testPoolOptions() throws URISyntaxException { NatsUri lastConnectedServer = new NatsUri(BOOT_ONE); // testing that the expected show up in the pool - Options o = new Options.Builder().servers(bootstrap).build(); + Options o = optionsBuilder(bootstrap).build(); NatsServerPool nsp = newNatsServerPool(o, null, discoveredServers); validateNslp(nsp, null, false, combined); @@ -50,7 +51,7 @@ public void testPoolOptions() throws URISyntaxException { validateNslp(nsp, lastConnectedServer, false, combined); // testing that noRandomize maintains order - o = new Options.Builder().noRandomize().servers(bootstrap).build(); + o = optionsBuilder(bootstrap).noRandomize().build(); nsp = newNatsServerPool(o, null, discoveredServers); validateNslp(nsp, null, true, combined); @@ -59,19 +60,19 @@ public void testPoolOptions() throws URISyntaxException { validateNslp(nsp, lastConnectedServer, true, combined); // testing that ignoreDiscoveredServers ignores discovered servers - o = new Options.Builder().ignoreDiscoveredServers().servers(bootstrap).build(); + o = optionsBuilder(bootstrap).ignoreDiscoveredServers().build(); nsp = newNatsServerPool(o, null, discoveredServers); validateNslp(nsp, null, false, BOOT_ONE, BOOT_TWO); // testing that duplicates don't get added String[] secureAndNotSecure = new String[]{BOOT_ONE, BOOT_ONE_SECURE}; String[] secureBootstrap = new String[]{BOOT_ONE_SECURE}; - o = new Options.Builder().servers(secureAndNotSecure).build(); + o = optionsBuilder(secureAndNotSecure).build(); nsp = newNatsServerPool(o, null, null); validateNslp(nsp, null, false, secureBootstrap); secureAndNotSecure = new String[]{BOOT_ONE_SECURE, BOOT_ONE}; - o = new Options.Builder().servers(secureAndNotSecure).build(); + o = optionsBuilder(secureAndNotSecure).build(); nsp = newNatsServerPool(o, null, null); validateNslp(nsp, null, false, secureBootstrap); } @@ -81,7 +82,7 @@ public void testMaxReconnects() throws URISyntaxException { NatsUri failed = new NatsUri(BOOT_ONE); // testing that servers that fail max times and is removed - Options o = new Options.Builder().server(BOOT_ONE).maxReconnects(3).build(); + Options o = optionsBuilder(BOOT_ONE).maxReconnects(3).build(); NatsServerPool nsp = newNatsServerPool(o, null, null); for (int x = 0; x < 4; x++) { nsp.nextServer(); @@ -95,7 +96,7 @@ public void testMaxReconnects() throws URISyntaxException { validateNslp(nsp, null, false, BOOT_ONE); // testing that servers that fail max times and is removed - o = new Options.Builder().server(BOOT_ONE).maxReconnects(0).build(); + o = optionsBuilder(BOOT_ONE).maxReconnects(0).build(); nsp = newNatsServerPool(o, null, null); nsp.nextServer(); validateNslp(nsp, null, false, BOOT_ONE); @@ -110,7 +111,7 @@ public void testMaxReconnects() throws URISyntaxException { @Test public void testPruning() throws URISyntaxException { // making sure that pruning happens. get baseline - Options o = new Options.Builder().servers(bootstrap).maxReconnects(0).build(); + Options o = optionsBuilder(bootstrap).maxReconnects(0).build(); NatsServerPool nsp = newNatsServerPool(o, null, discoveredServers); validateNslp(nsp, null, false, combined); @@ -128,13 +129,13 @@ public void testPruning() throws URISyntaxException { public void testResolvingHostname() throws URISyntaxException { // resolving host name is false NatsUri ngs = new NatsUri(HOST_THAT_CAN_BE_RESOLVED); - Options o = new Options.Builder().noResolveHostnames().build(); + Options o = optionsBuilder().noResolveHostnames().build(); NatsServerPool nsp = newNatsServerPool(o, null, null); List resolved = nsp.resolveHostToIps(HOST_THAT_CAN_BE_RESOLVED); assertNull(resolved); // resolving host name is true - o = new Options.Builder().build(); + o = optionsBuilder().build(); nsp = newNatsServerPool(o, null, null); resolved = nsp.resolveHostToIps(HOST_THAT_CAN_BE_RESOLVED); assertNotNull(resolved); @@ -150,7 +151,7 @@ private static NatsServerPool newNatsServerPool(Options o, NatsUri last, List { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - - assertThrows(JetStreamApiException.class, () -> nc.getStreamContext(stream())); - assertThrows(JetStreamApiException.class, () -> nc.getStreamContext(stream(), JetStreamOptions.DEFAULT_JS_OPTIONS)); - assertThrows(JetStreamApiException.class, () -> js.getStreamContext(stream())); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - StreamContext streamContext = nc.getStreamContext(tsc.stream); - assertEquals(tsc.stream, streamContext.getStreamName()); - _testStreamContext(jsm, js, tsc, streamContext); - - tsc = new TestingStreamContainer(jsm); - streamContext = js.getStreamContext(tsc.stream); - assertEquals(tsc.stream, streamContext.getStreamName()); - _testStreamContext(jsm, js, tsc, streamContext); + public void testStreamContextErrors() throws Exception { + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jsm, js) -> { + assertThrows(JetStreamApiException.class, () -> nc.getStreamContext(random())); + assertThrows(JetStreamApiException.class, () -> nc.getStreamContext(random(), JetStreamOptions.DEFAULT_JS_OPTIONS)); + assertThrows(JetStreamApiException.class, () -> js.getStreamContext(random())); }); } - private void _testStreamContext(JetStreamManagement jsm, JetStream js, TestingStreamContainer tsc, StreamContext streamContext) throws IOException, JetStreamApiException { - String durable = durable(); + @Test + public void testStreamContextFromConnection() throws Exception { + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { + StreamContext streamContext = nc.getStreamContext(jstc.stream); + assertEquals(jstc.stream, streamContext.getStreamName()); + _testStreamContext(jstc, streamContext); + }); + } + + @Test + public void testStreamContextFromContext() throws Exception { + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { + StreamContext streamContext = jstc.js.getStreamContext(jstc.stream); + assertEquals(jstc.stream, streamContext.getStreamName()); + _testStreamContext(jstc, streamContext); + }); + } + + private void _testStreamContext(JetStreamTestingContext jstc, StreamContext streamContext) throws IOException, JetStreamApiException { + String durable = random(); assertThrows(JetStreamApiException.class, () -> streamContext.getConsumerContext(durable)); assertThrows(JetStreamApiException.class, () -> streamContext.deleteConsumer(durable)); ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(durable).build(); ConsumerContext consumerContext = streamContext.createOrUpdateConsumer(cc); ConsumerInfo ci = consumerContext.getConsumerInfo(); - assertEquals(tsc.stream, ci.getStreamName()); + assertEquals(jstc.stream, ci.getStreamName()); assertEquals(durable, ci.getName()); ci = streamContext.getConsumerInfo(durable); assertNotNull(ci); - assertEquals(tsc.stream, ci.getStreamName()); + assertEquals(jstc.stream, ci.getStreamName()); assertEquals(durable, ci.getName()); assertEquals(1, streamContext.getConsumerNames().size()); @@ -87,12 +95,12 @@ private void _testStreamContext(JetStreamManagement jsm, JetStream js, TestingSt ci = consumerContext.getConsumerInfo(); assertNotNull(ci); - assertEquals(tsc.stream, ci.getStreamName()); + assertEquals(jstc.stream, ci.getStreamName()); assertEquals(durable, ci.getName()); ci = consumerContext.getCachedConsumerInfo(); assertNotNull(ci); - assertEquals(tsc.stream, ci.getStreamName()); + assertEquals(jstc.stream, ci.getStreamName()); assertEquals(durable, ci.getName()); streamContext.deleteConsumer(durable); @@ -101,12 +109,12 @@ private void _testStreamContext(JetStreamManagement jsm, JetStream js, TestingSt assertThrows(JetStreamApiException.class, () -> streamContext.deleteConsumer(durable)); // coverage - js.publish(tsc.subject(), "one".getBytes()); - js.publish(tsc.subject(), "two".getBytes()); - js.publish(tsc.subject(), "three".getBytes()); - js.publish(tsc.subject(), "four".getBytes()); - js.publish(tsc.subject(), "five".getBytes()); - js.publish(tsc.subject(), "six".getBytes()); + jstc.js.publish(jstc.subject(), "one".getBytes()); + jstc.js.publish(jstc.subject(), "two".getBytes()); + jstc.js.publish(jstc.subject(), "three".getBytes()); + jstc.js.publish(jstc.subject(), "four".getBytes()); + jstc.js.publish(jstc.subject(), "five".getBytes()); + jstc.js.publish(jstc.subject(), "six".getBytes()); assertTrue(streamContext.deleteMessage(3)); assertTrue(streamContext.deleteMessage(4, true)); @@ -114,13 +122,13 @@ private void _testStreamContext(JetStreamManagement jsm, JetStream js, TestingSt MessageInfo mi = streamContext.getMessage(1); assertEquals(1, mi.getSeq()); - mi = streamContext.getFirstMessage(tsc.subject()); + mi = streamContext.getFirstMessage(jstc.subject()); assertEquals(1, mi.getSeq()); - mi = streamContext.getLastMessage(tsc.subject()); + mi = streamContext.getLastMessage(jstc.subject()); assertEquals(6, mi.getSeq()); - mi = streamContext.getNextMessage(3, tsc.subject()); + mi = streamContext.getNextMessage(3, jstc.subject()); assertEquals(5, mi.getSeq()); assertNotNull(streamContext.getStreamInfo()); @@ -129,25 +137,25 @@ private void _testStreamContext(JetStreamManagement jsm, JetStream js, TestingSt streamContext.purge(PurgeOptions.builder().sequence(5).build()); assertThrows(JetStreamApiException.class, () -> streamContext.getMessage(1)); - StreamInfo si = jsm.getStreamInfo(tsc.stream); + StreamInfo si = jstc.jsm.getStreamInfo(jstc.stream); assertEquals(2, si.getStreamState().getMsgCount()); assertEquals(5, si.getStreamState().getFirstSequence()); assertEquals(6, si.getStreamState().getLastSequence()); - js.publish(tsc.subject(), "aone".getBytes()); - js.publish(tsc.subject(), "btwo".getBytes()); - js.publish(tsc.subject(), "cthree".getBytes()); - js.publish(tsc.subject(), "dfour".getBytes()); - js.publish(tsc.subject(), "efive".getBytes()); - js.publish(tsc.subject(), "fsix".getBytes()); + jstc.js.publish(jstc.subject(), "aone".getBytes()); + jstc.js.publish(jstc.subject(), "btwo".getBytes()); + jstc.js.publish(jstc.subject(), "cthree".getBytes()); + jstc.js.publish(jstc.subject(), "dfour".getBytes()); + jstc.js.publish(jstc.subject(), "efive".getBytes()); + jstc.js.publish(jstc.subject(), "fsix".getBytes()); - si = jsm.getStreamInfo(tsc.stream); + si = jstc.jsm.getStreamInfo(jstc.stream); assertEquals(8, si.getStreamState().getMsgCount()); assertEquals(12, si.getStreamState().getLastSequence()); streamContext.purge(); - si = jsm.getStreamInfo(tsc.stream); + si = jstc.jsm.getStreamInfo(jstc.stream); assertEquals(0, si.getStreamState().getMsgCount()); assertEquals(12, si.getStreamState().getLastSequence()); } @@ -186,57 +194,52 @@ private String validateConsumerNameForOrdered(BaseConsumerContext bcc, MessageCo static int FETCH_ORDERED = 3; @Test public void testFetch() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - TestingStreamContainer tsc = new TestingStreamContainer(nc); - JetStream js = nc.jetStream(); + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { for (int x = 1; x <= 20; x++) { - js.publish(tsc.subject(), ("test-fetch-msg-" + x).getBytes()); + jstc.js.publish(jstc.subject(), ("test-fetch-msg-" + x).getBytes()); } for (int f = FETCH_EPHEMERAL; f <= FETCH_ORDERED; f++) { // 1. Different fetch sizes demonstrate expiration behavior // 1A. equal number of messages to the fetch size - _testFetch("1A", nc, tsc, 20, 0, 20, f, false); + _testFetch("1A", jstc, 20, 0, 20, f, false); // 1B. more messages than the fetch size - _testFetch("1B", nc, tsc, 10, 0, 10, f, false); + _testFetch("1B", jstc, 10, 0, 10, f, false); // 1C. fewer messages than the fetch size - _testFetch("1C", nc, tsc, 40, 0, 40, f, false); + _testFetch("1C", jstc, 40, 0, 40, f, false); // 1D. simple-consumer-40msgs was created in 1C and has no messages available - _testFetch("1D", nc, tsc, 40, 0, 40, f, false); + _testFetch("1D", jstc, 40, 0, 40, f, false); // 2. Different max bytes sizes demonstrate expiration behavior // - each test message is approximately 100 bytes // 2A. max bytes are reached before message count - _testFetch("2A", nc, tsc, 0, 750, 20, f, false); + _testFetch("2A", jstc, 0, 750, 20, f, false); // 2B. fetch size is reached before byte count - _testFetch("2B", nc, tsc, 10, 1500, 10, f, false); + _testFetch("2B", jstc, 10, 1500, 10, f, false); if (f == FETCH_DURABLE) { // this is long-running, so don't want to test every time // 2C. fewer bytes than the byte count - _testFetch("2C", nc, tsc, 0, 3000, 40, f, false); + _testFetch("2C", jstc, 0, 3000, 40, f, false); } else if (f == FETCH_ORDERED) { // just to get coverage of testing with a consumer name prefix - _testFetch("1A", nc, tsc, 20, 0, 20, f, true); - _testFetch("2A", nc, tsc, 0, 750, 20, f, true); + _testFetch("1A", jstc, 20, 0, 20, f, true); + _testFetch("2A", jstc, 0, 750, 20, f, true); } } }); } - private void _testFetch(String label, Connection nc, TestingStreamContainer tsc, int maxMessages, int maxBytes, int testAmount, int fetchType, boolean useConsumerPrefix) throws Exception { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - - StreamContext ctx = js.getStreamContext(tsc.stream); + private void _testFetch(String label, JetStreamTestingContext jstc, int maxMessages, int maxBytes, int testAmount, int fetchType, boolean useConsumerPrefix) throws Exception { + StreamContext ctx = jstc.js.getStreamContext(jstc.stream); String consumerName = null; String consumerNamePrefix = null; @@ -244,7 +247,7 @@ private void _testFetch(String label, Connection nc, TestingStreamContainer tsc, if (fetchType == FETCH_ORDERED) { OrderedConsumerConfiguration occ = new OrderedConsumerConfiguration(); if (useConsumerPrefix) { - consumerNamePrefix = prefix(); + consumerNamePrefix = random(); occ.consumerNamePrefix(consumerNamePrefix); } consumerContext = ctx.createOrderedConsumer(occ); @@ -263,7 +266,7 @@ private void _testFetch(String label, Connection nc, TestingStreamContainer tsc, consumerName = consumerName + "E"; cc = builder.name(consumerName).inactiveThreshold(10_000).build(); } - jsm.addOrUpdateConsumer(tsc.stream, cc); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); consumerContext = ctx.getConsumerContext(consumerName); assertEquals(consumerName, consumerContext.getConsumerName()); } @@ -324,25 +327,20 @@ else if (maxBytes == 0) { private String generateConsumerName(int maxMessages, int maxBytes) { return maxBytes == 0 - ? variant() + "-" + maxMessages + "msgs" - : variant() + "-" + maxBytes + "bytes-" + maxMessages + "msgs"; + ? random() + "-" + maxMessages + "msgs" + : random() + "-" + maxBytes + "bytes-" + maxMessages + "msgs"; } @Test public void testFetchNoWaitPlusExpires() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - JetStream js = nc.jetStream(); - - jsm.addOrUpdateConsumer(tsc.stream, ConsumerConfiguration.builder() - .name(tsc.consumerName()) + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { + jstc.jsm.addOrUpdateConsumer(jstc.stream, ConsumerConfiguration.builder() + .name(jstc.consumerName()) .inactiveThreshold(100000) // I could have used a durable, but this is long enough for the test - .filterSubject(tsc.subject()) + .filterSubject(jstc.subject()) .build()); - ConsumerContext cc = nc.getConsumerContext(tsc.stream, tsc.consumerName()); + ConsumerContext cc = nc.getConsumerContext(jstc.stream, jstc.consumerName()); FetchConsumeOptions fco = FetchConsumeOptions.builder().maxMessages(10).noWait().build(); // No Wait, No Messages @@ -351,14 +349,14 @@ public void testFetchNoWaitPlusExpires() throws Exception { assertEquals(0, count); // no messages // No Wait, One Message - js.publish(tsc.subject(), "DATA-A".getBytes()); + jstc.js.publish(jstc.subject(), "DATA-A".getBytes()); fc = cc.fetch(fco); count = readMessages(fc); assertEquals(1, count); // 1 message // No Wait, Two Messages - js.publish(tsc.subject(), "DATA-B".getBytes()); - js.publish(tsc.subject(), "DATA-C".getBytes()); + jstc.js.publish(jstc.subject(), "DATA-B".getBytes()); + jstc.js.publish(jstc.subject(), "DATA-C".getBytes()); fc = cc.fetch(fco); count = readMessages(fc); assertEquals(2, count); // 2 messages @@ -372,9 +370,9 @@ public void testFetchNoWaitPlusExpires() throws Exception { // With Expires, One to Three Message fco = FetchConsumeOptions.builder().maxMessages(10).noWaitExpiresIn(1000).build(); fc = cc.fetch(fco); - js.publish(tsc.subject(), "DATA-D".getBytes()); - js.publish(tsc.subject(), "DATA-E".getBytes()); - js.publish(tsc.subject(), "DATA-F".getBytes()); + jstc.js.publish(jstc.subject(), "DATA-D".getBytes()); + jstc.js.publish(jstc.subject(), "DATA-E".getBytes()); + jstc.js.publish(jstc.subject(), "DATA-F".getBytes()); count = readMessages(fc); // With Long (Default) Expires, Leftovers @@ -399,52 +397,42 @@ private int readMessages(FetchConsumer fc) throws InterruptedException, JetStrea @Test public void testIterableConsumer() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - JetStream js = nc.jetStream(); - + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { // Pre define a consumer - ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(tsc.consumerName()).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(jstc.consumerName()).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); // Consumer[Context] - ConsumerContext consumerContext = js.getConsumerContext(tsc.stream, tsc.consumerName()); - validateConsumerName(consumerContext, null, tsc.consumerName()); + ConsumerContext consumerContext = jstc.js.getConsumerContext(jstc.stream, jstc.consumerName()); + validateConsumerName(consumerContext, null, jstc.consumerName()); int stopCount = 500; // create the consumer then use it try (IterableConsumer consumer = consumerContext.iterate()) { - validateConsumerName(consumerContext, consumer, tsc.consumerName()); - _testIterableBasic(js, stopCount, consumer, tsc.subject()); + validateConsumerName(consumerContext, consumer, jstc.consumerName()); + _testIterableBasic(jstc.js, stopCount, consumer, jstc.subject()); } // coverage IterableConsumer consumer = consumerContext.iterate(ConsumeOptions.DEFAULT_CONSUME_OPTIONS); - validateConsumerName(consumerContext, consumer, tsc.consumerName()); + validateConsumerName(consumerContext, consumer, jstc.consumerName()); consumer.close(); + //noinspection DataFlowIssue assertThrows(IllegalArgumentException.class, () -> consumerContext.iterate(null)); }); } @Test public void testOrderedConsumerDeliverPolices() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - // Setup - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - - jsPublish(js, tsc.subject(), 101, 3, 100); - ZonedDateTime startTime = getStartTimeFirstMessage(js, tsc); + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { + jsPublish(jstc.js, jstc.subject(), 101, 3, 100); + ZonedDateTime startTime = getStartTimeFirstMessage(jstc); - StreamContext sctx = nc.getStreamContext(tsc.stream); + StreamContext sctx = nc.getStreamContext(jstc.stream); // test a start time OrderedConsumerConfiguration occ = new OrderedConsumerConfiguration() - .filterSubject(tsc.subject()) + .filterSubject(jstc.subject()) .deliverPolicy(DeliverPolicy.ByStartTime) .startTime(startTime); OrderedConsumerContext occtx = sctx.createOrderedConsumer(occ); @@ -455,7 +443,7 @@ public void testOrderedConsumerDeliverPolices() throws Exception { // test a start sequence occ = new OrderedConsumerConfiguration() - .filterSubject(tsc.subject()) + .filterSubject(jstc.subject()) .deliverPolicy(DeliverPolicy.ByStartSequence) .startSequence(2); occtx = sctx.createOrderedConsumer(occ); @@ -491,29 +479,25 @@ public void testOrderedConsumerCoverage() { @Test public void testOrderedIterableConsumerBasic() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - StreamContext sctx = nc.getStreamContext(tsc.stream); + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { + StreamContext sctx = nc.getStreamContext(jstc.stream); int stopCount = 500; - OrderedConsumerConfiguration occ = new OrderedConsumerConfiguration().filterSubject(tsc.subject()); + OrderedConsumerConfiguration occ = new OrderedConsumerConfiguration().filterSubject(jstc.subject()); OrderedConsumerContext occtx = sctx.createOrderedConsumer(occ); assertNull(occtx.getConsumerName()); try (IterableConsumer consumer = occtx.iterate()) { validateConsumerNameForOrdered(occtx, consumer, null); - _testIterableBasic(js, stopCount, consumer, tsc.subject()); + _testIterableBasic(jstc.js, stopCount, consumer, jstc.subject()); } - String consumerNamePrefix = prefix(); - occ = new OrderedConsumerConfiguration().filterSubject(tsc.subject()).consumerNamePrefix(consumerNamePrefix); + String consumerNamePrefix = random(); + occ = new OrderedConsumerConfiguration().filterSubject(jstc.subject()).consumerNamePrefix(consumerNamePrefix); occtx = sctx.createOrderedConsumer(occ); assertNull(occtx.getConsumerName()); try (IterableConsumer consumer = occtx.iterate()) { validateConsumerNameForOrdered(occtx, consumer, consumerNamePrefix); - _testIterableBasic(js, stopCount, consumer, tsc.subject()); + _testIterableBasic(jstc.js, stopCount, consumer, jstc.subject()); } }); } @@ -559,21 +543,16 @@ private void _testIterableBasic(JetStream js, int stopCount, IterableConsumer co @Test public void testConsumeWithHandler() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - - JetStream js = nc.jetStream(); - jsPublish(js, tsc.subject(), 2500); + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { + jsPublish(jstc.js, jstc.subject(), 2500); // Pre define a consumer - ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(tsc.consumerName()).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(jstc.consumerName()).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); // Consumer[Context] - ConsumerContext consumerContext = js.getConsumerContext(tsc.stream, tsc.consumerName()); - validateConsumerName(consumerContext, null, tsc.consumerName()); + ConsumerContext consumerContext = jstc.js.getConsumerContext(jstc.stream, jstc.consumerName()); + validateConsumerName(consumerContext, null, jstc.consumerName()); int stopCount = 500; @@ -587,16 +566,16 @@ public void testConsumeWithHandler() throws Exception { }; try (MessageConsumer mcon = consumerContext.consume(handler)) { - validateConsumerName(consumerContext, mcon, tsc.consumerName()); + validateConsumerName(consumerContext, mcon, jstc.consumerName()); latch.await(); stopAndWaitForFinished(mcon); assertTrue(atomicCount.get() > 500); } - StreamContext sctx = nc.getStreamContext(tsc.stream); + StreamContext sctx = nc.getStreamContext(jstc.stream); OrderedConsumerContext orderedConsumerContext = - sctx.createOrderedConsumer(new OrderedConsumerConfiguration().filterSubject(tsc.subject())); + sctx.createOrderedConsumer(new OrderedConsumerConfiguration().filterSubject(jstc.subject())); assertNull(orderedConsumerContext.getConsumerName()); CountDownLatch orderedLatch = new CountDownLatch(1); @@ -615,9 +594,9 @@ public void testConsumeWithHandler() throws Exception { assertTrue(atomicCount.get() > 500); } - String prefix = prefix(); + String prefix = random(); OrderedConsumerContext orderedConsumerContextPrefixed = - sctx.createOrderedConsumer(new OrderedConsumerConfiguration().filterSubject(tsc.subject()).consumerNamePrefix(prefix)); + sctx.createOrderedConsumer(new OrderedConsumerConfiguration().filterSubject(jstc.subject()).consumerNamePrefix(prefix)); assertNull(orderedConsumerContextPrefixed.getConsumerName()); CountDownLatch orderedLatchPrefixed = new CountDownLatch(1); @@ -652,21 +631,17 @@ private static void stopAndWaitForFinished(MessageConsumer mcon) throws Interrup @Test public void testNext() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - JetStream js = nc.jetStream(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - jsPublish(js, tsc.subject(), 4); + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { + jsPublish(jstc.js, jstc.subject(), 4); - String name = name(); + String name = random(); // Pre define a consumer ConsumerConfiguration cc = ConsumerConfiguration.builder().durable(name).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); // Consumer[Context] - ConsumerContext consumerContext = js.getConsumerContext(tsc.stream, name); + ConsumerContext consumerContext = jstc.js.getConsumerContext(jstc.stream, name); validateConsumerName(consumerContext, null, name); assertThrows(IllegalArgumentException.class, () -> consumerContext.next(1)); // max wait too small @@ -676,7 +651,7 @@ public void testNext() throws Exception { assertNotNull(consumerContext.next()); assertNull(consumerContext.next(1000)); - StreamContext sctx = js.getStreamContext(tsc.stream); + StreamContext sctx = jstc.js.getStreamContext(jstc.stream); OrderedConsumerContext occtx = sctx.createOrderedConsumer(new OrderedConsumerConfiguration()); assertNull(occtx.getConsumerName()); assertThrows(IllegalArgumentException.class, () -> occtx.next(1)); // max wait too small @@ -700,7 +675,7 @@ public void testNext() throws Exception { cname1 = validateConsumerNameForOrdered(occtx, null, null); assertNotEquals(cname1, cname2); - String prefix = prefix(); + String prefix = random(); OrderedConsumerContext occtxPrefixed = sctx.createOrderedConsumer(new OrderedConsumerConfiguration().consumerNamePrefix(prefix)); assertNull(occtxPrefixed.getConsumerName()); assertThrows(IllegalArgumentException.class, () -> occtxPrefixed.next(1)); // max wait too small @@ -728,37 +703,32 @@ public void testNext() throws Exception { @Test public void testCoverage() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - JetStream js = nc.jetStream(); - + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { // Pre define a consumer - jsm.addOrUpdateConsumer(tsc.stream, ConsumerConfiguration.builder().durable(tsc.consumerName(1)).build()); - jsm.addOrUpdateConsumer(tsc.stream, ConsumerConfiguration.builder().durable(tsc.consumerName(2)).build()); - jsm.addOrUpdateConsumer(tsc.stream, ConsumerConfiguration.builder().durable(tsc.consumerName(3)).build()); - jsm.addOrUpdateConsumer(tsc.stream, ConsumerConfiguration.builder().durable(tsc.consumerName(4)).build()); + jstc.jsm.addOrUpdateConsumer(jstc.stream, ConsumerConfiguration.builder().durable(jstc.consumerName(1)).build()); + jstc.jsm.addOrUpdateConsumer(jstc.stream, ConsumerConfiguration.builder().durable(jstc.consumerName(2)).build()); + jstc.jsm.addOrUpdateConsumer(jstc.stream, ConsumerConfiguration.builder().durable(jstc.consumerName(3)).build()); + jstc.jsm.addOrUpdateConsumer(jstc.stream, ConsumerConfiguration.builder().durable(jstc.consumerName(4)).build()); // Stream[Context] - StreamContext sctx1 = nc.getStreamContext(tsc.stream); - nc.getStreamContext(tsc.stream, JetStreamOptions.DEFAULT_JS_OPTIONS); - js.getStreamContext(tsc.stream); + StreamContext sctx1 = nc.getStreamContext(jstc.stream); + nc.getStreamContext(jstc.stream, JetStreamOptions.DEFAULT_JS_OPTIONS); + jstc.js.getStreamContext(jstc.stream); // Consumer[Context] - ConsumerContext cctx1 = nc.getConsumerContext(tsc.stream, tsc.consumerName(1)); - ConsumerContext cctx2 = nc.getConsumerContext(tsc.stream, tsc.consumerName(2), JetStreamOptions.DEFAULT_JS_OPTIONS); - ConsumerContext cctx3 = js.getConsumerContext(tsc.stream, tsc.consumerName(3)); - ConsumerContext cctx4 = sctx1.getConsumerContext(tsc.consumerName(4)); - ConsumerContext cctx5 = sctx1.createOrUpdateConsumer(ConsumerConfiguration.builder().durable(tsc.consumerName(5)).build()); - ConsumerContext cctx6 = sctx1.createOrUpdateConsumer(ConsumerConfiguration.builder().durable(tsc.consumerName(6)).build()); - - after(cctx1.iterate(), tsc.consumerName(1), true); - after(cctx2.iterate(ConsumeOptions.DEFAULT_CONSUME_OPTIONS), tsc.consumerName(2), true); - after(cctx3.consume(m -> {}), tsc.consumerName(3), true); - after(cctx4.consume(ConsumeOptions.DEFAULT_CONSUME_OPTIONS, m -> {}), tsc.consumerName(4), true); - after(cctx5.fetchMessages(1), tsc.consumerName(5), false); - after(cctx6.fetchBytes(1000), tsc.consumerName(6), false); + ConsumerContext cctx1 = nc.getConsumerContext(jstc.stream, jstc.consumerName(1)); + ConsumerContext cctx2 = nc.getConsumerContext(jstc.stream, jstc.consumerName(2), JetStreamOptions.DEFAULT_JS_OPTIONS); + ConsumerContext cctx3 = jstc.js.getConsumerContext(jstc.stream, jstc.consumerName(3)); + ConsumerContext cctx4 = sctx1.getConsumerContext(jstc.consumerName(4)); + ConsumerContext cctx5 = sctx1.createOrUpdateConsumer(ConsumerConfiguration.builder().durable(jstc.consumerName(5)).build()); + ConsumerContext cctx6 = sctx1.createOrUpdateConsumer(ConsumerConfiguration.builder().durable(jstc.consumerName(6)).build()); + + after(cctx1.iterate(), jstc.consumerName(1), true); + after(cctx2.iterate(ConsumeOptions.DEFAULT_CONSUME_OPTIONS), jstc.consumerName(2), true); + after(cctx3.consume(m -> {}), jstc.consumerName(3), true); + after(cctx4.consume(ConsumeOptions.DEFAULT_CONSUME_OPTIONS, m -> {}), jstc.consumerName(4), true); + after(cctx5.fetchMessages(1), jstc.consumerName(5), false); + after(cctx6.fetchBytes(1000), jstc.consumerName(6), false); }); } @@ -955,46 +925,42 @@ protected Boolean beforeQueueProcessorImpl(NatsMessage msg) { @Test public void testOrderedBehaviorNext() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - // Setup - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - StreamContext sctx = js.getStreamContext(tsc.stream); + runInJsServer(VersionUtils::atLeast2_9_1, nc -> { + JetStreamTestingContext jstc = new JetStreamTestingContext(nc); + StreamContext sctx = jstc.js.getStreamContext(jstc.stream); - jsPublish(js, tsc.subject(), 101, 6, 100); - ZonedDateTime startTime = getStartTimeFirstMessage(js, tsc); + jsPublish(jstc.js, jstc.subject(), 101, 6, 100); + ZonedDateTime startTime = getStartTimeFirstMessage(jstc); // New pomm factory in place before each subscription is made // test with and without a consumer name prefix - ((NatsJetStream)js)._pullOrderedMessageManagerFactory = PullOrderedNextTestDropSimulator::new; + jstc.js._pullOrderedMessageManagerFactory = PullOrderedNextTestDropSimulator::new; _testOrderedNext(sctx, 1, new OrderedConsumerConfiguration() - .filterSubject(tsc.subject())); + .filterSubject(jstc.subject())); _testOrderedNext(sctx, 1, new OrderedConsumerConfiguration() - .consumerNamePrefix(prefix()) - .filterSubject(tsc.subject())); + .consumerNamePrefix(random()) + .filterSubject(jstc.subject())); - ((NatsJetStream)js)._pullOrderedMessageManagerFactory = PullOrderedNextTestDropSimulator::new; - _testOrderedNext(sctx, 2, new OrderedConsumerConfiguration().filterSubject(tsc.subject()) + jstc.js._pullOrderedMessageManagerFactory = PullOrderedNextTestDropSimulator::new; + _testOrderedNext(sctx, 2, new OrderedConsumerConfiguration().filterSubject(jstc.subject()) .deliverPolicy(DeliverPolicy.ByStartTime).startTime(startTime)); - _testOrderedNext(sctx, 2, new OrderedConsumerConfiguration().filterSubject(tsc.subject()) - .consumerNamePrefix(prefix()) + _testOrderedNext(sctx, 2, new OrderedConsumerConfiguration().filterSubject(jstc.subject()) + .consumerNamePrefix(random()) .deliverPolicy(DeliverPolicy.ByStartTime).startTime(startTime)); - ((NatsJetStream)js)._pullOrderedMessageManagerFactory = PullOrderedNextTestDropSimulator::new; - _testOrderedNext(sctx, 2, new OrderedConsumerConfiguration().filterSubject(tsc.subject()) + jstc.js._pullOrderedMessageManagerFactory = PullOrderedNextTestDropSimulator::new; + _testOrderedNext(sctx, 2, new OrderedConsumerConfiguration().filterSubject(jstc.subject()) .deliverPolicy(DeliverPolicy.ByStartSequence).startSequence(2)); - _testOrderedNext(sctx, 2, new OrderedConsumerConfiguration().filterSubject(tsc.subject()) - .consumerNamePrefix(prefix()) + _testOrderedNext(sctx, 2, new OrderedConsumerConfiguration().filterSubject(jstc.subject()) + .consumerNamePrefix(random()) .deliverPolicy(DeliverPolicy.ByStartSequence).startSequence(2)); }); } - private ZonedDateTime getStartTimeFirstMessage(JetStream js, TestingStreamContainer tsc) throws IOException, JetStreamApiException, InterruptedException { + private ZonedDateTime getStartTimeFirstMessage(JetStreamTestingContext jstc) throws IOException, JetStreamApiException, InterruptedException { ZonedDateTime startTime; - JetStreamSubscription sub = js.subscribe(tsc.subject()); + JetStreamSubscription sub = jstc.js.subscribe(jstc.subject()); Message mt = sub.nextMessage(1000); startTime = mt.metaData().timestamp().plus(30, ChronoUnit.MILLIS); sub.unsubscribe(); @@ -1041,39 +1007,35 @@ protected Boolean beforeQueueProcessorImpl(NatsMessage msg) { @Test public void testOrderedBehaviorFetch() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - // Setup - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); + runInJsServer(VersionUtils::atLeast2_9_1, nc -> { + JetStreamTestingContext jstc = new JetStreamTestingContext(nc); + StreamContext sctx = jstc.js.getStreamContext(jstc.stream); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - StreamContext sctx = js.getStreamContext(tsc.stream); - - jsPublish(js, tsc.subject(), 101, 6, 100); - ZonedDateTime startTime = getStartTimeFirstMessage(js, tsc); + jsPublish(jstc.js, jstc.subject(), 101, 6, 100); + ZonedDateTime startTime = getStartTimeFirstMessage(jstc); // New pomm factory in place before subscriptions are made - ((NatsJetStream)js)._pullOrderedMessageManagerFactory = PullOrderedTestDropSimulator::new; + jstc.js._pullOrderedMessageManagerFactory = PullOrderedTestDropSimulator::new; // Set the Consumer Sequence For Stream Sequence 3 statically for ease CS_FOR_SS_3 = 3; - _testOrderedFetch(sctx, 1, new OrderedConsumerConfiguration().filterSubject(tsc.subject())); + _testOrderedFetch(sctx, 1, new OrderedConsumerConfiguration().filterSubject(jstc.subject())); _testOrderedFetch(sctx, 1, new OrderedConsumerConfiguration() - .consumerNamePrefix(prefix()) - .filterSubject(tsc.subject())); + .consumerNamePrefix(random()) + .filterSubject(jstc.subject())); CS_FOR_SS_3 = 2; - _testOrderedFetch(sctx, 2, new OrderedConsumerConfiguration().filterSubject(tsc.subject()) + _testOrderedFetch(sctx, 2, new OrderedConsumerConfiguration().filterSubject(jstc.subject()) .deliverPolicy(DeliverPolicy.ByStartTime).startTime(startTime)); - _testOrderedFetch(sctx, 2, new OrderedConsumerConfiguration().filterSubject(tsc.subject()) - .consumerNamePrefix(prefix()) + _testOrderedFetch(sctx, 2, new OrderedConsumerConfiguration().filterSubject(jstc.subject()) + .consumerNamePrefix(random()) .deliverPolicy(DeliverPolicy.ByStartTime).startTime(startTime)); CS_FOR_SS_3 = 2; - _testOrderedFetch(sctx, 2, new OrderedConsumerConfiguration().filterSubject(tsc.subject()) + _testOrderedFetch(sctx, 2, new OrderedConsumerConfiguration().filterSubject(jstc.subject()) .deliverPolicy(DeliverPolicy.ByStartSequence).startSequence(2)); - _testOrderedFetch(sctx, 2, new OrderedConsumerConfiguration().filterSubject(tsc.subject()) - .consumerNamePrefix(prefix()) + _testOrderedFetch(sctx, 2, new OrderedConsumerConfiguration().filterSubject(jstc.subject()) + .consumerNamePrefix(random()) .deliverPolicy(DeliverPolicy.ByStartSequence).startSequence(2)); }); } @@ -1115,43 +1077,36 @@ private void _testOrderedFetch(StreamContext sctx, int expectedStreamSeq, Ordere @Test public void testOrderedBehaviorIterable() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - jsServer.setExitOnDisconnect(); - jsServer.setExitOnHeartbeatError(); - - // Setup - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); + runInJsServer(VersionUtils::atLeast2_9_1, nc -> { + JetStreamTestingContext jstc = new JetStreamTestingContext(nc); + StreamContext sctx = jstc.js.getStreamContext(jstc.stream); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - StreamContext sctx = js.getStreamContext(tsc.stream); - - jsPublish(js, tsc.subject(), 101, 6, 100); - ZonedDateTime startTime = getStartTimeFirstMessage(js, tsc); + jsPublish(jstc.js, jstc.subject(), 101, 6, 100); + ZonedDateTime startTime = getStartTimeFirstMessage(jstc); // New pomm factory in place before each subscription is made // Set the Consumer Sequence For Stream Sequence 3 statically for ease CS_FOR_SS_3 = 3; - ((NatsJetStream)js)._pullOrderedMessageManagerFactory = PullOrderedTestDropSimulator::new; - _testOrderedIterate(sctx, 1, new OrderedConsumerConfiguration().filterSubject(tsc.subject())); + jstc.js._pullOrderedMessageManagerFactory = PullOrderedTestDropSimulator::new; + _testOrderedIterate(sctx, 1, new OrderedConsumerConfiguration().filterSubject(jstc.subject())); _testOrderedIterate(sctx, 1, new OrderedConsumerConfiguration() - .consumerNamePrefix(prefix()) - .filterSubject(tsc.subject())); + .consumerNamePrefix(random()) + .filterSubject(jstc.subject())); CS_FOR_SS_3 = 2; - ((NatsJetStream)js)._pullOrderedMessageManagerFactory = PullOrderedTestDropSimulator::new; - _testOrderedIterate(sctx, 2, new OrderedConsumerConfiguration().filterSubject(tsc.subject()) + jstc.js._pullOrderedMessageManagerFactory = PullOrderedTestDropSimulator::new; + _testOrderedIterate(sctx, 2, new OrderedConsumerConfiguration().filterSubject(jstc.subject()) .deliverPolicy(DeliverPolicy.ByStartTime).startTime(startTime)); - _testOrderedIterate(sctx, 2, new OrderedConsumerConfiguration().filterSubject(tsc.subject()) - .consumerNamePrefix(prefix()) + _testOrderedIterate(sctx, 2, new OrderedConsumerConfiguration().filterSubject(jstc.subject()) + .consumerNamePrefix(random()) .deliverPolicy(DeliverPolicy.ByStartTime).startTime(startTime)); CS_FOR_SS_3 = 2; - ((NatsJetStream)js)._pullOrderedMessageManagerFactory = PullOrderedTestDropSimulator::new; - _testOrderedIterate(sctx, 2, new OrderedConsumerConfiguration().filterSubject(tsc.subject()) + jstc.js._pullOrderedMessageManagerFactory = PullOrderedTestDropSimulator::new; + _testOrderedIterate(sctx, 2, new OrderedConsumerConfiguration().filterSubject(jstc.subject()) .deliverPolicy(DeliverPolicy.ByStartSequence).startSequence(2)); - _testOrderedIterate(sctx, 2, new OrderedConsumerConfiguration().filterSubject(tsc.subject()) - .consumerNamePrefix(prefix()) + _testOrderedIterate(sctx, 2, new OrderedConsumerConfiguration().filterSubject(jstc.subject()) + .consumerNamePrefix(random()) .deliverPolicy(DeliverPolicy.ByStartSequence).startSequence(2)); }); } @@ -1172,7 +1127,7 @@ private void _testOrderedIterate(StreamContext sctx, int expectedStreamSeq, Orde } @Test - public void testOrderedConsumeConstruction() throws Exception { + public void testOrderedConsumeConstruction() { OrderedConsumerConfiguration occ = new OrderedConsumerConfiguration().filterSubject(null); assertNotNull(occ.getFilterSubjects()); assertEquals(GREATER_THAN, occ.getFilterSubject()); @@ -1206,28 +1161,28 @@ public void testOrderedConsumeConstruction() throws Exception { @Test public void testOrderedConsume() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - // Setup - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - OrderedConsumerConfiguration occ = new OrderedConsumerConfiguration().filterSubject(tsc.subject()); - _testOrderedConsume(js, tsc, occ); + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { + OrderedConsumerConfiguration occ = new OrderedConsumerConfiguration() + .filterSubject(jstc.subject()); + _testOrderedConsume(jstc, occ); + }); + } - tsc = new TestingStreamContainer(jsm); - occ = new OrderedConsumerConfiguration() - .consumerNamePrefix(prefix()) - .filterSubject(tsc.subject()); - _testOrderedConsume(js, tsc, occ); + @Test + public void testOrderedConsumeWithPrefix() throws Exception { + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { + OrderedConsumerConfiguration occ = new OrderedConsumerConfiguration() + .consumerNamePrefix(random()) + .filterSubject(jstc.subject()); + _testOrderedConsume(jstc, occ); }); } - private void _testOrderedConsume(JetStream js, TestingStreamContainer tsc, OrderedConsumerConfiguration occ) throws Exception { - StreamContext sctx = js.getStreamContext(tsc.stream); + private void _testOrderedConsume(JetStreamTestingContext jstc, OrderedConsumerConfiguration occ) throws Exception { + StreamContext sctx = jstc.js.getStreamContext(jstc.stream); // Get this in place before subscriptions are made - ((NatsJetStream) js)._pullOrderedMessageManagerFactory = PullOrderedTestDropSimulator::new; + jstc.js._pullOrderedMessageManagerFactory = PullOrderedTestDropSimulator::new; CountDownLatch msgLatch = new CountDownLatch(6); AtomicInteger received = new AtomicInteger(); @@ -1241,7 +1196,7 @@ private void _testOrderedConsume(JetStream js, TestingStreamContainer tsc, Order assertNull(occtx.getConsumerName()); try (MessageConsumer mcon = occtx.consume(handler)) { validateConsumerNameForOrdered(occtx, mcon, occ.getConsumerNamePrefix()); - jsPublish(js, tsc.subject(), 201, 6); + jsPublish(jstc.js, jstc.subject(), 201, 6); // wait for the messages awaitAndAssert(msgLatch); @@ -1257,18 +1212,14 @@ private void _testOrderedConsume(JetStream js, TestingStreamContainer tsc, Order @Test public void testOrderedConsumeMultipleSubjects() throws Exception { - jsServer.run(TestBase::atLeast2_10, nc -> { - // Setup - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); - - TestingStreamContainer tsc = new TestingStreamContainer(jsm, 2); - jsPublish(js, tsc.subject(0), 10); - jsPublish(js, tsc.subject(1), 5); + runInLrServer(VersionUtils::atLeast2_10, (nc, jsm, js) -> { + JetStreamTestingContext jstc = new JetStreamTestingContext(nc, 2); + jsPublish(jstc.js, jstc.subject(0), 10); + jsPublish(jstc.js, jstc.subject(1), 5); - StreamContext sctx = js.getStreamContext(tsc.stream); + StreamContext sctx = jstc.js.getStreamContext(jstc.stream); - OrderedConsumerConfiguration occ = new OrderedConsumerConfiguration().filterSubjects(tsc.subject(0), tsc.subject(1)); + OrderedConsumerConfiguration occ = new OrderedConsumerConfiguration().filterSubjects(jstc.subject(0), jstc.subject(1)); OrderedConsumerContext occtx = sctx.createOrderedConsumer(occ); int count0 = 0; @@ -1276,7 +1227,7 @@ public void testOrderedConsumeMultipleSubjects() throws Exception { try (FetchConsumer fc = occtx.fetch(FetchConsumeOptions.builder().maxMessages(20).expiresIn(2000).build())) { Message m = fc.nextMessage(); while (m != null) { - if (m.getSubject().equals(tsc.subject(0))) { + if (m.getSubject().equals(jstc.subject(0))) { count0++; } else { @@ -1294,17 +1245,10 @@ public void testOrderedConsumeMultipleSubjects() throws Exception { @Test public void testOrderedMultipleWays() throws Exception { - jsServer.run(TestBase::atLeast2_9_1, nc -> { - // Setup - JetStream js = nc.jetStream(); - JetStreamManagement jsm = nc.jetStreamManagement(); + runInLrServer(VersionUtils::atLeast2_9_1, (nc, jstc) -> { + StreamContext sctx = jstc.js.getStreamContext(jstc.stream); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - createMemoryStream(jsm, tsc.stream, tsc.subject()); - - StreamContext sctx = js.getStreamContext(tsc.stream); - - OrderedConsumerConfiguration occ = new OrderedConsumerConfiguration().filterSubject(tsc.subject()); + OrderedConsumerConfiguration occ = new OrderedConsumerConfiguration().filterSubject(jstc.subject()); OrderedConsumerContext occtx = sctx.createOrderedConsumer(occ); // can't do others while doing next @@ -1328,8 +1272,8 @@ public void testOrderedMultipleWays() throws Exception { //noinspection ResultOfMethodCallIgnored latch.await(3000, TimeUnit.MILLISECONDS); - for (int x = 0 ; x < 10_000; x++) { - js.publish(tsc.subject(), ("multiple" + x).getBytes()); + for (int x = 0; x < 10_000; x++) { + jstc.js.publish(jstc.subject(), ("multiple" + x).getBytes()); } // can do others now @@ -1398,6 +1342,7 @@ public void testOrderedMultipleWays() throws Exception { private void validateCantCallOtherMethods(OrderedConsumerContext ctx) { assertThrows(IOException.class, () -> ctx.next(1000)); assertThrows(IOException.class, () -> ctx.fetchMessages(1)); + //noinspection DataFlowIssue assertThrows(IllegalArgumentException.class, () -> ctx.consume(null)); } @@ -1516,28 +1461,25 @@ private Object roundTripSerialize(Serializable s) throws IOException, ClassNotFo @Test public void testOverflowFetch() throws Exception { - ListenerForTesting l = new ListenerForTesting(); - Options.Builder b = Options.builder().errorListener(l); - jsServer.run(b, TestBase::atLeast2_11, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - JetStream js = nc.jetStream(); - jsPublish(js, tsc.subject(), 100); + ListenerForTesting listener = new ListenerForTesting(); + runInJsServer(listener, VersionUtils::atLeast2_9_1, nc -> { + JetStreamTestingContext jstc = new JetStreamTestingContext(nc); + jsPublish(jstc.js, jstc.subject(), 100); // Testing min ack pending - String group = variant(); - String cname = variant(); + String group = random(); + String cname = random(); ConsumerConfiguration cc = ConsumerConfiguration.builder() .name(cname) .priorityPolicy(PriorityPolicy.Overflow) .priorityGroups(group) .ackWait(10_000) - .filterSubjects(tsc.subject()).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + .filterSubjects(jstc.subject()).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - ConsumerContext ctxPrime = nc.getConsumerContext(tsc.stream, cname); - ConsumerContext ctxOver = nc.getConsumerContext(tsc.stream, cname); + ConsumerContext ctxPrime = nc.getConsumerContext(jstc.stream, cname); + ConsumerContext ctxOver = nc.getConsumerContext(jstc.stream, cname); FetchConsumeOptions fcoNoMin = FetchConsumeOptions.builder() .maxMessages(5).expiresIn(1000).group(group) @@ -1582,28 +1524,24 @@ private void _overflowFetch(String cname, ConsumerContext cctx, FetchConsumeOpti @Test public void testOverflowIterate() throws Exception { - ListenerForTesting l = new ListenerForTesting(); - Options.Builder b = Options.builder().errorListener(l); - runInJsServer(b, TestBase::atLeast2_11, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - JetStream js = nc.jetStream(); - jsPublish(js, tsc.subject(), 100); + ListenerForTesting listener = new ListenerForTesting(); + runInLrServer(listener, VersionUtils::atLeast2_11, (nc, jstc) -> { + jsPublish(jstc.js, jstc.subject(), 100); // Testing min ack pending - String group = variant(); - String cname = variant(); + String group = random(); + String cname = random(); ConsumerConfiguration cc = ConsumerConfiguration.builder() .name(cname) .priorityPolicy(PriorityPolicy.Overflow) .priorityGroups(group) .ackWait(30_000) - .filterSubjects(tsc.subject()).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + .filterSubjects(jstc.subject()).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - ConsumerContext ctxPrime = nc.getConsumerContext(tsc.stream, cname); - ConsumerContext ctxOver = nc.getConsumerContext(tsc.stream, cname); + ConsumerContext ctxPrime = nc.getConsumerContext(jstc.stream, cname); + ConsumerContext ctxOver = nc.getConsumerContext(jstc.stream, cname); validateConsumerName(ctxPrime, null, cname); validateConsumerName(ctxOver, null, cname); @@ -1672,28 +1610,24 @@ public void testOverflowIterate() throws Exception { @Test public void testOverflowConsume() throws Exception { - ListenerForTesting l = new ListenerForTesting(); - Options.Builder b = Options.builder().errorListener(l); - runInJsServer(b, TestBase::atLeast2_11, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - JetStream js = nc.jetStream(); - jsPublish(js, tsc.subject(), 1000); + ListenerForTesting listener = new ListenerForTesting(); + runInLrServer(listener, VersionUtils::atLeast2_11, (nc, jstc) -> { + jsPublish(jstc.js, jstc.subject(), 1000); // Testing min ack pending - String group = variant(); - String cname = variant(); + String group = random(); + String cname = random(); ConsumerConfiguration cc = ConsumerConfiguration.builder() .name(cname) .priorityPolicy(PriorityPolicy.Overflow) .priorityGroups(group) .ackWait(30_000) - .filterSubjects(tsc.subject()).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + .filterSubjects(jstc.subject()).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - ConsumerContext ctxPrime = nc.getConsumerContext(tsc.stream, cname); - ConsumerContext ctxOver = nc.getConsumerContext(tsc.stream, cname); + ConsumerContext ctxPrime = nc.getConsumerContext(jstc.stream, cname); + ConsumerContext ctxOver = nc.getConsumerContext(jstc.stream, cname); validateConsumerName(ctxPrime, null, cname); validateConsumerName(ctxOver, null, cname); @@ -1742,20 +1676,15 @@ public void testOverflowConsume() throws Exception { @Test public void testFinishEmptyStream() throws Exception { - ListenerForTesting l = new ListenerForTesting(); - Options.Builder b = Options.builder().errorListener(l); - runInJsServer(b, nc -> { - JetStreamManagement jsm = nc.jetStreamManagement(); - TestingStreamContainer tsc = new TestingStreamContainer(jsm); - - String name = variant(); - + ListenerForTesting listener = new ListenerForTesting(); + runInLrServer(listener, (nc, jstc) -> { + String name = random(); ConsumerConfiguration cc = ConsumerConfiguration.builder() .name(name) - .filterSubjects(tsc.subject()).build(); - jsm.addOrUpdateConsumer(tsc.stream, cc); + .filterSubjects(jstc.subject()).build(); + jstc.jsm.addOrUpdateConsumer(jstc.stream, cc); - ConsumerContext cctx = nc.getConsumerContext(tsc.stream, name); + ConsumerContext cctx = nc.getConsumerContext(jstc.stream, name); MessageHandler handler = Message::ack; @@ -1782,14 +1711,14 @@ public void testReconnectOverOrdered() throws Exception { // ------------------------------------------------------------ int port = NatsTestServer.nextPort(); ListenerForTesting lft = new ListenerForTesting(); - Options options = new Options.Builder() + Options options = optionsBuilder() .connectionListener(lft) .errorListener(lft) .server(NatsTestServer.getNatsLocalhostUri(port)).build(); NatsConnection nc; - String stream = stream(); - String subject = subject(); + String stream = random(); + String subject = random(); AtomicBoolean allInOrder = new AtomicBoolean(true); AtomicInteger atomicCount = new AtomicInteger(); @@ -1812,7 +1741,7 @@ public void testReconnectOverOrdered() throws Exception { //noinspection unused try (NatsTestServer ts = new NatsTestServer(port, false, true)) { - nc = (NatsConnection) standardConnection(options); + nc = (NatsConnection) standardConnectionWait(options); StreamConfiguration sc = StreamConfiguration.builder() .name(stream) .storageType(StorageType.File) // file since we are killing the server and bringing it back up. diff --git a/src/test/java/io/nats/client/impl/SlowConsumerTests.java b/src/test/java/io/nats/client/impl/SlowConsumerTests.java index 262bb9720..f76ced752 100644 --- a/src/test/java/io/nats/client/impl/SlowConsumerTests.java +++ b/src/test/java/io/nats/client/impl/SlowConsumerTests.java @@ -22,13 +22,14 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; import static org.junit.jupiter.api.Assertions.assertEquals; public class SlowConsumerTests { @Test public void testDefaultPendingLimits() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { Subscription sub = nc.subscribe("subject"); @@ -45,7 +46,7 @@ public void testDefaultPendingLimits() throws Exception { @Test public void testSlowSubscriberByMessages() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { Subscription sub = nc.subscribe("subject"); @@ -78,7 +79,7 @@ public void testSlowSubscriberByMessages() throws Exception { @Test public void testSlowSubscriberByBytes() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { Subscription sub = nc.subscribe("subject"); @@ -109,7 +110,7 @@ public void testSlowSubscriberByBytes() throws Exception { @Test public void testSlowSDispatcherByMessages() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { final CompletableFuture ok = new CompletableFuture<>(); @@ -150,7 +151,7 @@ public void testSlowSDispatcherByMessages() throws Exception { @Test public void testSlowSDispatcherByBytes() throws Exception { - try (NatsTestServer ts = new NatsTestServer(false); + try (NatsTestServer ts = new NatsTestServer(); NatsConnection nc = (NatsConnection) Nats.connect(ts.getURI())) { final CompletableFuture ok = new CompletableFuture<>(); @@ -191,9 +192,8 @@ public void testSlowSDispatcherByBytes() throws Exception { @Test public void testSlowSubscriberNotification() throws Exception { ListenerForTesting listener = new ListenerForTesting(); - try (NatsTestServer ts = new NatsTestServer(false); - NatsConnection nc = (NatsConnection) Nats.connect(new Options.Builder(). - server(ts.getURI()).errorListener(listener).build())) { + try (NatsTestServer ts = new NatsTestServer(); + NatsConnection nc = (NatsConnection) Nats.connect(optionsBuilder(ts).errorListener(listener).build())) { Subscription sub = nc.subscribe("subject"); sub.setPendingLimits(1, -1); diff --git a/src/test/java/io/nats/client/impl/TLSConnectTests.java b/src/test/java/io/nats/client/impl/TLSConnectTests.java index 4dddd0e66..619802bf3 100644 --- a/src/test/java/io/nats/client/impl/TLSConnectTests.java +++ b/src/test/java/io/nats/client/impl/TLSConnectTests.java @@ -16,7 +16,6 @@ import io.nats.client.*; import io.nats.client.ConnectionListener.Events; import io.nats.client.utils.CloseOnUpgradeAttempt; -import io.nats.client.utils.TestBase; import org.junit.jupiter.api.Test; import javax.net.ssl.SSLContext; @@ -30,7 +29,12 @@ import java.util.concurrent.atomic.AtomicReference; import static io.nats.client.Options.PROP_SSL_CONTEXT_FACTORY_CLASS; -import static io.nats.client.utils.TestBase.*; +import static io.nats.client.utils.ConnectionUtils.*; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.TestBase.assertCanConnectAndPubSub; +import static io.nats.client.utils.TestBase.flushAndWaitLong; +import static io.nats.client.utils.VersionUtils.atLeast2_10_3; +import static io.nats.client.utils.VersionUtils.ensureRunServerInfo; import static org.junit.jupiter.api.Assertions.*; public class TLSConnectTests { @@ -47,8 +51,7 @@ private String convertToProtocol(String proto, NatsTestServer... servers) { } private static Options createTestOptionsManually(String servers) throws Exception { - return new Options.Builder() - .server(servers) + return optionsBuilder(servers) .maxReconnects(0) .sslContext(SslTestingHelper.createTestSSLContext()) .build(); @@ -64,8 +67,7 @@ private static Options createTestOptionsViaProperties(String servers) { } private static Options createTestOptionsViaFactoryInstance(String servers) { - return new Options.Builder() - .server(servers) + return optionsBuilder(servers) .maxReconnects(0) .sslContextFactory(new SSLContextFactoryForTesting()) .build(); @@ -74,8 +76,8 @@ private static Options createTestOptionsViaFactoryInstance(String servers) { private static Options createTestOptionsViaFactoryClassName(String servers) { Properties properties = new Properties(); properties.setProperty(PROP_SSL_CONTEXT_FACTORY_CLASS, SSLContextFactoryForTesting.class.getCanonicalName()); - return new Options.Builder(properties) - .server(servers) + return optionsBuilder(servers) + .properties(properties) .maxReconnects(0) .sslContextFactory(new SSLContextFactoryForTesting()) .build(); @@ -95,15 +97,13 @@ public void testSimpleTLSConnection() throws Exception { @Test public void testSimpleTlsFirstConnection() throws Exception { - if (TestBase.atLeast2_10_3(ensureRunServerInfo())) { + if (atLeast2_10_3(ensureRunServerInfo())) { try (NatsTestServer ts = new NatsTestServer( NatsTestServer.builder() .configFilePath("src/test/resources/tls_first.conf") - .connectValidateTlsFirstMode()) + .skipConnectValidate()) ) { - String servers = ts.getURI(); - Options options = new Options.Builder() - .server(servers) + Options options = optionsBuilder(ts) .maxReconnects(0) .tlsFirst() .sslContext(SslTestingHelper.createTestSSLContext()) @@ -166,7 +166,7 @@ public void testVerifiedTLSConnection() throws Exception { public void testOpenTLSConnection() throws Exception { try (NatsTestServer ts = new NatsTestServer("src/test/resources/tls.conf", false)) { String servers = ts.getURI(); - Options options = new Options.Builder() + Options options = optionsBuilder() .server(servers) .maxReconnects(0) .opentls() @@ -207,7 +207,7 @@ public void testURISchemeIPTLSConnection() throws Exception { public void testURISchemeOpenTLSConnection() throws Exception { try (NatsTestServer ts = new NatsTestServer("src/test/resources/tls.conf", false)) { String servers = convertToProtocol("opentls", ts); - Options options = new Options.Builder() + Options options = optionsBuilder() .server(servers) .maxReconnects(0) .opentls() @@ -229,7 +229,7 @@ public void testMultipleUrlOpenTLSConnection() throws Exception { NatsTestServer server2 = new NatsTestServer("src/test/resources/tls.conf", false); ) { String servers = convertToProtocol("opentls", server1, server2); - Options options = new Options.Builder() + Options options = optionsBuilder() .server(servers) .maxReconnects(0) .opentls() @@ -249,12 +249,11 @@ public void testTLSMessageFlow() throws Exception { try (NatsTestServer ts = new NatsTestServer("src/test/resources/tlsverify.conf", false)) { SSLContext ctx = SslTestingHelper.createTestSSLContext(); int msgCount = 100; - Options options = new Options.Builder() - .server(ts.getURI()) + Options options = optionsBuilder(ts) .maxReconnects(0) .sslContext(ctx) .build(); - Connection nc = standardConnection(options); + Connection nc = standardConnectionWait(options); Dispatcher d = nc.createDispatcher((msg) -> { nc.publish(msg.getReplyTo(), new byte[16]); }); @@ -281,15 +280,15 @@ public void testTLSOnReconnect() throws InterruptedException, Exception { // Use two server ports to avoid port release timing issues try (NatsTestServer ts = new NatsTestServer("src/test/resources/tlsverify.conf", port, false)) { SSLContext ctx = SslTestingHelper.createTestSSLContext(); - Options options = new Options.Builder(). - server(ts.getURI()). - server(NatsTestServer.getNatsLocalhostUri(newPort)). - maxReconnects(-1). - sslContext(ctx). - connectionListener(listener). - reconnectWait(Duration.ofMillis(10)). - build(); - nc = standardConnection(options); + Options options = optionsBuilder() + .server(ts.getURI()) + .server(NatsTestServer.getNatsLocalhostUri(newPort)) + .maxReconnects(-1) + .sslContext(ctx) + .connectionListener(listener) + .reconnectWait(Duration.ofMillis(10)) + .build(); + nc = standardConnectionWait(options); assertInstanceOf(SocketDataPort.class, ((NatsConnection) nc).getDataPort(), "Correct data port class"); listener.prepForStatusChange(Events.DISCONNECTED); } @@ -298,7 +297,7 @@ public void testTLSOnReconnect() throws InterruptedException, Exception { listener.prepForStatusChange(Events.RESUBSCRIBED); try (NatsTestServer ignored = new NatsTestServer("src/test/resources/tlsverify.conf", newPort, false)) { - listenerConnectionWait(nc, listener, 10000); + listenerConnectionWait(nc, listener, VERY_LONG_CONNECTION_WAIT_MS); } standardCloseConnection(nc); @@ -309,12 +308,11 @@ public void testDisconnectOnUpgrade() { assertThrows(IOException.class, () -> { try (NatsTestServer ts = new NatsTestServer("src/test/resources/tlsverify.conf", false)) { SSLContext ctx = SslTestingHelper.createTestSSLContext(); - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(0). - dataPortType(CloseOnUpgradeAttempt.class.getCanonicalName()). - sslContext(ctx). - build(); + Options options = optionsBuilder(ts) + .maxReconnects(0) + .dataPortType(CloseOnUpgradeAttempt.class.getCanonicalName()) + .sslContext(ctx) + .build(); Nats.connect(options); } }); @@ -324,10 +322,7 @@ public void testDisconnectOnUpgrade() { public void testServerSecureClientNotMismatch() { assertThrows(IOException.class, () -> { try (NatsTestServer ts = new NatsTestServer("src/test/resources/tlsverify.conf", false)) { - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(0). - build(); + Options options = optionsBuilder(ts).maxReconnects(0).build(); Nats.connect(options); } }); @@ -338,11 +333,7 @@ public void testClientSecureServerNotMismatch() { assertThrows(IOException.class, () -> { try (NatsTestServer ts = new NatsTestServer()) { SSLContext ctx = SslTestingHelper.createTestSSLContext(); - Options options = new Options.Builder(). - server(ts.getURI()). - maxReconnects(0). - sslContext(ctx). - build(); + Options options = optionsBuilder(ts).maxReconnects(0).sslContext(ctx).build(); Nats.connect(options); } }); @@ -361,12 +352,11 @@ public void exceptionOccurred(Connection conn, Exception exp) { assertThrows(IOException.class, () -> { try (NatsTestServer ts = new NatsTestServer("src/test/resources/tlsverify.conf", false)) { SSLContext ctx = SslTestingHelper.createEmptySSLContext(); - Options options = new Options.Builder() - .server(ts.getURI()) - .maxReconnects(0) - .sslContext(ctx) - .errorListener(el) - .build(); + Options options = optionsBuilder(ts) + .maxReconnects(0) + .sslContext(ctx) + .errorListener(el) + .build(); Nats.connect(options); } }); @@ -379,7 +369,7 @@ public void exceptionOccurred(Connection conn, Exception exp) { @Test public void testSSLContextFactoryPropertiesPassOnCorrectly() throws NoSuchAlgorithmException { SSLContextFactoryForTesting factory = new SSLContextFactoryForTesting(); - new Options.Builder() + optionsBuilder() .sslContextFactory(factory) .keystorePath("keystorePath") .keystorePassword("ksp".toCharArray()) @@ -414,8 +404,7 @@ public ProxyConnection(String servers, boolean tlsFirst, ErrorListener listener, } private static Options makeMiddleman(String servers, boolean tlsFirst, ErrorListener listener) throws Exception { - Options.Builder builder = new Options.Builder() - .server(servers) + Options.Builder builder = optionsBuilder(servers) .maxReconnects(0) .sslContext(SslTestingHelper.createTestSSLContext()) .errorListener(listener); @@ -450,12 +439,12 @@ void handleInfo(String infoJson) { */ @Test public void testProxyTlsFirst() throws Exception { - if (TestBase.atLeast2_10_3(ensureRunServerInfo())) { + if (atLeast2_10_3(ensureRunServerInfo())) { // cannot check connect b/c tls first try (NatsTestServer ts = new NatsTestServer( NatsTestServer.builder() .configFilePath("src/test/resources/tls_first.conf") - .connectValidateTlsFirstMode()) + .skipConnectValidate()) ) { // 1. client tls first | secure proxy | server insecure -> connects ProxyConnection connTI = new ProxyConnection(ts.getURI(), true, null, SERVER_INSECURE); diff --git a/src/test/java/io/nats/client/impl/ValidateIssue1426Test.java b/src/test/java/io/nats/client/impl/ValidateIssue1426Test.java index a8116d28f..93e59b83f 100644 --- a/src/test/java/io/nats/client/impl/ValidateIssue1426Test.java +++ b/src/test/java/io/nats/client/impl/ValidateIssue1426Test.java @@ -13,6 +13,7 @@ package io.nats.client.impl; +import io.nats.NatsRunnerUtils; import io.nats.client.*; import org.junit.jupiter.api.Test; @@ -25,6 +26,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -50,8 +52,7 @@ public void errorOccurred(Connection conn, String error) { } }; - Options options = new Options.Builder() - .servers(new String[]{"nats://" + "127.0.0.1:" + port}) + Options options = optionsBuilder("nats://" + NatsRunnerUtils.getDefaultLocalhostHost().host + ":" + port) .token(new char[]{'1', '2', '3', '4'}) .maxMessagesInOutgoingQueue(NUMBER_OF_SUBS ) .reconnectBufferSize(NUMBER_OF_SUBS * 100) diff --git a/src/test/java/io/nats/client/impl/WebsocketConnectTests.java b/src/test/java/io/nats/client/impl/WebsocketConnectTests.java index c1641a421..861be9e10 100644 --- a/src/test/java/io/nats/client/impl/WebsocketConnectTests.java +++ b/src/test/java/io/nats/client/impl/WebsocketConnectTests.java @@ -33,6 +33,7 @@ import static io.nats.client.ConnectionListener.Events.CONNECTED; import static io.nats.client.NatsTestServer.*; +import static io.nats.client.utils.ConnectionUtils.*; import static org.junit.jupiter.api.Assertions.*; public class WebsocketConnectTests extends TestBase { @@ -52,7 +53,7 @@ public void testRequestReply() throws Exception { } private static void standardRequestReply(Options options) throws InterruptedException, IOException { - try (Connection connection = standardConnection(options)) { + try (Connection connection = standardConnectionWait(options)) { Dispatcher dispatcher = connection.createDispatcher(msg -> { connection.publish(msg.getReplyTo(), (new String(msg.getData()) + ":REPLY").getBytes()); }); @@ -222,7 +223,7 @@ public void testTLSMessageFlow() throws Exception { .maxReconnects(0) .sslContext(ctx) .build(); - Connection nc = standardConnection(options); + Connection nc = standardConnectionWait(options); Dispatcher d = nc.createDispatcher((msg) -> { nc.publish(msg.getReplyTo(), new byte[16]); }); diff --git a/src/test/java/io/nats/client/support/JsonParsingTests.java b/src/test/java/io/nats/client/support/JsonParsingTests.java index e2ca4d8fa..105cbfded 100644 --- a/src/test/java/io/nats/client/support/JsonParsingTests.java +++ b/src/test/java/io/nats/client/support/JsonParsingTests.java @@ -13,6 +13,7 @@ package io.nats.client.support; +import io.nats.client.utils.TestBase; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jspecify.annotations.NonNull; @@ -43,39 +44,39 @@ public void testStringParsing() { List list = new ArrayList<>(); int x = 0; - addField(key(x++), "b4\\after", oMap, list, encodeds, decodeds); - addField(key(x++), "b4/after", oMap, list, encodeds, decodeds); - addField(key(x++), "b4\"after", oMap, list, encodeds, decodeds); - addField(key(x++), "b4\tafter", oMap, list, encodeds, decodeds); - addField(key(x++), "b4\\bafter", oMap, list, encodeds, decodeds); - addField(key(x++), "b4\\fafter", oMap, list, encodeds, decodeds); - addField(key(x++), "b4\\nafter", oMap, list, encodeds, decodeds); - addField(key(x++), "b4\\rafter", oMap, list, encodeds, decodeds); - addField(key(x++), "b4\\tafter", oMap, list, encodeds, decodeds); - addField(key(x++), "b4" + (char) 0 + "after", oMap, list, encodeds, decodeds); - addField(key(x++), "b4" + (char) 1 + "after", oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), "b4\\after", oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), "b4/after", oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), "b4\"after", oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), "b4\tafter", oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), "b4\\bafter", oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), "b4\\fafter", oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), "b4\\nafter", oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), "b4\\rafter", oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), "b4\\tafter", oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), "b4" + (char) 0 + "after", oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), "b4" + (char) 1 + "after", oMap, list, encodeds, decodeds); List utfs = dataAsLines("utf8-only-no-ws-test-strings.txt"); for (String u : utfs) { String uu = "b4\b\f\n\r\t" + u + "after"; - addField(key(x++), uu, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), uu, oMap, list, encodeds, decodeds); } - addField(key(x++), PLAIN, oMap, list, encodeds, decodeds); - addField(key(x++), HAS_SPACE, oMap, list, encodeds, decodeds); - addField(key(x++), HAS_PRINTABLE, oMap, list, encodeds, decodeds); - addField(key(x++), HAS_DOT, oMap, list, encodeds, decodeds); - addField(key(x++), STAR_NOT_SEGMENT, oMap, list, encodeds, decodeds); - addField(key(x++), GT_NOT_SEGMENT, oMap, list, encodeds, decodeds); - addField(key(x++), HAS_DASH, oMap, list, encodeds, decodeds); - addField(key(x++), HAS_UNDER, oMap, list, encodeds, decodeds); - addField(key(x++), HAS_DOLLAR, oMap, list, encodeds, decodeds); - addField(key(x++), HAS_LOW, oMap, list, encodeds, decodeds); - addField(key(x++), HAS_127, oMap, list, encodeds, decodeds); - addField(key(x++), HAS_FWD_SLASH, oMap, list, encodeds, decodeds); - addField(key(x++), HAS_BACK_SLASH, oMap, list, encodeds, decodeds); - addField(key(x++), HAS_EQUALS, oMap, list, encodeds, decodeds); - addField(key(x++), HAS_TIC, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), PLAIN, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), HAS_SPACE, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), HAS_PRINTABLE, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), HAS_DOT, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), STAR_NOT_SEGMENT, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), GT_NOT_SEGMENT, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), HAS_DASH, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), HAS_UNDER, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), HAS_DOLLAR, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), HAS_LOW, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), HAS_127, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), HAS_FWD_SLASH, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), HAS_BACK_SLASH, oMap, list, encodeds, decodeds); + addField(TestBase.data(x++), HAS_EQUALS, oMap, list, encodeds, decodeds); + addField(TestBase.data(x), HAS_TIC, oMap, list, encodeds, decodeds); for (int i = 0; i < list.size(); i++) { JsonValue v = list.get(i); diff --git a/src/test/java/io/nats/client/support/JsonUtilsTests.java b/src/test/java/io/nats/client/support/JsonUtilsTests.java index ccc8ad5b7..fde80f9f1 100644 --- a/src/test/java/io/nats/client/support/JsonUtilsTests.java +++ b/src/test/java/io/nats/client/support/JsonUtilsTests.java @@ -186,11 +186,13 @@ public void testAddFields() { addRawJson(sb, "n/a", ""); assertEquals(0, sb.length()); - //noinspection UnnecessaryBoxing - addField(sb, "iminusone", new Integer(-1)); + //noinspection WrapperTypeMayBePrimitive + Integer i = -1; + addField(sb, "iminusone", i); assertEquals(0, sb.length()); - addField(sb, "lminusone", new Long(-1)); + Long l = -1L; + addField(sb, "lminusone", l); assertEquals(0, sb.length()); addFieldWhenGteMinusOne(sb, "lnull", null); @@ -235,15 +237,15 @@ public void testAddFields() { addFieldWhenGtZero(sb, "longnull", (Long) null); assertEquals(87, sb.length()); - //noinspection UnnecessaryBoxing - addFieldWhenGtZero(sb, "intnotgt0", new Integer(0)); + i = 0; + addFieldWhenGtZero(sb, "intnotgt0", i); assertEquals(87, sb.length()); addFieldWhenGtZero(sb, "longnotgt0", 0L); assertEquals(87, sb.length()); - //noinspection UnnecessaryBoxing - addFieldWhenGtZero(sb, "intgt0", new Integer(1)); + i = 1; + addFieldWhenGtZero(sb, "intgt0", i); assertEquals(98, sb.length()); addFieldWhenGtZero(sb, "longgt0", 1L); diff --git a/src/test/java/io/nats/client/support/JwtUtilsTests.java b/src/test/java/io/nats/client/support/JwtUtilsTests.java index 32ca9114d..8c0e6651e 100644 --- a/src/test/java/io/nats/client/support/JwtUtilsTests.java +++ b/src/test/java/io/nats/client/support/JwtUtilsTests.java @@ -21,7 +21,7 @@ import java.util.List; import static io.nats.client.support.JwtUtils.*; -import static io.nats.client.utils.TestBase.sleep; +import static io.nats.client.utils.ThreadUtils.sleep; import static org.junit.jupiter.api.Assertions.*; public class JwtUtilsTests { diff --git a/src/test/java/io/nats/client/support/ScheduledTaskTests.java b/src/test/java/io/nats/client/support/ScheduledTaskTests.java index 093619df8..cb77d0509 100644 --- a/src/test/java/io/nats/client/support/ScheduledTaskTests.java +++ b/src/test/java/io/nats/client/support/ScheduledTaskTests.java @@ -1,12 +1,12 @@ package io.nats.client.support; +import io.nats.client.utils.TestBase; import org.junit.jupiter.api.Test; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static io.nats.client.utils.TestBase.variant; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -30,7 +30,7 @@ public void testScheduledTask() throws InterruptedException { AtomicInteger counter100 = new AtomicInteger(); SttRunnable sttr100 = new SttRunnable(400, counter100); - String id = "100-" + variant(); + String id = "100-" + TestBase.random(); ScheduledTask task100 = new ScheduledTask(id, stpe, 0, 100, TimeUnit.MILLISECONDS, sttr100); validateTaskPeriods(task100, 0, 100); assertEquals(id, task100.getId()); diff --git a/src/test/java/io/nats/client/utils/ConnectionUtils.java b/src/test/java/io/nats/client/utils/ConnectionUtils.java new file mode 100644 index 000000000..00aa0d4ae --- /dev/null +++ b/src/test/java/io/nats/client/utils/ConnectionUtils.java @@ -0,0 +1,167 @@ +// Copyright 2025 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client.utils; + +import io.nats.client.Connection; +import io.nats.client.Nats; +import io.nats.client.Options; +import io.nats.client.impl.ListenerForTesting; +import org.opentest4j.AssertionFailedError; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static io.nats.client.utils.ThreadUtils.sleep; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; + +public abstract class ConnectionUtils { + + public static final long STANDARD_CONNECTION_WAIT_MS = 5000; + public static final long LONG_CONNECTION_WAIT_MS = 7500; + public static final long VERY_LONG_CONNECTION_WAIT_MS = 10000; + public static final long STANDARD_FLUSH_TIMEOUT_MS = 2000; + public static final long MEDIUM_FLUSH_TIMEOUT_MS = 5000; + public static final long LONG_TIMEOUT_MS = 15000; + + // ---------------------------------------------------------------------------------------------------- + // connect or wait for a connection + // ---------------------------------------------------------------------------------------------------- + public static Options.Builder standardOptionsBuilder() { + return Options.builder().reportNoResponders().errorListener(new ListenerForTesting()); + } + + public static Options.Builder standardOptionsBuilder(String serverURL) { + return standardOptionsBuilder().server(serverURL); + } + + public static Options standardOptions() { + return standardOptionsBuilder().build(); + } + + public static Options standardOptions(String serverURL) { + return standardOptionsBuilder(serverURL).build(); + } + + public static Connection connectionWait(Connection conn, long millis) { + return waitUntilStatus(conn, millis, Connection.Status.CONNECTED); + } + + public static Connection standardConnectionWait() throws IOException, InterruptedException { + return standardConnectionWait( Nats.connect(standardOptions()) ); + } + + public static Connection standardConnectionWait(String serverURL) throws IOException, InterruptedException { + return connectionWait(Nats.connect(standardOptions(serverURL)), STANDARD_CONNECTION_WAIT_MS); + } + + public static Connection standardConnectionWait(Options options) throws IOException, InterruptedException { + return connectionWait(Nats.connect(options), STANDARD_CONNECTION_WAIT_MS); + } + + public static Connection standardConnectionWait(Connection conn) { + return connectionWait(conn, STANDARD_CONNECTION_WAIT_MS); + } + + public static Connection longConnectionWait(String serverURL) throws IOException, InterruptedException { + return connectionWait(Nats.connect(standardOptions(serverURL)), LONG_CONNECTION_WAIT_MS); + } + + public static Connection longConnectionWait(Options options) throws IOException, InterruptedException { + return connectionWait(Nats.connect(options), LONG_CONNECTION_WAIT_MS); + } + + public static Connection listenerConnectionWait(Options options, ListenerForTesting listener) throws IOException, InterruptedException { + Connection conn = Nats.connect(options); + listenerConnectionWait(conn, listener, LONG_CONNECTION_WAIT_MS); + return conn; + } + + public static void listenerConnectionWait(Connection conn, ListenerForTesting listener) { + listenerConnectionWait(conn, listener, LONG_CONNECTION_WAIT_MS); + } + + public static void listenerConnectionWait(Connection conn, ListenerForTesting listener, long millis) { + listener.waitForStatusChange(millis, TimeUnit.MILLISECONDS); + assertConnected(conn); + } + + // ---------------------------------------------------------------------------------------------------- + // close + // ---------------------------------------------------------------------------------------------------- + public static void standardCloseConnection(Connection conn) { + closeConnection(conn, STANDARD_CONNECTION_WAIT_MS); + } + + public static void closeConnection(Connection conn, long millis) { + if (conn != null) { + close(conn); + waitUntilStatus(conn, millis, Connection.Status.CLOSED); + assertClosed(conn); + } + } + + public static void close(Connection conn) { + try { conn.close(); } catch (InterruptedException e) { /* ignored */ } + } + + // ---------------------------------------------------------------------------------------------------- + // connection waiting + // ---------------------------------------------------------------------------------------------------- + public static Connection waitUntilStatus(Connection conn, long millis, Connection.Status waitUntilStatus) { + long times = (millis + 99) / 100; + for (long x = 0; x < times; x++) { + sleep(100); + if (conn.getStatus() == waitUntilStatus) { + return conn; + } + } + + throw new AssertionFailedError(expectingMessage(conn, waitUntilStatus)); + } + + // ---------------------------------------------------------------------------------------------------- + // assertions + // ---------------------------------------------------------------------------------------------------- + public static void assertConnected(Connection conn) { + assertSame(Connection.Status.CONNECTED, conn.getStatus(), + () -> expectingMessage(conn, Connection.Status.CONNECTED)); + } + + public static void assertNotConnected(Connection conn) { + assertNotSame(Connection.Status.CONNECTED, conn.getStatus(), + () -> "Failed not expecting Connection Status " + Connection.Status.CONNECTED.name()); + } + + public static void assertClosed(Connection conn) { + assertSame(Connection.Status.CLOSED, conn.getStatus(), + () -> expectingMessage(conn, Connection.Status.CLOSED)); + } + + public static void assertCanConnect() throws IOException, InterruptedException { + standardCloseConnection( standardConnectionWait() ); + } + + public static void assertCanConnect(String serverURL) throws IOException, InterruptedException { + standardCloseConnection( standardConnectionWait(serverURL) ); + } + + public static void assertCanConnect(Options options) throws IOException, InterruptedException { + standardCloseConnection( standardConnectionWait(options) ); + } + + private static String expectingMessage(Connection conn, Connection.Status expecting) { + return "Failed expecting Connection Status " + expecting.name() + " but was " + conn.getStatus(); + } +} diff --git a/src/test/java/io/nats/client/utils/LongRunningServer.java b/src/test/java/io/nats/client/utils/LongRunningServer.java new file mode 100644 index 000000000..370113095 --- /dev/null +++ b/src/test/java/io/nats/client/utils/LongRunningServer.java @@ -0,0 +1,114 @@ +// Copyright 2025 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client.utils; + +import io.nats.client.Connection; +import io.nats.client.Nats; +import io.nats.client.NatsTestServer; + +import java.io.IOException; +import java.util.concurrent.locks.ReentrantLock; + +import static io.nats.client.utils.ConnectionUtils.VERY_LONG_CONNECTION_WAIT_MS; +import static io.nats.client.utils.ConnectionUtils.connectionWait; +import static io.nats.client.utils.OptionsUtils.options; +import static io.nats.client.utils.VersionUtils.initVersionServerInfo; + +public abstract class LongRunningServer { + + private static final ReentrantLock lock = new ReentrantLock(); + private static final int MAX_TRIES = 3; + + private static NatsTestServer natsTestServer; + private static String server; + private static Connection[] ncs; + + public static String server() throws IOException { + if (server == null) { + ensureInitialized(); + } + return server; + } + + // do not use LrConns in try-resources + public static Connection getLrConn() throws IOException, InterruptedException { + return _getLrConnection(0, 0); + } + + public static Connection getLrConn2() throws IOException, InterruptedException { + return _getLrConnection(1, 0); + } + + private static Connection _getLrConnection(int ix, int tries) throws IOException, InterruptedException { + lock.lock(); + try { + if (ncs == null) { + ncs = new Connection[2]; + } + if (ncs[ix] == null) { + ncs[ix] = connectionWait(Nats.connect(options(server())), VERY_LONG_CONNECTION_WAIT_MS); + initVersionServerInfo(ncs[ix]); + } + else if (ncs[ix].getStatus() != Connection.Status.CONNECTED) { + if (++tries < MAX_TRIES) { + Connection c = ncs[ix]; + c.getOptions().getExecutor().execute(() -> { + try { + c.close(); + } + catch (InterruptedException ignore) { + } + }); + ncs[ix] = null; + return _getLrConnection(ix, tries); + } + } + return ncs[ix]; + } + finally { + lock.unlock(); + } + } + + private static void ensureInitialized() throws IOException { + lock.lock(); + try { + if (natsTestServer == null) { + natsTestServer = new NatsTestServer( + NatsTestServer.builder() + .jetstream(true) + .customName("LongRunningServer") + ); + server = natsTestServer.getURI(); + + final Thread shutdownHookThread = new Thread("LongRunningServer-Shutdown-Hook") { + @Override + public void run() { + try { + natsTestServer.shutdown(false); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + }; + + Runtime.getRuntime().addShutdownHook(shutdownHookThread); + } + } + finally { + lock.unlock(); + } + } +} diff --git a/src/test/java/io/nats/client/utils/OptionsUtils.java b/src/test/java/io/nats/client/utils/OptionsUtils.java new file mode 100644 index 000000000..3f24cfa2e --- /dev/null +++ b/src/test/java/io/nats/client/utils/OptionsUtils.java @@ -0,0 +1,111 @@ +// Copyright 2025 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client.utils; + +import io.nats.client.ErrorListener; +import io.nats.client.NatsTestServer; +import io.nats.client.Options; +import org.jspecify.annotations.NonNull; + +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import static io.nats.NatsRunnerUtils.getNatsLocalhostUri; + +/** + * ---------------------------------------------------------------------------------------------------- + * THIS IS THE PREFERRED WAY TO BUILD ANY OPTIONS FOR TESTING + * ---------------------------------------------------------------------------------------------------- + */ +public abstract class OptionsUtils { + + private static ExecutorService ES; + private static ScheduledThreadPoolExecutor SCHD; + private static ThreadFactory CB; + private static ThreadFactory CN; + + public static ErrorListener NOOP_EL = new ErrorListener() {}; + + public static Options.Builder optionsBuilder(ErrorListener el) { + return optionsBuilder().errorListener(el); + } + + public static Options.Builder optionsBuilder(NatsTestServer ts) { + return optionsBuilder().server(ts.getNatsLocalhostUri()); + } + + public static Options.Builder optionsBuilder(int port) { + return optionsBuilder().server(getNatsLocalhostUri(port)); + } + + public static Options.Builder optionsBuilder(String... servers) { + return optionsBuilder().servers(servers); + } + + public static Options options(String... servers) { + return optionsBuilder().servers(servers).build(); + } + + public static Options.Builder optionsBuilder() { + if (ES == null) { + ES = new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 500L, TimeUnit.MILLISECONDS, + new SynchronousQueue<>(), + new TestThreadFactory("ES")); + } + + if (SCHD == null) { + SCHD = new ScheduledThreadPoolExecutor(3, new TestThreadFactory("SCHD")); + SCHD.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + SCHD.setRemoveOnCancelPolicy(true); + } + + if (CB == null) { + CB = new TestThreadFactory("CB"); + } + + if (CN == null) { + CN = new TestThreadFactory("CN"); + } + + return Options.builder() + .executor(ES) + .scheduledExecutor(SCHD) + .callbackThreadFactory(CB) + .connectThreadFactory(CN) + .errorListener(NOOP_EL); + } + + static class TestThreadFactory implements ThreadFactory { + final String name; + final AtomicInteger threadNo; + + public TestThreadFactory(String name) { + this.name = name; + this.threadNo = new AtomicInteger(0); + } + + public Thread newThread(@NonNull Runnable r) { + String threadName = "test." + name + "." + threadNo.incrementAndGet(); + Thread t = new Thread(r, threadName); + if (t.isDaemon()) { + t.setDaemon(false); + } + if (t.getPriority() != Thread.NORM_PRIORITY) { + t.setPriority(Thread.NORM_PRIORITY); + } + return t; + } + } +} diff --git a/src/test/java/io/nats/client/utils/TestBase.java b/src/test/java/io/nats/client/utils/TestBase.java index a4d29b48e..2603b3934 100644 --- a/src/test/java/io/nats/client/utils/TestBase.java +++ b/src/test/java/io/nats/client/utils/TestBase.java @@ -14,12 +14,14 @@ package io.nats.client.utils; import io.nats.client.*; -import io.nats.client.api.ServerInfo; -import io.nats.client.impl.ListenerForTesting; -import io.nats.client.impl.NatsMessage; +import io.nats.client.api.StorageType; +import io.nats.client.api.StreamConfiguration; +import io.nats.client.api.StreamInfo; +import io.nats.client.impl.*; import io.nats.client.support.NatsJetStreamClientError; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.function.Executable; -import org.opentest4j.AssertionFailedError; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -28,16 +30,23 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.LockSupport; import java.util.function.Supplier; import static io.nats.client.support.NatsConstants.DOT; import static io.nats.client.support.NatsConstants.EMPTY; import static io.nats.client.support.NatsJetStreamClientError.KIND_ILLEGAL_ARGUMENT; import static io.nats.client.support.NatsJetStreamClientError.KIND_ILLEGAL_STATE; +import static io.nats.client.utils.ConnectionUtils.*; +import static io.nats.client.utils.OptionsUtils.optionsBuilder; +import static io.nats.client.utils.ThreadUtils.sleep; +import static io.nats.client.utils.VersionUtils.*; import static org.junit.jupiter.api.Assertions.*; public class TestBase { + @BeforeAll + public static void setup(TestInfo info) { + TestDebugger.clazz(info.getDisplayName()); + } public static final String STAR_SEGMENT = "*.star.*.segment.*"; public static final String GT_NOT_LAST_SEGMENT = "gt.>.notlast"; @@ -74,18 +83,12 @@ public class TestBase { public static final String META_KEY = "meta-test-key"; public static final String META_VALUE = "meta-test-value"; - public static final long STANDARD_CONNECTION_WAIT_MS = 5000; - public static final long LONG_CONNECTION_WAIT_MS = 7500; - public static final long STANDARD_FLUSH_TIMEOUT_MS = 2000; - public static final long MEDIUM_FLUSH_TIMEOUT_MS = 5000; - public static final long LONG_TIMEOUT_MS = 15000; - public static String[] BAD_SUBJECTS_OR_QUEUES = new String[] { HAS_SPACE, HAS_CR, HAS_LF, HAS_TAB, STARTS_SPACE, ENDS_SPACE, null, EMPTY }; // ---------------------------------------------------------------------------------------------------- - // runners + // runners / test interfaces // ---------------------------------------------------------------------------------------------------- public interface InServerTest { void test(Connection nc) throws Exception; @@ -105,246 +108,201 @@ default void append(int index, Options.Builder builder) {} default boolean includeAllServers() { return false; } } - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public interface VersionCheck { - boolean runTest(ServerInfo si); - } - - public static boolean atLeast2_9_0() { - return atLeast2_9_0(RUN_SERVER_INFO); + public interface InJetStreamTest { + void test(Connection nc, JetStreamManagement jsm, JetStream js) throws Exception; } - public static boolean atLeast2_9_0(Connection nc) { - return atLeast2_9_0(nc.getServerInfo()); + public interface InJetStreamTestingContextTest { + void test(Connection nc, JetStreamTestingContext jstc) throws Exception; } - public static boolean atLeast2_9_0(ServerInfo si) { - return si.isSameOrNewerThanVersion("2.9.0"); - } - - public static boolean atLeast2_10_26(ServerInfo si) { - return si.isSameOrNewerThanVersion("2.10.26"); + // ---------------------------------------------------------------------------------------------------- + // runners / js cleanup + // ---------------------------------------------------------------------------------------------------- + public static void cleanupJs(Connection c) + { + try { + cleanupJs(c.jetStreamManagement()); + } catch (Exception ignore) {} } - public static boolean atLeast2_9_1(ServerInfo si) { - return si.isSameOrNewerThanVersion("2.9.1"); + public static void cleanupJs(JetStreamManagement jsm) + { + try { + List streams = jsm.getStreamNames(); + for (String s : streams) { + jsm.deleteStream(s); + } + } catch (Exception ignore) {} } - public static boolean atLeast2_10() { - return atLeast2_10(RUN_SERVER_INFO); - } + // ---------------------------------------------------------------------------------------------------- + // runners -> new server + // ---------------------------------------------------------------------------------------------------- + private static void _runInServer(boolean jetstream, Options.Builder builder, VersionCheck vc, InServerTest inServerTest) throws Exception { + if (vc != null && VERSION_SERVER_INFO != null && !vc.runTest(VERSION_SERVER_INFO)) { + return; // had vc, already had run server info and fails check + } - public static boolean atLeast2_10(ServerInfo si) { - return si.isNewerVersionThan("2.9.99"); - } + try (NatsTestServer ts = new NatsTestServer(NatsTestServer.builder().jetstream(jetstream))) { + if (builder == null) { + builder = optionsBuilder(); + } - public static boolean atLeast2_10_3(ServerInfo si) { - return si.isSameOrNewerThanVersion("2.10.3"); + try (Connection nc = standardConnectionWait(builder.server(ts.getURI()).build())) { + initVersionServerInfo(nc); + if (vc == null || vc.runTest(VERSION_SERVER_INFO)) { + try { + inServerTest.test(nc); + } + finally { + if (jetstream) { + cleanupJs(nc); + } + } + } + } + } } - public static boolean atLeast2_11() { - return atLeast2_11(RUN_SERVER_INFO); + public static void runInServer(InServerTest inServerTest) throws Exception { + _runInServer(false, null, null, inServerTest); } - public static boolean atLeast2_11(ServerInfo si) { - return si.isNewerVersionThan("2.10.99"); + public static void runInServer(Options.Builder builder, InServerTest inServerTest) throws Exception { + _runInServer(false, builder, null, inServerTest); } - public static boolean before2_11() { - return before2_11(RUN_SERVER_INFO); + public static void runInJsServer(InServerTest inServerTest) throws Exception { + _runInServer(true, null, null, inServerTest); } - public static boolean before2_11(ServerInfo si) { - return si.isOlderThanVersion("2.11"); + public static void runInJsServer(VersionCheck vc, InServerTest inServerTest) throws Exception { + _runInServer(true, null, vc, inServerTest); } - public static boolean atLeast2_12() { - return atLeast2_12(RUN_SERVER_INFO); + public static void runInJsServer(ErrorListener el, InServerTest inServerTest) throws Exception { + _runInServer(true, optionsBuilder(el), null, inServerTest); } - public static boolean atLeast2_12(ServerInfo si) { - return si.isSameOrNewerThanVersion("2.11.99"); + public static void runInJsServer(ErrorListener el, VersionCheck vc, InServerTest inServerTest) throws Exception { + _runInServer(true, optionsBuilder(el), vc, inServerTest); } - public static void runInServer(InServerTest inServerTest) throws Exception { - runInServer(false, false, null, null, inServerTest); + // ---------------------------------------------------------------------------------------------------- + // runners -> long running server + // ---------------------------------------------------------------------------------------------------- + public static void runInLrServer(InServerTest test) throws Exception { + _runInLrServer(null, null, test, null, null); } - public static void runInServer(Options.Builder builder, InServerTest inServerTest) throws Exception { - runInServer(false, false, builder, null, inServerTest); + public static void runInLrServerCloseableConnection(InServerTest test) throws Exception { + _runInLrServer(optionsBuilder(LongRunningServer.server()), null, test, null, null); } - public static void runInServer(boolean debug, InServerTest inServerTest) throws Exception { - runInServer(debug, false, null, null, inServerTest); + public static void runInLrServer(Options.Builder builder, InServerTest test) throws Exception { + _runInLrServer(builder, null, test, null, null); } - public static void runInJsServer(InServerTest inServerTest) throws Exception { - runInServer(false, true, null, null, inServerTest); + public static void runInLrServer(InJetStreamTest jsTest) throws Exception { + _runInLrServer(null, null, null, jsTest, null); } - public static void runInJsServer(ErrorListener el, InServerTest inServerTest) throws Exception { - runInServer(false, true, new Options.Builder().errorListener(el), null, inServerTest); + public static void runInLrServer(VersionCheck vc, InJetStreamTest jsTest) throws Exception { + _runInLrServer(null, vc, null, jsTest, null); } - public static void runInJsServer(VersionCheck vc, ErrorListener el, InServerTest inServerTest) throws Exception { - runInServer(false, true, new Options.Builder().errorListener(el), vc, inServerTest); + public static void runInLrServer(Options.Builder builder, VersionCheck vc, InJetStreamTest jsTest) throws Exception { + _runInLrServer(builder, vc, null, jsTest, null); } - public static void runInJsServer(Options.Builder builder, InServerTest inServerTest) throws Exception { - runInServer(false, true, builder, null, inServerTest); + public static void runInLrServer(ErrorListener el, InJetStreamTest jsTest) throws Exception { + _runInLrServer(optionsBuilder(el), null, null, jsTest, null); } - public static void runInJsServer(Options.Builder builder, VersionCheck vc, InServerTest inServerTest) throws Exception { - runInServer(false, true, builder, vc, inServerTest); + public static void runInLrServer(ErrorListener el, VersionCheck vc, InJetStreamTest jsTest) throws Exception { + _runInLrServer(optionsBuilder(el), vc, null, jsTest, null); } - public static void runInJsServer(VersionCheck vc, InServerTest inServerTest) throws Exception { - runInServer(false, true, null, vc, inServerTest); + public static void runInLrServer(InJetStreamTestingContextTest oneSubjectJstcTest) throws Exception { + _runInLrServer(null, null, null, null, oneSubjectJstcTest); } - public static void runInJsServer(boolean debug, InServerTest inServerTest) throws Exception { - runInServer(debug, true, null, null, inServerTest); + public static void runInLrServer(VersionCheck vc, InJetStreamTestingContextTest oneSubjectJstcTest) throws Exception { + _runInLrServer(null, vc, null, null, oneSubjectJstcTest); } - public static void runInServer(boolean debug, boolean jetstream, InServerTest inServerTest) throws Exception { - runInServer(debug, jetstream, null, null, inServerTest); + public static void runInLrServer(ErrorListener el, VersionCheck vc, InJetStreamTestingContextTest oneSubjectJstcTest) throws Exception { + _runInLrServer(optionsBuilder(el), vc, null, null, oneSubjectJstcTest); } - public static void runInServer(boolean debug, boolean jetstream, Options.Builder builder, InServerTest inServerTest) throws Exception { - runInServer(debug, jetstream, builder, null, inServerTest); + public static void runInLrServer(ErrorListener el, InJetStreamTestingContextTest oneSubjectJstcTest) throws Exception { + _runInLrServer(optionsBuilder(el), null, null, null, oneSubjectJstcTest); } - public static ServerInfo RUN_SERVER_INFO; - - public static ServerInfo ensureRunServerInfo() throws Exception { - if (RUN_SERVER_INFO == null) { - runInServer(false, false, null, null, nc -> {}); - } - return RUN_SERVER_INFO; + public static void runInLrServer(Options.Builder builder, InJetStreamTestingContextTest oneSubjectJstcTest) throws Exception { + _runInLrServer(builder, null, null, null, oneSubjectJstcTest); } - public static void initRunServerInfo(Connection nc) { - if (RUN_SERVER_INFO == null) { - RUN_SERVER_INFO = nc.getServerInfo(); - } + public static void runInLrServer(Options.Builder builder, VersionCheck vc, InJetStreamTestingContextTest oneSubjectJstcTest) throws Exception { + _runInLrServer(builder, vc, null, null, oneSubjectJstcTest); } - public static void runInServer(boolean debug, boolean jetstream, Options.Builder builder, VersionCheck vc, InServerTest inServerTest) throws Exception { - if (vc != null && RUN_SERVER_INFO != null) { - if (!vc.runTest(RUN_SERVER_INFO)) { - return; - } - vc = null; // since we've already determined it should run, null this out so we don't check below + private static void _runInLrServer(Options.Builder builder, VersionCheck vc, + InServerTest test, + InJetStreamTest jsTest, + InJetStreamTestingContextTest oneSubjectJstcTest) throws Exception { + if (vc != null && VERSION_SERVER_INFO != null && !vc.runTest(VERSION_SERVER_INFO)) { + return; // had vc, already had run server info and fails check } + // no builder, we can use the long-running connection since it's totally generic + // with a builder, just make a fresh connection and close it at the end. + boolean closeWhenDone; + Connection nc; if (builder == null) { - builder = new Options.Builder(); + closeWhenDone = false; + nc = LongRunningServer.getLrConn(); + } + else { + closeWhenDone = true; + nc = longConnectionWait(builder.server(LongRunningServer.server()).build()); } - try (NatsTestServer ts = new NatsTestServer(debug, jetstream); - Connection nc = standardConnection(builder.server(ts.getURI()).build())) - { - initRunServerInfo(nc); - - if (vc != null && !vc.runTest(RUN_SERVER_INFO)) { - return; - } - + initVersionServerInfo(nc); + if (vc == null || vc.runTest(VERSION_SERVER_INFO)) { try { - inServerTest.test(nc); - } - finally { - if (jetstream) { - cleanupJs(nc); + if (oneSubjectJstcTest != null) { + try (JetStreamTestingContext jstc = new JetStreamTestingContext(nc, 1)) { + oneSubjectJstcTest.test(nc, jstc); + } } - } - } - } - - public static class LongRunningNatsTestServer extends NatsTestServer { - public final boolean jetstream; - public final Options.Builder builder; - public final ListenerForTesting listenerForTesting; - - public LongRunningNatsTestServer(boolean debug, boolean jetstream, Options.Builder builder) throws IOException { - super(builder() - .debug(debug) - .jetstream(jetstream) - .connectValidateInitialDelay(100L) - .connectValidateSubsequentDelay(25L) - .connectValidateTries(15) - ); - this.jetstream = jetstream; - if (builder == null) { - this.builder = new Options.Builder(); - listenerForTesting = new ListenerForTesting(); - } - else { - this.builder = builder; - listenerForTesting = null; - } - } - - public void setExitOnDisconnect() { - if (listenerForTesting != null) { - listenerForTesting.setExitOnDisconnect(); - } - } - - public void setExitOnHeartbeatError() { - if (listenerForTesting != null) { - listenerForTesting.setExitOnHeartbeatError(); - } - } - - public Connection connect() throws IOException, InterruptedException { - return standardConnection(builder.server(getURI()).build()); - } - - public void run(InServerTest inServerTest) throws Exception { - run(null, null, inServerTest); - } - - public void run(VersionCheck vc, InServerTest inServerTest) throws Exception { - run(null, vc, inServerTest); - } - - public void run(Options.Builder builder, InServerTest inServerTest) throws Exception { - run(builder, null, inServerTest); - } - - public void run(Options.Builder builder, VersionCheck vc, InServerTest inServerTest) throws Exception { - if (vc != null && RUN_SERVER_INFO != null) { - if (!vc.runTest(RUN_SERVER_INFO)) { - return; + else if (jsTest != null) { + NatsJetStreamManagement jsm = (NatsJetStreamManagement) nc.jetStreamManagement(); + NatsJetStream js = (NatsJetStream) nc.jetStream(); + jsTest.test(nc, jsm, js); } - vc = null; // since we've already determined it should run, null this out so we don't check below - } - - if (builder == null) { - builder = new Options.Builder(); - } - - try (Connection nc = standardConnection(builder.server(getURI()).build())) - { - initRunServerInfo(nc); - - if (vc != null && !vc.runTest(RUN_SERVER_INFO)) { - return; + else { + test.test(nc); } - - try { - inServerTest.test(nc); + } + finally { + if (test == null) { + cleanupJs(nc); } - finally { - if (jetstream) { - cleanupJs(nc); + if (closeWhenDone) { + try { + nc.close(); } + catch (Exception ignore) {} } } } } + // ---------------------------------------------------------------------------------------------------- + // runners / external + // ---------------------------------------------------------------------------------------------------- public static void runInExternalServer(InServerTest inServerTest) throws Exception { runInExternalServer(Options.DEFAULT_URL, inServerTest); } @@ -355,6 +313,10 @@ public static void runInExternalServer(String url, InServerTest inServerTest) th } } + + // ---------------------------------------------------------------------------------------------------- + // runners / special + // ---------------------------------------------------------------------------------------------------- public static String HUB_DOMAIN = "HUB"; public static String LEAF_DOMAIN = "LEAF"; @@ -386,9 +348,9 @@ public static void runInJsHubLeaf(TwoServerTest twoServerTest) throws Exception }; try (NatsTestServer hub = new NatsTestServer(hubPort, false, true, null, hubInserts, null); - Connection nchub = standardConnection(hub.getURI()); + Connection nchub = standardConnectionWait(hub.getURI()); NatsTestServer leaf = new NatsTestServer(leafPort, false, true, null, leafInserts, null); - Connection ncleaf = standardConnection(leaf.getURI()) + Connection ncleaf = standardConnectionWait(leaf.getURI()) ) { try { twoServerTest.test(nchub, ncleaf); @@ -414,8 +376,8 @@ public static void runInJsCluster(ThreeServerTestOptions tstOpts, ThreeServerTes String dir1 = tempJsStoreDir(); String dir2 = tempJsStoreDir(); String dir3 = tempJsStoreDir(); - String cluster = "cluster_" + variant(); - String serverPrefix = "server_" + variant() + "_"; + String cluster = "cluster_" + random(); + String serverPrefix = "server_" + random() + "_"; if (tstOpts == null) { tstOpts = new ThreeServerTestOptions() {}; @@ -429,9 +391,9 @@ public static void runInJsCluster(ThreeServerTestOptions tstOpts, ThreeServerTes try (NatsTestServer srv1 = new NatsTestServer(port1, false, true, null, server1Inserts, null); NatsTestServer srv2 = new NatsTestServer(port2, false, true, null, server2Inserts, null); NatsTestServer srv3 = new NatsTestServer(port3, false, true, null, server3Inserts, null); - Connection nc1 = standardConnection(makeOptions(0, tstOpts, srv1, srv2, srv3)); - Connection nc2 = standardConnection(makeOptions(1, tstOpts, srv2, srv1, srv3)); - Connection nc3 = standardConnection(makeOptions(2, tstOpts, srv3, srv1, srv2)) + Connection nc1 = standardConnectionWait(makeOptions(0, tstOpts, srv1, srv2, srv3)); + Connection nc2 = standardConnectionWait(makeOptions(1, tstOpts, srv2, srv1, srv3)); + Connection nc3 = standardConnectionWait(makeOptions(2, tstOpts, srv3, srv1, srv2)) ) { try { threeServerTest.test(nc1, nc2, nc3); @@ -495,206 +457,75 @@ private static Options makeOptions(int id, ThreeServerTestOptions tstOpts, NatsT } private static String tempJsStoreDir() throws IOException { - return Files.createTempDirectory(variant()).toString().replace("\\", "\\\\"); // when on windows this is necessary. unix doesn't have backslash - } - - public static void cleanupJs(Connection c) - { - try { - JetStreamManagement jsm = c.jetStreamManagement(); - List streams = jsm.getStreamNames(); - for (String s : streams) - { - jsm.deleteStream(s); - } - } catch (Exception ignore) {} + return Files.createTempDirectory(random()).toString().replace("\\", "\\\\"); // when on windows this is necessary. unix doesn't have backslash } // ---------------------------------------------------------------------------------------------------- // data makers // ---------------------------------------------------------------------------------------------------- - public static final String STREAM = "stream"; - public static final String MIRROR = "mirror"; - public static final String SOURCE = "source"; - public static final String SUBJECT = "subject"; - public static final String SUBJECT_STAR = SUBJECT + ".*"; - public static final String SUBJECT_GT = SUBJECT + ".>"; - public static final String QUEUE = "queue"; - public static final String DURABLE = "durable"; - public static final String NAME = "name"; - public static final String DELIVER = "deliver"; - public static final String MESSAGE_ID = "mid"; - public static final String BUCKET = "bucket"; - public static final String KEY = "key"; public static final String DATA = "data"; - public static final String PREFIX = "prefix"; - - public static String variant(Object variant) { - return variant == null ? NUID.nextGlobalSequence() : "" + variant; - } - public static String variant() { + public static String random() { return NUID.nextGlobalSequence(); } - private static int pi0 = -1; - private static int pi1 = 0; - public static String prefix() { - if (++pi0 == 26) { - pi0 = 0; - if (++pi1 == 26) { - pi1 = 0; - } + public static String randomWide(int width) { + StringBuilder sb = new StringBuilder(); + while (sb.length() < width) { + sb.append(random()); } - return PREFIX + (char)('A' + pi1) + (char)('A' + pi0); - } - - public static String stream() { - return STREAM + "-" + variant(); - } - - public static String stream(Object variant) { - return STREAM + "-" + variant(variant); - } - - public static String mirror() { - return MIRROR + "-" + variant(); - } - - public static String mirror(Object variant) { - return MIRROR + "-" + variant(variant); - } - - public static String source() { - return SOURCE + "-" + variant(); - } - - public static String source(Object variant) { - return SOURCE + "-" + variant(variant); + return sb.substring(0, width); } - public static String subject() { - return SUBJECT + "-" + variant(); - } - public static String subject(Object variant) { - return SUBJECT + "-" + variant(variant); + public static String subject(int variant) { + return "subject-" + variant; } - public static String subjectDot(String field) { - return SUBJECT + DOT + field; + public static String subjectGt(String subject) { + return subject + ".>"; } - public static String queue(Object variant) { - return QUEUE + "-" + variant(variant); + public static String subjectStar(String subject) { + return subject + ".*"; } - public static String durable() { - return DURABLE + "-" + variant(); - } - - public static String durable(Object variant) { - return DURABLE + "-" + variant(variant); - } - - public static String durable(String vary, Object variant) { - return DURABLE + "-" + vary + "-" + variant(variant); - } - - public static String name() { - return NAME + "-" + variant(); - } - - public static String name(Object variant) { - return NAME + "-" + variant(variant); - } - - public static String deliver() { - return DELIVER + "-" + variant(); - } - - public static String deliver(Object variant) { - return DELIVER + "-" + variant(variant); - } - - public static String bucket(Object variant) { - return BUCKET + "-" + variant(variant); - } - - public static String bucket() { - return bucket(null); - } - - public static String key(Object variant) { - return KEY + "-" + variant(variant); - } - - public static String key() { - return KEY + "-" + variant(); - } - - public static String messageId(Object variant) { - return MESSAGE_ID + "-" + variant(variant); + public static String subjectDot(String subject, String field) { + return subject + DOT + field; } public static String data(Object variant) { - return DATA + "-" + variant(variant); + return DATA + "-" + variant; } public static byte[] dataBytes() { - return data(variant()).getBytes(StandardCharsets.US_ASCII); + return data(random()).getBytes(StandardCharsets.US_ASCII); } public static byte[] dataBytes(Object variant) { return data(variant).getBytes(StandardCharsets.US_ASCII); } public static NatsMessage getDataMessage(String data) { - return new NatsMessage(SUBJECT, null, data.getBytes(StandardCharsets.US_ASCII)); + return new NatsMessage(random(), null, data.getBytes(StandardCharsets.US_ASCII)); } // ---------------------------------------------------------------------------------------------------- // assertions // ---------------------------------------------------------------------------------------------------- - public static void assertConnected(Connection conn) { - assertSame(Connection.Status.CONNECTED, conn.getStatus(), - () -> expectingMessage(conn, Connection.Status.CONNECTED)); - } - - public static void assertNotConnected(Connection conn) { - assertNotSame(Connection.Status.CONNECTED, conn.getStatus(), - () -> "Failed not expecting Connection Status " + Connection.Status.CONNECTED.name()); - } - - public static void assertClosed(Connection conn) { - assertSame(Connection.Status.CLOSED, conn.getStatus(), - () -> expectingMessage(conn, Connection.Status.CLOSED)); - } - - public static void assertCanConnect() throws IOException, InterruptedException { - standardCloseConnection( standardConnection() ); - } - - public static void assertCanConnect(String serverURL) throws IOException, InterruptedException { - standardCloseConnection( standardConnection(serverURL) ); - } - - public static void assertCanConnect(Options options) throws IOException, InterruptedException { - standardCloseConnection( standardConnection(options) ); - } public static void assertCanConnectAndPubSub() throws IOException, InterruptedException { - Connection conn = standardConnection(); + Connection conn = standardConnectionWait(); assertPubSub(conn); standardCloseConnection(conn); } public static void assertCanConnectAndPubSub(String serverURL) throws IOException, InterruptedException { - Connection conn = standardConnection(serverURL); + Connection conn = standardConnectionWait(serverURL); assertPubSub(conn); standardCloseConnection(conn); } public static void assertCanConnectAndPubSub(Options options) throws IOException, InterruptedException { - Connection conn = standardConnection(options); + Connection conn = standardConnectionWait(options); assertPubSub(conn); standardCloseConnection(conn); } @@ -712,7 +543,7 @@ public static void assertByteArraysEqual(byte[] data1, byte[] data2) { } public static void assertPubSub(Connection conn) throws InterruptedException { - String subject = subject(); + String subject = random(); String data = data(null); Subscription sub = conn.subscribe(subject); conn.publish(subject, data.getBytes()); @@ -721,38 +552,6 @@ public static void assertPubSub(Connection conn) throws InterruptedException { assertEquals(data, new String(m.getData())); } - // ---------------------------------------------------------------------------------------------------- - // utils / macro utils - // ---------------------------------------------------------------------------------------------------- - public static void sleep(long ms) { - try { Thread.sleep(ms); } catch (InterruptedException ignored) { /* ignored */ } - } - - public static void park(Duration d) { - try { LockSupport.parkNanos(d.toNanos()); } catch (Exception ignored) { /* ignored */ } - } - - public static void debugPrintln(Object... debug) { - StringBuilder sb = new StringBuilder(); - sb.append(System.currentTimeMillis()); - sb.append(" ["); - sb.append(Thread.currentThread().getName()); - sb.append(","); - sb.append(Thread.currentThread().getPriority()); - sb.append("] "); - boolean flag = true; - for (Object o : debug) { - if (flag) { - flag = false; - } - else { - sb.append(" | "); - } - sb.append(o); - } - System.out.println(sb.toString()); - } - // ---------------------------------------------------------------------------------------------------- // flush // ---------------------------------------------------------------------------------------------------- @@ -777,101 +576,6 @@ public static void flushAndWaitLong(Connection conn, ListenerForTesting listener flushAndWait(conn, listener, STANDARD_FLUSH_TIMEOUT_MS, LONG_TIMEOUT_MS); } - // ---------------------------------------------------------------------------------------------------- - // connect or wait for a connection - // ---------------------------------------------------------------------------------------------------- - public static Options.Builder standardOptionsBuilder() { - return Options.builder().reportNoResponders().errorListener(new ListenerForTesting()); - } - - public static Options.Builder standardOptionsBuilder(String serverURL) { - return standardOptionsBuilder().server(serverURL); - } - - public static Options standardOptions() { - return standardOptionsBuilder().build(); - } - - public static Options standardOptions(String serverURL) { - return standardOptionsBuilder(serverURL).build(); - } - - public static Connection standardConnection() throws IOException, InterruptedException { - return standardConnectionWait( Nats.connect(standardOptions()) ); - } - - public static Connection standardConnection(String serverURL) throws IOException, InterruptedException { - return standardConnectionWait( Nats.connect(standardOptions(serverURL)) ); - } - - public static Connection standardConnection(Options options) throws IOException, InterruptedException { - return standardConnectionWait( Nats.connect(options) ); - } - - public static Connection listenerConnectionWait(Options options, ListenerForTesting listener) throws IOException, InterruptedException { - return listenerConnectionWait( Nats.connect(options), listener ); - } - - public static Connection listenerConnectionWait(Connection conn, ListenerForTesting listener) { - return listenerConnectionWait(conn, listener, STANDARD_CONNECTION_WAIT_MS); - } - - public static Connection standardConnectionWait(Connection conn) { - return connectionWait(conn, STANDARD_CONNECTION_WAIT_MS); - } - - public static Connection longConnectionWait(Options options) throws IOException, InterruptedException { - return connectionWait( Nats.connect(options), LONG_CONNECTION_WAIT_MS ); - } - - public static Connection connectionWait(Connection conn, long millis) { - return waitUntilStatus(conn, millis, Connection.Status.CONNECTED); - } - - public static Connection listenerConnectionWait(Connection conn, ListenerForTesting listener, long millis) { - listener.waitForStatusChange(millis, TimeUnit.MILLISECONDS); - assertConnected(conn); - return conn; - } - - // ---------------------------------------------------------------------------------------------------- - // close - // ---------------------------------------------------------------------------------------------------- - public static void standardCloseConnection(Connection conn) { - closeConnection(conn, STANDARD_CONNECTION_WAIT_MS); - } - - public static void closeConnection(Connection conn, long millis) { - if (conn != null) { - close(conn); - waitUntilStatus(conn, millis, Connection.Status.CLOSED); - assertClosed(conn); - } - } - - public static void close(Connection conn) { - try { conn.close(); } catch (InterruptedException e) { /* ignored */ } - } - - // ---------------------------------------------------------------------------------------------------- - // connection waiting - // ---------------------------------------------------------------------------------------------------- - public static Connection waitUntilStatus(Connection conn, long millis, Connection.Status waitUntilStatus) { - long times = (millis + 99) / 100; - for (long x = 0; x < times; x++) { - sleep(100); - if (conn.getStatus() == waitUntilStatus) { - return conn; - } - } - - throw new AssertionFailedError(expectingMessage(conn, waitUntilStatus)); - } - - private static String expectingMessage(Connection conn, Connection.Status expecting) { - return "Failed expecting Connection Status " + expecting.name() + " but was " + conn.getStatus(); - } - public static void assertTrueByTimeout(long millis, Supplier test) { long times = (millis + 99) / 100; for (long x = 0; x < times; x++) { @@ -912,4 +616,29 @@ else if (atLeast2_9_0() ){ assertEquals(0, metadata.size()); } } + + // ---------------------------------------------------------------------------------------------------- + // JetStream JetStream JetStream JetStream JetStream JetStream JetStream JetStream JetStream JetStream + // ---------------------------------------------------------------------------------------------------- + public static StreamInfo createMemoryStream(JetStreamManagement jsm, String streamName, String... subjects) throws IOException, JetStreamApiException { + if (streamName == null) { + streamName = random(); + } + + if (subjects == null || subjects.length == 0) { + subjects = new String[]{random()}; + } + + StreamConfiguration sc = StreamConfiguration.builder() + .name(streamName) + .storageType(StorageType.Memory) + .subjects(subjects).build(); + + return jsm.addStream(sc); + } + + public static StreamInfo createMemoryStream(Connection nc, String streamName, String... subjects) + throws IOException, JetStreamApiException { + return createMemoryStream(nc.jetStreamManagement(), streamName, subjects); + } } diff --git a/src/test/java/io/nats/client/utils/ThreadUtils.java b/src/test/java/io/nats/client/utils/ThreadUtils.java new file mode 100644 index 000000000..820748d6c --- /dev/null +++ b/src/test/java/io/nats/client/utils/ThreadUtils.java @@ -0,0 +1,27 @@ +// Copyright 2025 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client.utils; + +import java.time.Duration; +import java.util.concurrent.locks.LockSupport; + +public abstract class ThreadUtils { + public static void sleep(long ms) { + try { Thread.sleep(ms); } catch (InterruptedException ignored) { /* ignored */ } + } + + public static void park(Duration d) { + try { LockSupport.parkNanos(d.toNanos()); } catch (Exception ignored) { /* ignored */ } + } +} diff --git a/src/test/java/io/nats/client/utils/VersionUtils.java b/src/test/java/io/nats/client/utils/VersionUtils.java new file mode 100644 index 000000000..da5a4bcd1 --- /dev/null +++ b/src/test/java/io/nats/client/utils/VersionUtils.java @@ -0,0 +1,91 @@ +// Copyright 2025 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.nats.client.utils; + +import io.nats.client.Connection; +import io.nats.client.api.ServerInfo; + +public abstract class VersionUtils { + public static ServerInfo VERSION_SERVER_INFO; + + public interface VersionCheck { + boolean runTest(ServerInfo si); + } + + public static ServerInfo ensureRunServerInfo() throws Exception { +// if (VERSION_SERVER_INFO == null) { +// _runInServer(false, null, null, nc -> {}); +// } +// return VERSION_SERVER_INFO; + return null; + } + + public static void initVersionServerInfo(Connection nc) { + if (VERSION_SERVER_INFO == null) { + VERSION_SERVER_INFO = nc.getServerInfo(); + } + } + + public static boolean atLeast2_9_0() { + return atLeast2_9_0(VERSION_SERVER_INFO); + } + + public static boolean atLeast2_9_0(Connection nc) { + return atLeast2_9_0(nc.getServerInfo()); + } + + public static boolean atLeast2_9_0(ServerInfo si) { + return si.isSameOrNewerThanVersion("2.9.0"); + } + + public static boolean atLeast2_10_26(ServerInfo si) { + return si.isSameOrNewerThanVersion("2.10.26"); + } + + public static boolean atLeast2_9_1(ServerInfo si) { + return si.isSameOrNewerThanVersion("2.9.1"); + } + + public static boolean atLeast2_10() { + return atLeast2_10(VERSION_SERVER_INFO); + } + + public static boolean atLeast2_10(ServerInfo si) { + return si.isNewerVersionThan("2.9.99"); + } + + public static boolean atLeast2_10_3(ServerInfo si) { + return si.isSameOrNewerThanVersion("2.10.3"); + } + + public static boolean atLeast2_11(ServerInfo si) { + return si.isNewerVersionThan("2.10.99"); + } + + public static boolean before2_11() { + return before2_11(VERSION_SERVER_INFO); + } + + public static boolean before2_11(ServerInfo si) { + return si.isOlderThanVersion("2.11"); + } + + public static boolean atLeast2_12() { + return atLeast2_12(VERSION_SERVER_INFO); + } + + public static boolean atLeast2_12(ServerInfo si) { + return si.isSameOrNewerThanVersion("2.11.99"); + } +} diff --git a/src/test/java/io/nats/service/ServiceTests.java b/src/test/java/io/nats/service/ServiceTests.java index cbcdf8fad..50900a594 100644 --- a/src/test/java/io/nats/service/ServiceTests.java +++ b/src/test/java/io/nats/service/ServiceTests.java @@ -45,6 +45,9 @@ import static io.nats.client.support.JsonValueUtils.readString; import static io.nats.client.support.NatsConstants.DOT; import static io.nats.client.support.NatsConstants.EMPTY; +import static io.nats.client.utils.ConnectionUtils.standardConnectionWait; +import static io.nats.client.utils.OptionsUtils.options; +import static io.nats.client.utils.ThreadUtils.sleep; import static io.nats.service.Service.SRV_PING; import static io.nats.service.ServiceMessage.NATS_SERVICE_ERROR; import static io.nats.service.ServiceMessage.NATS_SERVICE_ERROR_CODE; @@ -68,9 +71,9 @@ public class ServiceTests extends JetStreamTestBase { @Test public void testServiceWorkflow() throws Exception { try (NatsTestServer ts = new NatsTestServer()) { - try (Connection serviceNc1 = standardConnection(ts.getURI()); - Connection serviceNc2 = standardConnection(ts.getURI()); - Connection clientNc = standardConnection(ts.getURI())) { + try (Connection serviceNc1 = standardConnectionWait(ts.getURI()); + Connection serviceNc2 = standardConnectionWait(ts.getURI()); + Connection clientNc = standardConnectionWait(ts.getURI())) { Endpoint endEcho = Endpoint.builder() .name(ECHO_ENDPOINT_NAME) @@ -469,9 +472,9 @@ private static String reverse(byte[] data) { @Test public void testQueueGroup() throws Exception { try (NatsTestServer ts = new NatsTestServer()) { - try (Connection serviceNc1 = standardConnection(ts.getURI()); - Connection serviceNc2 = standardConnection(ts.getURI()); - Connection clientNc = standardConnection(ts.getURI())) { + try (Connection serviceNc1 = standardConnectionWait(ts.getURI()); + Connection serviceNc2 = standardConnectionWait(ts.getURI()); + Connection clientNc = standardConnectionWait(ts.getURI())) { String yesQueueSubject = "subjyes"; String noQueueSubject = "subjno"; @@ -563,9 +566,9 @@ public void testQueueGroup() throws Exception { @Test public void testResponsesFromAllInstances() throws Exception { try (NatsTestServer ts = new NatsTestServer()) { - try (Connection serviceNc1 = standardConnection(ts.getURI()); - Connection serviceNc2 = standardConnection(ts.getURI()); - Connection clientNc = standardConnection(ts.getURI())) { + try (Connection serviceNc1 = standardConnectionWait(ts.getURI()); + Connection serviceNc2 = standardConnectionWait(ts.getURI()); + Connection clientNc = standardConnectionWait(ts.getURI())) { Endpoint ep = Endpoint.builder() .name("ep") @@ -655,7 +658,7 @@ else if (response.getName().equals(SERVICE_NAME_2)) { @Test public void testDispatchers() throws Exception { try (NatsTestServer ts = new NatsTestServer()) { - try (Connection nc = standardConnection(ts.getURI())) { + try (Connection nc = standardConnectionWait(ts.getURI())) { Map dispatchers = getDispatchers(nc); assertEquals(0, dispatchers.size()); @@ -756,19 +759,20 @@ public void testDispatchers() throws Exception { @Test public void testServiceBuilderConstruction() { - Options options = new Options.Builder().build(); + Options options = options(); Connection conn = new MockNatsConnection(options); ServiceEndpoint se = ServiceEndpoint.builder() - .endpoint(new Endpoint(name(0))) + .endpoint(new Endpoint(random())) .handler(m -> { }) .build(); // minimum valid service - Service service = Service.builder().connection(conn).name(NAME).version("1.0.0").addServiceEndpoint(se).build(); + String name = random(); + Service service = Service.builder().connection(conn).name(name).version("1.0.0").addServiceEndpoint(se).build(); assertNotNull(service.toString()); // coverage assertNotNull(service.getId()); - assertEquals(NAME, service.getName()); + assertEquals(name, service.getName()); assertEquals(ServiceBuilder.DEFAULT_DRAIN_TIMEOUT, service.getDrainTimeout()); assertEquals("1.0.0", service.getVersion()); assertNull(service.getDescription()); @@ -777,7 +781,7 @@ public void testServiceBuilderConstruction() { // additional fields Map meta = new HashMap<>(); meta.put("foo", "bar"); - service = Service.builder().connection(conn).name(NAME).version("1.0.0").addServiceEndpoint(se) + service = Service.builder().connection(conn).name(name).version("1.0.0").addServiceEndpoint(se) .description("desc") .drainTimeout(Duration.ofSeconds(1)) .metadata(meta) @@ -788,14 +792,14 @@ public void testServiceBuilderConstruction() { assertNotNull(service.getPingResponse().getMetadata()); // more coverage - service = Service.builder().connection(conn).name(NAME).version("1.0.0").addServiceEndpoint(se) + service = Service.builder().connection(conn).name(name).version("1.0.0").addServiceEndpoint(se) .drainTimeout(null) .metadata(new HashMap<>()) .build(); assertEquals(ServiceBuilder.DEFAULT_DRAIN_TIMEOUT, service.getDrainTimeout()); //noinspection deprecation - service = Service.builder().connection(conn).name(NAME).version("1.0.0").addServiceEndpoint(se) + service = Service.builder().connection(conn).name(name).version("1.0.0").addServiceEndpoint(se) .drainTimeout(1000) .schemaDispatcher(null) // COVERAGE for deprecated .build(); @@ -821,7 +825,7 @@ public void testServiceBuilderConstruction() { assertThrows(IllegalArgumentException.class, () -> Service.builder().version("not-semver")); IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, - () -> Service.builder().name(NAME).version("1.0.0").addServiceEndpoint(se).build()); + () -> Service.builder().name(name).version("1.0.0").addServiceEndpoint(se).build()); assertTrue(iae.getMessage().contains("Connection cannot be null")); iae = assertThrows(IllegalArgumentException.class, @@ -829,33 +833,34 @@ public void testServiceBuilderConstruction() { assertTrue(iae.getMessage().contains("Name cannot be null or empty")); iae = assertThrows(IllegalArgumentException.class, - () -> Service.builder().connection(conn).name(NAME).addServiceEndpoint(se).build()); + () -> Service.builder().connection(conn).name(name).addServiceEndpoint(se).build()); assertTrue(iae.getMessage().contains("Version cannot be null or empty")); assertDoesNotThrow( - () -> Service.builder().connection(conn).name(NAME).version("1.0.0").build()); + () -> Service.builder().connection(conn).name(name).version("1.0.0").build()); } @Test public void testAddingEndpointAfterServiceBuilderConstruction() { - Options options = new Options.Builder().build(); + Options options = options(); Connection conn = new MockNatsConnection(options); ServiceEndpoint se = ServiceEndpoint.builder() - .endpoint(new Endpoint(name(0))) + .endpoint(new Endpoint(random())) .handler(m -> { }) .build(); // minimum valid service - Service service = Service.builder().connection(conn).name(NAME).version("1.0.0").addServiceEndpoint(se).build(); + String name = random(); + Service service = Service.builder().connection(conn).name(name).version("1.0.0").addServiceEndpoint(se).build(); assertNotNull(service.toString()); // coverage assertNotNull(service.getId()); - assertEquals(NAME, service.getName()); + assertEquals(name, service.getName()); assertEquals(ServiceBuilder.DEFAULT_DRAIN_TIMEOUT, service.getDrainTimeout()); assertEquals("1.0.0", service.getVersion()); assertNull(service.getDescription()); - service = Service.builder().connection(conn).name(NAME).version("1.0.0") + service = Service.builder().connection(conn).name(name).version("1.0.0") .description("desc") .drainTimeout(Duration.ofSeconds(1)) .build(); @@ -884,7 +889,7 @@ public void testAddingEndpointAfterServiceBuilderConstruction() { assertThrows(IllegalArgumentException.class, () -> Service.builder().version("not-semver")); IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, - () -> Service.builder().name(NAME).version("1.0.0").addServiceEndpoint(se).build()); + () -> Service.builder().name(name).version("1.0.0").addServiceEndpoint(se).build()); assertTrue(iae.getMessage().contains("Connection cannot be null")); iae = assertThrows(IllegalArgumentException.class, @@ -892,10 +897,10 @@ public void testAddingEndpointAfterServiceBuilderConstruction() { assertTrue(iae.getMessage().contains("Name cannot be null or empty")); iae = assertThrows(IllegalArgumentException.class, - () -> Service.builder().connection(conn).name(NAME).addServiceEndpoint(se).build()); + () -> Service.builder().connection(conn).name(name).addServiceEndpoint(se).build()); assertTrue(iae.getMessage().contains("Version cannot be null or empty")); - assertDoesNotThrow(() -> Service.builder().connection(conn).name(NAME).version("1.0.0").build()); + assertDoesNotThrow(() -> Service.builder().connection(conn).name(name).version("1.0.0").build()); } @Test @@ -1027,41 +1032,44 @@ public void testEndpointConstruction() { EqualsVerifier.simple().forClass(Endpoint.class).verify(); Map metadata = new HashMap<>(); - Endpoint e = new Endpoint(NAME); - assertEpNameSubQ(e, NAME); + String name = random(); + String subject = random(); + + Endpoint e = new Endpoint(name); + assertEpNameSubQ(e, name, name); assertEquals(e, Endpoint.builder().endpoint(e).build()); assertNull(e.getMetadata()); String jep = e.toJson(); String sep = e.toString(); assertTrue(sep.contains(jep)); - e = new Endpoint(NAME, metadata); - assertEpNameSubQ(e, NAME); + e = new Endpoint(name, metadata); + assertEpNameSubQ(e, name, name); assertEquals(e, Endpoint.builder().endpoint(e).build()); assertNull(e.getMetadata()); - e = new Endpoint(NAME, SUBJECT); - assertEpNameSubQ(e); + e = new Endpoint(name, subject); + assertEpNameSubQ(e, name, subject); assertEquals(e, Endpoint.builder().endpoint(e).build()); assertEquals(Endpoint.DEFAULT_QGROUP, e.getQueueGroup()); e = Endpoint.builder() - .name(NAME).subject(SUBJECT) + .name(name).subject(subject) .build(); - assertEpNameSubQ(e); + assertEpNameSubQ(e, name, subject); assertEquals(e, Endpoint.builder().endpoint(e).build()); e = Endpoint.builder() - .name(NAME).subject(SUBJECT) + .name(name).subject(subject) .metadata(metadata) .queueGroup(null) // coverage .build(); - assertEpNameSubQ(e); + assertEpNameSubQ(e, name, subject); assertNull(e.getMetadata()); assertEquals(Endpoint.DEFAULT_QGROUP, e.getQueueGroup()); e = Endpoint.builder() - .name(NAME).subject(SUBJECT) + .name(name).subject(subject) .metadata(metadata) .queueGroup("qg") // coverage .build(); @@ -1070,30 +1078,30 @@ public void testEndpointConstruction() { metadata.put("k", "v"); - e = new Endpoint(NAME, SUBJECT, metadata); - assertEpNameSubQ(e); + e = new Endpoint(name, subject, metadata); + assertEpNameSubQ(e, name, subject); assertTrue(JsonUtils.mapEquals(metadata, e.getMetadata())); e = Endpoint.builder() - .name(NAME).subject(SUBJECT) + .name(name).subject(subject) .metadata(metadata) .build(); - assertEpNameSubQ(e); + assertEpNameSubQ(e, name, subject); assertTrue(JsonUtils.mapEquals(metadata, e.getMetadata())); // internal allows null queue group - e = new Endpoint(NAME, SUBJECT, null, metadata, false); + e = new Endpoint(name, subject, null, metadata, false); assertNull(e.getQueueGroup()); // some subject testing - e = new Endpoint(NAME, "foo.>"); + e = new Endpoint(name, "foo.>"); assertEquals("foo.>", e.getSubject()); - e = new Endpoint(NAME, "foo.*"); + e = new Endpoint(name, "foo.*"); assertEquals("foo.*", e.getSubject()); // coverage - e = new Endpoint(NAME, SUBJECT, metadata); - assertEpNameSubQ(e); + e = new Endpoint(name, subject, metadata); + assertEpNameSubQ(e, name, subject); assertTrue(JsonUtils.mapEquals(metadata, e.getMetadata())); assertThrows(IllegalArgumentException.class, () -> Endpoint.builder().build()); @@ -1114,20 +1122,16 @@ public void testEndpointConstruction() { assertThrows(IllegalArgumentException.class, () -> new Endpoint(HAS_TIC)); // typical subjects are bad - assertThrows(IllegalArgumentException.class, () -> new Endpoint(NAME, HAS_SPACE)); - assertThrows(IllegalArgumentException.class, () -> new Endpoint(NAME, HAS_CR)); - assertThrows(IllegalArgumentException.class, () -> new Endpoint(NAME, HAS_LF)); - assertThrows(IllegalArgumentException.class, () -> new Endpoint(NAME, STAR_NOT_SEGMENT)); - assertThrows(IllegalArgumentException.class, () -> new Endpoint(NAME, GT_NOT_SEGMENT)); - assertThrows(IllegalArgumentException.class, () -> new Endpoint(NAME, STARTS_WITH_DOT)); - } - - private static void assertEpNameSubQ(Endpoint ep) { - assertEpNameSubQ(ep, SUBJECT); + assertThrows(IllegalArgumentException.class, () -> new Endpoint(name, HAS_SPACE)); + assertThrows(IllegalArgumentException.class, () -> new Endpoint(name, HAS_CR)); + assertThrows(IllegalArgumentException.class, () -> new Endpoint(name, HAS_LF)); + assertThrows(IllegalArgumentException.class, () -> new Endpoint(name, STAR_NOT_SEGMENT)); + assertThrows(IllegalArgumentException.class, () -> new Endpoint(name, GT_NOT_SEGMENT)); + assertThrows(IllegalArgumentException.class, () -> new Endpoint(name, STARTS_WITH_DOT)); } - private static void assertEpNameSubQ(Endpoint ep, String exSubject) { - assertEquals(NAME, ep.getName()); + private static void assertEpNameSubQ(Endpoint ep, String name, String exSubject) { + assertEquals(name, ep.getName()); assertEquals(exSubject, ep.getSubject()); assertEquals(Endpoint.DEFAULT_QGROUP, ep.getQueueGroup()); } @@ -1180,37 +1184,40 @@ public void testEndpointResponseConstruction() { @Test public void testGroupConstruction() { - Group g1 = new Group(subject(1)); - Group g2 = new Group(subject(2)); - Group g3 = new Group(subject(3)); - assertEquals(subject(1), g1.getName()); - assertEquals(subject(1), g1.getSubject()); - assertEquals(subject(2), g2.getName()); - assertEquals(subject(2), g2.getSubject()); - assertEquals(subject(3), g3.getName()); - assertEquals(subject(3), g3.getSubject()); + String subject1 = random(); + String subject2 = random(); + String subject3 = random(); + Group g1 = new Group(subject1); + Group g2 = new Group(subject2); + Group g3 = new Group(subject3); + assertEquals(subject1, g1.getName()); + assertEquals(subject1, g1.getSubject()); + assertEquals(subject2, g2.getName()); + assertEquals(subject2, g2.getSubject()); + assertEquals(subject3, g3.getName()); + assertEquals(subject3, g3.getSubject()); assertNull(g1.getNext()); assertNull(g2.getNext()); assertNull(g3.getNext()); - assertTrue(g1.toString().contains(subject(1))); // coverage - assertTrue(g2.toString().contains(subject(2))); // coverage - assertTrue(g3.toString().contains(subject(3))); // coverage + assertTrue(g1.toString().contains(subject1)); // coverage + assertTrue(g2.toString().contains(subject2)); // coverage + assertTrue(g3.toString().contains(subject3)); // coverage assertEquals(g1, g1.appendGroup(g2)); - assertEquals(subject(2), g1.getNext().getName()); + assertEquals(subject2, g1.getNext().getName()); assertNull(g2.getNext()); - assertEquals(subject(1), g1.getName()); - assertEquals(subject(1) + DOT + subject(2), g1.getSubject()); - assertEquals(subject(2), g2.getName()); - assertEquals(subject(2), g2.getSubject()); - assertTrue(g1.toString().contains(subject(2))); // coverage + assertEquals(subject1, g1.getName()); + assertEquals(subject1 + DOT + subject2, g1.getSubject()); + assertEquals(subject2, g2.getName()); + assertEquals(subject2, g2.getSubject()); + assertTrue(g1.toString().contains(subject2)); // coverage assertEquals(g1, g1.appendGroup(g3)); - assertEquals(subject(2), g1.getNext().getName()); - assertEquals(subject(3), g1.getNext().getNext().getName()); - assertEquals(subject(1), g1.getName()); - assertEquals(subject(1) + DOT + subject(2) + DOT + subject(3), g1.getSubject()); - assertTrue(g1.toString().contains(subject(3))); // coverage + assertEquals(subject2, g1.getNext().getName()); + assertEquals(subject3, g1.getNext().getNext().getName()); + assertEquals(subject1, g1.getName()); + assertEquals(subject1 + DOT + subject2 + DOT + subject3, g1.getSubject()); + assertTrue(g1.toString().contains(subject3)); // coverage g1 = new Group("foo.*"); assertEquals("foo.*", g1.getName()); @@ -1227,10 +1234,12 @@ public void testGroupConstruction() { @Test public void testServiceEndpointConstruction() { - Group g1 = new Group(subject(1)); - Group g2 = new Group(subject(2)).appendGroup(g1); - Endpoint e1 = new Endpoint(name(100), subject(100)); - Endpoint e2 = new Endpoint(name(200), subject(200)); + String subject1 = random(); + String subject2 = random(); + Group g1 = new Group(subject1); + Group g2 = new Group(subject2).appendGroup(g1); + Endpoint e1 = new Endpoint(random(), random()); + Endpoint e2 = new Endpoint(random(), random()); ServiceMessageHandler smh = m -> {}; Supplier sds = () -> null;