Skip to content

Commit 302b2d8

Browse files
author
James Williams
authored
TypeDB Cluster Authentication Behaviour Tests (#408)
## What is the goal of this PR? We've added behaviour tests for password policy complexity and expiration to TypeDB Cluster. Following new methodology which isolates the test runners made in typedb-common, we've increased the Bazel test timeout to 30 minutes. Previously this wasn't necessary as we would re-use the runners between runs following a 'cleaning' step, but not rebooting the runner. Given each boot of TypeDB takes 3-4 seconds, the 97 scenarios in the match behaviour file would take ~339.5 seconds in booting time alone, exhausting the default Bazel test limit of 300 seconds. Now we take a more comprehensive approach, deleting all of the state stored in TypeDB between runs and allowing runners to be reset. This is more correct, but it takes more time. ## What are the changes implemented in this PR? Many new steps have been declared in `UserSteps`: * `users get user: {word}` * `users get all` * `users contains: {word}` * `users not contains: {word}` * `users create: {word}, {word}` * `users delete: {word}` * `user password update: {word}, {word}` * `user expiry-seconds` * `users password set: {word}, {word}` * All of the above with the extension `; throws exception` We've also declared the following steps for `ConnectionStepsCluster`: * `typedb has configuration` * `typedb starts` * `typedb stops` * `user connect: {word}, {word}` * `user disconnect`
1 parent c022e9b commit 302b2d8

File tree

12 files changed

+226
-96
lines changed

12 files changed

+226
-96
lines changed

.factory/automation.yml

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ build:
5353
bazel run @vaticle_dependencies//distribution/artifact:create-netrc
5454
bazel build //...
5555
bazel run @vaticle_dependencies//tool/checkstyle:test-coverage
56-
bazel test $(bazel query 'kind(checkstyle_test, //...)') --test_output=errors
56+
bazel test $(bazel query 'kind(checkstyle_test, //...)') --test_output=errors --test_timeout=1800
5757
build-dependency:
5858
image: vaticle-ubuntu-22.04
5959
command: |
@@ -66,30 +66,30 @@ build:
6666
export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME
6767
export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD
6868
bazel run @vaticle_dependencies//distribution/artifact:create-netrc
69-
bazel test //test/integration/... --test_output=errors
69+
bazel test //test/integration/... --test_output=errors --test_timeout=1800
7070
test-behaviour-connection-core:
7171
image: vaticle-ubuntu-22.04
7272
command: |
7373
export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME
7474
export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD
7575
bazel run @vaticle_dependencies//distribution/artifact:create-netrc
76-
.factory/test-core.sh //test/behaviour/connection/... --test_output=errors --jobs=1
76+
.factory/test-core.sh //test/behaviour/connection/... --test_output=errors --jobs=1 --test_timeout=1800
7777
# TODO: delete --jobs=1 if we fix the issue with excess memory usage
78-
# test-behaviour-connection-cluster:
79-
# image: vaticle-ubuntu-22.04
80-
# command: |
81-
# export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME
82-
# export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD
83-
# bazel run @vaticle_dependencies//distribution/artifact:create-netrc
84-
# .factory/test-cluster.sh //test/behaviour/connection/... --test_output=errors --jobs=1
78+
test-behaviour-connection-cluster:
79+
image: vaticle-ubuntu-22.04
80+
command: |
81+
export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME
82+
export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD
83+
bazel run @vaticle_dependencies//distribution/artifact:create-netrc
84+
.factory/test-cluster.sh //test/behaviour/connection/... --test_output=errors --jobs=1 --test_timeout=1800
8585
# TODO: delete --jobs=1 if we fix the issue with excess memory usage
8686
test-behaviour-concept-core:
8787
image: vaticle-ubuntu-22.04
8888
command: |
8989
export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME
9090
export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD
9191
bazel run @vaticle_dependencies//distribution/artifact:create-netrc
92-
.factory/test-core.sh //test/behaviour/concept/... --test_output=errors
92+
.factory/test-core.sh //test/behaviour/concept/... --test_output=errors --test_timeout=1800
9393
# test-behaviour-concept-cluster:
9494
# image: vaticle-ubuntu-22.04
9595
# command: |
@@ -103,8 +103,8 @@ build:
103103
export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME
104104
export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD
105105
bazel run @vaticle_dependencies//distribution/artifact:create-netrc
106-
.factory/test-core.sh //test/behaviour/typeql/language/match/... --test_output=errors
107-
.factory/test-core.sh //test/behaviour/typeql/language/get/... --test_output=errors
106+
.factory/test-core.sh //test/behaviour/typeql/language/match/... --test_output=errors --test_timeout=1800
107+
.factory/test-core.sh //test/behaviour/typeql/language/get/... --test_output=errors --test_timeout=1800
108108
# test-behaviour-match-cluster:
109109
# image: vaticle-ubuntu-22.04
110110
# command: |
@@ -119,34 +119,34 @@ build:
119119
export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME
120120
export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD
121121
bazel run @vaticle_dependencies//distribution/artifact:create-netrc
122-
.factory/test-core.sh //test/behaviour/typeql/language/insert/... --test_output=errors
123-
.factory/test-core.sh //test/behaviour/typeql/language/delete/... --test_output=errors
124-
.factory/test-core.sh //test/behaviour/typeql/language/update/... --test_output=errors
122+
.factory/test-core.sh //test/behaviour/typeql/language/insert/... --test_output=errors --test_timeout=1800
123+
.factory/test-core.sh //test/behaviour/typeql/language/delete/... --test_output=errors --test_timeout=1800
124+
.factory/test-core.sh //test/behaviour/typeql/language/update/... --test_output=errors --test_timeout=1800
125125
test-behaviour-writable-cluster:
126126
image: vaticle-ubuntu-22.04
127127
command: |
128128
export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME
129129
export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD
130130
bazel run @vaticle_dependencies//distribution/artifact:create-netrc
131-
.factory/test-cluster.sh //test/behaviour/typeql/language/insert/... --test_output=errors
132-
.factory/test-cluster.sh //test/behaviour/typeql/language/delete/... --test_output=errors
133-
.factory/test-cluster.sh //test/behaviour/typeql/language/update/... --test_output=errors
131+
.factory/test-cluster.sh //test/behaviour/typeql/language/insert/... --test_output=errors --test_timeout=1800
132+
.factory/test-cluster.sh //test/behaviour/typeql/language/delete/... --test_output=errors --test_timeout=1800
133+
.factory/test-cluster.sh //test/behaviour/typeql/language/update/... --test_output=errors --test_timeout=1800
134134
test-behaviour-definable-core:
135135
image: vaticle-ubuntu-22.04
136136
command: |
137137
export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME
138138
export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD
139139
bazel run @vaticle_dependencies//distribution/artifact:create-netrc
140-
.factory/test-core.sh //test/behaviour/typeql/language/define/... --test_output=errors
141-
.factory/test-core.sh //test/behaviour/typeql/language/undefine/... --test_output=errors
140+
.factory/test-core.sh //test/behaviour/typeql/language/define/... --test_output=errors --test_timeout=1800
141+
.factory/test-core.sh //test/behaviour/typeql/language/undefine/... --test_output=errors --test_timeout=1800
142142
test-behaviour-definable-cluster:
143143
image: vaticle-ubuntu-22.04
144144
command: |
145145
export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME
146146
export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD
147147
bazel run @vaticle_dependencies//distribution/artifact:create-netrc
148-
.factory/test-cluster.sh //test/behaviour/typeql/language/define/... --test_output=errors
149-
.factory/test-cluster.sh //test/behaviour/typeql/language/undefine/... --test_output=errors
148+
.factory/test-cluster.sh //test/behaviour/typeql/language/define/... --test_output=errors --test_timeout=1800
149+
.factory/test-cluster.sh //test/behaviour/typeql/language/undefine/... --test_output=errors --test_timeout=1800
150150
deploy-maven-snapshot:
151151
image: vaticle-ubuntu-22.04
152152
dependencies: [

common/exception/ErrorMessage.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ public static class Client extends ErrorMessage {
6060
new Client(15, "The user '%s' does not exist.");
6161
public static final ErrorMessage CLUSTER_TOKEN_CREDENTIAL_INVALID =
6262
new Client(16, "Invalid token credential.");
63+
public static final ErrorMessage CLUSTER_PASSWORD_CREDENTIAL_EXPIRED =
64+
new Client(17, "Expired password credential.");
6365

6466
private static final String codePrefix = "CLI";
6567
private static final String messagePrefix = "Client Error";

common/exception/TypeDBClientException.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class TypeDBClientException extends RuntimeException {
3131
// TODO: propagate exception from the server side in a less-brittle way
3232
private static final String CLUSTER_REPLICA_NOT_PRIMARY_ERROR_CODE = "[RPL01]";
3333
private static final String CLUSTER_TOKEN_CREDENTIAL_INVALID_ERROR_CODE = "[CLS08]";
34+
private static final String CLUSTER_PASSWORD_CREDENTIAL_EXPIRED_ERROR_CODE = "[CLS10]";
3435

3536
@Nullable
3637
private final ErrorMessage errorMessage;
@@ -53,6 +54,8 @@ public static TypeDBClientException of(StatusRuntimeException sre) {
5354
return new TypeDBClientException(ErrorMessage.Client.CLUSTER_REPLICA_NOT_PRIMARY);
5455
} else if (isTokenCredentialInvalid(sre)) {
5556
return new TypeDBClientException(ErrorMessage.Client.CLUSTER_TOKEN_CREDENTIAL_INVALID);
57+
} else if (isPasswordCredentialExpired(sre)) {
58+
return new TypeDBClientException(ErrorMessage.Client.CLUSTER_PASSWORD_CREDENTIAL_EXPIRED);
5659
} else {
5760
return new TypeDBClientException(sre.getStatus().getDescription(), sre);
5861
}
@@ -77,6 +80,11 @@ private static boolean isTokenCredentialInvalid(StatusRuntimeException statusRun
7780
statusRuntimeException.getStatus().getDescription().contains(CLUSTER_TOKEN_CREDENTIAL_INVALID_ERROR_CODE);
7881
}
7982

83+
private static boolean isPasswordCredentialExpired(StatusRuntimeException statusRuntimeException) {
84+
return statusRuntimeException.getStatus().getCode() == Status.Code.UNAUTHENTICATED &&
85+
statusRuntimeException.getStatus().getDescription() != null &&
86+
statusRuntimeException.getStatus().getDescription().contains(CLUSTER_PASSWORD_CREDENTIAL_EXPIRED_ERROR_CODE);
87+
}
8088
public String getName() {
8189
return this.getClass().getName();
8290
}

dependencies/vaticle/artifacts.bzl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def vaticle_typedb_artifact():
2929
artifact_name = "typedb-server-{platform}-{version}.{ext}",
3030
tag_source = deployment["artifact.release"],
3131
commit_source = deployment["artifact.snapshot"],
32-
commit = "ab258158efd6f608fe6df2fb4ebf6e5d9f710765",
32+
commit = "de6c2e8054b5c0fc6d8bc6e0071bc1a7c276efe6",
3333
)
3434

3535
def vaticle_typedb_cluster_artifact():
@@ -39,5 +39,5 @@ def vaticle_typedb_cluster_artifact():
3939
artifact_name = "typedb-cluster-all-{platform}-{version}.{ext}",
4040
tag_source = deployment_private["artifact.release"],
4141
commit_source = deployment_private["artifact.snapshot"],
42-
commit = "ff021f6201db41c1f81af1dfec5c90b8b9803efe",
42+
commit = "302c0db2a29d515b75b7ef219ddfe3b85edbf9c8",
4343
)

dependencies/vaticle/repositories.bzl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ def vaticle_typeql():
3131
def vaticle_typedb_common():
3232
git_repository(
3333
name = "vaticle_typedb_common",
34-
remote = "https://github.com/vaticle/typedb-common",
35-
commit = "01420b86213dc931ff97188daa06b9ecf06f3932" # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_common
34+
remote = "https://github.com/jamesreprise/typedb-common",
35+
commit = "a5d4ddf53e0f3bed3d73fa10137edf1a3732595e", # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_common
3636
)
3737

3838
def vaticle_dependencies():
@@ -53,7 +53,7 @@ def vaticle_typedb_behaviour():
5353
git_repository(
5454
name = "vaticle_typedb_behaviour",
5555
remote = "https://github.com/vaticle/typedb-behaviour",
56-
commit = "ff4f58b4db2200b907c60b28a2b8ee661449e9c0" # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_behaviour
56+
commit = "a8c2bd79c4e440d4682e68bb15b4578456eefd2a" # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_behaviour
5757
)
5858

5959
def vaticle_factory_tracing():

test/behaviour/BehaviourTest.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
package com.vaticle.typedb.client.test.behaviour;
2323

24-
import com.vaticle.typedb.common.test.TypeDBRunner;
2524
import com.vaticle.typedb.common.test.TypeDBSingleton;
2625
import org.junit.AfterClass;
2726

@@ -33,8 +32,6 @@ public abstract class BehaviourTest {
3332

3433
@AfterClass
3534
public static void afterAll() {
36-
TypeDBRunner server = TypeDBSingleton.getTypeDBRunner();
37-
assert server != null;
38-
server.stop();
35+
TypeDBSingleton.deleteTypeDBRunner();
3936
}
4037
}

test/behaviour/connection/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ java_library(
6666
":steps-base",
6767
"//:client-java",
6868
"//api:api",
69+
"//test/behaviour/util:util",
6970
"@vaticle_typedb_common//test:typedb-runner",
7071

7172
# External dependencies from Maven

test/behaviour/connection/ConnectionStepsBase.java

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import com.vaticle.typedb.client.api.TypeDBOptions;
2626
import com.vaticle.typedb.client.api.TypeDBSession;
2727
import com.vaticle.typedb.client.api.TypeDBTransaction;
28-
import com.vaticle.typedb.client.api.database.Database;
2928
import com.vaticle.typedb.common.test.TypeDBSingleton;
3029

3130
import java.util.ArrayList;
@@ -40,9 +39,7 @@
4039

4140
import static com.vaticle.typedb.common.collection.Collections.map;
4241
import static com.vaticle.typedb.common.collection.Collections.pair;
43-
import static org.junit.Assert.assertFalse;
4442
import static org.junit.Assert.assertNotNull;
45-
import static org.junit.Assert.assertNull;
4643
import static org.junit.Assert.assertTrue;
4744

4845
public abstract class ConnectionStepsBase {
@@ -68,7 +65,9 @@ public static TypeDBTransaction tx() {
6865
return sessionsToTransactions.get(sessions.get(0)).get(0);
6966
}
7067

71-
abstract void beforeAll();
68+
void beforeAll() {
69+
TypeDBSingleton.deleteTypeDBRunner();
70+
}
7271

7372
void before() {
7473
if (!isBeforeAllRan) {
@@ -78,13 +77,10 @@ void before() {
7877
isBeforeAllRan = true;
7978
}
8079
}
81-
assertNull(client);
82-
String address = TypeDBSingleton.getTypeDBRunner().address();
83-
assertNotNull(address);
84-
client = createTypeDBClient(address);
85-
client.databases().all().forEach(Database::delete);
8680
sessionOptions = createOptions().infer(true);
8781
transactionOptions = createOptions().infer(true);
82+
TypeDBSingleton.resetTypeDBRunner();
83+
8884
System.out.println("ConnectionSteps.before");
8985
}
9086

@@ -105,21 +101,20 @@ void after() {
105101
}));
106102
CompletableFuture.allOf(closures.toArray(CompletableFuture[]::new)).join();
107103
sessionsParallel.clear();
108-
109104
sessionsToTransactions.clear();
110105
sessionsToTransactionsParallel.clear();
111106
sessionsParallelToTransactionsParallel.clear();
112-
client.databases().all().forEach(Database::delete);
113-
client.close();
114-
assertFalse(client.isOpen());
115-
client = null;
107+
108+
TypeDBSingleton.resetTypeDBRunner();
116109
System.out.println("ConnectionSteps.after");
117110
}
118111

119112
abstract TypeDBClient createTypeDBClient(String address);
120113

121114
abstract TypeDBOptions createOptions();
122115

116+
abstract void open_connection();
117+
123118
void connection_has_been_opened() {
124119
assertNotNull(client);
125120
assertTrue(client.isOpen());
@@ -128,6 +123,6 @@ void connection_has_been_opened() {
128123
void connection_does_not_have_any_database() {
129124
assertNotNull(client);
130125
assertTrue(client.isOpen());
126+
assertTrue(client.databases().all().isEmpty());
131127
}
132-
133128
}

test/behaviour/connection/ConnectionStepsCluster.java

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,25 @@
2525
import com.vaticle.typedb.client.api.TypeDBClient;
2626
import com.vaticle.typedb.client.api.TypeDBCredential;
2727
import com.vaticle.typedb.client.api.TypeDBOptions;
28+
import com.vaticle.typedb.common.test.TypeDBRunner;
2829
import com.vaticle.typedb.common.test.cluster.TypeDBClusterRunner;
2930
import com.vaticle.typedb.common.test.TypeDBSingleton;
3031
import io.cucumber.java.After;
3132
import io.cucumber.java.Before;
3233
import io.cucumber.java.en.Given;
34+
import io.cucumber.java.en.When;
3335

34-
import java.io.IOException;
3536
import java.nio.file.Paths;
36-
import java.util.concurrent.TimeoutException;
37+
import java.util.HashMap;
38+
import java.util.Map;
39+
40+
import static com.vaticle.typedb.client.test.behaviour.util.Util.assertThrows;
3741

3842
public class ConnectionStepsCluster extends ConnectionStepsBase {
3943

4044
@Override
41-
void beforeAll() {
42-
TypeDBClusterRunner cluster = TypeDBClusterRunner.create(Paths.get("."), 1);
43-
cluster.start();
44-
TypeDBSingleton.setTypeDBRunner(cluster);
45+
public void beforeAll() {
46+
super.beforeAll();
4547
}
4648

4749
@Before
@@ -56,22 +58,80 @@ public synchronized void after() {
5658

5759
@Override
5860
TypeDBClient createTypeDBClient(String address) {
59-
return TypeDB.clusterClient(address, new TypeDBCredential("admin", "password", false));
61+
return createTypeDBClient(address, "admin", "password", false);
62+
}
63+
64+
TypeDBClient createTypeDBClient(String address, String username, String password, boolean tlsEnabled) {
65+
return TypeDB.clusterClient(address, new TypeDBCredential(username, password, tlsEnabled));
6066
}
6167

6268
@Override
6369
TypeDBOptions createOptions() {
6470
return TypeDBOptions.cluster();
6571
}
6672

73+
@Override
74+
@When("open connection")
75+
public void open_connection() {
76+
client = createTypeDBClient(TypeDBSingleton.getTypeDBRunner().address());
77+
}
78+
6779
@Given("connection has been opened")
6880
public void connection_has_been_opened() {
6981
super.connection_has_been_opened();
7082
}
7183

84+
@Given("typedb has configuration")
85+
public void typedb_has_configuration(Map<String, String> map) {
86+
TypeDBSingleton.deleteTypeDBRunner();
87+
Map<String, String> serverOpts = new HashMap<>();
88+
for (Map.Entry<String, String> entry : map.entrySet()) {
89+
serverOpts.put("--" + entry.getKey(), entry.getValue());
90+
}
91+
TypeDBClusterRunner clusterRunner = TypeDBClusterRunner.create(Paths.get("."), 1, serverOpts);
92+
TypeDBSingleton.setTypeDBRunner(clusterRunner);
93+
}
94+
95+
@When("typedb starts")
96+
public void typedb_starts() {
97+
TypeDBRunner runner = TypeDBSingleton.getTypeDBRunner();
98+
if (runner != null && runner.isStopped()) {
99+
runner.start();
100+
} else {
101+
TypeDBClusterRunner clusterRunner = TypeDBClusterRunner.create(Paths.get("."), 1);
102+
TypeDBSingleton.setTypeDBRunner(clusterRunner);
103+
clusterRunner.start();
104+
}
105+
}
106+
107+
@When("typedb stops")
108+
public void typedb_stops() {
109+
TypeDBSingleton.getTypeDBRunner().stop();
110+
}
111+
112+
@When("user connect: {word}, {word}")
113+
public void user_connect(String username, String password) {
114+
if (client != null) {
115+
client.close();
116+
client = null;
117+
}
118+
119+
client = createTypeDBClient(TypeDBSingleton.getTypeDBRunner().address(), username, password, false);
120+
}
121+
122+
@When("user disconnect")
123+
public void disconnect_current_user() {
124+
client.close();
125+
client = null;
126+
}
127+
128+
@When("user connect: {word}, {word}; throws exception")
129+
public void user_connect_throws_exception(String username, String password) {
130+
assertThrows(() -> createTypeDBClient(TypeDBSingleton.getTypeDBRunner().address(), username, password, false));
131+
}
132+
72133
@Given("connection does not have any database")
73134
public void connection_does_not_have_any_database() {
74135
super.connection_does_not_have_any_database();
75136
}
76-
77137
}

0 commit comments

Comments
 (0)