cond, int intervalMillis, int ti
throw new RuntimeException(ex);
}
}
+
+ /**
+ * Skip the test if the version that the {@link Weaviate}
+ * container is running is older than the required one.
+ */
+ public static void requireAtLeast(Weaviate.Version required) {
+ var actual = SemanticVersion.of(Weaviate.VERSION);
+ Assumptions.assumeThat(actual)
+ .as("requires at least %s, but running %s", required.semver, actual)
+ .isGreaterThanOrEqualTo(required.semver);
+ }
+
+ public static void requireAtLeast(Weaviate.Version required, Runnable r) {
+ var actual = SemanticVersion.of(Weaviate.VERSION);
+ if (actual.compareTo(required.semver) >= 0) {
+ r.run();
+ }
+ }
}
diff --git a/src/it/java/io/weaviate/containers/Container.java b/src/it/java/io/weaviate/containers/Container.java
index 7c71ed980..304dbea91 100644
--- a/src/it/java/io/weaviate/containers/Container.java
+++ b/src/it/java/io/weaviate/containers/Container.java
@@ -18,18 +18,6 @@ public class Container {
public static final Img2VecNeural IMG2VEC_NEURAL = Img2VecNeural.createDefault();
public static final MinIo MINIO = MinIo.createDefault();
- /**
- * Stop all shared Testcontainers created in {@link #startAll}.
- *
- * Testcontainer's Ryuk will reap any dangling containers after the tests
- * finish. However, since {@link Weaviate} instances also hold a
- * {@link WeaviateClient}, we want to stop them proactively to
- * close client connections.
- */
- static void stopAll() {
- WEAVIATE.stop();
- }
-
public static ContainerGroup compose(Weaviate weaviate, GenericContainer>... containers) {
return new ContainerGroup(weaviate, containers);
}
diff --git a/src/it/java/io/weaviate/containers/TestListener.java b/src/it/java/io/weaviate/containers/TestListener.java
deleted file mode 100644
index 72889125b..000000000
--- a/src/it/java/io/weaviate/containers/TestListener.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package io.weaviate.containers;
-
-import org.junit.runner.Result;
-import org.junit.runner.notification.RunListener;
-
-public class TestListener extends RunListener {
-
- @Override
- public void testRunFinished(Result result) throws Exception {
- Container.stopAll();
- super.testRunFinished(result);
- }
-
-}
diff --git a/src/it/java/io/weaviate/containers/Weaviate.java b/src/it/java/io/weaviate/containers/Weaviate.java
index 1c7fc259e..4c063e0ad 100644
--- a/src/it/java/io/weaviate/containers/Weaviate.java
+++ b/src/it/java/io/weaviate/containers/Weaviate.java
@@ -16,18 +16,41 @@
import org.testcontainers.lifecycle.Startable;
import org.testcontainers.weaviate.WeaviateContainer;
+import io.weaviate.ConcurrentTest;
import io.weaviate.client6.v1.api.Config;
import io.weaviate.client6.v1.api.WeaviateClient;
import io.weaviate.client6.v1.internal.ObjectBuilder;
+import io.weaviate.client6.v1.internal.VersionSupport.SemanticVersion;
public class Weaviate extends WeaviateContainer {
- public static final String VERSION = "1.33.0";
public static final String DOCKER_IMAGE = "semitechnologies/weaviate";
+ public static final String LATEST_VERSION = Version.V134.semver.toString();
+ public static final String VERSION;
+
+ static {
+ VERSION = System.getenv().getOrDefault("WEAVIATE_VERSION", LATEST_VERSION);
+ }
public static String OIDC_ISSUER = "https://auth.wcs.api.weaviate.io/auth/realms/SeMI";
private volatile SharedClient clientInstance;
private final String containerName;
+ public enum Version {
+ V132(1, 32),
+ V133(1, 33),
+ V134(1, 34);
+
+ public final SemanticVersion semver;
+
+ private Version(int major, int minor) {
+ this.semver = new SemanticVersion(major, minor);
+ }
+
+ public void orSkip() {
+ ConcurrentTest.requireAtLeast(this);
+ }
+ }
+
/**
* By default, testcontainer's name is only available after calling
* {@link #start}.
@@ -71,6 +94,22 @@ public WeaviateClient getClient() {
return clientInstance;
}
+ /**
+ * Get client that is not shared with other tests / callers.
+ * The returned client is not wrapped in an instance of {@link SharedClient},
+ * so it can be auto-closed by the try-with-resources statement when it exists.
+ */
+ public WeaviateClient getBareClient() {
+ if (!isRunning()) {
+ start();
+ }
+ try {
+ return new WeaviateClient(Config.of(defaultConfigFn()));
+ } catch (Exception e) {
+ throw new RuntimeException("create WeaviateClient for Weaviate container", e);
+ }
+ }
+
/**
* Create a new instance of WeaviateClient connected to this container.
* Prefer using {@link #getClient} unless your test needs the initialization
@@ -377,6 +416,7 @@ private void bindNodes(int gossip, int data, int raft) {
nodes.forEach(node -> node
.withEnv("CLUSTER_GOSSIP_BIND_PORT", String.valueOf(gossip))
.withEnv("CLUSTER_DATA_BIND_PORT", String.valueOf(data))
+ .withEnv("REPLICA_MOVEMENT_ENABLED", "true")
.withEnv("RAFT_PORT", String.valueOf(raft))
.withEnv("RAFT_BOOTSTRAP_EXPECT", "1"));
diff --git a/src/it/java/io/weaviate/integration/AliasITest.java b/src/it/java/io/weaviate/integration/AliasITest.java
index ab180d6f0..eb5bca8eb 100644
--- a/src/it/java/io/weaviate/integration/AliasITest.java
+++ b/src/it/java/io/weaviate/integration/AliasITest.java
@@ -4,16 +4,23 @@
import java.util.List;
import org.assertj.core.api.Assertions;
+import org.junit.BeforeClass;
import org.junit.Test;
import io.weaviate.ConcurrentTest;
import io.weaviate.client6.v1.api.WeaviateClient;
import io.weaviate.client6.v1.api.alias.Alias;
import io.weaviate.containers.Container;
+import io.weaviate.containers.Weaviate;
public class AliasITest extends ConcurrentTest {
private static final WeaviateClient client = Container.WEAVIATE.getClient();
+ @BeforeClass
+ public static void __() {
+ Weaviate.Version.V132.orSkip();
+ }
+
@Test
public void test_aliasLifecycle() throws IOException {
// Arrange
diff --git a/src/it/java/io/weaviate/integration/BackupITest.java b/src/it/java/io/weaviate/integration/BackupITest.java
index bf0997930..c540afaae 100644
--- a/src/it/java/io/weaviate/integration/BackupITest.java
+++ b/src/it/java/io/weaviate/integration/BackupITest.java
@@ -29,6 +29,8 @@ public class BackupITest extends ConcurrentTest {
@Test
public void test_lifecycle() throws IOException, TimeoutException {
+ Weaviate.Version.V132.orSkip(); // List backups not implemented in earlier versions
+
// Arrange
String nsA = ns("A"), nsB = ns("B"), nsC = ns("C"), nsBig = ns("Big");
String backup_1 = ns("backup_1").toLowerCase();
@@ -122,6 +124,8 @@ public void test_lifecycle() throws IOException, TimeoutException {
@Test
public void test_lifecycle_async() throws ExecutionException, InterruptedException, Exception {
+ Weaviate.Version.V132.orSkip(); // List backups not implemented in earlier versions
+
// Arrange
String nsA = ns("A"), nsB = ns("B"), nsC = ns("C"), nsBig = ns("Big");
String backup_1 = ns("backup_1").toLowerCase();
diff --git a/src/it/java/io/weaviate/integration/ClusterITest.java b/src/it/java/io/weaviate/integration/ClusterITest.java
index b7716717e..baea17f70 100644
--- a/src/it/java/io/weaviate/integration/ClusterITest.java
+++ b/src/it/java/io/weaviate/integration/ClusterITest.java
@@ -14,6 +14,7 @@
import io.weaviate.client6.v1.api.cluster.replication.ReplicationState;
import io.weaviate.client6.v1.api.cluster.replication.ReplicationType;
import io.weaviate.containers.Weaviate;
+import io.weaviate.containers.Weaviate.Version;
public class ClusterITest extends ConcurrentTest {
private static final WeaviateClient client = Weaviate.cluster(3).getClient();
@@ -58,6 +59,8 @@ public void test_listNodes() throws IOException {
@Test
public void test_replicateLifecycle() throws IOException {
+ Version.V132.orSkip();
+
// Arrange
// We must create the collection first before any shards exist on the nodes.
diff --git a/src/it/java/io/weaviate/integration/CollectionsITest.java b/src/it/java/io/weaviate/integration/CollectionsITest.java
index 4906d48d5..c28890430 100644
--- a/src/it/java/io/weaviate/integration/CollectionsITest.java
+++ b/src/it/java/io/weaviate/integration/CollectionsITest.java
@@ -22,6 +22,7 @@
import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw;
import io.weaviate.client6.v1.api.collections.vectorizers.SelfProvidedVectorizer;
import io.weaviate.containers.Container;
+import io.weaviate.containers.Weaviate;
public class CollectionsITest extends ConcurrentTest {
private static WeaviateClient client = Container.WEAVIATE.getClient();
@@ -96,31 +97,34 @@ public void testCrossReferences() throws IOException {
}
@Test
- public void testListDeleteAll() throws IOException {
- var nsA = ns("A");
- var nsB = ns("B");
- var nsC = ns("C");
-
- client.collections.create(nsA);
- client.collections.create(nsB);
- client.collections.create(nsC);
-
- Assertions.assertThat(client.collections.exists(nsA)).isTrue();
- Assertions.assertThat(client.collections.exists(nsB)).isTrue();
- Assertions.assertThat(client.collections.exists(nsC)).isTrue();
- Assertions.assertThat(client.collections.exists(ns("X"))).isFalse();
-
- var all = client.collections.list();
- Assertions.assertThat(all)
- .hasSizeGreaterThanOrEqualTo(3)
- .extracting(CollectionConfig::collectionName)
- .contains(nsA, nsB, nsC);
-
- client.collections.deleteAll();
-
- all = client.collections.list();
- Assertions.assertThat(all.isEmpty());
-
+ public void testListDeleteAll() throws Exception {
+ // Use a separate container for this test so as not to interfere
+ // with other tests.
+ try (final var _client = Weaviate.createDefault().getBareClient()) {
+ var nsA = ns("A");
+ var nsB = ns("B");
+ var nsC = ns("C");
+
+ _client.collections.create(nsA);
+ _client.collections.create(nsB);
+ _client.collections.create(nsC);
+
+ Assertions.assertThat(_client.collections.exists(nsA)).isTrue();
+ Assertions.assertThat(_client.collections.exists(nsB)).isTrue();
+ Assertions.assertThat(_client.collections.exists(nsC)).isTrue();
+ Assertions.assertThat(_client.collections.exists(ns("X"))).isFalse();
+
+ var all = _client.collections.list();
+ Assertions.assertThat(all)
+ .hasSizeGreaterThanOrEqualTo(3)
+ .extracting(CollectionConfig::collectionName)
+ .contains(nsA, nsB, nsC);
+
+ _client.collections.deleteAll();
+
+ all = _client.collections.list();
+ Assertions.assertThat(all.isEmpty());
+ }
}
@Test
diff --git a/src/it/java/io/weaviate/integration/RbacITest.java b/src/it/java/io/weaviate/integration/RbacITest.java
index 9bdbed2e9..ea61b96e7 100644
--- a/src/it/java/io/weaviate/integration/RbacITest.java
+++ b/src/it/java/io/weaviate/integration/RbacITest.java
@@ -1,7 +1,8 @@
package io.weaviate.integration;
import java.io.IOException;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.InstanceOfAssertFactories;
@@ -64,20 +65,29 @@ public void test_roles_Lifecycle() throws IOException {
var myCollection = "Things";
var nsRole = ns("VectorOwner");
- Permission[] permissions = new Permission[] {
- Permission.aliases("ThingsAlias", myCollection, AliasesPermission.Action.CREATE),
- Permission.backups(myCollection, BackupsPermission.Action.MANAGE),
- Permission.cluster(ClusterPermission.Action.READ),
- Permission.nodes(myCollection, NodesPermission.Action.READ),
- Permission.roles(VIEWER_ROLE, Scope.MATCH, RolesPermission.Action.CREATE),
- Permission.collections(myCollection, CollectionsPermission.Action.CREATE),
- Permission.data(myCollection, DataPermission.Action.UPDATE),
- Permission.groups("my-group", GroupType.OIDC, GroupsPermission.Action.READ),
- Permission.tenants(myCollection, "my-tenant", TenantsPermission.Action.DELETE),
- Permission.users("my-user", UsersPermission.Action.READ),
- Permission.replicate(myCollection, "my-shard", ReplicatePermission.Action.READ),
+ List permissions = new ArrayList<>() {
+ {
+ add(Permission.backups(myCollection, BackupsPermission.Action.MANAGE));
+ add(Permission.cluster(ClusterPermission.Action.READ));
+ add(Permission.nodes(myCollection, NodesPermission.Action.READ));
+ add(Permission.roles(VIEWER_ROLE, Scope.MATCH, RolesPermission.Action.CREATE));
+ add(Permission.collections(myCollection, CollectionsPermission.Action.CREATE));
+ add(Permission.data(myCollection, DataPermission.Action.UPDATE));
+ add(Permission.tenants(myCollection, "my-tenant", TenantsPermission.Action.DELETE));
+ add(Permission.users("my-user", UsersPermission.Action.READ));
+ add(Permission.replicate(myCollection, "my-shard", ReplicatePermission.Action.READ));
+ }
};
+ requireAtLeast(Weaviate.Version.V132, () -> {
+ permissions.add(
+ Permission.aliases("ThingsAlias", myCollection, AliasesPermission.Action.CREATE));
+ });
+ requireAtLeast(Weaviate.Version.V133, () -> {
+ permissions.add(
+ Permission.groups("my-group", GroupType.OIDC, GroupsPermission.Action.READ));
+ });
+
// Act: create role
client.roles.create(nsRole, permissions);
@@ -86,7 +96,7 @@ public void test_roles_Lifecycle() throws IOException {
.as("created role")
.returns(nsRole, Role::name)
.extracting(Role::permissions, InstanceOfAssertFactories.list(Permission.class))
- .containsAll(Arrays.asList(permissions));
+ .containsAll(permissions);
// Act:: add extra permissions
var extra = new Permission[] {
@@ -150,6 +160,8 @@ public void test_roles_userAssignments() throws IOException {
@Test
public void test_groups() throws IOException {
+ Weaviate.Version.V133.orSkip();
+
var mediaGroup = "./media-group";
var friendGroup = "./friend-group";
diff --git a/src/main/java/io/weaviate/client6/v1/api/WeaviateClient.java b/src/main/java/io/weaviate/client6/v1/api/WeaviateClient.java
index a4c51cbe9..63e14c3bf 100644
--- a/src/main/java/io/weaviate/client6/v1/api/WeaviateClient.java
+++ b/src/main/java/io/weaviate/client6/v1/api/WeaviateClient.java
@@ -13,6 +13,7 @@
import io.weaviate.client6.v1.internal.ObjectBuilder;
import io.weaviate.client6.v1.internal.Timeout;
import io.weaviate.client6.v1.internal.TokenProvider;
+import io.weaviate.client6.v1.internal.VersionSupport;
import io.weaviate.client6.v1.internal.grpc.DefaultGrpcTransport;
import io.weaviate.client6.v1.internal.grpc.GrpcChannelOptions;
import io.weaviate.client6.v1.internal.grpc.GrpcTransport;
@@ -105,6 +106,10 @@ public WeaviateClient(Config config) {
throw ex;
}
+ if (!VersionSupport.isSupported(meta.version())) {
+ throw new WeaviateUnsupportedVersionException(meta.version());
+ }
+
if (meta.grpcMaxMessageSize() != null) {
grpcOpt = grpcOpt.withMaxMessageSize(meta.grpcMaxMessageSize());
}
diff --git a/src/main/java/io/weaviate/client6/v1/api/WeaviateClientAsync.java b/src/main/java/io/weaviate/client6/v1/api/WeaviateClientAsync.java
index 7858300ec..1e5b07ae3 100644
--- a/src/main/java/io/weaviate/client6/v1/api/WeaviateClientAsync.java
+++ b/src/main/java/io/weaviate/client6/v1/api/WeaviateClientAsync.java
@@ -15,6 +15,7 @@
import io.weaviate.client6.v1.internal.ObjectBuilder;
import io.weaviate.client6.v1.internal.Timeout;
import io.weaviate.client6.v1.internal.TokenProvider;
+import io.weaviate.client6.v1.internal.VersionSupport;
import io.weaviate.client6.v1.internal.grpc.DefaultGrpcTransport;
import io.weaviate.client6.v1.internal.grpc.GrpcChannelOptions;
import io.weaviate.client6.v1.internal.grpc.GrpcTransport;
@@ -108,6 +109,10 @@ public WeaviateClientAsync(Config config) {
throw ex;
}
+ if (!VersionSupport.isSupported(meta.version())) {
+ throw new WeaviateUnsupportedVersionException(meta.version());
+ }
+
if (meta.grpcMaxMessageSize() != null) {
grpcOpt = grpcOpt.withMaxMessageSize(meta.grpcMaxMessageSize());
}
diff --git a/src/main/java/io/weaviate/client6/v1/api/WeaviateUnsupportedVersionException.java b/src/main/java/io/weaviate/client6/v1/api/WeaviateUnsupportedVersionException.java
new file mode 100644
index 000000000..1e4e27580
--- /dev/null
+++ b/src/main/java/io/weaviate/client6/v1/api/WeaviateUnsupportedVersionException.java
@@ -0,0 +1,17 @@
+package io.weaviate.client6.v1.api;
+
+import io.weaviate.client6.v1.internal.VersionSupport;
+
+/**
+ * This exception is thrown when the client refuses to talk to an unsupported
+ * version of the server, see {@link VersionSupport#MINIMAL_SUPPORTED_VERSION}.
+ */
+public class WeaviateUnsupportedVersionException extends WeaviateException {
+ public WeaviateUnsupportedVersionException(String actual) {
+ this(VersionSupport.MINIMAL_SUPPORTED_VERSION.toString(), actual);
+ }
+
+ public WeaviateUnsupportedVersionException(String minimal, String actual) {
+ super("Server version %s is not supported. Earliest supported version is %s.".formatted(actual, minimal));
+ }
+}
diff --git a/src/main/java/io/weaviate/client6/v1/api/backup/Backup.java b/src/main/java/io/weaviate/client6/v1/api/backup/Backup.java
index 066fe2e08..9fa1c1025 100644
--- a/src/main/java/io/weaviate/client6/v1/api/backup/Backup.java
+++ b/src/main/java/io/weaviate/client6/v1/api/backup/Backup.java
@@ -34,7 +34,7 @@ public record Backup(
/** Time at which the backup was completed, successfully or otherwise. */
@SerializedName("completedAt") OffsetDateTime completedAt,
/** Backup size in GiB. */
- @SerializedName("size") Integer sizeGiB,
+ @SerializedName("size") Float sizeGiB,
/**
* This value indicates if a backup is being created or restored from.
* For operations like LIST this value is null.
diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceAddManyResponse.java b/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceAddManyResponse.java
index d0fc89ace..d7c70d7fa 100644
--- a/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceAddManyResponse.java
+++ b/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceAddManyResponse.java
@@ -24,11 +24,17 @@ public ReferenceAddManyResponse deserialize(JsonElement json, Type typeOfT, Json
int i = 0;
for (var el : json.getAsJsonArray()) {
var result = el.getAsJsonObject().get("result").getAsJsonObject();
- if (result.get("status").getAsString().equals("FAILED")) {
- var errorMsg = result
- .get("errors").getAsJsonObject()
- .get("error").getAsJsonArray()
- .get(0).getAsString();
+ if (result.get("status").getAsString().equals("FAILED")
+ && result.has("errors")) {
+ String errorMsg;
+ try {
+ errorMsg = result
+ .get("errors").getAsJsonObject()
+ .get("error").getAsJsonArray()
+ .get(0).getAsString();
+ } catch (Exception e) {
+ errorMsg = result.get("errors").toString();
+ }
var batchErr = new BatchError(errorMsg, null, i);
errors.add(batchErr);
diff --git a/src/main/java/io/weaviate/client6/v1/api/rbac/roles/WeaviateRolesClient.java b/src/main/java/io/weaviate/client6/v1/api/rbac/roles/WeaviateRolesClient.java
index b1120465a..4cea55f8b 100644
--- a/src/main/java/io/weaviate/client6/v1/api/rbac/roles/WeaviateRolesClient.java
+++ b/src/main/java/io/weaviate/client6/v1/api/rbac/roles/WeaviateRolesClient.java
@@ -29,6 +29,21 @@ public WeaviateRolesClient(RestTransport restTransport) {
* or the server being unavailable.
*/
public void create(String roleName, Permission... permissions) throws IOException {
+ create(roleName, Arrays.asList(permissions));
+ }
+
+ /**
+ * Create a new role.
+ *
+ * @param roleName Role name.
+ * @param permissions Permissions granted to the role.
+ * @throws WeaviateApiException in case the server returned with an
+ * error status code.
+ * @throws IOException in case the request was not sent successfully
+ * due to a malformed request, a networking error
+ * or the server being unavailable.
+ */
+ public void create(String roleName, List permissions) throws IOException {
var role = new Role(roleName, permissions);
this.restTransport.performRequest(new CreateRoleRequest(role), CreateRoleRequest._ENDPOINT);
}
diff --git a/src/main/java/io/weaviate/client6/v1/internal/VersionSupport.java b/src/main/java/io/weaviate/client6/v1/internal/VersionSupport.java
new file mode 100644
index 000000000..05763677c
--- /dev/null
+++ b/src/main/java/io/weaviate/client6/v1/internal/VersionSupport.java
@@ -0,0 +1,48 @@
+package io.weaviate.client6.v1.internal;
+
+import java.util.Arrays;
+
+public final class VersionSupport {
+ public static final SemanticVersion MINIMAL_SUPPORTED_VERSION = new SemanticVersion(1, 32);
+
+ /**
+ * Returns true if the {@code version} is the same as or older than the
+ * {@link VersionSupport#MINIMAL_SUPPORTED_VERSION}.
+ */
+ public static boolean isSupported(String version) {
+ var semver = SemanticVersion.of(version);
+ return semver.compareTo(MINIMAL_SUPPORTED_VERSION) >= 0;
+ }
+
+ public record SemanticVersion(int major, int minor, String patch) implements Comparable {
+
+ public SemanticVersion(int major, int minor) {
+ this(major, minor, null);
+ }
+
+ /**
+ * Parse semantic version from a formatted string,
+ * e.g. {@code "(v)1.23.6-rc.1"}.
+ */
+ public static SemanticVersion of(String version) {
+ var parts = version.replaceFirst("v", "").split("\\.");
+ var major = Integer.valueOf(parts[0].replaceAll("[^0-9]", ""));
+ var minor = Integer.valueOf(parts[1].replaceAll("[^0-9]", ""));
+ var patch = parts.length > 2
+ ? String.join(".", Arrays.stream(parts, 2, parts.length).toList())
+ : null;
+ return new SemanticVersion(major, minor, patch);
+ }
+
+ @Override
+ public int compareTo(SemanticVersion that) {
+ var this_v = Integer.valueOf("%d%d".formatted(this.major, this.minor));
+ var that_v = Integer.valueOf("%d%d".formatted(that.major, that.minor));
+ return this_v.compareTo(that_v);
+ }
+
+ public String toString() {
+ return String.join(".", String.valueOf(major), String.valueOf(minor), patch != null ? patch : "");
+ }
+ }
+}
diff --git a/src/test/java/io/weaviate/client6/v1/internal/VersionSupportTest.java b/src/test/java/io/weaviate/client6/v1/internal/VersionSupportTest.java
new file mode 100644
index 000000000..e735f6738
--- /dev/null
+++ b/src/test/java/io/weaviate/client6/v1/internal/VersionSupportTest.java
@@ -0,0 +1,40 @@
+package io.weaviate.client6.v1.internal;
+
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import com.jparams.junit4.JParamsTestRunner;
+import com.jparams.junit4.data.DataMethod;
+
+import io.weaviate.client6.v1.internal.VersionSupport.SemanticVersion;
+
+@RunWith(JParamsTestRunner.class)
+public class VersionSupportTest {
+ public static Object[][] testCases() {
+ return new Object[][] {
+ { "1.31.6", "v1.32.1", true }, // can have a leading v
+ { "v1.33.0", "1.32.1", false }, // can have a leading v
+ { "2.36.2", "2.36.0-rc.3", true }, // patch ignored
+ { "1.12", "1.11", false }, // omit patch
+ { "0.55.6", "0.1.0", false }, // can start with zero
+ };
+ }
+
+ @Test
+ @DataMethod(source = VersionSupportTest.class, method = "testCases")
+ public void test_isSupported(String minimal, String actual, boolean isSupported) {
+ var v_minimal = SemanticVersion.of(minimal);
+ var v_actual = SemanticVersion.of(actual);
+
+ if (isSupported) {
+ Assertions.assertThat(v_actual)
+ .describedAs("%s supported (minimal=%s)", actual, minimal)
+ .isGreaterThanOrEqualTo(v_minimal);
+ } else {
+ Assertions.assertThat(v_actual)
+ .describedAs("%s not supported (minimal=%s)", actual, minimal)
+ .isLessThan(v_minimal);
+ }
+ }
+}