From f51ec62c7831806edd4716cd88810065f03254c2 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 31 May 2023 18:27:36 +0400 Subject: [PATCH 01/84] fmt --- src/connection/network/proto/message.rs | 1 + src/transaction/query.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 493b94ff..b9f3c54b 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -28,6 +28,7 @@ use typedb_protocol::{ }; use super::{FromProto, IntoProto, TryFromProto, TryIntoProto}; +use crate::answer::{ConceptMapGroup, NumericGroup}; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, common::{info::DatabaseInfo, RequestID, Result}, diff --git a/src/transaction/query.rs b/src/transaction/query.rs index e19378cd..8cb300b5 100644 --- a/src/transaction/query.rs +++ b/src/transaction/query.rs @@ -23,6 +23,7 @@ use std::sync::Arc; use futures::Stream; +use crate::answer::NumericGroup; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, common::Result, From e795331675cd6c2c1b96a071390040711689f331 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 31 May 2023 18:56:37 +0400 Subject: [PATCH 02/84] fmt --- src/connection/network/proto/message.rs | 1 - src/transaction/query.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index b9f3c54b..493b94ff 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -28,7 +28,6 @@ use typedb_protocol::{ }; use super::{FromProto, IntoProto, TryFromProto, TryIntoProto}; -use crate::answer::{ConceptMapGroup, NumericGroup}; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, common::{info::DatabaseInfo, RequestID, Result}, diff --git a/src/transaction/query.rs b/src/transaction/query.rs index 8cb300b5..e19378cd 100644 --- a/src/transaction/query.rs +++ b/src/transaction/query.rs @@ -23,7 +23,6 @@ use std::sync::Arc; use futures::Stream; -use crate::answer::NumericGroup; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, common::Result, From cad83b4f5a70daa299d955223b6f35df9603d0c0 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 6 Jun 2023 17:47:35 +0400 Subject: [PATCH 03/84] wip --- src/connection/message.rs | 4 ++ src/connection/network/proto/message.rs | 22 +++++++++- src/connection/network/proto/mod.rs | 1 + src/connection/network/proto/user.rs | 34 +++++++++++++++ src/lib.rs | 1 + src/user/mod.rs | 25 +++++++++++ src/user/user.rs | 37 ++++++++++++++++ src/user/user_manager.rs | 42 ++++++++++++++++++ tests/BUILD | 1 + tests/behaviour/connection/mod.rs | 1 + tests/behaviour/connection/user/mod.rs | 35 +++++++++++++++ tests/behaviour/connection/user/steps.rs | 54 ++++++++++++++++++++++++ 12 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 src/connection/network/proto/user.rs create mode 100644 src/user/mod.rs create mode 100644 src/user/user.rs create mode 100644 src/user/user_manager.rs create mode 100644 tests/behaviour/connection/user/mod.rs create mode 100644 tests/behaviour/connection/user/steps.rs diff --git a/src/connection/message.rs b/src/connection/message.rs index 30da85d7..c7d31e52 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -489,3 +489,7 @@ pub(super) enum LogicResponse { GetRule { rule: Rule }, GetRules { rules: Vec }, } + +pub(super) enum UserRequest { + UserDelete { thing: Thing }, +} diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 493b94ff..cd817582 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -24,7 +24,7 @@ use std::time::Duration; use itertools::Itertools; use typedb_protocol::{ attribute, attribute_type, concept_manager, database, database_manager, entity_type, logic_manager, query_manager, - r#type, relation, relation_type, role_type, rule, server_manager, session, thing, thing_type, transaction, + r#type, relation, relation_type, role_type, rule, server_manager, session, thing, thing_type, transaction, user_manager, }; use super::{FromProto, IntoProto, TryFromProto, TryIntoProto}; @@ -1158,6 +1158,26 @@ impl IntoProto for LogicRequest { logic_manager::Req { req: Some(req) } } } +impl IntoProto for UserRequest { + fn into_proto(self) -> logic_manager::Req { + let req = match self { + Self::PutRule { label, when, then } => { + logic_manager::req::Req::PutRuleReq(logic_manager::put_rule::Req { + label, + when: when.to_string(), + then: then.to_string(), + }) + } + Self::GetRule { label } => { + logic_manager::req::Req::GetRuleReq(logic_manager::get_rule::Req { label }) + } + Self::GetRules => { + logic_manager::req::Req::GetRulesReq(logic_manager::get_rules::Req {}) + } + }; + logic_manager::Req { req: Some(req) } + } +} impl TryFromProto for LogicResponse { fn try_from_proto(proto: logic_manager::Res) -> Result { diff --git a/src/connection/network/proto/mod.rs b/src/connection/network/proto/mod.rs index be389929..ae86af1e 100644 --- a/src/connection/network/proto/mod.rs +++ b/src/connection/network/proto/mod.rs @@ -24,6 +24,7 @@ mod concept; mod database; mod logic; mod message; +mod user; use crate::Result; diff --git a/src/connection/network/proto/user.rs b/src/connection/network/proto/user.rs new file mode 100644 index 00000000..6ae71f99 --- /dev/null +++ b/src/connection/network/proto/user.rs @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 Vaticle + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +use typedb_protocol::{User as UserProto}; + +use super::FromProto; +use crate::user::User; + +impl FromProto for User { + fn from_proto(proto: UserProto) -> Self { + Self { + username: proto.username, + password_expiry_seconds: proto.password_expiry_seconds, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index e532f4c3..d4c5cd0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,7 @@ mod connection; mod database; pub mod logic; pub mod transaction; +mod user; pub use self::{ common::{error, Credential, Error, Options, Result, SessionType, TransactionType}, diff --git a/src/user/mod.rs b/src/user/mod.rs new file mode 100644 index 00000000..8b686fcc --- /dev/null +++ b/src/user/mod.rs @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 Vaticle + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +mod user; +mod user_manager; + +pub use self::{user::User, user_manager::UserManager}; diff --git a/src/user/user.rs b/src/user/user.rs new file mode 100644 index 00000000..d3d03cd2 --- /dev/null +++ b/src/user/user.rs @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Vaticle + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#[derive(Clone, Debug)] +pub struct User { + pub username: String, + pub password_expiry_seconds: Option, +} + +impl User { + pub fn new(username: String, password_expiry_seconds: Option) -> Self { + Self { username, password_expiry_seconds } + } + + pub fn password_update(password_old: String, password_new: String) { + + } + +} diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs new file mode 100644 index 00000000..e3fecc2b --- /dev/null +++ b/src/user/user_manager.rs @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Vaticle + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +use std::future::Future; + +use super::{database::ServerDatabase, Database}; +use crate::{ + common::{error::ConnectionError, Result}, + connection::ServerConnection, + Connection, +}; + +#[derive(Clone, Debug)] +pub struct UserManager { + connection: Connection, +} + +impl UserManager { + pub fn new(connection: Connection) -> Self { + Self { connection } + } + + +} diff --git a/tests/BUILD b/tests/BUILD index 90eaca2a..d3d07077 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -52,6 +52,7 @@ rust_test( "@vaticle_typedb_behaviour//connection:database.feature", "@vaticle_typedb_behaviour//connection:session.feature", "@vaticle_typedb_behaviour//connection:transaction.feature", + "@vaticle_typedb_behaviour//connection:user.feature", "@vaticle_typedb_behaviour//typeql/language:define.feature", "@vaticle_typedb_behaviour//typeql/language:undefine.feature", "@vaticle_typedb_behaviour//typeql/language:insert.feature", diff --git a/tests/behaviour/connection/mod.rs b/tests/behaviour/connection/mod.rs index 2ab68731..523046df 100644 --- a/tests/behaviour/connection/mod.rs +++ b/tests/behaviour/connection/mod.rs @@ -23,3 +23,4 @@ pub(crate) mod database; pub(crate) mod session; pub(crate) mod steps; pub(crate) mod transaction; +mod user; diff --git a/tests/behaviour/connection/user/mod.rs b/tests/behaviour/connection/user/mod.rs new file mode 100644 index 00000000..3c0e54d5 --- /dev/null +++ b/tests/behaviour/connection/user/mod.rs @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Vaticle + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +mod steps; + +use serial_test::serial; + +use crate::behaviour::Context; + +#[tokio::test] +#[serial] +async fn test() { + // Bazel specific path: when running the test in bazel, the external data from + // @vaticle_typedb_behaviour is stored in a directory that is a sibling to + // the working directory. + assert!(Context::test("../vaticle_typedb_behaviour/connection/user.feature").await); +} diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs new file mode 100644 index 00000000..af663006 --- /dev/null +++ b/tests/behaviour/connection/user/steps.rs @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 Vaticle + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +use cucumber::{gherkin::Step, given, then, when}; +use typedb_client::{Options, TransactionType}; + +use crate::{ + behaviour::{Context}, + generic_step_impl, +}; + +generic_step_impl! { + + #[step(expr = "connection opens with authentication: {word}, {word}")] + async fn connection_opens_with_authentication(context: &mut Context, login: String, password: String) { + Connection::new_encrypted( + &["localhost:11729", "localhost:21729", "localhost:31729"], + Credential::with_tls( + login, + password, + Some(&PathBuf::from( + std::env::var("ROOT_CA") + .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), + )), + )?, + ) + } + + #[step(expr = "users delete: {word}")] + async fn user_delete(context: &mut Context, user: String) { + // for session_tracker in &mut context.session_trackers { + // session_tracker.open_transaction(type_.transaction_type).await.unwrap(); + // } + } + +} From d806ca5699013f80406db226fde9123c212ea208 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 6 Jun 2023 20:03:42 +0400 Subject: [PATCH 04/84] wip --- src/connection/message.rs | 15 +++++++++++++++ src/connection/network/proto/message.rs | 17 +++++++++++++++++ src/connection/network/proto/user.rs | 5 +++-- src/connection/network/stub.rs | 9 ++++++++- src/connection/network/transmitter/rpc.rs | 5 +++++ src/user/user_manager.rs | 10 ++++------ tests/behaviour/connection/user/steps.rs | 12 +++++++----- 7 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/connection/message.rs b/src/connection/message.rs index c7d31e52..8c6d571a 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -36,6 +36,7 @@ use crate::{ logic::{Explanation, Rule}, Options, SessionType, TransactionType, }; +use crate::user::User; #[derive(Debug)] pub(super) enum Request { @@ -56,6 +57,13 @@ pub(super) enum Request { SessionPulse { session_id: SessionID }, Transaction(TransactionRequest), + + UserCreate { username: String, password: String }, + UserDelete { username: String }, + UserGet { username: String }, + UserPasswordSet { username: String, password: String }, + UsersAll, + UsersContain { username: String }, } #[derive(Debug)] @@ -97,6 +105,13 @@ pub(super) enum Response { request_sink: UnboundedSender, response_source: Streaming, }, + + UserCreate, + UserDelete, + UserGet { user: Option }, + UserPasswordSet, + UsersAll { users: Vec }, + UsersContain { contains: bool }, } #[derive(Debug)] diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index cd817582..5714f294 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -43,6 +43,7 @@ use crate::{ error::{ConnectionError, InternalError}, logic::{Explanation, Rule}, }; +use crate::user::User; impl TryIntoProto for Request { fn try_into_proto(self) -> Result { @@ -165,6 +166,15 @@ impl TryIntoProto for Request { } } +impl TryIntoProto for Request { + fn try_into_proto(self) -> Result { + match self { + Self::UserGet { username } => Ok(user_manager::get::Req { username }), + other => Err(InternalError::UnexpectedRequestType(format!("{other:?}")).into()), + } + } +} + impl TryFromProto for Response { fn try_from_proto(proto: server_manager::all::Res) -> Result { let servers = proto.servers.into_iter().map(|server| server.address.parse()).try_collect()?; @@ -335,6 +345,12 @@ impl TryFromProto for TransactionResponse { } } +impl FromProto for Response { + fn from_proto(proto: user_manager::get::Res) -> Self { + Self::UserGet { user: proto.user.and_then(|u| Some(User::from_proto(u))) } + } +} + impl IntoProto for QueryRequest { fn into_proto(self) -> query_manager::Req { let (req, options) = match self { @@ -1200,3 +1216,4 @@ impl TryFromProto for LogicResponse { }) } } + diff --git a/src/connection/network/proto/user.rs b/src/connection/network/proto/user.rs index 6ae71f99..4f159c41 100644 --- a/src/connection/network/proto/user.rs +++ b/src/connection/network/proto/user.rs @@ -26,9 +26,10 @@ use crate::user::User; impl FromProto for User { fn from_proto(proto: UserProto) -> Self { + let UserProto {username, password_expiry_seconds } = proto; Self { - username: proto.username, - password_expiry_seconds: proto.password_expiry_seconds, + username, + password_expiry_seconds, } } } diff --git a/src/connection/network/stub.rs b/src/connection/network/stub.rs index 59214ab0..318976f1 100644 --- a/src/connection/network/stub.rs +++ b/src/connection/network/stub.rs @@ -27,7 +27,7 @@ use tokio::sync::mpsc::{unbounded_channel as unbounded_async, UnboundedSender}; use tokio_stream::wrappers::UnboundedReceiverStream; use tonic::{Response, Status, Streaming}; use typedb_protocol::{ - database, database_manager, server_manager, session, transaction, type_db_client::TypeDbClient as GRPC, user, + database, database_manager, server_manager, session, transaction, type_db_client::TypeDbClient as GRPC, user, user_manager }; use super::channel::{CallCredentials, GRPCChannel}; @@ -166,6 +166,13 @@ impl RPCStub { .await } + pub(super) async fn user_get( + &mut self, + req: user_manager::get::Req, + ) -> Result { + self.single(|this| Box::pin(this.grpc.users_get(req.clone()))).await + } + async fn single(&mut self, call: F) -> Result where for<'a> F: Fn(&'a mut Self) -> BoxFuture<'a, TonicResult> + Send + Sync, diff --git a/src/connection/network/transmitter/rpc.rs b/src/connection/network/transmitter/rpc.rs index 5c0e3749..c32474c8 100644 --- a/src/connection/network/transmitter/rpc.rs +++ b/src/connection/network/transmitter/rpc.rs @@ -156,6 +156,11 @@ impl RPCTransmitter { let (request_sink, response_source) = rpc.transaction(transaction_request.into_proto()).await?; Ok(Response::TransactionOpen { request_sink, response_source }) } + + Request::UserGet { .. } => { + rpc.user_get(request.try_into_proto()?).await.map(Response::from_proto) + } + _ => todo!(), } } } diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index e3fecc2b..3944309f 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -21,22 +21,20 @@ use std::future::Future; -use super::{database::ServerDatabase, Database}; use crate::{ - common::{error::ConnectionError, Result}, + common::Result, connection::ServerConnection, Connection, }; #[derive(Clone, Debug)] pub struct UserManager { - connection: Connection, + // client: Client, } impl UserManager { - pub fn new(connection: Connection) -> Self { - Self { connection } + pub fn new() -> Self { + Self { } } - } diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index af663006..0a1420ac 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -19,8 +19,10 @@ * under the License. */ +use std::path::PathBuf; + use cucumber::{gherkin::Step, given, then, when}; -use typedb_client::{Options, TransactionType}; +use typedb_client::{Connection, Credential, Options, TransactionType}; use crate::{ behaviour::{Context}, @@ -34,14 +36,14 @@ generic_step_impl! { Connection::new_encrypted( &["localhost:11729", "localhost:21729", "localhost:31729"], Credential::with_tls( - login, - password, + &login.as_str(), + &password.as_str(), Some(&PathBuf::from( std::env::var("ROOT_CA") .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), )), - )?, - ) + ).unwrap(), + ).unwrap(); } #[step(expr = "users delete: {word}")] From f17cd62689b37d274f143cee6a27a1923e80e658 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 6 Jun 2023 21:00:19 +0400 Subject: [PATCH 05/84] get_user() --- src/connection/connection.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 37fe1a2f..2a13390d 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -49,6 +49,7 @@ use crate::{ error::InternalError, Credential, Options, }; +use crate::user::User; #[derive(Clone)] pub struct Connection { @@ -303,6 +304,13 @@ impl ServerConnection { other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), } } + + pub(crate) async fn get_user(&self, username: String) -> Result> { + match self.request_async(Request::UserGet { username }).await? { + Response::UserGet { user } => Ok(user), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } } impl fmt::Debug for ServerConnection { From 4f10b0a819b8f97ea3d6df102e5a534996e280fa Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 6 Jun 2023 21:00:54 +0400 Subject: [PATCH 06/84] `try_into_proto` for the rest requests --- src/connection/network/proto/message.rs | 47 ++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 5714f294..dc7c4b83 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -166,6 +166,42 @@ impl TryIntoProto for Request { } } +impl TryIntoProto for Request { + fn try_into_proto(self) -> Result { + match self { + Self::UsersAll => Ok(user_manager::all::Req {}), + other => Err(InternalError::UnexpectedRequestType(format!("{other:?}")).into()), + } + } +} + +impl TryIntoProto for Request { + fn try_into_proto(self) -> Result { + match self { + Self::UsersContain { username } => Ok(user_manager::contains::Req { username }), + other => Err(InternalError::UnexpectedRequestType(format!("{other:?}")).into()), + } + } +} + +impl TryIntoProto for Request { + fn try_into_proto(self) -> Result { + match self { + Self::UserCreate { username, password } => Ok(user_manager::create::Req { username, password }), + other => Err(InternalError::UnexpectedRequestType(format!("{other:?}")).into()), + } + } +} + +impl TryIntoProto for Request { + fn try_into_proto(self) -> Result { + match self { + Self::UserDelete { username} => Ok(user_manager::delete::Req { username }), + other => Err(InternalError::UnexpectedRequestType(format!("{other:?}")).into()), + } + } +} + impl TryIntoProto for Request { fn try_into_proto(self) -> Result { match self { @@ -175,6 +211,15 @@ impl TryIntoProto for Request { } } +impl TryIntoProto for Request { + fn try_into_proto(self) -> Result { + match self { + Self::UserPasswordSet { username, password } => Ok(user_manager::password_set::Req { username, password }), + other => Err(InternalError::UnexpectedRequestType(format!("{other:?}")).into()), + } + } +} + impl TryFromProto for Response { fn try_from_proto(proto: server_manager::all::Res) -> Result { let servers = proto.servers.into_iter().map(|server| server.address.parse()).try_collect()?; @@ -1174,6 +1219,7 @@ impl IntoProto for LogicRequest { logic_manager::Req { req: Some(req) } } } + impl IntoProto for UserRequest { fn into_proto(self) -> logic_manager::Req { let req = match self { @@ -1216,4 +1262,3 @@ impl TryFromProto for LogicResponse { }) } } - From d517616312436e2441f5a38ae47c88e8f1bdc4bd Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 7 Jun 2023 12:13:11 +0400 Subject: [PATCH 07/84] `from_proto` for responses --- src/connection/network/proto/message.rs | 32 ++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index dc7c4b83..bb80f010 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -390,9 +390,39 @@ impl TryFromProto for TransactionResponse { } } +impl FromProto for Response { + fn from_proto(proto: user_manager::all::Res) -> Self { + Self::UsersAll { users: proto.users.iter().map(|user| User::from_proto(user.clone())).collect::>() } + } +} + +impl FromProto for Response { + fn from_proto(proto: user_manager::contains::Res) -> Self { + Self::UsersContain { contains: proto.contains } + } +} + +impl FromProto for Response { + fn from_proto(_: user_manager::create::Res) -> Self { + Self::UserCreate {} + } +} + +impl FromProto for Response { + fn from_proto(_: user_manager::delete::Res) -> Self { + Self::UserDelete {} + } +} + impl FromProto for Response { fn from_proto(proto: user_manager::get::Res) -> Self { - Self::UserGet { user: proto.user.and_then(|u| Some(User::from_proto(u))) } + Self::UserGet { user: proto.user.and_then(|user| Some(User::from_proto(user))) } + } +} + +impl FromProto for Response { + fn from_proto(_: user_manager::password_set::Res) -> Self { + Self::UserPasswordSet {} } } From e58e701caa8d040f4d9e916413b7a3036afec287 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 7 Jun 2023 12:54:17 +0400 Subject: [PATCH 08/84] users functions --- src/connection/network/stub.rs | 35 +++++++++++++++++++++++ src/connection/network/transmitter/rpc.rs | 16 ++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/connection/network/stub.rs b/src/connection/network/stub.rs index 318976f1..408c3896 100644 --- a/src/connection/network/stub.rs +++ b/src/connection/network/stub.rs @@ -166,6 +166,20 @@ impl RPCStub { .await } + pub(super) async fn user_create( + &mut self, + req: user_manager::create::Req, + ) -> Result { + self.single(|this| Box::pin(this.grpc.users_create(req.clone()))).await + } + + pub(super) async fn user_delete( + &mut self, + req: user_manager::delete::Req, + ) -> Result { + self.single(|this| Box::pin(this.grpc.users_delete(req.clone()))).await + } + pub(super) async fn user_get( &mut self, req: user_manager::get::Req, @@ -173,6 +187,27 @@ impl RPCStub { self.single(|this| Box::pin(this.grpc.users_get(req.clone()))).await } + pub(super) async fn user_password_set( + &mut self, + req: user_manager::password_set::Req, + ) -> Result { + self.single(|this| Box::pin(this.grpc.users_password_set(req.clone()))).await + } + + pub(super) async fn users_all( + &mut self, + req: user_manager::all::Req, + ) -> Result { + self.single(|this| Box::pin(this.grpc.users_all(req.clone()))).await + } + + pub(super) async fn users_contain( + &mut self, + req: user_manager::contains::Req, + ) -> Result { + self.single(|this| Box::pin(this.grpc.users_contains(req.clone()))).await + } + async fn single(&mut self, call: F) -> Result where for<'a> F: Fn(&'a mut Self) -> BoxFuture<'a, TonicResult> + Send + Sync, diff --git a/src/connection/network/transmitter/rpc.rs b/src/connection/network/transmitter/rpc.rs index c32474c8..fc7f2010 100644 --- a/src/connection/network/transmitter/rpc.rs +++ b/src/connection/network/transmitter/rpc.rs @@ -157,10 +157,24 @@ impl RPCTransmitter { Ok(Response::TransactionOpen { request_sink, response_source }) } + Request::UserCreate { .. } => { + rpc.user_create(request.try_into_proto()?).await.map(Response::from_proto) + } + Request::UserDelete { .. } => { + rpc.user_delete(request.try_into_proto()?).await.map(Response::from_proto) + } Request::UserGet { .. } => { rpc.user_get(request.try_into_proto()?).await.map(Response::from_proto) } - _ => todo!(), + Request::UserPasswordSet { .. } => { + rpc.user_password_set(request.try_into_proto()?).await.map(Response::from_proto) + } + Request::UsersAll => { + rpc.users_all(request.try_into_proto()?).await.map(Response::from_proto) + } + Request::UsersContain { .. } => { + rpc.users_contain(request.try_into_proto()?).await.map(Response::from_proto) + } } } } From 2c3417e15dc558e1c0ed4ee7a04800a93a4e0bcd Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 7 Jun 2023 13:03:15 +0400 Subject: [PATCH 09/84] Add UserManager to Context --- src/lib.rs | 1 + tests/behaviour/mod.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d4c5cd0c..d6c43d60 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,4 +35,5 @@ pub use self::{ connection::Connection, database::{Database, DatabaseManager, Session}, transaction::Transaction, + user::{User, UserManager}, }; diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index e87c6607..087b8159 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -34,7 +34,7 @@ use typedb_client::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, concept::{Attribute, AttributeType, Entity, EntityType, Relation, RelationType, Thing}, logic::Rule, - Connection, Database, DatabaseManager, Result as TypeDBResult, Transaction, + Connection, Database, DatabaseManager, Result as TypeDBResult, Transaction, UserManager, }; use self::session_tracker::SessionTracker; @@ -45,6 +45,7 @@ pub struct Context { pub databases: DatabaseManager, pub session_trackers: Vec, pub things: HashMap>, + pub users: UserManager, pub answer: Vec, pub answer_group: Vec, pub numeric_answer: Option, @@ -172,6 +173,7 @@ impl Default for Context { databases, session_trackers: Vec::new(), things: HashMap::new(), + users: UserManager::new(), answer: Vec::new(), answer_group: Vec::new(), numeric_answer: None, From 89132038cf60b7f4f6163c1f0597be990eff8f80 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 7 Jun 2023 13:39:27 +0400 Subject: [PATCH 10/84] `ServerConnection` methods for users --- src/connection/connection.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 2a13390d..0b6428bb 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -305,12 +305,47 @@ impl ServerConnection { } } + pub(crate) async fn all_users(&self) -> Result> { + match self.request_async(Request::UsersAll).await? { + Response::UsersAll { users } => Ok(users), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } + + pub(crate) async fn contains_user(&self, username: String) -> Result { + match self.request_async(Request::UsersContain { username }).await? { + Response::UsersContain { contains } => Ok(contains), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } + + pub(crate) async fn create_user(&self, username: String, password: String) -> Result<()> { + match self.request_async(Request::UserCreate { username, password }).await? { + Response::UserCreate => Ok(()), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } + + pub(crate) async fn delete_user(&self, username: String) -> Result<()> { + match self.request_async(Request::UserDelete { username }).await? { + Response::UserDelete => Ok(()), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } + pub(crate) async fn get_user(&self, username: String) -> Result> { match self.request_async(Request::UserGet { username }).await? { Response::UserGet { user } => Ok(user), other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), } } + + pub(crate) async fn set_user_password(&self, username: String, password: String) -> Result<()> { + match self.request_async(Request::UserPasswordSet { username, password }).await? { + Response::UserPasswordSet => Ok(()), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } } impl fmt::Debug for ServerConnection { From b227cca11ba0384c66a3f3d312e498700e3c6e81 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 7 Jun 2023 14:15:19 +0400 Subject: [PATCH 11/84] wip --- src/user/user_manager.rs | 44 ++++++++++++++++++++++++++++++++-------- tests/behaviour/mod.rs | 3 ++- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index 3944309f..e140abd9 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -21,20 +21,48 @@ use std::future::Future; -use crate::{ - common::Result, - connection::ServerConnection, - Connection, -}; +use crate::{common::Result, connection::ServerConnection, Connection, User}; +use crate::error::ConnectionError; #[derive(Clone, Debug)] pub struct UserManager { - // client: Client, + connection: Connection, } impl UserManager { - pub fn new() -> Self { - Self { } + pub fn new(connection: Connection) -> Self { + Self { connection } } + // pub async fn contains(&self, username: String) -> Result { + // self.run_failsafe(name.into(), move |database, server_connection, _| async move { + // server_connection.database_exists(database.name().to_owned()).await + // }).await + // self.connection. + // } + + pub async fn all(&self) -> Result> { + let mut error_buffer = Vec::with_capacity(self.connection.server_count()); + for server_connection in self.connection.connections() { + match server_connection.all_users().await { + Ok(list) => { + return Ok(list); + }, + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + } + } + Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + } + + // pub fn all() -> Vec { + // + // } + // async fn run_failsafe(&self, name: String, task: F) -> Result + // where + // F: Fn(ServerDatabase, ServerConnection, bool) -> P, + // P: Future>, + // { + // Database::get(name, self.connection.clone()).await?.run_failsafe(&task).await + // } + } diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 087b8159..7191bfec 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -168,12 +168,13 @@ impl Default for Context { fn default() -> Self { let connection = Connection::new_plaintext("0.0.0.0:1729").unwrap(); let databases = DatabaseManager::new(connection.clone()); + let users = UserManager::new(connection.clone()); Self { connection, databases, session_trackers: Vec::new(), things: HashMap::new(), - users: UserManager::new(), + users, answer: Vec::new(), answer_group: Vec::new(), numeric_answer: None, From 7c009aca42607c34e8d7b0ea6cb3be9f52c3dac3 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 8 Jun 2023 20:32:28 +0400 Subject: [PATCH 12/84] get_all() and contains() --- src/user/user_manager.rs | 13 +++++++++++++ tests/behaviour/connection/user/steps.rs | 16 +++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index e140abd9..3e6f3888 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -54,6 +54,19 @@ impl UserManager { Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? } + pub async fn contains(&self, username: String) -> Result { + let mut error_buffer = Vec::with_capacity(self.connection.server_count()); + for server_connection in self.connection.connections() { + match server_connection.contains_user(username.clone()).await { + Ok(contains) => { + return Ok(contains); + }, + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + } + } + Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + } + // pub fn all() -> Vec { // // } diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 0a1420ac..5a9bc255 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -33,7 +33,7 @@ generic_step_impl! { #[step(expr = "connection opens with authentication: {word}, {word}")] async fn connection_opens_with_authentication(context: &mut Context, login: String, password: String) { - Connection::new_encrypted( + context.connection = Connection::new_encrypted( &["localhost:11729", "localhost:21729", "localhost:31729"], Credential::with_tls( &login.as_str(), @@ -46,6 +46,20 @@ generic_step_impl! { ).unwrap(); } + #[step(expr = "users get all")] + async fn users_get_all(context: &mut Context) { + let res = context.users.all().await; + assert!(res.is_ok()); + panic!("{res:?}"); + } + + #[step(expr = "users contains: {word}")] + async fn users_contains(context: &mut Context, username: String) { + let res = context.users.contains(username).await; + assert!(res.is_ok(), "{:?}", res.err()); + assert!(res.unwrap()); + } + #[step(expr = "users delete: {word}")] async fn user_delete(context: &mut Context, user: String) { // for session_tracker in &mut context.session_trackers { From c28ec59bdaaa7adb92b4b8d7553d8673c61806f5 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Fri, 9 Jun 2023 20:17:57 +0400 Subject: [PATCH 13/84] fmt --- src/connection/connection.rs | 2 +- src/connection/message.rs | 14 ++++++++++---- src/connection/network/proto/message.rs | 4 ++-- src/connection/network/proto/user.rs | 9 +++------ src/connection/network/stub.rs | 23 ++++++----------------- src/connection/network/transmitter/rpc.rs | 16 ++++------------ src/user/user.rs | 5 +---- src/user/user_manager.rs | 8 +++----- tests/behaviour/connection/user/steps.rs | 5 +---- 9 files changed, 31 insertions(+), 55 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 0b6428bb..9c1e3aa5 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -47,9 +47,9 @@ use crate::{ }, connection::message::{Request, Response, TransactionRequest}, error::InternalError, + user::User, Credential, Options, }; -use crate::user::User; #[derive(Clone)] pub struct Connection { diff --git a/src/connection/message.rs b/src/connection/message.rs index 8c6d571a..e1b5e80a 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -34,9 +34,9 @@ use crate::{ Thing, ThingType, Transitivity, Value, ValueType, }, logic::{Explanation, Rule}, + user::User, Options, SessionType, TransactionType, }; -use crate::user::User; #[derive(Debug)] pub(super) enum Request { @@ -108,10 +108,16 @@ pub(super) enum Response { UserCreate, UserDelete, - UserGet { user: Option }, + UserGet { + user: Option, + }, UserPasswordSet, - UsersAll { users: Vec }, - UsersContain { contains: bool }, + UsersAll { + users: Vec, + }, + UsersContain { + contains: bool, + }, } #[derive(Debug)] diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index bb80f010..94ae2003 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -42,8 +42,8 @@ use crate::{ }, error::{ConnectionError, InternalError}, logic::{Explanation, Rule}, + user::User, }; -use crate::user::User; impl TryIntoProto for Request { fn try_into_proto(self) -> Result { @@ -196,7 +196,7 @@ impl TryIntoProto for Request { impl TryIntoProto for Request { fn try_into_proto(self) -> Result { match self { - Self::UserDelete { username} => Ok(user_manager::delete::Req { username }), + Self::UserDelete { username } => Ok(user_manager::delete::Req { username }), other => Err(InternalError::UnexpectedRequestType(format!("{other:?}")).into()), } } diff --git a/src/connection/network/proto/user.rs b/src/connection/network/proto/user.rs index 4f159c41..c65823f8 100644 --- a/src/connection/network/proto/user.rs +++ b/src/connection/network/proto/user.rs @@ -19,17 +19,14 @@ * under the License. */ -use typedb_protocol::{User as UserProto}; +use typedb_protocol::User as UserProto; use super::FromProto; use crate::user::User; impl FromProto for User { fn from_proto(proto: UserProto) -> Self { - let UserProto {username, password_expiry_seconds } = proto; - Self { - username, - password_expiry_seconds, - } + let UserProto { username, password_expiry_seconds } = proto; + Self { username, password_expiry_seconds } } } diff --git a/src/connection/network/stub.rs b/src/connection/network/stub.rs index 408c3896..0ac525e6 100644 --- a/src/connection/network/stub.rs +++ b/src/connection/network/stub.rs @@ -27,7 +27,8 @@ use tokio::sync::mpsc::{unbounded_channel as unbounded_async, UnboundedSender}; use tokio_stream::wrappers::UnboundedReceiverStream; use tonic::{Response, Status, Streaming}; use typedb_protocol::{ - database, database_manager, server_manager, session, transaction, type_db_client::TypeDbClient as GRPC, user, user_manager + database, database_manager, server_manager, session, transaction, type_db_client::TypeDbClient as GRPC, user, + user_manager, }; use super::channel::{CallCredentials, GRPCChannel}; @@ -166,24 +167,15 @@ impl RPCStub { .await } - pub(super) async fn user_create( - &mut self, - req: user_manager::create::Req, - ) -> Result { + pub(super) async fn user_create(&mut self, req: user_manager::create::Req) -> Result { self.single(|this| Box::pin(this.grpc.users_create(req.clone()))).await } - pub(super) async fn user_delete( - &mut self, - req: user_manager::delete::Req, - ) -> Result { + pub(super) async fn user_delete(&mut self, req: user_manager::delete::Req) -> Result { self.single(|this| Box::pin(this.grpc.users_delete(req.clone()))).await } - pub(super) async fn user_get( - &mut self, - req: user_manager::get::Req, - ) -> Result { + pub(super) async fn user_get(&mut self, req: user_manager::get::Req) -> Result { self.single(|this| Box::pin(this.grpc.users_get(req.clone()))).await } @@ -194,10 +186,7 @@ impl RPCStub { self.single(|this| Box::pin(this.grpc.users_password_set(req.clone()))).await } - pub(super) async fn users_all( - &mut self, - req: user_manager::all::Req, - ) -> Result { + pub(super) async fn users_all(&mut self, req: user_manager::all::Req) -> Result { self.single(|this| Box::pin(this.grpc.users_all(req.clone()))).await } diff --git a/src/connection/network/transmitter/rpc.rs b/src/connection/network/transmitter/rpc.rs index fc7f2010..0ac63464 100644 --- a/src/connection/network/transmitter/rpc.rs +++ b/src/connection/network/transmitter/rpc.rs @@ -157,21 +157,13 @@ impl RPCTransmitter { Ok(Response::TransactionOpen { request_sink, response_source }) } - Request::UserCreate { .. } => { - rpc.user_create(request.try_into_proto()?).await.map(Response::from_proto) - } - Request::UserDelete { .. } => { - rpc.user_delete(request.try_into_proto()?).await.map(Response::from_proto) - } - Request::UserGet { .. } => { - rpc.user_get(request.try_into_proto()?).await.map(Response::from_proto) - } + Request::UserCreate { .. } => rpc.user_create(request.try_into_proto()?).await.map(Response::from_proto), + Request::UserDelete { .. } => rpc.user_delete(request.try_into_proto()?).await.map(Response::from_proto), + Request::UserGet { .. } => rpc.user_get(request.try_into_proto()?).await.map(Response::from_proto), Request::UserPasswordSet { .. } => { rpc.user_password_set(request.try_into_proto()?).await.map(Response::from_proto) } - Request::UsersAll => { - rpc.users_all(request.try_into_proto()?).await.map(Response::from_proto) - } + Request::UsersAll => rpc.users_all(request.try_into_proto()?).await.map(Response::from_proto), Request::UsersContain { .. } => { rpc.users_contain(request.try_into_proto()?).await.map(Response::from_proto) } diff --git a/src/user/user.rs b/src/user/user.rs index d3d03cd2..c0006f54 100644 --- a/src/user/user.rs +++ b/src/user/user.rs @@ -30,8 +30,5 @@ impl User { Self { username, password_expiry_seconds } } - pub fn password_update(password_old: String, password_new: String) { - - } - + pub fn password_update(password_old: String, password_new: String) {} } diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index 3e6f3888..c1768cf9 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -21,8 +21,7 @@ use std::future::Future; -use crate::{common::Result, connection::ServerConnection, Connection, User}; -use crate::error::ConnectionError; +use crate::{common::Result, connection::ServerConnection, error::ConnectionError, Connection, User}; #[derive(Clone, Debug)] pub struct UserManager { @@ -47,7 +46,7 @@ impl UserManager { match server_connection.all_users().await { Ok(list) => { return Ok(list); - }, + } Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), } } @@ -60,7 +59,7 @@ impl UserManager { match server_connection.contains_user(username.clone()).await { Ok(contains) => { return Ok(contains); - }, + } Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), } } @@ -77,5 +76,4 @@ impl UserManager { // { // Database::get(name, self.connection.clone()).await?.run_failsafe(&task).await // } - } diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 5a9bc255..da287deb 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -24,10 +24,7 @@ use std::path::PathBuf; use cucumber::{gherkin::Step, given, then, when}; use typedb_client::{Connection, Credential, Options, TransactionType}; -use crate::{ - behaviour::{Context}, - generic_step_impl, -}; +use crate::{behaviour::Context, generic_step_impl}; generic_step_impl! { From 6a2f20af310d8d758856eb7ad9dbdb725701c3a2 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 12 Jun 2023 12:31:09 +0400 Subject: [PATCH 14/84] Context::set_connection() replaces connection in DatabaseManager and UserManager. --- tests/behaviour/connection/user/steps.rs | 24 +++++++++++++----------- tests/behaviour/mod.rs | 7 +++++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index da287deb..d16ca916 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -30,17 +30,19 @@ generic_step_impl! { #[step(expr = "connection opens with authentication: {word}, {word}")] async fn connection_opens_with_authentication(context: &mut Context, login: String, password: String) { - context.connection = Connection::new_encrypted( - &["localhost:11729", "localhost:21729", "localhost:31729"], - Credential::with_tls( - &login.as_str(), - &password.as_str(), - Some(&PathBuf::from( - std::env::var("ROOT_CA") - .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), - )), - ).unwrap(), - ).unwrap(); + context.set_connection( + Connection::new_encrypted( + &["localhost:11729", "localhost:21729", "localhost:31729"], + Credential::with_tls( + &login.as_str(), + &password.as_str(), + Some(&PathBuf::from( + std::env::var("ROOT_CA") + .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), + )), + ).unwrap(), + ).unwrap() + ); } #[step(expr = "users get all")] diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 7191bfec..a14419ff 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -159,9 +159,16 @@ impl Context { self.insert_thing(var_name, attribute.map(Thing::Attribute)); } + pub async fn get_rule(&self, label: String) -> TypeDBResult { self.transaction().logic().get_rule(label).await } + + pub fn set_connection(&mut self, new_connection: Connection) { + self.connection = new_connection; + self.databases = DatabaseManager::new(self.connection.clone()); + self.users = UserManager::new(self.connection.clone()); + } } impl Default for Context { From 8eee27e7690c5441eb66e6719b1d5efc7ae73d82 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 12 Jun 2023 14:02:41 +0400 Subject: [PATCH 15/84] Delete all created users after scenario --- tests/behaviour/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index a14419ff..d827da9c 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -56,6 +56,7 @@ impl Context { const GROUP_COLUMN_NAME: &'static str = "owner"; const VALUE_COLUMN_NAME: &'static str = "value"; const DEFAULT_DATABASE: &'static str = "test"; + const ADMIN_USERNAME: &'static str = "admin"; async fn test(glob: &'static str) -> bool { let default_panic = std::panic::take_hook(); @@ -85,6 +86,8 @@ impl Context { async fn after_scenario(&self) -> TypeDBResult { try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await?; + try_join_all(self.users.all().await.unwrap().into_iter() + .filter(|user| user.username != Context::ADMIN_USERNAME).map(|user| self.users.delete(user.username))).await?; Ok(()) } From 62d28b9a413698f374f14d63b593d8699d84547d Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 12 Jun 2023 14:09:23 +0400 Subject: [PATCH 16/84] Some UserManager functions and BDD steps for them --- src/user/user_manager.rs | 49 +++++++++++++++++++----- tests/behaviour/connection/user/steps.rs | 26 +++++++++++-- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index c1768cf9..cbce8d69 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -66,14 +66,43 @@ impl UserManager { Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? } - // pub fn all() -> Vec { - // - // } - // async fn run_failsafe(&self, name: String, task: F) -> Result - // where - // F: Fn(ServerDatabase, ServerConnection, bool) -> P, - // P: Future>, - // { - // Database::get(name, self.connection.clone()).await?.run_failsafe(&task).await - // } + pub async fn create(&self, username: String, password: String) -> Result<()> { + let mut error_buffer = Vec::with_capacity(self.connection.server_count()); + for server_connection in self.connection.connections() { + match server_connection.create_user(username.clone(), password.clone()).await { + Ok(()) => { + return Ok(()); + } + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + } + } + Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + } + + pub async fn delete(&self, username: String) -> Result<()> { + let mut error_buffer = Vec::with_capacity(self.connection.server_count()); + for server_connection in self.connection.connections() { + match server_connection.delete_user(username.clone()).await { + Ok(()) => { + return Ok(()); + } + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + } + } + Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + } + + pub async fn set_password(&self, username: String, password: String) -> Result<()> { + let mut error_buffer = Vec::with_capacity(self.connection.server_count()); + for server_connection in self.connection.connections() { + match server_connection.set_user_password(username.clone(), password.clone()).await { + Ok(()) => { + return Ok(()); + } + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + } + } + Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + } + } diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index d16ca916..b6b3667c 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -59,11 +59,29 @@ generic_step_impl! { assert!(res.unwrap()); } + #[step(expr = "users not contains: {word}")] + async fn users_not_contains(context: &mut Context, username: String) { + let res = context.users.contains(username).await; + assert!(res.is_ok(), "{:?}", res.err()); + assert!(!res.unwrap()); + } + + #[step(expr = "users create: {word}, {word}")] + async fn users_create(context: &mut Context, username: String, password: String) { + let res = context.users.create(username, password).await; + assert!(res.is_ok(), "{:?}", res.err()); + } + + #[step(expr = "users password set: {word}, {word}")] + async fn users_password_set(context: &mut Context, username: String, password: String) { + let res = context.users.set_password(username, password).await; + assert!(res.is_ok(), "{:?}", res.err()); + } + #[step(expr = "users delete: {word}")] - async fn user_delete(context: &mut Context, user: String) { - // for session_tracker in &mut context.session_trackers { - // session_tracker.open_transaction(type_.transaction_type).await.unwrap(); - // } + async fn user_delete(context: &mut Context, username: String) { + let res = context.users.delete(username).await; + assert!(res.is_ok(), "{:?}", res.err()); } } From 950ba144593231718b45bb4467855d7ac6e1d1e3 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 15 Jun 2023 19:48:19 +0400 Subject: [PATCH 17/84] fmt --- src/user/user_manager.rs | 1 - tests/behaviour/mod.rs | 12 ++++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index cbce8d69..7b9160ef 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -104,5 +104,4 @@ impl UserManager { } Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? } - } diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index d827da9c..07f1d2b4 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -86,8 +86,16 @@ impl Context { async fn after_scenario(&self) -> TypeDBResult { try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await?; - try_join_all(self.users.all().await.unwrap().into_iter() - .filter(|user| user.username != Context::ADMIN_USERNAME).map(|user| self.users.delete(user.username))).await?; + try_join_all( + self.users + .all() + .await + .unwrap() + .into_iter() + .filter(|user| user.username != Context::ADMIN_USERNAME) + .map(|user| self.users.delete(user.username)), + ) + .await?; Ok(()) } From 136191e66fd44882d9d88d86c1ad532a39c8fc07 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 19 Jun 2023 08:45:30 +0300 Subject: [PATCH 18/84] Delete users after scanario only on cluster --- src/connection/connection.rs | 2 +- tests/behaviour/mod.rs | 48 +++++++++++++++++++++++++----------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 9c1e3aa5..aced39d1 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -107,7 +107,7 @@ impl Connection { self.background_runtime.force_close() } - pub(crate) fn server_count(&self) -> usize { + pub fn server_count(&self) -> usize { self.server_connections.len() } diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 07f1d2b4..bc2245b0 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -26,7 +26,7 @@ mod session_tracker; mod typeql; mod util; -use std::collections::HashMap; +use std::{collections::HashMap, path::PathBuf}; use cucumber::{StatsWriter, World}; use futures::future::try_join_all; @@ -34,7 +34,7 @@ use typedb_client::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, concept::{Attribute, AttributeType, Entity, EntityType, Relation, RelationType, Thing}, logic::Rule, - Connection, Database, DatabaseManager, Result as TypeDBResult, Transaction, UserManager, + Connection, Credential, Database, DatabaseManager, Result as TypeDBResult, Transaction, UserManager, }; use self::session_tracker::SessionTracker; @@ -57,6 +57,7 @@ impl Context { const VALUE_COLUMN_NAME: &'static str = "value"; const DEFAULT_DATABASE: &'static str = "test"; const ADMIN_USERNAME: &'static str = "admin"; + const ADMIN_PASSWORD: &'static str = "password"; async fn test(glob: &'static str) -> bool { let default_panic = std::panic::take_hook(); @@ -84,18 +85,37 @@ impl Context { t == "ignore" || t == "ignore-typedb" || t == "ignore-client-rust" || t == "ignore-typedb-client-rust" } - async fn after_scenario(&self) -> TypeDBResult { - try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await?; - try_join_all( - self.users - .all() - .await - .unwrap() - .into_iter() - .filter(|user| user.username != Context::ADMIN_USERNAME) - .map(|user| self.users.delete(user.username)), - ) - .await?; + async fn after_scenario(&mut self) -> TypeDBResult { + if self.connection.server_count() == 1 { + try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await?; + } else { + self.set_connection( + Connection::new_encrypted( + &["localhost:11729", "localhost:21729", "localhost:31729"], + Credential::with_tls( + &Context::ADMIN_USERNAME, + &Context::ADMIN_PASSWORD, + Some(&PathBuf::from( + std::env::var("ROOT_CA") + .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), + )), + ) + .unwrap(), + ) + .unwrap(), + ); + try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await?; + try_join_all( + self.users + .all() + .await + .unwrap() + .into_iter() + .filter(|user| user.username != Context::ADMIN_USERNAME) + .map(|user| self.users.delete(user.username)), + ) + .await?; + } Ok(()) } From a490fe883d9cfef0688bca2a488a512eef45b265 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 19 Jun 2023 08:46:51 +0300 Subject: [PATCH 19/84] Move auth connection step to connections --- tests/behaviour/connection/steps.rs | 25 ++++++++++++++++++++++++ tests/behaviour/connection/user/steps.rs | 17 ---------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index 3f2c2d8a..d9a72aca 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -19,7 +19,10 @@ * under the License. */ +use std::path::PathBuf; + use cucumber::{given, then, when}; +use typedb_client::{Connection, Credential, Options, TransactionType}; use crate::{behaviour::Context, generic_step_impl}; @@ -30,6 +33,23 @@ generic_step_impl! { #[step("connection opens with default authentication")] async fn connection_opens_with_default_authentication(_: &mut Context) {} + #[step(expr = "connection opens with authentication: {word}, {word}")] + async fn connection_opens_with_authentication(context: &mut Context, login: String, password: String) { + context.set_connection( + Connection::new_encrypted( + &["localhost:11729", "localhost:21729", "localhost:31729"], + Credential::with_tls( + &login.as_str(), + &password.as_str(), + Some(&PathBuf::from( + std::env::var("ROOT_CA") + .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), + )), + ).unwrap(), + ).unwrap() + ); + } + #[step("connection has been opened")] async fn connection_has_been_opened(_: &mut Context) {} @@ -37,4 +57,9 @@ generic_step_impl! { async fn connection_does_not_have_any_database(context: &mut Context) { assert!(context.databases.all().await.unwrap().is_empty()); } + + #[step("connection closes")] + async fn connection_closes(context: &mut Context) { + assert!(context.connection.clone().force_close().is_ok()); + } } diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index b6b3667c..93d6af1e 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -28,23 +28,6 @@ use crate::{behaviour::Context, generic_step_impl}; generic_step_impl! { - #[step(expr = "connection opens with authentication: {word}, {word}")] - async fn connection_opens_with_authentication(context: &mut Context, login: String, password: String) { - context.set_connection( - Connection::new_encrypted( - &["localhost:11729", "localhost:21729", "localhost:31729"], - Credential::with_tls( - &login.as_str(), - &password.as_str(), - Some(&PathBuf::from( - std::env::var("ROOT_CA") - .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), - )), - ).unwrap(), - ).unwrap() - ); - } - #[step(expr = "users get all")] async fn users_get_all(context: &mut Context) { let res = context.users.all().await; From 59a100a831c7f8f22a4c1131526e9fe10b284b17 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 19 Jun 2023 12:40:24 +0300 Subject: [PATCH 20/84] Simplification --- src/connection/network/proto/message.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 94ae2003..02a4bdba 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -416,7 +416,7 @@ impl FromProto for Response { impl FromProto for Response { fn from_proto(proto: user_manager::get::Res) -> Self { - Self::UserGet { user: proto.user.and_then(|user| Some(User::from_proto(user))) } + Self::UserGet { user: proto.user.map(User::from_proto) } } } From 3ff7f33ff8e07c4e63cb64f7cd45cb072a501ffb Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 19 Jun 2023 13:43:05 +0300 Subject: [PATCH 21/84] debug.feature --- tests/BUILD | 1 + tests/behaviour/connection/user/mod.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/BUILD b/tests/BUILD index d3d07077..4b643b45 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -72,6 +72,7 @@ rust_test( "@vaticle_typedb_behaviour//typeql/reasoner:type-hierarchy.feature", "@vaticle_typedb_behaviour//typeql/reasoner:value-predicate.feature", "@vaticle_typedb_behaviour//typeql/reasoner:variable-roles.feature", + "behaviour/debug.feature", ], ) diff --git a/tests/behaviour/connection/user/mod.rs b/tests/behaviour/connection/user/mod.rs index 3c0e54d5..5015e350 100644 --- a/tests/behaviour/connection/user/mod.rs +++ b/tests/behaviour/connection/user/mod.rs @@ -32,4 +32,5 @@ async fn test() { // @vaticle_typedb_behaviour is stored in a directory that is a sibling to // the working directory. assert!(Context::test("../vaticle_typedb_behaviour/connection/user.feature").await); + // assert!(Context::test("tests/behaviour/debug.feature").await); } From 4bdd6b7a136141ddd2441426ef7eee7771971812 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 21 Jun 2023 11:14:08 +0300 Subject: [PATCH 22/84] UserManager::get() --- src/user/user_manager.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index 7b9160ef..aa8a0c29 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -92,6 +92,19 @@ impl UserManager { Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? } + pub async fn get(&self, username: String) -> Result { + let mut error_buffer = Vec::with_capacity(self.connection.server_count()); + for server_connection in self.connection.connections() { + match server_connection.get_user(username.clone()).await { + Ok(user) => { + return Ok(user.unwrap()); + } + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + } + } + Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + } + pub async fn set_password(&self, username: String, password: String) -> Result<()> { let mut error_buffer = Vec::with_capacity(self.connection.server_count()); for server_connection in self.connection.connections() { From 3ca0453beb192e3268568c9628715a39c5720d67 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 21 Jun 2023 11:14:58 +0300 Subject: [PATCH 23/84] Remove User::new() --- src/user/user.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/user/user.rs b/src/user/user.rs index c0006f54..59462620 100644 --- a/src/user/user.rs +++ b/src/user/user.rs @@ -26,9 +26,5 @@ pub struct User { } impl User { - pub fn new(username: String, password_expiry_seconds: Option) -> Self { - Self { username, password_expiry_seconds } - } - pub fn password_update(password_old: String, password_new: String) {} } From 6cae861aaeb1319119d8414d5b1c865a6c16b56e Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 21 Jun 2023 13:20:32 +0300 Subject: [PATCH 24/84] todos --- src/user/user.rs | 5 ++++- tests/behaviour/connection/user/steps.rs | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/user/user.rs b/src/user/user.rs index 59462620..706ad269 100644 --- a/src/user/user.rs +++ b/src/user/user.rs @@ -26,5 +26,8 @@ pub struct User { } impl User { - pub fn password_update(password_old: String, password_new: String) {} + pub fn password_update(password_old: String, password_new: String) { + + todo!() + } } diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 93d6af1e..1ce457d5 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -67,4 +67,10 @@ generic_step_impl! { assert!(res.is_ok(), "{:?}", res.err()); } + #[step(expr = "user expiry-seconds")] + async fn user_expiry_seconds(context: &mut Context) { + context.connection + // TODO: get current user (not implemented yet) and return his password_expiry_seconds + } + } From 0b23c8c12a1ca9a3331898c111f2ee45669f0cff Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 21 Jun 2023 14:23:55 +0300 Subject: [PATCH 25/84] Store username in Connection --- src/connection/connection.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index aced39d1..8e73609d 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -55,6 +55,7 @@ use crate::{ pub struct Connection { server_connections: HashMap, background_runtime: Arc, + pub username: Option, } impl Connection { @@ -62,7 +63,7 @@ impl Connection { let address: Address = address.as_ref().parse()?; let background_runtime = Arc::new(BackgroundRuntime::new()?); let server_connection = ServerConnection::new_plaintext(background_runtime.clone(), address.clone())?; - Ok(Self { server_connections: [(address, server_connection)].into(), background_runtime }) + Ok(Self { server_connections: [(address, server_connection)].into(), background_runtime, username: None }) } pub fn new_encrypted + Sync>(init_addresses: &[T], credential: Credential) -> Result { @@ -78,7 +79,7 @@ impl Connection { server_connections.insert(address, server_connection); } - Ok(Self { server_connections, background_runtime }) + Ok(Self { server_connections, background_runtime, username: Some(credential.username().to_string()) }) } fn fetch_current_addresses( From 7be148ef518f65f2bf18b3e031adb63a36973f9d Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 21 Jun 2023 14:24:24 +0300 Subject: [PATCH 26/84] "user expiry-seconds" step --- tests/behaviour/connection/user/steps.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 1ce457d5..11948c38 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -22,7 +22,7 @@ use std::path::PathBuf; use cucumber::{gherkin::Step, given, then, when}; -use typedb_client::{Connection, Credential, Options, TransactionType}; +use typedb_client::{Connection, Credential, Options, Result as TypeDBResult, TransactionType}; use crate::{behaviour::Context, generic_step_impl}; @@ -68,9 +68,10 @@ generic_step_impl! { } #[step(expr = "user expiry-seconds")] - async fn user_expiry_seconds(context: &mut Context) { - context.connection - // TODO: get current user (not implemented yet) and return his password_expiry_seconds + async fn user_expiry_seconds(context: &mut Context) -> TypeDBResult { + assert!(context.connection.username.is_some()); + assert!(context.users.get(context.connection.username.clone().unwrap()).await?.password_expiry_seconds.is_some()); + Ok(()) } } From ca66d13c1dbf7e6166f0b3ca049160dcf211bcb3 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 22 Jun 2023 14:44:37 +0300 Subject: [PATCH 27/84] Renaming --- src/connection/connection.rs | 16 ++++++------ src/connection/message.rs | 20 +++++++-------- src/connection/network/proto/message.rs | 16 ++++++------ src/connection/network/stub.rs | 30 +++++++++++------------ src/connection/network/transmitter/rpc.rs | 12 ++++----- 5 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 8e73609d..b153b5ab 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -321,29 +321,29 @@ impl ServerConnection { } pub(crate) async fn create_user(&self, username: String, password: String) -> Result<()> { - match self.request_async(Request::UserCreate { username, password }).await? { - Response::UserCreate => Ok(()), + match self.request_async(Request::UsersCreate { username, password }).await? { + Response::UsersCreate => Ok(()), other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), } } pub(crate) async fn delete_user(&self, username: String) -> Result<()> { - match self.request_async(Request::UserDelete { username }).await? { - Response::UserDelete => Ok(()), + match self.request_async(Request::UsersDelete { username }).await? { + Response::UsersDelete => Ok(()), other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), } } pub(crate) async fn get_user(&self, username: String) -> Result> { - match self.request_async(Request::UserGet { username }).await? { - Response::UserGet { user } => Ok(user), + match self.request_async(Request::UsersGet { username }).await? { + Response::UsersGet { user } => Ok(user), other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), } } pub(crate) async fn set_user_password(&self, username: String, password: String) -> Result<()> { - match self.request_async(Request::UserPasswordSet { username, password }).await? { - Response::UserPasswordSet => Ok(()), + match self.request_async(Request::UsersPasswordSet { username, password }).await? { + Response::UsersPasswordSet => Ok(()), other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), } } diff --git a/src/connection/message.rs b/src/connection/message.rs index e1b5e80a..4df427c3 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -58,12 +58,12 @@ pub(super) enum Request { Transaction(TransactionRequest), - UserCreate { username: String, password: String }, - UserDelete { username: String }, - UserGet { username: String }, - UserPasswordSet { username: String, password: String }, UsersAll, UsersContain { username: String }, + UsersCreate { username: String, password: String }, + UsersDelete { username: String }, + UsersGet { username: String }, + UsersPasswordSet { username: String, password: String }, } #[derive(Debug)] @@ -106,18 +106,18 @@ pub(super) enum Response { response_source: Streaming, }, - UserCreate, - UserDelete, - UserGet { - user: Option, - }, - UserPasswordSet, UsersAll { users: Vec, }, UsersContain { contains: bool, }, + UsersCreate, + UsersDelete, + UsersGet { + user: Option, + }, + UsersPasswordSet, } #[derive(Debug)] diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 02a4bdba..64c42660 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -187,7 +187,7 @@ impl TryIntoProto for Request { impl TryIntoProto for Request { fn try_into_proto(self) -> Result { match self { - Self::UserCreate { username, password } => Ok(user_manager::create::Req { username, password }), + Self::UsersCreate { username, password } => Ok(user_manager::create::Req { username, password }), other => Err(InternalError::UnexpectedRequestType(format!("{other:?}")).into()), } } @@ -196,7 +196,7 @@ impl TryIntoProto for Request { impl TryIntoProto for Request { fn try_into_proto(self) -> Result { match self { - Self::UserDelete { username } => Ok(user_manager::delete::Req { username }), + Self::UsersDelete { username } => Ok(user_manager::delete::Req { username }), other => Err(InternalError::UnexpectedRequestType(format!("{other:?}")).into()), } } @@ -205,7 +205,7 @@ impl TryIntoProto for Request { impl TryIntoProto for Request { fn try_into_proto(self) -> Result { match self { - Self::UserGet { username } => Ok(user_manager::get::Req { username }), + Self::UsersGet { username } => Ok(user_manager::get::Req { username }), other => Err(InternalError::UnexpectedRequestType(format!("{other:?}")).into()), } } @@ -214,7 +214,7 @@ impl TryIntoProto for Request { impl TryIntoProto for Request { fn try_into_proto(self) -> Result { match self { - Self::UserPasswordSet { username, password } => Ok(user_manager::password_set::Req { username, password }), + Self::UsersPasswordSet { username, password } => Ok(user_manager::password_set::Req { username, password }), other => Err(InternalError::UnexpectedRequestType(format!("{other:?}")).into()), } } @@ -404,25 +404,25 @@ impl FromProto for Response { impl FromProto for Response { fn from_proto(_: user_manager::create::Res) -> Self { - Self::UserCreate {} + Self::UsersCreate {} } } impl FromProto for Response { fn from_proto(_: user_manager::delete::Res) -> Self { - Self::UserDelete {} + Self::UsersDelete {} } } impl FromProto for Response { fn from_proto(proto: user_manager::get::Res) -> Self { - Self::UserGet { user: proto.user.map(User::from_proto) } + Self::UsersGet { user: proto.user.map(User::from_proto) } } } impl FromProto for Response { fn from_proto(_: user_manager::password_set::Res) -> Self { - Self::UserPasswordSet {} + Self::UsersPasswordSet {} } } diff --git a/src/connection/network/stub.rs b/src/connection/network/stub.rs index 0ac525e6..662e9f52 100644 --- a/src/connection/network/stub.rs +++ b/src/connection/network/stub.rs @@ -167,36 +167,36 @@ impl RPCStub { .await } - pub(super) async fn user_create(&mut self, req: user_manager::create::Req) -> Result { + pub(super) async fn users_all(&mut self, req: user_manager::all::Req) -> Result { + self.single(|this| Box::pin(this.grpc.users_all(req.clone()))).await + } + + pub(super) async fn users_contain( + &mut self, + req: user_manager::contains::Req, + ) -> Result { + self.single(|this| Box::pin(this.grpc.users_contains(req.clone()))).await + } + + pub(super) async fn users_create(&mut self, req: user_manager::create::Req) -> Result { self.single(|this| Box::pin(this.grpc.users_create(req.clone()))).await } - pub(super) async fn user_delete(&mut self, req: user_manager::delete::Req) -> Result { + pub(super) async fn users_delete(&mut self, req: user_manager::delete::Req) -> Result { self.single(|this| Box::pin(this.grpc.users_delete(req.clone()))).await } - pub(super) async fn user_get(&mut self, req: user_manager::get::Req) -> Result { + pub(super) async fn users_get(&mut self, req: user_manager::get::Req) -> Result { self.single(|this| Box::pin(this.grpc.users_get(req.clone()))).await } - pub(super) async fn user_password_set( + pub(super) async fn users_password_set( &mut self, req: user_manager::password_set::Req, ) -> Result { self.single(|this| Box::pin(this.grpc.users_password_set(req.clone()))).await } - pub(super) async fn users_all(&mut self, req: user_manager::all::Req) -> Result { - self.single(|this| Box::pin(this.grpc.users_all(req.clone()))).await - } - - pub(super) async fn users_contain( - &mut self, - req: user_manager::contains::Req, - ) -> Result { - self.single(|this| Box::pin(this.grpc.users_contains(req.clone()))).await - } - async fn single(&mut self, call: F) -> Result where for<'a> F: Fn(&'a mut Self) -> BoxFuture<'a, TonicResult> + Send + Sync, diff --git a/src/connection/network/transmitter/rpc.rs b/src/connection/network/transmitter/rpc.rs index 0ac63464..f4e61874 100644 --- a/src/connection/network/transmitter/rpc.rs +++ b/src/connection/network/transmitter/rpc.rs @@ -157,16 +157,16 @@ impl RPCTransmitter { Ok(Response::TransactionOpen { request_sink, response_source }) } - Request::UserCreate { .. } => rpc.user_create(request.try_into_proto()?).await.map(Response::from_proto), - Request::UserDelete { .. } => rpc.user_delete(request.try_into_proto()?).await.map(Response::from_proto), - Request::UserGet { .. } => rpc.user_get(request.try_into_proto()?).await.map(Response::from_proto), - Request::UserPasswordSet { .. } => { - rpc.user_password_set(request.try_into_proto()?).await.map(Response::from_proto) - } Request::UsersAll => rpc.users_all(request.try_into_proto()?).await.map(Response::from_proto), Request::UsersContain { .. } => { rpc.users_contain(request.try_into_proto()?).await.map(Response::from_proto) } + Request::UsersCreate { .. } => rpc.users_create(request.try_into_proto()?).await.map(Response::from_proto), + Request::UsersDelete { .. } => rpc.users_delete(request.try_into_proto()?).await.map(Response::from_proto), + Request::UsersGet { .. } => rpc.users_get(request.try_into_proto()?).await.map(Response::from_proto), + Request::UsersPasswordSet { .. } => { + rpc.users_password_set(request.try_into_proto()?).await.map(Response::from_proto) + } } } } From aba25a2785cd4f9906b99281cc4d6309fdbf5f40 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 22 Jun 2023 15:22:08 +0300 Subject: [PATCH 28/84] User::password_update() --- src/connection/connection.rs | 12 ++++++++++++ src/connection/message.rs | 4 ++++ src/connection/network/proto/message.rs | 19 +++++++++++++++++- src/connection/network/stub.rs | 7 +++++++ src/connection/network/transmitter/rpc.rs | 4 ++++ src/user/user.rs | 24 ++++++++++++++++++++--- src/user/user_manager.rs | 11 +---------- 7 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index b153b5ab..a6d35b09 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -347,6 +347,18 @@ impl ServerConnection { other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), } } + + pub(crate) async fn update_user_password( + &self, + username: String, + password_old: String, + password_new: String, + ) -> Result<()> { + match self.request_async(Request::UserPasswordUpdate { username, password_old, password_new }).await? { + Response::UserPasswordUpdate => Ok(()), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } } impl fmt::Debug for ServerConnection { diff --git a/src/connection/message.rs b/src/connection/message.rs index 4df427c3..8d95ccf8 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -64,6 +64,8 @@ pub(super) enum Request { UsersDelete { username: String }, UsersGet { username: String }, UsersPasswordSet { username: String, password: String }, + + UserPasswordUpdate { username: String, password_old: String, password_new: String }, } #[derive(Debug)] @@ -118,6 +120,8 @@ pub(super) enum Response { user: Option, }, UsersPasswordSet, + + UserPasswordUpdate, } #[derive(Debug)] diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 64c42660..f2070855 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -24,7 +24,7 @@ use std::time::Duration; use itertools::Itertools; use typedb_protocol::{ attribute, attribute_type, concept_manager, database, database_manager, entity_type, logic_manager, query_manager, - r#type, relation, relation_type, role_type, rule, server_manager, session, thing, thing_type, transaction, user_manager, + r#type, relation, relation_type, role_type, rule, server_manager, session, thing, thing_type, transaction, user, user_manager, }; use super::{FromProto, IntoProto, TryFromProto, TryIntoProto}; @@ -220,6 +220,17 @@ impl TryIntoProto for Request { } } +impl TryIntoProto for Request { + fn try_into_proto(self) -> Result { + match self { + Self::UserPasswordUpdate { username, password_old, password_new } => { + Ok(user::password_update::Req { username, password_old, password_new }) + } + other => Err(InternalError::UnexpectedRequestType(format!("{other:?}")).into()), + } + } +} + impl TryFromProto for Response { fn try_from_proto(proto: server_manager::all::Res) -> Result { let servers = proto.servers.into_iter().map(|server| server.address.parse()).try_collect()?; @@ -426,6 +437,12 @@ impl FromProto for Response { } } +impl FromProto for Response { + fn from_proto(_: user::password_update::Res) -> Self { + Self::UserPasswordUpdate {} + } +} + impl IntoProto for QueryRequest { fn into_proto(self) -> query_manager::Req { let (req, options) = match self { diff --git a/src/connection/network/stub.rs b/src/connection/network/stub.rs index 662e9f52..3589cef8 100644 --- a/src/connection/network/stub.rs +++ b/src/connection/network/stub.rs @@ -197,6 +197,13 @@ impl RPCStub { self.single(|this| Box::pin(this.grpc.users_password_set(req.clone()))).await } + pub(super) async fn user_password_update( + &mut self, + req: user::password_update::Req, + ) -> Result { + self.single(|this| Box::pin(this.grpc.user_password_update(req.clone()))).await + } + async fn single(&mut self, call: F) -> Result where for<'a> F: Fn(&'a mut Self) -> BoxFuture<'a, TonicResult> + Send + Sync, diff --git a/src/connection/network/transmitter/rpc.rs b/src/connection/network/transmitter/rpc.rs index f4e61874..cf48fcbc 100644 --- a/src/connection/network/transmitter/rpc.rs +++ b/src/connection/network/transmitter/rpc.rs @@ -167,6 +167,10 @@ impl RPCTransmitter { Request::UsersPasswordSet { .. } => { rpc.users_password_set(request.try_into_proto()?).await.map(Response::from_proto) } + + Request::UserPasswordUpdate { .. } => { + rpc.user_password_update(request.try_into_proto()?).await.map(Response::from_proto) + } } } } diff --git a/src/user/user.rs b/src/user/user.rs index 706ad269..1231af7f 100644 --- a/src/user/user.rs +++ b/src/user/user.rs @@ -19,6 +19,8 @@ * under the License. */ +use crate::{common::Result, error::ConnectionError, Connection}; + #[derive(Clone, Debug)] pub struct User { pub username: String, @@ -26,8 +28,24 @@ pub struct User { } impl User { - pub fn password_update(password_old: String, password_new: String) { - - todo!() + pub async fn password_update( + &self, + connection: &Connection, + password_old: String, + password_new: String, + ) -> Result<()> { + let mut error_buffer = Vec::with_capacity(connection.server_count()); + for server_connection in connection.connections() { + match server_connection + .update_user_password(self.username.clone(), password_old.clone(), password_new.clone()) + .await + { + Ok(()) => { + return Ok(()); + } + Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), + } + } + Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? } } diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index aa8a0c29..5aef6fa3 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -19,9 +19,7 @@ * under the License. */ -use std::future::Future; - -use crate::{common::Result, connection::ServerConnection, error::ConnectionError, Connection, User}; +use crate::{common::Result, error::ConnectionError, Connection, User}; #[derive(Clone, Debug)] pub struct UserManager { @@ -33,13 +31,6 @@ impl UserManager { Self { connection } } - // pub async fn contains(&self, username: String) -> Result { - // self.run_failsafe(name.into(), move |database, server_connection, _| async move { - // server_connection.database_exists(database.name().to_owned()).await - // }).await - // self.connection. - // } - pub async fn all(&self) -> Result> { let mut error_buffer = Vec::with_capacity(self.connection.server_count()); for server_connection in self.connection.connections() { From 844c0bd109b163bd90ff565486223c37789a61ec Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 22 Jun 2023 15:54:15 +0300 Subject: [PATCH 29/84] "get connected user" --- tests/BUILD | 2 +- tests/behaviour/connection/user/steps.rs | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/tests/BUILD b/tests/BUILD index 4b643b45..cf2c236c 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -72,7 +72,7 @@ rust_test( "@vaticle_typedb_behaviour//typeql/reasoner:type-hierarchy.feature", "@vaticle_typedb_behaviour//typeql/reasoner:value-predicate.feature", "@vaticle_typedb_behaviour//typeql/reasoner:variable-roles.feature", - "behaviour/debug.feature", +# "behaviour/debug.feature", ], ) diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 11948c38..b8f87117 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -24,7 +24,7 @@ use std::path::PathBuf; use cucumber::{gherkin::Step, given, then, when}; use typedb_client::{Connection, Credential, Options, Result as TypeDBResult, TransactionType}; -use crate::{behaviour::Context, generic_step_impl}; +use crate::{assert_err, behaviour::Context, generic_step_impl}; generic_step_impl! { @@ -50,9 +50,13 @@ generic_step_impl! { } #[step(expr = "users create: {word}, {word}")] - async fn users_create(context: &mut Context, username: String, password: String) { - let res = context.users.create(username, password).await; - assert!(res.is_ok(), "{:?}", res.err()); + async fn users_create(context: &mut Context, username: String, password: String) -> TypeDBResult { + context.users.create(username, password).await + } + + #[step(expr = "users create: {word}, {word}; throws exception")] + async fn users_create_throws(context: &mut Context, username: String, password: String) { + assert_err!(users_create(context, username, password).await); } #[step(expr = "users password set: {word}, {word}")] @@ -61,6 +65,13 @@ generic_step_impl! { assert!(res.is_ok(), "{:?}", res.err()); } + // #[step(expr = "user password update: {word}, {word}")] + // async fn user_password_update(context: &mut Context, password_old: String, password_new: String) -> TypeDBResult { + // assert!(context.connection.username.is_some()); + // context.users.get(context.connection.username.clone().unwrap()).await?.password_update(context.connection, password_old, password_new).await?); + // assert!(res.is_ok(), "{:?}", res.err()); + // } + #[step(expr = "users delete: {word}")] async fn user_delete(context: &mut Context, username: String) { let res = context.users.delete(username).await; @@ -74,4 +85,9 @@ generic_step_impl! { Ok(()) } + #[step(expr = "get connected user")] + async fn get_connected_user(context: &mut Context) { + assert!(context.connection.username.is_some()); + } + } From fc9b770a3f70cea1e87faed5450517286fbd3736 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 22 Jun 2023 15:57:05 +0300 Subject: [PATCH 30/84] wip --- tests/behaviour/connection/user/steps.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index b8f87117..eba53497 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -91,3 +91,12 @@ generic_step_impl! { } } + +// Then user password update: new-password, bad-password; throws exception +// Then users get all; throws exception +// Then users get user: admin; throws exception +// Then users create: user3, password; throws exception +// Then users contains: admin; throws exception +// Then users delete: admin; throws exception +// Then users delete: user2; throws exception +// Then users password set: user2, new-password; throws exception \ No newline at end of file From 30fb4a59f4d2e874b67381e2fd5a854a01facb13 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 26 Jun 2023 12:56:14 +0100 Subject: [PATCH 31/84] Fix rebase --- src/connection/message.rs | 4 --- src/connection/network/proto/message.rs | 43 ++++++++++++------------ tests/behaviour/connection/user/steps.rs | 2 +- tests/behaviour/mod.rs | 1 - 4 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/connection/message.rs b/src/connection/message.rs index 8d95ccf8..be4654b7 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -514,7 +514,3 @@ pub(super) enum LogicResponse { GetRule { rule: Rule }, GetRules { rules: Vec }, } - -pub(super) enum UserRequest { - UserDelete { thing: Thing }, -} diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index f2070855..83f00936 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -24,7 +24,8 @@ use std::time::Duration; use itertools::Itertools; use typedb_protocol::{ attribute, attribute_type, concept_manager, database, database_manager, entity_type, logic_manager, query_manager, - r#type, relation, relation_type, role_type, rule, server_manager, session, thing, thing_type, transaction, user, user_manager, + r#type, relation, relation_type, role_type, rule, server_manager, session, thing, thing_type, transaction, user, + user_manager, }; use super::{FromProto, IntoProto, TryFromProto, TryIntoProto}; @@ -1267,26 +1268,26 @@ impl IntoProto for LogicRequest { } } -impl IntoProto for UserRequest { - fn into_proto(self) -> logic_manager::Req { - let req = match self { - Self::PutRule { label, when, then } => { - logic_manager::req::Req::PutRuleReq(logic_manager::put_rule::Req { - label, - when: when.to_string(), - then: then.to_string(), - }) - } - Self::GetRule { label } => { - logic_manager::req::Req::GetRuleReq(logic_manager::get_rule::Req { label }) - } - Self::GetRules => { - logic_manager::req::Req::GetRulesReq(logic_manager::get_rules::Req {}) - } - }; - logic_manager::Req { req: Some(req) } - } -} +// impl IntoProto for UserRequest { +// fn into_proto(self) -> logic_manager::Req { +// let req = match self { +// Self::PutRule { label, when, then } => { +// logic_manager::req::Req::PutRuleReq(logic_manager::put_rule::Req { +// label, +// when: when.to_string(), +// then: then.to_string(), +// }) +// } +// Self::GetRule { label } => { +// logic_manager::req::Req::GetRuleReq(logic_manager::get_rule::Req { label }) +// } +// Self::GetRules => { +// logic_manager::req::Req::GetRulesReq(logic_manager::get_rules::Req {}) +// } +// }; +// logic_manager::Req { req: Some(req) } +// } +// } impl TryFromProto for LogicResponse { fn try_from_proto(proto: logic_manager::Res) -> Result { diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index eba53497..4763b454 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -99,4 +99,4 @@ generic_step_impl! { // Then users contains: admin; throws exception // Then users delete: admin; throws exception // Then users delete: user2; throws exception -// Then users password set: user2, new-password; throws exception \ No newline at end of file +// Then users password set: user2, new-password; throws exception diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index bc2245b0..bf449107 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -190,7 +190,6 @@ impl Context { self.insert_thing(var_name, attribute.map(Thing::Attribute)); } - pub async fn get_rule(&self, label: String) -> TypeDBResult { self.transaction().logic().get_rule(label).await } From 7e15e0306363c71064b1bb3eb6c0d9e2e917f8eb Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 26 Jun 2023 16:29:02 +0100 Subject: [PATCH 32/84] Connect to at least one cluster node --- src/connection/connection.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index a6d35b09..b888c2de 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -72,13 +72,23 @@ impl Connection { let init_addresses = init_addresses.iter().map(|addr| addr.as_ref().parse()).try_collect()?; let addresses = Self::fetch_current_addresses(background_runtime.clone(), init_addresses, credential.clone())?; - let mut server_connections = HashMap::with_capacity(addresses.len()); + let mut server_connections = HashMap::new(); + let mut error_buffer = Vec::new(); for address in addresses { let server_connection = - ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone())?; - server_connections.insert(address, server_connection); + ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone()); + match server_connection { + Ok(server_connection) => { + server_connections.insert(address, server_connection); + }, + Err(err) => { + error_buffer.push(format!("- {}: {}", address, err)); + }, + } + } + if server_connections.is_empty() { + Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? } - Ok(Self { server_connections, background_runtime, username: Some(credential.username().to_string()) }) } From cf534b686b3cb4b05550a264d399f6876fffd311 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 26 Jun 2023 16:30:18 +0100 Subject: [PATCH 33/84] User integration tests --- tests/integration/mod.rs | 1 + tests/integration/user.rs | 91 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 tests/integration/user.rs diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs index 5a87fc7d..dc60ecb2 100644 --- a/tests/integration/mod.rs +++ b/tests/integration/mod.rs @@ -23,3 +23,4 @@ mod common; mod logic; mod queries; mod runtimes; +mod user; diff --git a/tests/integration/user.rs b/tests/integration/user.rs new file mode 100644 index 00000000..588d4ec1 --- /dev/null +++ b/tests/integration/user.rs @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2022 Vaticle + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +use std::path::PathBuf; + +use futures::future::try_join_all; +use serial_test::serial; +use typedb_client::{Connection, Credential, Result as TypeDBResult, UserManager}; + +use super::common; +use crate::test_for_each_arg; + +test_for_each_arg! { + { + cluster => common::new_cluster_connection().unwrap(), + } + + async fn create_and_delete_user(connection: Connection) -> TypeDBResult { + let users = UserManager::new(connection); + users.create(String::from("user"), String::from("password")).await?; + assert_eq!(2, users.all().await?.len()); + users.delete(String::from("user")).await?; + assert_eq!(1, users.all().await?.len()); + Ok(()) + } + + async fn create_users_and_purge(connection: Connection) -> TypeDBResult { + let users = UserManager::new(connection); + users.create(String::from("user"), String::from("password")).await?; + assert_eq!(2, users.all().await?.len()); + users.create(String::from("user2"), String::from("password2")).await?; + assert_eq!(3, users.all().await?.len()); + + try_join_all( + users + .all() + .await + .unwrap() + .into_iter() + .filter(|user| dbg!(&user.username) != "admin") + .map(|user| users.delete(dbg!(user.username))), + ) + .await?; + assert_eq!(1, users.all().await?.len()); + Ok(()) + } + + async fn create_users_reconnect_and_purge(connection: Connection) -> TypeDBResult { + assert_eq!(3, connection.server_count()); + + let users = UserManager::new(connection); + users.create(String::from("user"), String::from("password")).await?; + assert_eq!(2, users.all().await?.len()); + users.create(String::from("user2"), String::from("password2")).await?; + assert_eq!(3, users.all().await?.len()); + + let connection = common::new_cluster_connection().unwrap(); + let users = UserManager::new(connection); + assert_eq!(3, users.all().await?.len()); + try_join_all( + users + .all() + .await + .unwrap() + .into_iter() + .filter(|user| dbg!(&user.username) != "admin") + .map(|user| users.delete(dbg!(user.username))), + ) + .await?; + assert_eq!(1, users.all().await?.len()); + Ok(()) + } +} \ No newline at end of file From c430bf0549f3dcf78e0c11f91a51f26153f07e80 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 26 Jun 2023 16:51:06 +0100 Subject: [PATCH 34/84] Steps with "throws" --- tests/behaviour/connection/user/steps.rs | 76 ++++++++++++++++-------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 4763b454..ef945853 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -29,24 +29,49 @@ use crate::{assert_err, behaviour::Context, generic_step_impl}; generic_step_impl! { #[step(expr = "users get all")] - async fn users_get_all(context: &mut Context) { - let res = context.users.all().await; - assert!(res.is_ok()); - panic!("{res:?}"); + async fn users_get_all(context: &mut Context) -> TypeDBResult { + context.users.all().await?; + Ok(()) + } + + #[step(expr = "users get all; throws exception")] + async fn users_get_all_throws(context: &mut Context) { + assert_err!(users_get_all(context).await); + } + + #[step(expr = "users get user: {word}")] + async fn users_get_user(context: &mut Context, username: String) -> TypeDBResult { + context.users.get(username).await?; + Ok(()) + } + + #[step(expr = "users get user: {word}; throws exception")] + async fn users_get_user_throws(context: &mut Context, username: String) { + assert_err!(users_get_user(context, username).await); } #[step(expr = "users contains: {word}")] - async fn users_contains(context: &mut Context, username: String) { - let res = context.users.contains(username).await; - assert!(res.is_ok(), "{:?}", res.err()); - assert!(res.unwrap()); + async fn users_contains(context: &mut Context, username: String) -> TypeDBResult { + let contains = context.users.contains(username).await?; + assert!(contains); + Ok(()) + } + + #[step(expr = "users contains: {word}; throws exception")] + async fn users_contains_throws(context: &mut Context, username: String) { + assert_err!(users_contains(context, username).await); } #[step(expr = "users not contains: {word}")] - async fn users_not_contains(context: &mut Context, username: String) { - let res = context.users.contains(username).await; - assert!(res.is_ok(), "{:?}", res.err()); - assert!(!res.unwrap()); + async fn users_not_contains(context: &mut Context, username: String) -> TypeDBResult { + let contains = context.users.contains(username).await?; + assert!(!contains); + Ok(()) + } + + #[step(expr = "users not contains: {word}; throws exception")] + async fn users_not_contains_throws(context: &mut Context, username: String) { + assert_err!(users_not_contains(context, username).await); } #[step(expr = "users create: {word}, {word}")] @@ -60,9 +85,13 @@ generic_step_impl! { } #[step(expr = "users password set: {word}, {word}")] - async fn users_password_set(context: &mut Context, username: String, password: String) { - let res = context.users.set_password(username, password).await; - assert!(res.is_ok(), "{:?}", res.err()); + async fn users_password_set(context: &mut Context, username: String, password: String) -> TypeDBResult { + context.users.set_password(username, password).await + } + + #[step(expr = "users password set: {word}, {word}; throws exception")] + async fn users_password_set_throws(context: &mut Context, username: String, password: String) { + assert_err!(users_password_set(context, username, password).await); } // #[step(expr = "user password update: {word}, {word}")] @@ -73,9 +102,13 @@ generic_step_impl! { // } #[step(expr = "users delete: {word}")] - async fn user_delete(context: &mut Context, username: String) { - let res = context.users.delete(username).await; - assert!(res.is_ok(), "{:?}", res.err()); + async fn user_delete(context: &mut Context, username: String) -> TypeDBResult { + context.users.delete(username).await + } + + #[step(expr = "users delete: {word}; throws exception")] + async fn user_delete_throws(context: &mut Context, username: String) { + assert_err!(user_delete(context, username).await); } #[step(expr = "user expiry-seconds")] @@ -93,10 +126,3 @@ generic_step_impl! { } // Then user password update: new-password, bad-password; throws exception -// Then users get all; throws exception -// Then users get user: admin; throws exception -// Then users create: user3, password; throws exception -// Then users contains: admin; throws exception -// Then users delete: admin; throws exception -// Then users delete: user2; throws exception -// Then users password set: user2, new-password; throws exception From 39eb5b62f5fb0542b81d71cf2b619ae815479016 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 26 Jun 2023 16:53:26 +0100 Subject: [PATCH 35/84] fmt --- src/connection/connection.rs | 4 ++-- tests/integration/user.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index b888c2de..dd1220c0 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -80,10 +80,10 @@ impl Connection { match server_connection { Ok(server_connection) => { server_connections.insert(address, server_connection); - }, + } Err(err) => { error_buffer.push(format!("- {}: {}", address, err)); - }, + } } } if server_connections.is_empty() { diff --git a/tests/integration/user.rs b/tests/integration/user.rs index 588d4ec1..a3d6bc82 100644 --- a/tests/integration/user.rs +++ b/tests/integration/user.rs @@ -88,4 +88,4 @@ test_for_each_arg! { assert_eq!(1, users.all().await?.len()); Ok(()) } -} \ No newline at end of file +} From af3f1c3a4e1917533c409184eb1b00f940c0eba9 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 26 Jun 2023 17:10:45 +0100 Subject: [PATCH 36/84] Steps for updating password --- tests/behaviour/connection/user/steps.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index ef945853..468c5031 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -19,10 +19,8 @@ * under the License. */ -use std::path::PathBuf; - -use cucumber::{gherkin::Step, given, then, when}; -use typedb_client::{Connection, Credential, Options, Result as TypeDBResult, TransactionType}; +use cucumber::{given, then, when}; +use typedb_client::Result as TypeDBResult; use crate::{assert_err, behaviour::Context, generic_step_impl}; @@ -94,12 +92,18 @@ generic_step_impl! { assert_err!(users_password_set(context, username, password).await); } - // #[step(expr = "user password update: {word}, {word}")] - // async fn user_password_update(context: &mut Context, password_old: String, password_new: String) -> TypeDBResult { - // assert!(context.connection.username.is_some()); - // context.users.get(context.connection.username.clone().unwrap()).await?.password_update(context.connection, password_old, password_new).await?); - // assert!(res.is_ok(), "{:?}", res.err()); - // } + // TODO: Add tests for these two steps to BDD + #[step(expr = "user password update: {word}, {word}")] + async fn user_password_update(context: &mut Context, password_old: String, password_new: String) -> TypeDBResult { + let connected_user = context.connection.username.clone(); + assert!(connected_user.is_some()); + context.users.get(connected_user.unwrap()).await?.password_update(&context.connection, password_old, password_new).await + } + + #[step(expr = "user password update: {word}, {word}; throws exception")] + async fn user_password_update_throws(context: &mut Context, password_old: String, password_new: String) { + assert_err!(user_password_update(context, password_old, password_new).await); + } #[step(expr = "users delete: {word}")] async fn user_delete(context: &mut Context, username: String) -> TypeDBResult { From 86e46f6823a1a1a7bcedf1eda9b0d5c6d561bba7 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 26 Jun 2023 17:10:52 +0100 Subject: [PATCH 37/84] Cleanup --- tests/behaviour/connection/steps.rs | 2 +- tests/integration/user.rs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index d9a72aca..9bf5c0d4 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -22,7 +22,7 @@ use std::path::PathBuf; use cucumber::{given, then, when}; -use typedb_client::{Connection, Credential, Options, TransactionType}; +use typedb_client::{Connection, Credential}; use crate::{behaviour::Context, generic_step_impl}; diff --git a/tests/integration/user.rs b/tests/integration/user.rs index a3d6bc82..156b5c0c 100644 --- a/tests/integration/user.rs +++ b/tests/integration/user.rs @@ -19,11 +19,9 @@ * under the License. */ -use std::path::PathBuf; - use futures::future::try_join_all; use serial_test::serial; -use typedb_client::{Connection, Credential, Result as TypeDBResult, UserManager}; +use typedb_client::{Connection, Result as TypeDBResult, UserManager}; use super::common; use crate::test_for_each_arg; From 560fdc81673e11ae622cf3c04d690b5adbc0cf79 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 26 Jun 2023 17:53:30 +0100 Subject: [PATCH 38/84] Connect to cluster by default, add tests to automation.yml --- .factory/automation.yml | 23 +++++++++++++++++++---- tests/BUILD | 1 - tests/behaviour/mod.rs | 14 +++++++++++++- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/.factory/automation.yml b/.factory/automation.yml index 32ca5053..04325c4b 100644 --- a/.factory/automation.yml +++ b/.factory/automation.yml @@ -97,9 +97,9 @@ build: export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh bazel run @vaticle_dependencies//distribution/artifact:create-netrc - tools/start-core-server.sh + source tools/start-cluster-servers.sh # use source to receive export vars bazel test //tests --test_arg=-- --test_arg=behaviour::concept --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 - tools/stop-core-server.sh + tools/stop-cluster-servers.sh exit $TEST_SUCCESS test-behaviour-connection: image: vaticle-ubuntu-22.04 @@ -110,9 +110,22 @@ build: export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh bazel run @vaticle_dependencies//distribution/artifact:create-netrc - tools/start-core-server.sh + source tools/start-cluster-servers.sh # use source to receive export vars bazel test //tests --test_arg=-- --test_arg=behaviour::connection --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 - tools/stop-core-server.sh + tools/stop-cluster-servers.sh + exit $TEST_SUCCESS + test-behaviour-typeql: + image: vaticle-ubuntu-22.04 + dependencies: + - build + command: | + export ARTIFACT_USERNAME=$REPO_VATICLE_USERNAME + export ARTIFACT_PASSWORD=$REPO_VATICLE_PASSWORD + bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh + bazel run @vaticle_dependencies//distribution/artifact:create-netrc + source tools/start-cluster-servers.sh # use source to receive export vars + bazel test //tests --test_arg=-- --test_arg=behaviour::typeql --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + tools/stop-cluster-servers.sh exit $TEST_SUCCESS deploy-crate-snapshot: filter: @@ -124,7 +137,9 @@ build: - test-integration-core - test-integration-cluster - test-integration-runtimes + - test-behaviour-concept - test-behaviour-connection + - test-behaviour-typeql command: | export DEPLOY_CRATE_TOKEN=$REPO_VATICLE_CRATES_TOKEN bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh diff --git a/tests/BUILD b/tests/BUILD index cf2c236c..d3d07077 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -72,7 +72,6 @@ rust_test( "@vaticle_typedb_behaviour//typeql/reasoner:type-hierarchy.feature", "@vaticle_typedb_behaviour//typeql/reasoner:value-predicate.feature", "@vaticle_typedb_behaviour//typeql/reasoner:variable-roles.feature", -# "behaviour/debug.feature", ], ) diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index bf449107..4f60661c 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -203,7 +203,19 @@ impl Context { impl Default for Context { fn default() -> Self { - let connection = Connection::new_plaintext("0.0.0.0:1729").unwrap(); + let connection = Connection::new_encrypted( + &["localhost:11729", "localhost:21729", "localhost:31729"], + Credential::with_tls( + &Context::ADMIN_USERNAME, + &Context::ADMIN_PASSWORD, + Some(&PathBuf::from( + std::env::var("ROOT_CA") + .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), + )), + ) + .unwrap(), + ) + .unwrap(); let databases = DatabaseManager::new(connection.clone()); let users = UserManager::new(connection.clone()); Self { From 364a549026aae24c3ad4a2250f2781843c41146e Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 27 Jun 2023 09:00:05 +0100 Subject: [PATCH 39/84] Set ROOT_CA for Factory BDD tests --- .factory/automation.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.factory/automation.yml b/.factory/automation.yml index 04325c4b..d0c89599 100644 --- a/.factory/automation.yml +++ b/.factory/automation.yml @@ -98,7 +98,7 @@ build: bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh bazel run @vaticle_dependencies//distribution/artifact:create-netrc source tools/start-cluster-servers.sh # use source to receive export vars - bazel test //tests --test_arg=-- --test_arg=behaviour::concept --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=behaviour::concept --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 tools/stop-cluster-servers.sh exit $TEST_SUCCESS test-behaviour-connection: @@ -111,7 +111,7 @@ build: bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh bazel run @vaticle_dependencies//distribution/artifact:create-netrc source tools/start-cluster-servers.sh # use source to receive export vars - bazel test //tests --test_arg=-- --test_arg=behaviour::connection --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=behaviour::connection --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 tools/stop-cluster-servers.sh exit $TEST_SUCCESS test-behaviour-typeql: @@ -124,7 +124,7 @@ build: bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh bazel run @vaticle_dependencies//distribution/artifact:create-netrc source tools/start-cluster-servers.sh # use source to receive export vars - bazel test //tests --test_arg=-- --test_arg=behaviour::typeql --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=behaviour::typeql --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 tools/stop-cluster-servers.sh exit $TEST_SUCCESS deploy-crate-snapshot: From 6f5c0cd68a41f5740fc74d79aaa829288274c378 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 27 Jun 2023 09:19:13 +0100 Subject: [PATCH 40/84] Remove redundant TODO --- tests/behaviour/connection/user/steps.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 468c5031..65a4fef8 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -92,7 +92,6 @@ generic_step_impl! { assert_err!(users_password_set(context, username, password).await); } - // TODO: Add tests for these two steps to BDD #[step(expr = "user password update: {word}, {word}")] async fn user_password_update(context: &mut Context, password_old: String, password_new: String) -> TypeDBResult { let connected_user = context.connection.username.clone(); From 48c3507c13b32089e2f8baff594acdacff74da9d Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 27 Jun 2023 10:29:28 +0100 Subject: [PATCH 41/84] Always run after_scenario on cluster --- tests/behaviour/mod.rs | 50 +++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 4f60661c..b450d633 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -86,36 +86,32 @@ impl Context { } async fn after_scenario(&mut self) -> TypeDBResult { - if self.connection.server_count() == 1 { - try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await?; - } else { - self.set_connection( - Connection::new_encrypted( - &["localhost:11729", "localhost:21729", "localhost:31729"], - Credential::with_tls( - &Context::ADMIN_USERNAME, - &Context::ADMIN_PASSWORD, - Some(&PathBuf::from( - std::env::var("ROOT_CA") - .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), - )), - ) - .unwrap(), + self.set_connection( + Connection::new_encrypted( + &["localhost:11729", "localhost:21729", "localhost:31729"], + Credential::with_tls( + &Context::ADMIN_USERNAME, + &Context::ADMIN_PASSWORD, + Some(&PathBuf::from( + std::env::var("ROOT_CA") + .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), + )), ) .unwrap(), - ); - try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await?; - try_join_all( - self.users - .all() - .await - .unwrap() - .into_iter() - .filter(|user| user.username != Context::ADMIN_USERNAME) - .map(|user| self.users.delete(user.username)), ) - .await?; - } + .unwrap(), + ); + try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await?; + try_join_all( + self.users + .all() + .await + .unwrap() + .into_iter() + .filter(|user| user.username != Context::ADMIN_USERNAME) + .map(|user| self.users.delete(user.username)), + ) + .await?; Ok(()) } From 7a218d88027eaa5c33f4a09505c987c44bb9d414 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 27 Jun 2023 10:29:54 +0100 Subject: [PATCH 42/84] Trying to sleep after create/delete db --- tests/behaviour/connection/database/steps.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index ce39534a..eeb1a83c 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -23,6 +23,7 @@ use std::collections::HashSet; use cucumber::{gherkin::Step, given, then, when}; use futures::{future::try_join_all, TryFutureExt}; +use tokio::time::{sleep, Duration}; use typedb_client::Database; use crate::{ @@ -34,6 +35,7 @@ generic_step_impl! { #[step(expr = "connection create database: {word}")] pub async fn connection_create_database(context: &mut Context, name: String) { context.databases.create(name).await.unwrap(); + sleep(Duration::from_millis(300)).await; } #[step(expr = "connection create database(s):")] @@ -41,16 +43,19 @@ generic_step_impl! { for name in util::iter_table(step) { context.databases.create(name).await.unwrap(); } + sleep(Duration::from_millis(300)).await; } #[step("connection create databases in parallel:")] async fn connection_create_databases_in_parallel(context: &mut Context, step: &Step) { try_join_all(util::iter_table(step).map(|name| context.databases.create(name))).await.unwrap(); + sleep(Duration::from_millis(300)).await; } #[step(expr = "connection delete database: {word}")] pub async fn connection_delete_database(context: &mut Context, name: String) { context.databases.get(name).and_then(Database::delete).await.unwrap(); + sleep(Duration::from_millis(300)).await; } #[step(expr = "connection delete database(s):")] @@ -58,6 +63,7 @@ generic_step_impl! { for name in util::iter_table(step) { context.databases.get(name).and_then(Database::delete).await.unwrap(); } + sleep(Duration::from_millis(300)).await; } #[step(expr = "connection delete databases in parallel:")] @@ -65,6 +71,7 @@ generic_step_impl! { try_join_all(util::iter_table(step).map(|name| context.databases.get(name).and_then(Database::delete))) .await .unwrap(); + sleep(Duration::from_millis(300)).await; } #[step(expr = "connection delete database; throws exception: {word}")] From a8bfe2f58dcae960c4e61669ed35f113dd94ac73 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 27 Jun 2023 11:12:54 +0100 Subject: [PATCH 43/84] Sleep for 2s --- tests/behaviour/connection/database/steps.rs | 12 ++++++------ tests/behaviour/mod.rs | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index eeb1a83c..93a347c9 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -35,7 +35,7 @@ generic_step_impl! { #[step(expr = "connection create database: {word}")] pub async fn connection_create_database(context: &mut Context, name: String) { context.databases.create(name).await.unwrap(); - sleep(Duration::from_millis(300)).await; + sleep(Duration::from_millis(2000)).await; } #[step(expr = "connection create database(s):")] @@ -43,19 +43,19 @@ generic_step_impl! { for name in util::iter_table(step) { context.databases.create(name).await.unwrap(); } - sleep(Duration::from_millis(300)).await; + sleep(Duration::from_millis(2000)).await; } #[step("connection create databases in parallel:")] async fn connection_create_databases_in_parallel(context: &mut Context, step: &Step) { try_join_all(util::iter_table(step).map(|name| context.databases.create(name))).await.unwrap(); - sleep(Duration::from_millis(300)).await; + sleep(Duration::from_millis(2000)).await; } #[step(expr = "connection delete database: {word}")] pub async fn connection_delete_database(context: &mut Context, name: String) { context.databases.get(name).and_then(Database::delete).await.unwrap(); - sleep(Duration::from_millis(300)).await; + sleep(Duration::from_millis(2000)).await; } #[step(expr = "connection delete database(s):")] @@ -63,7 +63,7 @@ generic_step_impl! { for name in util::iter_table(step) { context.databases.get(name).and_then(Database::delete).await.unwrap(); } - sleep(Duration::from_millis(300)).await; + sleep(Duration::from_millis(2000)).await; } #[step(expr = "connection delete databases in parallel:")] @@ -71,7 +71,7 @@ generic_step_impl! { try_join_all(util::iter_table(step).map(|name| context.databases.get(name).and_then(Database::delete))) .await .unwrap(); - sleep(Duration::from_millis(300)).await; + sleep(Duration::from_millis(2000)).await; } #[step(expr = "connection delete database; throws exception: {word}")] diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index b450d633..c3add76c 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -30,6 +30,7 @@ use std::{collections::HashMap, path::PathBuf}; use cucumber::{StatsWriter, World}; use futures::future::try_join_all; +use tokio::time::{sleep, Duration}; use typedb_client::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, concept::{Attribute, AttributeType, Entity, EntityType, Relation, RelationType, Thing}, @@ -112,6 +113,7 @@ impl Context { .map(|user| self.users.delete(user.username)), ) .await?; + sleep(Duration::from_millis(2000)).await; Ok(()) } From c10088b9689dfd832cba05f1653cc4bf6780db18 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 28 Jun 2023 16:06:52 +0100 Subject: [PATCH 44/84] Wait in steps until success or TL --- tests/behaviour/connection/database/steps.rs | 48 ++++++++++++++------ tests/behaviour/connection/steps.rs | 6 +++ tests/behaviour/connection/user/steps.rs | 17 +++++-- tests/behaviour/mod.rs | 3 +- 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index 93a347c9..cc00299d 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -43,19 +43,16 @@ generic_step_impl! { for name in util::iter_table(step) { context.databases.create(name).await.unwrap(); } - sleep(Duration::from_millis(2000)).await; } - #[step("connection create databases in parallel:")] + #[step(expr = "connection create databases in parallel:")] async fn connection_create_databases_in_parallel(context: &mut Context, step: &Step) { try_join_all(util::iter_table(step).map(|name| context.databases.create(name))).await.unwrap(); - sleep(Duration::from_millis(2000)).await; } #[step(expr = "connection delete database: {word}")] pub async fn connection_delete_database(context: &mut Context, name: String) { context.databases.get(name).and_then(Database::delete).await.unwrap(); - sleep(Duration::from_millis(2000)).await; } #[step(expr = "connection delete database(s):")] @@ -63,7 +60,6 @@ generic_step_impl! { for name in util::iter_table(step) { context.databases.get(name).and_then(Database::delete).await.unwrap(); } - sleep(Duration::from_millis(2000)).await; } #[step(expr = "connection delete databases in parallel:")] @@ -71,7 +67,6 @@ generic_step_impl! { try_join_all(util::iter_table(step).map(|name| context.databases.get(name).and_then(Database::delete))) .await .unwrap(); - sleep(Duration::from_millis(2000)).await; } #[step(expr = "connection delete database; throws exception: {word}")] @@ -88,27 +83,52 @@ generic_step_impl! { #[step(expr = "connection has database: {word}")] async fn connection_has_database(context: &mut Context, name: String) { - assert!(context.databases.contains(name).await.unwrap()); + let mut count_pauses = 0; + while !context.databases.contains(name.clone()).await.unwrap() && count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + count_pauses += 1; + }; + assert!(context.databases.contains(name.clone()).await.unwrap()); } #[step(expr = "connection has database(s):")] async fn connection_has_databases(context: &mut Context, step: &Step) { let names: HashSet = util::iter_table(step).map(|name| name.to_owned()).collect(); - let all_databases = context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect(); + let mut count_pauses = 0; + while context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>() != names && count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + count_pauses += 1; + }; + let all_databases: HashSet<_, _> = context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect(); assert_eq!(names, all_databases); } #[step(expr = "connection does not have database: {word}")] async fn connection_does_not_have_database(context: &mut Context, name: String) { - assert!(!context.databases.contains(name).await.unwrap()); + let mut count_pauses = 0; + while context.databases.contains(name.clone()).await.unwrap() && count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + count_pauses += 1; + }; + assert!(!context.databases.contains(name.clone()).await.unwrap()); } #[step(expr = "connection does not have database(s):")] async fn connection_does_not_have_databases(context: &mut Context, step: &Step) { - let all_databases: HashSet = - context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect(); - for name in util::iter_table(step) { - assert!(!all_databases.contains(name)); - } + let mut count_pauses = 0; + while count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + let all_databases: HashSet = + context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect(); + let mut all_not_contained = true; + for name in util::iter_table(step) { + all_not_contained &= !all_databases.contains(name); + } + if all_not_contained { + break; + } + sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + count_pauses += 1; + }; + assert!(count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS, "Connection has at least one of these databases."); } } diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index 9bf5c0d4..a2997861 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -22,6 +22,7 @@ use std::path::PathBuf; use cucumber::{given, then, when}; +use tokio::time::{sleep, Duration}; use typedb_client::{Connection, Credential}; use crate::{behaviour::Context, generic_step_impl}; @@ -55,6 +56,11 @@ generic_step_impl! { #[step("connection does not have any database")] async fn connection_does_not_have_any_database(context: &mut Context) { + let mut count_pauses = 0; + while !context.databases.all().await.unwrap().is_empty() && count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + count_pauses += 1; + }; assert!(context.databases.all().await.unwrap().is_empty()); } diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 65a4fef8..230cbfd4 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -20,6 +20,7 @@ */ use cucumber::{given, then, when}; +use tokio::time::{sleep, Duration}; use typedb_client::Result as TypeDBResult; use crate::{assert_err, behaviour::Context, generic_step_impl}; @@ -50,8 +51,12 @@ generic_step_impl! { #[step(expr = "users contains: {word}")] async fn users_contains(context: &mut Context, username: String) -> TypeDBResult { - let contains = context.users.contains(username).await?; - assert!(contains); + let mut count_pauses = 0; + while !context.users.contains(username.clone()).await? && count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + count_pauses += 1; + }; + assert!(context.users.contains(username.clone()).await?); Ok(()) } @@ -62,8 +67,12 @@ generic_step_impl! { #[step(expr = "users not contains: {word}")] async fn users_not_contains(context: &mut Context, username: String) -> TypeDBResult { - let contains = context.users.contains(username).await?; - assert!(!contains); + let mut count_pauses = 0; + while context.users.contains(username.clone()).await? && count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + count_pauses += 1; + }; + assert!(!context.users.contains(username.clone()).await?); Ok(()) } diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index c3add76c..aee8bb61 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -59,6 +59,8 @@ impl Context { const DEFAULT_DATABASE: &'static str = "test"; const ADMIN_USERNAME: &'static str = "admin"; const ADMIN_PASSWORD: &'static str = "password"; + const PAUSE_BETWEEN_STEP_CHECKS_MS: u64 = 500; + const PAUSES_LIMIT_BETWEEN_STEP_CHECKS: u64 = 10; async fn test(glob: &'static str) -> bool { let default_panic = std::panic::take_hook(); @@ -113,7 +115,6 @@ impl Context { .map(|user| self.users.delete(user.username)), ) .await?; - sleep(Duration::from_millis(2000)).await; Ok(()) } From 2808f9b10b87ee9bc60bff61caffc18b846846dd Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 28 Jun 2023 16:41:38 +0100 Subject: [PATCH 45/84] Increase test TL --- tests/BUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/BUILD b/tests/BUILD index d3d07077..a8f0c489 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -29,6 +29,7 @@ load("@vaticle_typedb_common//runner:rules.bzl", "native_typedb_artifact") rust_test( name = "tests", srcs = glob(["**/*.rs"]), + timeout = "eternal", deps = [ "//:typedb_client", "@vaticle_typeql//rust:typeql_lang", @@ -72,6 +73,7 @@ rust_test( "@vaticle_typedb_behaviour//typeql/reasoner:type-hierarchy.feature", "@vaticle_typedb_behaviour//typeql/reasoner:value-predicate.feature", "@vaticle_typedb_behaviour//typeql/reasoner:variable-roles.feature", +# "behaviour/connection/database/debug.feature", ], ) From 42040d24ba3f6e938e05976a1ca8d6dbbcfa281c Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 28 Jun 2023 16:42:27 +0100 Subject: [PATCH 46/84] Increase step TL --- tests/behaviour/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index aee8bb61..9c8509a7 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -60,7 +60,7 @@ impl Context { const ADMIN_USERNAME: &'static str = "admin"; const ADMIN_PASSWORD: &'static str = "password"; const PAUSE_BETWEEN_STEP_CHECKS_MS: u64 = 500; - const PAUSES_LIMIT_BETWEEN_STEP_CHECKS: u64 = 10; + const PAUSES_LIMIT_BETWEEN_STEP_CHECKS: u64 = 15; async fn test(glob: &'static str) -> bool { let default_panic = std::panic::take_hook(); From 1486d0f46bf5764becd64a89872798605a584a0d Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 09:56:48 +0100 Subject: [PATCH 47/84] Wait on db creation step --- tests/behaviour/connection/database/steps.rs | 30 +++++++++++--------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index cc00299d..c024b765 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -22,9 +22,10 @@ use std::collections::HashSet; use cucumber::{gherkin::Step, given, then, when}; -use futures::{future::try_join_all, TryFutureExt}; +use futures::{future::{join_all, try_join_all}, TryFutureExt}; use tokio::time::{sleep, Duration}; use typedb_client::Database; +use typedb_client::Result as TypeDBResult; use crate::{ behaviour::{util, Context}, @@ -34,14 +35,18 @@ use crate::{ generic_step_impl! { #[step(expr = "connection create database: {word}")] pub async fn connection_create_database(context: &mut Context, name: String) { - context.databases.create(name).await.unwrap(); - sleep(Duration::from_millis(2000)).await; + let mut count_pauses = 0; + while context.databases.create(name.clone()).await.is_err() && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { + sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + count_pauses += 1; + }; + assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Database {name} couldn't be created."); } #[step(expr = "connection create database(s):")] async fn connection_create_databases(context: &mut Context, step: &Step) { for name in util::iter_table(step) { - context.databases.create(name).await.unwrap(); + connection_create_database(context, name.into()).await; } } @@ -84,39 +89,38 @@ generic_step_impl! { #[step(expr = "connection has database: {word}")] async fn connection_has_database(context: &mut Context, name: String) { let mut count_pauses = 0; - while !context.databases.contains(name.clone()).await.unwrap() && count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + while !context.databases.contains(name.clone()).await.unwrap() && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; count_pauses += 1; }; - assert!(context.databases.contains(name.clone()).await.unwrap()); + assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection doesn't contain database {name}."); } #[step(expr = "connection has database(s):")] async fn connection_has_databases(context: &mut Context, step: &Step) { let names: HashSet = util::iter_table(step).map(|name| name.to_owned()).collect(); let mut count_pauses = 0; - while context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>() != names && count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + while context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>() != names && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; count_pauses += 1; }; - let all_databases: HashSet<_, _> = context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect(); - assert_eq!(names, all_databases); + assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection doesn't contain at least one of databases."); } #[step(expr = "connection does not have database: {word}")] async fn connection_does_not_have_database(context: &mut Context, name: String) { let mut count_pauses = 0; - while context.databases.contains(name.clone()).await.unwrap() && count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + while context.databases.contains(name.clone()).await.unwrap() && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; count_pauses += 1; }; - assert!(!context.databases.contains(name.clone()).await.unwrap()); + assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection contains database {name}."); } #[step(expr = "connection does not have database(s):")] async fn connection_does_not_have_databases(context: &mut Context, step: &Step) { let mut count_pauses = 0; - while count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + while count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { let all_databases: HashSet = context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect(); let mut all_not_contained = true; @@ -129,6 +133,6 @@ generic_step_impl! { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; count_pauses += 1; }; - assert!(count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS, "Connection has at least one of these databases."); + assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection contains at least one of databases."); } } From 9ecf43db4e8ea7b617aa4d32edc593f046a8c133 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 09:58:24 +0100 Subject: [PATCH 48/84] Wait on user steps --- tests/behaviour/connection/user/steps.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 230cbfd4..9ac193a2 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -52,11 +52,11 @@ generic_step_impl! { #[step(expr = "users contains: {word}")] async fn users_contains(context: &mut Context, username: String) -> TypeDBResult { let mut count_pauses = 0; - while !context.users.contains(username.clone()).await? && count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + while !context.users.contains(username.clone()).await? && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; count_pauses += 1; }; - assert!(context.users.contains(username.clone()).await?); + assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "User not exists."); Ok(()) } @@ -68,11 +68,11 @@ generic_step_impl! { #[step(expr = "users not contains: {word}")] async fn users_not_contains(context: &mut Context, username: String) -> TypeDBResult { let mut count_pauses = 0; - while context.users.contains(username.clone()).await? && count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + while context.users.contains(username.clone()).await? && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; count_pauses += 1; }; - assert!(!context.users.contains(username.clone()).await?); + assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "User exists."); Ok(()) } @@ -136,5 +136,3 @@ generic_step_impl! { } } - -// Then user password update: new-password, bad-password; throws exception From 928a6a06a14056d74cb9fe0855c27355675681a3 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 10:00:40 +0100 Subject: [PATCH 49/84] Wait on connection with auth --- tests/behaviour/connection/steps.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index a2997861..5696a295 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -36,8 +36,20 @@ generic_step_impl! { #[step(expr = "connection opens with authentication: {word}, {word}")] async fn connection_opens_with_authentication(context: &mut Context, login: String, password: String) { - context.set_connection( - Connection::new_encrypted( + let mut connection = Connection::new_encrypted( + &["localhost:11729", "localhost:21729", "localhost:31729"], + Credential::with_tls( + &login.as_str(), + &password.as_str(), + Some(&PathBuf::from( + std::env::var("ROOT_CA") + .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), + )), + ).unwrap(), + ); + let mut count_pauses = 0; + while connection.is_err() && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { + connection = Connection::new_encrypted( &["localhost:11729", "localhost:21729", "localhost:31729"], Credential::with_tls( &login.as_str(), @@ -47,8 +59,10 @@ generic_step_impl! { .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), )), ).unwrap(), - ).unwrap() - ); + ); + count_pauses += 1; + } + context.set_connection(connection.unwrap()); } #[step("connection has been opened")] @@ -57,7 +71,7 @@ generic_step_impl! { #[step("connection does not have any database")] async fn connection_does_not_have_any_database(context: &mut Context) { let mut count_pauses = 0; - while !context.databases.all().await.unwrap().is_empty() && count_pauses < Context::PAUSES_LIMIT_BETWEEN_STEP_CHECKS { + while !context.databases.all().await.unwrap().is_empty() && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; count_pauses += 1; }; From be6b0d65706af77c965af96ced8fb6618037d4e1 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 10:01:06 +0100 Subject: [PATCH 50/84] Refactoring --- tests/behaviour/connection/database/mod.rs | 1 + tests/behaviour/mod.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/behaviour/connection/database/mod.rs b/tests/behaviour/connection/database/mod.rs index ac57625c..2d368c7f 100644 --- a/tests/behaviour/connection/database/mod.rs +++ b/tests/behaviour/connection/database/mod.rs @@ -32,4 +32,5 @@ async fn test() { // @vaticle_typedb_behaviour is stored in a directory that is a sibling to // the working directory. assert!(Context::test("../vaticle_typedb_behaviour/connection/database.feature").await); + // assert!(Context::test("tests/behaviour/connection/database/debug.feature").await); } diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 9c8509a7..14742532 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -60,7 +60,7 @@ impl Context { const ADMIN_USERNAME: &'static str = "admin"; const ADMIN_PASSWORD: &'static str = "password"; const PAUSE_BETWEEN_STEP_CHECKS_MS: u64 = 500; - const PAUSES_LIMIT_BETWEEN_STEP_CHECKS: u64 = 15; + const STEP_CHECKS_ITERATIONS_LIMIT: u64 = 15; async fn test(glob: &'static str) -> bool { let default_panic = std::panic::take_hook(); From ee0c340937ce464b78bb3d75a7cfad3bbf6982ce Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 10:52:08 +0100 Subject: [PATCH 51/84] Connect to at least one server (until cluster not fixed) --- src/connection/connection.rs | 6 ++++++ tests/behaviour/connection/steps.rs | 31 +++++++++++++++-------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index dd1220c0..3cb95cf3 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -89,6 +89,12 @@ impl Connection { if server_connections.is_empty() { Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? } + // let mut server_connections = HashMap::with_capacity(addresses.len()); + // for address in addresses { + // let server_connection = + // ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone())?; + // server_connections.insert(address, server_connection); + // } Ok(Self { server_connections, background_runtime, username: Some(credential.username().to_string()) }) } diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index 5696a295..927dfc8d 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -47,21 +47,22 @@ generic_step_impl! { )), ).unwrap(), ); - let mut count_pauses = 0; - while connection.is_err() && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { - connection = Connection::new_encrypted( - &["localhost:11729", "localhost:21729", "localhost:31729"], - Credential::with_tls( - &login.as_str(), - &password.as_str(), - Some(&PathBuf::from( - std::env::var("ROOT_CA") - .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), - )), - ).unwrap(), - ); - count_pauses += 1; - } + // let mut count_pauses = 0; + // while connection.is_err() && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { + // connection = Connection::new_encrypted( + // &["localhost:11729", "localhost:21729", "localhost:31729"], + // Credential::with_tls( + // &login.as_str(), + // &password.as_str(), + // Some(&PathBuf::from( + // std::env::var("ROOT_CA") + // .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), + // )), + // ).unwrap(), + // ); + // count_pauses += 1; + // sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + // } context.set_connection(connection.unwrap()); } From 2b695052e91dd9b193223354a9fa471d324ec887 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 10:52:25 +0100 Subject: [PATCH 52/84] fmt --- tests/behaviour/connection/database/steps.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index c024b765..a544841f 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -22,10 +22,12 @@ use std::collections::HashSet; use cucumber::{gherkin::Step, given, then, when}; -use futures::{future::{join_all, try_join_all}, TryFutureExt}; +use futures::{ + future::{join_all, try_join_all}, + TryFutureExt, +}; use tokio::time::{sleep, Duration}; -use typedb_client::Database; -use typedb_client::Result as TypeDBResult; +use typedb_client::{Database, Result as TypeDBResult}; use crate::{ behaviour::{util, Context}, From 02e3122b9ac82d271e564645dbf38972e66c954f Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 11:26:11 +0100 Subject: [PATCH 53/84] Cleanup --- src/connection/network/proto/message.rs | 21 ---------- tests/behaviour/connection/database/steps.rs | 40 ++++++++++---------- tests/behaviour/connection/steps.rs | 12 +++--- tests/behaviour/connection/user/steps.rs | 16 ++++---- 4 files changed, 34 insertions(+), 55 deletions(-) diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 83f00936..d065405a 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -1268,27 +1268,6 @@ impl IntoProto for LogicRequest { } } -// impl IntoProto for UserRequest { -// fn into_proto(self) -> logic_manager::Req { -// let req = match self { -// Self::PutRule { label, when, then } => { -// logic_manager::req::Req::PutRuleReq(logic_manager::put_rule::Req { -// label, -// when: when.to_string(), -// then: then.to_string(), -// }) -// } -// Self::GetRule { label } => { -// logic_manager::req::Req::GetRuleReq(logic_manager::get_rule::Req { label }) -// } -// Self::GetRules => { -// logic_manager::req::Req::GetRulesReq(logic_manager::get_rules::Req {}) -// } -// }; -// logic_manager::Req { req: Some(req) } -// } -// } - impl TryFromProto for LogicResponse { fn try_from_proto(proto: logic_manager::Res) -> Result { match proto.res { diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index a544841f..ceaedbb8 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -37,12 +37,12 @@ use crate::{ generic_step_impl! { #[step(expr = "connection create database: {word}")] pub async fn connection_create_database(context: &mut Context, name: String) { - let mut count_pauses = 0; - while context.databases.create(name.clone()).await.is_err() && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { + let mut waiting_iterations = 0; + while context.databases.create(name.clone()).await.is_err() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - count_pauses += 1; + waiting_iterations += 1; }; - assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Database {name} couldn't be created."); + assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Database {name} couldn't be created."); } #[step(expr = "connection create database(s):")] @@ -90,39 +90,39 @@ generic_step_impl! { #[step(expr = "connection has database: {word}")] async fn connection_has_database(context: &mut Context, name: String) { - let mut count_pauses = 0; - while !context.databases.contains(name.clone()).await.unwrap() && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { + let mut waiting_iterations = 0; + while !context.databases.contains(name.clone()).await.unwrap() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - count_pauses += 1; + waiting_iterations += 1; }; - assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection doesn't contain database {name}."); + assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection doesn't contain database {name}."); } #[step(expr = "connection has database(s):")] async fn connection_has_databases(context: &mut Context, step: &Step) { let names: HashSet = util::iter_table(step).map(|name| name.to_owned()).collect(); - let mut count_pauses = 0; - while context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>() != names && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { + let mut waiting_iterations = 0; + while context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>() != names && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - count_pauses += 1; + waiting_iterations += 1; }; - assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection doesn't contain at least one of databases."); + assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection doesn't contain at least one of databases."); } #[step(expr = "connection does not have database: {word}")] async fn connection_does_not_have_database(context: &mut Context, name: String) { - let mut count_pauses = 0; - while context.databases.contains(name.clone()).await.unwrap() && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { + let mut waiting_iterations = 0; + while context.databases.contains(name.clone()).await.unwrap() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - count_pauses += 1; + waiting_iterations += 1; }; - assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection contains database {name}."); + assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection contains database {name}."); } #[step(expr = "connection does not have database(s):")] async fn connection_does_not_have_databases(context: &mut Context, step: &Step) { - let mut count_pauses = 0; - while count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { + let mut waiting_iterations = 0; + while waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { let all_databases: HashSet = context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect(); let mut all_not_contained = true; @@ -133,8 +133,8 @@ generic_step_impl! { break; } sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - count_pauses += 1; + waiting_iterations += 1; }; - assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection contains at least one of databases."); + assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection contains at least one of databases."); } } diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index 927dfc8d..6022b80f 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -47,8 +47,8 @@ generic_step_impl! { )), ).unwrap(), ); - // let mut count_pauses = 0; - // while connection.is_err() && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { + // let mut waiting_iterations = 0; + // while connection.is_err() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { // connection = Connection::new_encrypted( // &["localhost:11729", "localhost:21729", "localhost:31729"], // Credential::with_tls( @@ -60,7 +60,7 @@ generic_step_impl! { // )), // ).unwrap(), // ); - // count_pauses += 1; + // waiting_iterations += 1; // sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; // } context.set_connection(connection.unwrap()); @@ -71,10 +71,10 @@ generic_step_impl! { #[step("connection does not have any database")] async fn connection_does_not_have_any_database(context: &mut Context) { - let mut count_pauses = 0; - while !context.databases.all().await.unwrap().is_empty() && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { + let mut waiting_iterations = 0; + while !context.databases.all().await.unwrap().is_empty() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - count_pauses += 1; + waiting_iterations += 1; }; assert!(context.databases.all().await.unwrap().is_empty()); } diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 9ac193a2..413ce1b2 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -51,12 +51,12 @@ generic_step_impl! { #[step(expr = "users contains: {word}")] async fn users_contains(context: &mut Context, username: String) -> TypeDBResult { - let mut count_pauses = 0; - while !context.users.contains(username.clone()).await? && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { + let mut waiting_iterations = 0; + while !context.users.contains(username.clone()).await? && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - count_pauses += 1; + waiting_iterations += 1; }; - assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "User not exists."); + assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "User not exists."); Ok(()) } @@ -67,12 +67,12 @@ generic_step_impl! { #[step(expr = "users not contains: {word}")] async fn users_not_contains(context: &mut Context, username: String) -> TypeDBResult { - let mut count_pauses = 0; - while context.users.contains(username.clone()).await? && count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT { + let mut waiting_iterations = 0; + while context.users.contains(username.clone()).await? && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - count_pauses += 1; + waiting_iterations += 1; }; - assert!(count_pauses < Context::STEP_CHECKS_ITERATIONS_LIMIT, "User exists."); + assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "User exists."); Ok(()) } From 7c1b09653a01c8d5e0544e01c22084cf7e013cd4 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 11:36:59 +0100 Subject: [PATCH 54/84] Fix "connection does not have any database" step --- tests/behaviour/connection/steps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index 6022b80f..dc62d193 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -76,7 +76,7 @@ generic_step_impl! { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; waiting_iterations += 1; }; - assert!(context.databases.all().await.unwrap().is_empty()); + assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection has at least one database."); } #[step("connection closes")] From d880473dd82a0e12fc05a6929d69b16b38f92eae Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 11:59:06 +0100 Subject: [PATCH 55/84] Add waiting to "connection create databases in parallel" step --- tests/behaviour/connection/database/steps.rs | 14 +++++--------- tests/behaviour/util.rs | 12 +++++++++++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index ceaedbb8..11ab8234 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -27,22 +27,17 @@ use futures::{ TryFutureExt, }; use tokio::time::{sleep, Duration}; -use typedb_client::{Database, Result as TypeDBResult}; +use typedb_client::Database; use crate::{ - behaviour::{util, Context}, + behaviour::{util, util::create_database_with_waiting, Context}, generic_step_impl, }; generic_step_impl! { #[step(expr = "connection create database: {word}")] pub async fn connection_create_database(context: &mut Context, name: String) { - let mut waiting_iterations = 0; - while context.databases.create(name.clone()).await.is_err() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - waiting_iterations += 1; - }; - assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Database {name} couldn't be created."); + create_database_with_waiting(&context.databases, name).await; } #[step(expr = "connection create database(s):")] @@ -54,7 +49,8 @@ generic_step_impl! { #[step(expr = "connection create databases in parallel:")] async fn connection_create_databases_in_parallel(context: &mut Context, step: &Step) { - try_join_all(util::iter_table(step).map(|name| context.databases.create(name))).await.unwrap(); + join_all(util::iter_table(step).map(|name| create_database_with_waiting(&context.databases, name.to_string()))).await; + // try_join_all(util::iter_table(step).map(|name| context.databases.create(name))).await.unwrap(); } #[step(expr = "connection delete database: {word}")] diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index d046a963..07e596c3 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -28,12 +28,13 @@ use futures::{ TryFutureExt, TryStreamExt, }; use regex::{Captures, Regex}; +use tokio::time::{sleep, Duration}; use typedb_client::{ answer::ConceptMap, concept::{Annotation, Attribute, Concept, Entity, Relation, Value}, logic::Rule, transaction::concept::api::ThingAPI, - Result as TypeDBResult, + DatabaseManager, Result as TypeDBResult, }; use typeql_lang::{parse_patterns, parse_query, pattern::Variable}; @@ -185,3 +186,12 @@ pub async fn match_answer_rule(answer_identifiers: &HashMap<&str, &str>, answer: && when == answer.when && then == Variable::Thing(answer.then.clone()) } + +pub async fn create_database_with_waiting(databases: &DatabaseManager, name: String) { + let mut waiting_iterations = 0; + while databases.create(name.clone()).await.is_err() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { + sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + waiting_iterations += 1; + } + assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Database {name} couldn't be created."); +} From 0197ca969e9951a3c24aa41dd68ed00140ad9da1 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 11:59:12 +0100 Subject: [PATCH 56/84] Cleanup --- tests/behaviour/connection/steps.rs | 2 +- tests/behaviour/mod.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index dc62d193..757cfe8d 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -36,7 +36,7 @@ generic_step_impl! { #[step(expr = "connection opens with authentication: {word}, {word}")] async fn connection_opens_with_authentication(context: &mut Context, login: String, password: String) { - let mut connection = Connection::new_encrypted( + let connection = Connection::new_encrypted( &["localhost:11729", "localhost:21729", "localhost:31729"], Credential::with_tls( &login.as_str(), diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 14742532..2f17559b 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -30,7 +30,6 @@ use std::{collections::HashMap, path::PathBuf}; use cucumber::{StatsWriter, World}; use futures::future::try_join_all; -use tokio::time::{sleep, Duration}; use typedb_client::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, concept::{Attribute, AttributeType, Entity, EntityType, Relation, RelationType, Thing}, From 3fa0e87a3a77db509cbf74d6bf9cffabb4470bdf Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 14:16:50 +0100 Subject: [PATCH 57/84] Step TL -> 15 ms --- tests/behaviour/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 2f17559b..655ac583 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -59,7 +59,7 @@ impl Context { const ADMIN_USERNAME: &'static str = "admin"; const ADMIN_PASSWORD: &'static str = "password"; const PAUSE_BETWEEN_STEP_CHECKS_MS: u64 = 500; - const STEP_CHECKS_ITERATIONS_LIMIT: u64 = 15; + const STEP_CHECKS_ITERATIONS_LIMIT: u64 = 30; async fn test(glob: &'static str) -> bool { let default_panic = std::panic::take_hook(); From 4d5ba4bc95f909fe7f36b52478e885e5cbf3693c Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 15:46:48 +0100 Subject: [PATCH 58/84] Wait in the beginning of after_scenario() --- tests/behaviour/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 655ac583..21f0568e 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -30,6 +30,7 @@ use std::{collections::HashMap, path::PathBuf}; use cucumber::{StatsWriter, World}; use futures::future::try_join_all; +use tokio::time::{sleep, Duration}; use typedb_client::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, concept::{Attribute, AttributeType, Entity, EntityType, Relation, RelationType, Thing}, @@ -88,6 +89,7 @@ impl Context { } async fn after_scenario(&mut self) -> TypeDBResult { + sleep(Duration::from_millis(3000)).await; self.set_connection( Connection::new_encrypted( &["localhost:11729", "localhost:21729", "localhost:31729"], From ca1c99e6e2ab2d2e79053bcb12ac53972aa070ef Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 29 Jun 2023 17:32:38 +0100 Subject: [PATCH 59/84] Wait less after scenario but repeat cleaning in the "no databases" step --- tests/behaviour/connection/steps.rs | 3 ++- tests/behaviour/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index 757cfe8d..589edd5a 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -73,7 +73,8 @@ generic_step_impl! { async fn connection_does_not_have_any_database(context: &mut Context) { let mut waiting_iterations = 0; while !context.databases.all().await.unwrap().is_empty() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + // sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + let _ = context.after_scenario().await; waiting_iterations += 1; }; assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection has at least one database."); diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 21f0568e..c7e7e3f7 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -88,8 +88,8 @@ impl Context { t == "ignore" || t == "ignore-typedb" || t == "ignore-client-rust" || t == "ignore-typedb-client-rust" } - async fn after_scenario(&mut self) -> TypeDBResult { - sleep(Duration::from_millis(3000)).await; + pub async fn after_scenario(&mut self) -> TypeDBResult { + sleep(Duration::from_millis(100)).await; self.set_connection( Connection::new_encrypted( &["localhost:11729", "localhost:21729", "localhost:31729"], From 9172088f62bbb116a62c20253a3cc5f80c0f74c0 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Fri, 30 Jun 2023 09:50:16 +0100 Subject: [PATCH 60/84] Cleanup --- src/connection/connection.rs | 7 +------ tests/behaviour/connection/database/steps.rs | 5 ++--- tests/behaviour/connection/steps.rs | 18 ------------------ tests/behaviour/mod.rs | 6 +++--- 4 files changed, 6 insertions(+), 30 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 3cb95cf3..d485f8b6 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -72,6 +72,7 @@ impl Connection { let init_addresses = init_addresses.iter().map(|addr| addr.as_ref().parse()).try_collect()?; let addresses = Self::fetch_current_addresses(background_runtime.clone(), init_addresses, credential.clone())?; + // FIXME: switch to connecting to all servers (not at least one) when the issues with auth in Cluster will be fixed (PR#482) let mut server_connections = HashMap::new(); let mut error_buffer = Vec::new(); for address in addresses { @@ -89,12 +90,6 @@ impl Connection { if server_connections.is_empty() { Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? } - // let mut server_connections = HashMap::with_capacity(addresses.len()); - // for address in addresses { - // let server_connection = - // ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone())?; - // server_connections.insert(address, server_connection); - // } Ok(Self { server_connections, background_runtime, username: Some(credential.username().to_string()) }) } diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index 11ab8234..e768278d 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -50,7 +50,6 @@ generic_step_impl! { #[step(expr = "connection create databases in parallel:")] async fn connection_create_databases_in_parallel(context: &mut Context, step: &Step) { join_all(util::iter_table(step).map(|name| create_database_with_waiting(&context.databases, name.to_string()))).await; - // try_join_all(util::iter_table(step).map(|name| context.databases.create(name))).await.unwrap(); } #[step(expr = "connection delete database: {word}")] @@ -102,7 +101,7 @@ generic_step_impl! { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; waiting_iterations += 1; }; - assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection doesn't contain at least one of databases."); + assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection doesn't contain at least one of the databases."); } #[step(expr = "connection does not have database: {word}")] @@ -131,6 +130,6 @@ generic_step_impl! { sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; waiting_iterations += 1; }; - assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection contains at least one of databases."); + assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection contains at least one of the databases."); } } diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index 589edd5a..bae07111 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -22,7 +22,6 @@ use std::path::PathBuf; use cucumber::{given, then, when}; -use tokio::time::{sleep, Duration}; use typedb_client::{Connection, Credential}; use crate::{behaviour::Context, generic_step_impl}; @@ -47,22 +46,6 @@ generic_step_impl! { )), ).unwrap(), ); - // let mut waiting_iterations = 0; - // while connection.is_err() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - // connection = Connection::new_encrypted( - // &["localhost:11729", "localhost:21729", "localhost:31729"], - // Credential::with_tls( - // &login.as_str(), - // &password.as_str(), - // Some(&PathBuf::from( - // std::env::var("ROOT_CA") - // .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), - // )), - // ).unwrap(), - // ); - // waiting_iterations += 1; - // sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - // } context.set_connection(connection.unwrap()); } @@ -73,7 +56,6 @@ generic_step_impl! { async fn connection_does_not_have_any_database(context: &mut Context) { let mut waiting_iterations = 0; while !context.databases.all().await.unwrap().is_empty() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - // sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; let _ = context.after_scenario().await; waiting_iterations += 1; }; diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index c7e7e3f7..224b78b6 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -59,8 +59,8 @@ impl Context { const DEFAULT_DATABASE: &'static str = "test"; const ADMIN_USERNAME: &'static str = "admin"; const ADMIN_PASSWORD: &'static str = "password"; - const PAUSE_BETWEEN_STEP_CHECKS_MS: u64 = 500; - const STEP_CHECKS_ITERATIONS_LIMIT: u64 = 30; + const PAUSE_BETWEEN_STEP_CHECKS_MS: u64 = 250; + const STEP_CHECKS_ITERATIONS_LIMIT: u64 = 20; async fn test(glob: &'static str) -> bool { let default_panic = std::panic::take_hook(); @@ -89,7 +89,7 @@ impl Context { } pub async fn after_scenario(&mut self) -> TypeDBResult { - sleep(Duration::from_millis(100)).await; + sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; self.set_connection( Connection::new_encrypted( &["localhost:11729", "localhost:21729", "localhost:31729"], From 73ef364126942b6b311d3af61d5dc4dd80d84668 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Fri, 30 Jun 2023 11:13:08 +0100 Subject: [PATCH 61/84] Increase machine size for a large test --- .factory/automation.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.factory/automation.yml b/.factory/automation.yml index d0c89599..c3db3d0a 100644 --- a/.factory/automation.yml +++ b/.factory/automation.yml @@ -115,6 +115,7 @@ build: tools/stop-cluster-servers.sh exit $TEST_SUCCESS test-behaviour-typeql: + machine: 8-core-32-gb image: vaticle-ubuntu-22.04 dependencies: - build From 3d2d6eeda7825d56d6ef0c1f5e663dba8e393ce2 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Fri, 30 Jun 2023 11:37:15 +0100 Subject: [PATCH 62/84] Refactoring --- tests/behaviour/connection/database/steps.rs | 22 ++++---------------- tests/behaviour/connection/steps.rs | 2 +- tests/behaviour/connection/user/steps.rs | 22 ++++++++------------ tests/behaviour/util.rs | 21 ++++++++++++------- 4 files changed, 28 insertions(+), 39 deletions(-) diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index e768278d..0032c013 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -30,6 +30,7 @@ use tokio::time::{sleep, Duration}; use typedb_client::Database; use crate::{ + assert_with_waiting, behaviour::{util, util::create_database_with_waiting, Context}, generic_step_impl, }; @@ -85,33 +86,18 @@ generic_step_impl! { #[step(expr = "connection has database: {word}")] async fn connection_has_database(context: &mut Context, name: String) { - let mut waiting_iterations = 0; - while !context.databases.contains(name.clone()).await.unwrap() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - waiting_iterations += 1; - }; - assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection doesn't contain database {name}."); + assert_with_waiting!(context.databases.contains(name.clone()).await.unwrap(), "Connection doesn't contain database {name}."); } #[step(expr = "connection has database(s):")] async fn connection_has_databases(context: &mut Context, step: &Step) { let names: HashSet = util::iter_table(step).map(|name| name.to_owned()).collect(); - let mut waiting_iterations = 0; - while context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>() != names && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - waiting_iterations += 1; - }; - assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection doesn't contain at least one of the databases."); + assert_with_waiting!(context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>() == names, "Connection doesn't contain at least one of the databases."); } #[step(expr = "connection does not have database: {word}")] async fn connection_does_not_have_database(context: &mut Context, name: String) { - let mut waiting_iterations = 0; - while context.databases.contains(name.clone()).await.unwrap() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - waiting_iterations += 1; - }; - assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection contains database {name}."); + assert_with_waiting!(!context.databases.contains(name.clone()).await.unwrap(), "Connection contains database {name}."); } #[step(expr = "connection does not have database(s):")] diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index bae07111..47b905a9 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -24,7 +24,7 @@ use std::path::PathBuf; use cucumber::{given, then, when}; use typedb_client::{Connection, Credential}; -use crate::{behaviour::Context, generic_step_impl}; +use crate::{assert_with_waiting, behaviour::Context, generic_step_impl}; generic_step_impl! { #[step("typedb starts")] diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 413ce1b2..5f36c7ca 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -23,7 +23,7 @@ use cucumber::{given, then, when}; use tokio::time::{sleep, Duration}; use typedb_client::Result as TypeDBResult; -use crate::{assert_err, behaviour::Context, generic_step_impl}; +use crate::{assert_err, assert_with_waiting, behaviour::Context, generic_step_impl}; generic_step_impl! { @@ -51,12 +51,13 @@ generic_step_impl! { #[step(expr = "users contains: {word}")] async fn users_contains(context: &mut Context, username: String) -> TypeDBResult { - let mut waiting_iterations = 0; - while !context.users.contains(username.clone()).await? && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - waiting_iterations += 1; - }; - assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "User not exists."); + assert_with_waiting!(context.users.contains(username.clone()).await?, "User doesn't exist."); + // let mut waiting_iterations = 0; + // while !context.users.contains(username.clone()).await? && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { + // sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + // waiting_iterations += 1; + // }; + // assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "User not exists."); Ok(()) } @@ -67,12 +68,7 @@ generic_step_impl! { #[step(expr = "users not contains: {word}")] async fn users_not_contains(context: &mut Context, username: String) -> TypeDBResult { - let mut waiting_iterations = 0; - while context.users.contains(username.clone()).await? && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - waiting_iterations += 1; - }; - assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "User exists."); + assert_with_waiting!(!context.users.contains(username.clone()).await?, "User exists."); Ok(()) } diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index 07e596c3..75fc9e42 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -38,7 +38,7 @@ use typedb_client::{ }; use typeql_lang::{parse_patterns, parse_query, pattern::Variable}; -use crate::behaviour::Context; +use crate::{assert_with_waiting, behaviour::Context}; pub fn iter_table(step: &Step) -> impl Iterator { step.table().unwrap().rows.iter().flatten().map(String::as_str) @@ -188,10 +188,17 @@ pub async fn match_answer_rule(answer_identifiers: &HashMap<&str, &str>, answer: } pub async fn create_database_with_waiting(databases: &DatabaseManager, name: String) { - let mut waiting_iterations = 0; - while databases.create(name.clone()).await.is_err() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - waiting_iterations += 1; - } - assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Database {name} couldn't be created."); + assert_with_waiting!(!databases.create(name.clone()).await.is_err(), "Database {name} couldn't be created."); +} + +#[macro_export] +macro_rules! assert_with_waiting { + ($expr:expr, $message:expr) => {{ + let mut waiting_iterations = 0; + while !($expr) && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { + sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + waiting_iterations += 1; + } + assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, $message); + }}; } From 110cc52704757c71e5623a9c84bc5c0297c4ade7 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 3 Jul 2023 17:49:40 +0100 Subject: [PATCH 63/84] Fix PR issues --- src/connection/network/proto/message.rs | 10 +- src/user/user_manager.rs | 60 +++---- tests/BUILD | 1 - tests/behaviour/connection/database/mod.rs | 1 - tests/behaviour/connection/database/steps.rs | 37 ++--- tests/behaviour/connection/steps.rs | 9 +- tests/behaviour/connection/user/mod.rs | 1 - tests/behaviour/connection/user/steps.rs | 21 +-- tests/behaviour/mod.rs | 41 +++-- tests/behaviour/util.rs | 23 +-- tests/integration/user.rs | 157 ++++++++++++------- 11 files changed, 192 insertions(+), 169 deletions(-) diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index d065405a..d4b0e892 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -404,7 +404,7 @@ impl TryFromProto for TransactionResponse { impl FromProto for Response { fn from_proto(proto: user_manager::all::Res) -> Self { - Self::UsersAll { users: proto.users.iter().map(|user| User::from_proto(user.clone())).collect::>() } + Self::UsersAll { users: proto.users.into_iter().map(User::from_proto).collect() } } } @@ -415,14 +415,14 @@ impl FromProto for Response { } impl FromProto for Response { - fn from_proto(_: user_manager::create::Res) -> Self { - Self::UsersCreate {} + fn from_proto(_proto: user_manager::create::Res) -> Self { + Self::UsersCreate } } impl FromProto for Response { - fn from_proto(_: user_manager::delete::Res) -> Self { - Self::UsersDelete {} + fn from_proto(_proto: user_manager::delete::Res) -> Self { + Self::UsersDelete } } diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index 5aef6fa3..b108c460 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -19,7 +19,9 @@ * under the License. */ -use crate::{common::Result, error::ConnectionError, Connection, User}; +use std::future::Future; + +use crate::{common::Result, connection::ServerConnection, error::ConnectionError, Connection, User}; #[derive(Clone, Debug)] pub struct UserManager { @@ -45,16 +47,10 @@ impl UserManager { } pub async fn contains(&self, username: String) -> Result { - let mut error_buffer = Vec::with_capacity(self.connection.server_count()); - for server_connection in self.connection.connections() { - match server_connection.contains_user(username.clone()).await { - Ok(contains) => { - return Ok(contains); - } - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), - } - } - Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + self.run_on_nodes(username, |server_connection, username| async move { + server_connection.contains_user(username).await + }) + .await } pub async fn create(&self, username: String, password: String) -> Result<()> { @@ -71,24 +67,26 @@ impl UserManager { } pub async fn delete(&self, username: String) -> Result<()> { - let mut error_buffer = Vec::with_capacity(self.connection.server_count()); - for server_connection in self.connection.connections() { - match server_connection.delete_user(username.clone()).await { - Ok(()) => { - return Ok(()); - } - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), - } - } - Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + self.run_on_nodes(username, |server_connection, username| async move { + server_connection.delete_user(username).await + }) + .await } - pub async fn get(&self, username: String) -> Result { + pub async fn get(&self, username: String) -> Result> { + self.run_on_nodes( + username, + |server_connection, username| async move { server_connection.get_user(username).await }, + ) + .await + } + + pub async fn set_password(&self, username: String, password: String) -> Result<()> { let mut error_buffer = Vec::with_capacity(self.connection.server_count()); for server_connection in self.connection.connections() { - match server_connection.get_user(username.clone()).await { - Ok(user) => { - return Ok(user.unwrap()); + match server_connection.set_user_password(username.clone(), password.clone()).await { + Ok(()) => { + return Ok(()); } Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), } @@ -96,12 +94,16 @@ impl UserManager { Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? } - pub async fn set_password(&self, username: String, password: String) -> Result<()> { + async fn run_on_nodes(&self, username: String, task: F) -> Result + where + F: Fn(ServerConnection, String) -> P, + P: Future>, + { let mut error_buffer = Vec::with_capacity(self.connection.server_count()); for server_connection in self.connection.connections() { - match server_connection.set_user_password(username.clone(), password.clone()).await { - Ok(()) => { - return Ok(()); + match task(server_connection.clone(), username.clone()).await { + Ok(res) => { + return Ok(res); } Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), } diff --git a/tests/BUILD b/tests/BUILD index a8f0c489..34ea87e3 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -73,7 +73,6 @@ rust_test( "@vaticle_typedb_behaviour//typeql/reasoner:type-hierarchy.feature", "@vaticle_typedb_behaviour//typeql/reasoner:value-predicate.feature", "@vaticle_typedb_behaviour//typeql/reasoner:variable-roles.feature", -# "behaviour/connection/database/debug.feature", ], ) diff --git a/tests/behaviour/connection/database/mod.rs b/tests/behaviour/connection/database/mod.rs index 2d368c7f..ac57625c 100644 --- a/tests/behaviour/connection/database/mod.rs +++ b/tests/behaviour/connection/database/mod.rs @@ -32,5 +32,4 @@ async fn test() { // @vaticle_typedb_behaviour is stored in a directory that is a sibling to // the working directory. assert!(Context::test("../vaticle_typedb_behaviour/connection/database.feature").await); - // assert!(Context::test("tests/behaviour/connection/database/debug.feature").await); } diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index 0032c013..7110d918 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -24,14 +24,18 @@ use std::collections::HashSet; use cucumber::{gherkin::Step, given, then, when}; use futures::{ future::{join_all, try_join_all}, - TryFutureExt, + stream, StreamExt, TryFutureExt, }; -use tokio::time::{sleep, Duration}; +use tokio::time::sleep; use typedb_client::Database; use crate::{ - assert_with_waiting, - behaviour::{util, util::create_database_with_waiting, Context}, + assert_with_timeout, + behaviour::{ + util, + util::{create_database_with_waiting, iter_table}, + Context, + }, generic_step_impl, }; @@ -86,36 +90,25 @@ generic_step_impl! { #[step(expr = "connection has database: {word}")] async fn connection_has_database(context: &mut Context, name: String) { - assert_with_waiting!(context.databases.contains(name.clone()).await.unwrap(), "Connection doesn't contain database {name}."); + assert_with_timeout!(context.databases.contains(name.clone()).await.unwrap(), "Connection doesn't contain database {name}."); } #[step(expr = "connection has database(s):")] async fn connection_has_databases(context: &mut Context, step: &Step) { let names: HashSet = util::iter_table(step).map(|name| name.to_owned()).collect(); - assert_with_waiting!(context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>() == names, "Connection doesn't contain at least one of the databases."); + assert_with_timeout!(context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>() == names, "Connection doesn't contain at least one of the databases."); } #[step(expr = "connection does not have database: {word}")] async fn connection_does_not_have_database(context: &mut Context, name: String) { - assert_with_waiting!(!context.databases.contains(name.clone()).await.unwrap(), "Connection contains database {name}."); + assert_with_timeout!(!context.databases.contains(name.clone()).await.unwrap(), "Connection contains database {name}."); } #[step(expr = "connection does not have database(s):")] async fn connection_does_not_have_databases(context: &mut Context, step: &Step) { - let mut waiting_iterations = 0; - while waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - let all_databases: HashSet = - context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect(); - let mut all_not_contained = true; - for name in util::iter_table(step) { - all_not_contained &= !all_databases.contains(name); - } - if all_not_contained { - break; - } - sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - waiting_iterations += 1; - }; - assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection contains at least one of the databases."); + assert_with_timeout!( + stream::iter(iter_table(step)).all(|name| async { !context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>().contains(name) }).await, + "Connection contains at least one of the databases.", + ) } } diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index 47b905a9..1e576cef 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -19,12 +19,10 @@ * under the License. */ -use std::path::PathBuf; - use cucumber::{given, then, when}; use typedb_client::{Connection, Credential}; -use crate::{assert_with_waiting, behaviour::Context, generic_step_impl}; +use crate::{behaviour::Context, generic_step_impl}; generic_step_impl! { #[step("typedb starts")] @@ -40,10 +38,7 @@ generic_step_impl! { Credential::with_tls( &login.as_str(), &password.as_str(), - Some(&PathBuf::from( - std::env::var("ROOT_CA") - .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), - )), + Some(&context.tls_root_ca), ).unwrap(), ); context.set_connection(connection.unwrap()); diff --git a/tests/behaviour/connection/user/mod.rs b/tests/behaviour/connection/user/mod.rs index 5015e350..3c0e54d5 100644 --- a/tests/behaviour/connection/user/mod.rs +++ b/tests/behaviour/connection/user/mod.rs @@ -32,5 +32,4 @@ async fn test() { // @vaticle_typedb_behaviour is stored in a directory that is a sibling to // the working directory. assert!(Context::test("../vaticle_typedb_behaviour/connection/user.feature").await); - // assert!(Context::test("tests/behaviour/debug.feature").await); } diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 5f36c7ca..3353f6c7 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -20,13 +20,12 @@ */ use cucumber::{given, then, when}; -use tokio::time::{sleep, Duration}; +use tokio::time::sleep; use typedb_client::Result as TypeDBResult; -use crate::{assert_err, assert_with_waiting, behaviour::Context, generic_step_impl}; +use crate::{assert_err, assert_with_timeout, behaviour::Context, generic_step_impl}; generic_step_impl! { - #[step(expr = "users get all")] async fn users_get_all(context: &mut Context) -> TypeDBResult { context.users.all().await?; @@ -51,13 +50,7 @@ generic_step_impl! { #[step(expr = "users contains: {word}")] async fn users_contains(context: &mut Context, username: String) -> TypeDBResult { - assert_with_waiting!(context.users.contains(username.clone()).await?, "User doesn't exist."); - // let mut waiting_iterations = 0; - // while !context.users.contains(username.clone()).await? && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - // sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - // waiting_iterations += 1; - // }; - // assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "User not exists."); + assert_with_timeout!(context.users.contains(username.clone()).await?, "User doesn't exist."); Ok(()) } @@ -68,7 +61,7 @@ generic_step_impl! { #[step(expr = "users not contains: {word}")] async fn users_not_contains(context: &mut Context, username: String) -> TypeDBResult { - assert_with_waiting!(!context.users.contains(username.clone()).await?, "User exists."); + assert_with_timeout!(!context.users.contains(username.clone()).await?, "User exists."); Ok(()) } @@ -101,7 +94,8 @@ generic_step_impl! { async fn user_password_update(context: &mut Context, password_old: String, password_new: String) -> TypeDBResult { let connected_user = context.connection.username.clone(); assert!(connected_user.is_some()); - context.users.get(connected_user.unwrap()).await?.password_update(&context.connection, password_old, password_new).await + context.users.get(connected_user.unwrap()).await?.unwrap() + .password_update(&context.connection, password_old, password_new).await } #[step(expr = "user password update: {word}, {word}; throws exception")] @@ -122,7 +116,7 @@ generic_step_impl! { #[step(expr = "user expiry-seconds")] async fn user_expiry_seconds(context: &mut Context) -> TypeDBResult { assert!(context.connection.username.is_some()); - assert!(context.users.get(context.connection.username.clone().unwrap()).await?.password_expiry_seconds.is_some()); + assert!(context.users.get(context.connection.username.clone().unwrap()).await?.unwrap().password_expiry_seconds.is_some()); Ok(()) } @@ -130,5 +124,4 @@ generic_step_impl! { async fn get_connected_user(context: &mut Context) { assert!(context.connection.username.is_some()); } - } diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 224b78b6..c55145f3 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -42,11 +42,12 @@ use self::session_tracker::SessionTracker; #[derive(Debug, World)] pub struct Context { + pub tls_root_ca: PathBuf, pub connection: Connection, pub databases: DatabaseManager, + pub users: UserManager, pub session_trackers: Vec, pub things: HashMap>, - pub users: UserManager, pub answer: Vec, pub answer_group: Vec, pub numeric_answer: Option, @@ -59,8 +60,8 @@ impl Context { const DEFAULT_DATABASE: &'static str = "test"; const ADMIN_USERNAME: &'static str = "admin"; const ADMIN_PASSWORD: &'static str = "password"; - const PAUSE_BETWEEN_STEP_CHECKS_MS: u64 = 250; - const STEP_CHECKS_ITERATIONS_LIMIT: u64 = 20; + const PAUSE_BETWEEN_STEP_CHECKS: Duration = Duration::from_millis(250); + const STEP_CHECKS_ITERATIONS_LIMIT: u32 = 20; async fn test(glob: &'static str) -> bool { let default_panic = std::panic::take_hook(); @@ -89,19 +90,12 @@ impl Context { } pub async fn after_scenario(&mut self) -> TypeDBResult { - sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; + sleep(Context::PAUSE_BETWEEN_STEP_CHECKS).await; self.set_connection( Connection::new_encrypted( &["localhost:11729", "localhost:21729", "localhost:31729"], - Credential::with_tls( - &Context::ADMIN_USERNAME, - &Context::ADMIN_PASSWORD, - Some(&PathBuf::from( - std::env::var("ROOT_CA") - .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), - )), - ) - .unwrap(), + Credential::with_tls(&Context::ADMIN_USERNAME, &Context::ADMIN_PASSWORD, Some(&self.tls_root_ca)) + .unwrap(), ) .unwrap(), ); @@ -198,32 +192,33 @@ impl Context { self.connection = new_connection; self.databases = DatabaseManager::new(self.connection.clone()); self.users = UserManager::new(self.connection.clone()); + self.session_trackers.clear(); + self.answer.clear(); + self.answer_group.clear(); + self.numeric_answer = None; + self.numeric_answer_group.clear(); } } impl Default for Context { fn default() -> Self { + let tls_root_ca = PathBuf::from( + std::env::var("ROOT_CA").expect("ROOT_CA environment variable needs to be set for cluster tests to run"), + ); let connection = Connection::new_encrypted( &["localhost:11729", "localhost:21729", "localhost:31729"], - Credential::with_tls( - &Context::ADMIN_USERNAME, - &Context::ADMIN_PASSWORD, - Some(&PathBuf::from( - std::env::var("ROOT_CA") - .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), - )), - ) - .unwrap(), + Credential::with_tls(&Context::ADMIN_USERNAME, &Context::ADMIN_PASSWORD, Some(&tls_root_ca)).unwrap(), ) .unwrap(); let databases = DatabaseManager::new(connection.clone()); let users = UserManager::new(connection.clone()); Self { + tls_root_ca, connection, databases, + users, session_trackers: Vec::new(), things: HashMap::new(), - users, answer: Vec::new(), answer_group: Vec::new(), numeric_answer: None, diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index 75fc9e42..e5046377 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -28,7 +28,7 @@ use futures::{ TryFutureExt, TryStreamExt, }; use regex::{Captures, Regex}; -use tokio::time::{sleep, Duration}; +use tokio::time::sleep; use typedb_client::{ answer::ConceptMap, concept::{Annotation, Attribute, Concept, Entity, Relation, Value}, @@ -38,7 +38,7 @@ use typedb_client::{ }; use typeql_lang::{parse_patterns, parse_query, pattern::Variable}; -use crate::{assert_with_waiting, behaviour::Context}; +use crate::{assert_with_timeout, behaviour::Context}; pub fn iter_table(step: &Step) -> impl Iterator { step.table().unwrap().rows.iter().flatten().map(String::as_str) @@ -188,17 +188,20 @@ pub async fn match_answer_rule(answer_identifiers: &HashMap<&str, &str>, answer: } pub async fn create_database_with_waiting(databases: &DatabaseManager, name: String) { - assert_with_waiting!(!databases.create(name.clone()).await.is_err(), "Database {name} couldn't be created."); + assert_with_timeout!(databases.create(name.clone()).await.is_ok(), "Database {name} couldn't be created.", name); } #[macro_export] -macro_rules! assert_with_waiting { - ($expr:expr, $message:expr) => {{ - let mut waiting_iterations = 0; - while !($expr) && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - sleep(Duration::from_millis(Context::PAUSE_BETWEEN_STEP_CHECKS_MS)).await; - waiting_iterations += 1; +macro_rules! assert_with_timeout { + ($expr:expr, $message:expr $(, $($arg:expr),*)?) => {{ + 't: { + for _ in 0..Context::STEP_CHECKS_ITERATIONS_LIMIT { + if $expr { + break 't; + } + sleep(Context::PAUSE_BETWEEN_STEP_CHECKS).await; + } + panic!($message); } - assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, $message); }}; } diff --git a/tests/integration/user.rs b/tests/integration/user.rs index 156b5c0c..9928a079 100644 --- a/tests/integration/user.rs +++ b/tests/integration/user.rs @@ -21,69 +21,114 @@ use futures::future::try_join_all; use serial_test::serial; -use typedb_client::{Connection, Result as TypeDBResult, UserManager}; +use tokio::time::{sleep, Duration}; +use typedb_client::{Result as TypeDBResult, UserManager}; use super::common; -use crate::test_for_each_arg; -test_for_each_arg! { - { - cluster => common::new_cluster_connection().unwrap(), - } +#[tokio::test] +#[serial] +async fn create_and_delete_user() -> TypeDBResult { + let connection = common::new_cluster_connection()?; + let users = UserManager::new(connection); - async fn create_and_delete_user(connection: Connection) -> TypeDBResult { - let users = UserManager::new(connection); - users.create(String::from("user"), String::from("password")).await?; - assert_eq!(2, users.all().await?.len()); - users.delete(String::from("user")).await?; - assert_eq!(1, users.all().await?.len()); - Ok(()) - } + try_join_all( + users + .all() + .await + .unwrap() + .into_iter() + .filter(|user| &user.username != "admin") + .map(|user| users.delete(user.username)), + ) + .await?; + sleep(Duration::from_millis(2000)).await; - async fn create_users_and_purge(connection: Connection) -> TypeDBResult { - let users = UserManager::new(connection); - users.create(String::from("user"), String::from("password")).await?; - assert_eq!(2, users.all().await?.len()); - users.create(String::from("user2"), String::from("password2")).await?; - assert_eq!(3, users.all().await?.len()); + users.create(String::from("user"), String::from("password")).await?; + assert_eq!(2, users.all().await?.len()); + users.delete(String::from("user")).await?; + assert_eq!(1, users.all().await?.len()); + Ok(()) +} + +#[tokio::test] +#[serial] +async fn create_users_and_purge() -> TypeDBResult { + let connection = common::new_cluster_connection()?; + let users = UserManager::new(connection); + + try_join_all( + users + .all() + .await + .unwrap() + .into_iter() + .filter(|user| &user.username != "admin") + .map(|user| users.delete(user.username)), + ) + .await?; + sleep(Duration::from_millis(2000)).await; + + users.create(String::from("user"), String::from("password")).await?; + assert_eq!(2, users.all().await?.len()); + users.create(String::from("user2"), String::from("password2")).await?; + assert_eq!(3, users.all().await?.len()); + + try_join_all( + users + .all() + .await + .unwrap() + .into_iter() + .filter(|user| &user.username != "admin") + .map(|user| users.delete(user.username)), + ) + .await?; + sleep(Duration::from_millis(2000)).await; + + assert_eq!(1, users.all().await?.len()); + Ok(()) +} + +#[tokio::test] +#[serial] +async fn create_users_reconnect_and_purge() -> TypeDBResult { + let connection = common::new_cluster_connection()?; + assert_eq!(3, connection.server_count()); + let users = UserManager::new(connection); - try_join_all( - users - .all() - .await - .unwrap() - .into_iter() - .filter(|user| dbg!(&user.username) != "admin") - .map(|user| users.delete(dbg!(user.username))), - ) - .await?; - assert_eq!(1, users.all().await?.len()); - Ok(()) - } + try_join_all( + users + .all() + .await + .unwrap() + .into_iter() + .filter(|user| &user.username != "admin") + .map(|user| users.delete(user.username)), + ) + .await?; + sleep(Duration::from_millis(2000)).await; - async fn create_users_reconnect_and_purge(connection: Connection) -> TypeDBResult { - assert_eq!(3, connection.server_count()); + users.create(String::from("user"), String::from("password")).await?; + assert_eq!(2, users.all().await?.len()); + users.create(String::from("user2"), String::from("password2")).await?; + assert_eq!(3, users.all().await?.len()); - let users = UserManager::new(connection); - users.create(String::from("user"), String::from("password")).await?; - assert_eq!(2, users.all().await?.len()); - users.create(String::from("user2"), String::from("password2")).await?; - assert_eq!(3, users.all().await?.len()); + let connection = common::new_cluster_connection().unwrap(); + let users = UserManager::new(connection); + assert_eq!(3, users.all().await?.len()); + try_join_all( + users + .all() + .await + .unwrap() + .into_iter() + .filter(|user| &user.username != "admin") + .map(|user| users.delete(user.username)), + ) + .await?; + sleep(Duration::from_millis(2000)).await; - let connection = common::new_cluster_connection().unwrap(); - let users = UserManager::new(connection); - assert_eq!(3, users.all().await?.len()); - try_join_all( - users - .all() - .await - .unwrap() - .into_iter() - .filter(|user| dbg!(&user.username) != "admin") - .map(|user| users.delete(dbg!(user.username))), - ) - .await?; - assert_eq!(1, users.all().await?.len()); - Ok(()) - } + assert_eq!(1, users.all().await?.len()); + Ok(()) } From 77e6435ec9ce3900c9b2164fdf0053cbf17f6a3d Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 3 Jul 2023 18:04:18 +0100 Subject: [PATCH 64/84] Fix PR issues, fmt --- src/connection/network/proto/message.rs | 8 ++-- tests/behaviour/connection/database/steps.rs | 40 +++++++++++++++++--- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index d4b0e892..e027c600 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -433,14 +433,14 @@ impl FromProto for Response { } impl FromProto for Response { - fn from_proto(_: user_manager::password_set::Res) -> Self { - Self::UsersPasswordSet {} + fn from_proto(_proto: user_manager::password_set::Res) -> Self { + Self::UsersPasswordSet } } impl FromProto for Response { - fn from_proto(_: user::password_update::Res) -> Self { - Self::UserPasswordUpdate {} + fn from_proto(_proto: user::password_update::Res) -> Self { + Self::UserPasswordUpdate } } diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index 7110d918..0be35636 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -54,7 +54,8 @@ generic_step_impl! { #[step(expr = "connection create databases in parallel:")] async fn connection_create_databases_in_parallel(context: &mut Context, step: &Step) { - join_all(util::iter_table(step).map(|name| create_database_with_waiting(&context.databases, name.to_string()))).await; + join_all(util::iter_table(step).map(|name| create_database_with_waiting(&context.databases, name.to_string()))) + .await; } #[step(expr = "connection delete database: {word}")] @@ -90,24 +91,53 @@ generic_step_impl! { #[step(expr = "connection has database: {word}")] async fn connection_has_database(context: &mut Context, name: String) { - assert_with_timeout!(context.databases.contains(name.clone()).await.unwrap(), "Connection doesn't contain database {name}."); + assert_with_timeout!( + context.databases.contains(name.clone()).await.unwrap(), + "Connection doesn't contain database {name}." + ); } #[step(expr = "connection has database(s):")] async fn connection_has_databases(context: &mut Context, step: &Step) { let names: HashSet = util::iter_table(step).map(|name| name.to_owned()).collect(); - assert_with_timeout!(context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>() == names, "Connection doesn't contain at least one of the databases."); + assert_with_timeout!( + context + .databases + .all() + .await + .unwrap() + .into_iter() + .map(|db| db.name().to_owned()) + .collect::>() + == names, + "Connection doesn't contain at least one of the databases." + ); } #[step(expr = "connection does not have database: {word}")] async fn connection_does_not_have_database(context: &mut Context, name: String) { - assert_with_timeout!(!context.databases.contains(name.clone()).await.unwrap(), "Connection contains database {name}."); + assert_with_timeout!( + !context.databases.contains(name.clone()).await.unwrap(), + "Connection contains database {name}." + ); } #[step(expr = "connection does not have database(s):")] async fn connection_does_not_have_databases(context: &mut Context, step: &Step) { assert_with_timeout!( - stream::iter(iter_table(step)).all(|name| async { !context.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>().contains(name) }).await, + stream::iter(iter_table(step)) + .all(|name| async { + !context + .databases + .all() + .await + .unwrap() + .into_iter() + .map(|db| db.name().to_owned()) + .collect::>() + .contains(name) + }) + .await, "Connection contains at least one of the databases.", ) } From a0f345aa74c005ddc17d11b9a205c97b221e6ebe Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 4 Jul 2023 11:16:26 +0100 Subject: [PATCH 65/84] Fix PR issues --- tests/behaviour/connection/steps.rs | 26 +++++++++++++++++--------- tests/behaviour/mod.rs | 9 +++++++++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index 1e576cef..f358e117 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -20,9 +20,10 @@ */ use cucumber::{given, then, when}; -use typedb_client::{Connection, Credential}; +use tokio::time::sleep; +use typedb_client::{Connection, Credential, Result as TypeDBResult}; -use crate::{behaviour::Context, generic_step_impl}; +use crate::{assert_with_timeout, behaviour::Context, generic_step_impl}; generic_step_impl! { #[step("typedb starts")] @@ -48,13 +49,20 @@ generic_step_impl! { async fn connection_has_been_opened(_: &mut Context) {} #[step("connection does not have any database")] - async fn connection_does_not_have_any_database(context: &mut Context) { - let mut waiting_iterations = 0; - while !context.databases.all().await.unwrap().is_empty() && waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT { - let _ = context.after_scenario().await; - waiting_iterations += 1; - }; - assert!(waiting_iterations < Context::STEP_CHECKS_ITERATIONS_LIMIT, "Connection has at least one database."); + async fn connection_does_not_have_any_database(context: &mut Context) -> TypeDBResult { + assert_with_timeout!( + { + if context.databases.all().await.unwrap().is_empty() { + true + } else { + context.cleanup_databases().await?; + context.cleanup_users().await?; + false + } + }, + "Connection has at least one database.", + ); + Ok(()) } #[step("connection closes")] diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index c55145f3..1d336370 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -99,7 +99,16 @@ impl Context { ) .unwrap(), ); + self.cleanup_databases().await?; + self.cleanup_users().await + } + + pub async fn cleanup_databases(&mut self) -> TypeDBResult { try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await?; + Ok(()) + } + + pub async fn cleanup_users(&mut self) -> TypeDBResult { try_join_all( self.users .all() From b4549d6e184b1ab1cdcfdc57f6fe4ae1cdfc3d4d Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 4 Jul 2023 11:41:30 +0100 Subject: [PATCH 66/84] Fix PR issues --- src/connection/connection.rs | 8 ++++++-- tests/behaviour/connection/user/steps.rs | 8 ++++---- tests/integration/user.rs | 1 - 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index d485f8b6..86d6b7ba 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -55,7 +55,7 @@ use crate::{ pub struct Connection { server_connections: HashMap, background_runtime: Arc, - pub username: Option, + username: Option, } impl Connection { @@ -119,7 +119,7 @@ impl Connection { self.background_runtime.force_close() } - pub fn server_count(&self) -> usize { + pub(crate) fn server_count(&self) -> usize { self.server_connections.len() } @@ -137,6 +137,10 @@ impl Connection { self.server_connections.values() } + pub fn user(&self) -> Option { + self.username.clone() + } + pub(crate) fn unable_to_connect_error(&self) -> Error { Error::Connection(ConnectionError::ClusterUnableToConnect( self.addresses().map(Address::to_string).collect::>().join(","), diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 3353f6c7..52a5628d 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -92,7 +92,7 @@ generic_step_impl! { #[step(expr = "user password update: {word}, {word}")] async fn user_password_update(context: &mut Context, password_old: String, password_new: String) -> TypeDBResult { - let connected_user = context.connection.username.clone(); + let connected_user = context.connection.user(); assert!(connected_user.is_some()); context.users.get(connected_user.unwrap()).await?.unwrap() .password_update(&context.connection, password_old, password_new).await @@ -115,13 +115,13 @@ generic_step_impl! { #[step(expr = "user expiry-seconds")] async fn user_expiry_seconds(context: &mut Context) -> TypeDBResult { - assert!(context.connection.username.is_some()); - assert!(context.users.get(context.connection.username.clone().unwrap()).await?.unwrap().password_expiry_seconds.is_some()); + assert!(context.connection.user().is_some()); + assert!(context.users.get(context.connection.user().unwrap()).await?.unwrap().password_expiry_seconds.is_some()); Ok(()) } #[step(expr = "get connected user")] async fn get_connected_user(context: &mut Context) { - assert!(context.connection.username.is_some()); + assert!(context.connection.user().is_some()); } } diff --git a/tests/integration/user.rs b/tests/integration/user.rs index 9928a079..0f8adf63 100644 --- a/tests/integration/user.rs +++ b/tests/integration/user.rs @@ -94,7 +94,6 @@ async fn create_users_and_purge() -> TypeDBResult { #[serial] async fn create_users_reconnect_and_purge() -> TypeDBResult { let connection = common::new_cluster_connection()?; - assert_eq!(3, connection.server_count()); let users = UserManager::new(connection); try_join_all( From b3a0b16c4b14909d1072c78e709db39f5f5b4d2f Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 4 Jul 2023 13:55:51 +0100 Subject: [PATCH 67/84] Fix PR issues --- src/connection/connection.rs | 8 ++-- src/user/user.rs | 7 +--- src/user/user_manager.rs | 74 +++++++++++++++--------------------- tests/integration/user.rs | 56 +++++++-------------------- 4 files changed, 48 insertions(+), 97 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 86d6b7ba..3f6269ca 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -335,14 +335,14 @@ impl ServerConnection { } } - pub(crate) async fn create_user(&self, username: String, password: String) -> Result<()> { + pub(crate) async fn create_user(&self, username: String, password: String) -> Result { match self.request_async(Request::UsersCreate { username, password }).await? { Response::UsersCreate => Ok(()), other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), } } - pub(crate) async fn delete_user(&self, username: String) -> Result<()> { + pub(crate) async fn delete_user(&self, username: String) -> Result { match self.request_async(Request::UsersDelete { username }).await? { Response::UsersDelete => Ok(()), other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), @@ -356,7 +356,7 @@ impl ServerConnection { } } - pub(crate) async fn set_user_password(&self, username: String, password: String) -> Result<()> { + pub(crate) async fn set_user_password(&self, username: String, password: String) -> Result { match self.request_async(Request::UsersPasswordSet { username, password }).await? { Response::UsersPasswordSet => Ok(()), other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), @@ -368,7 +368,7 @@ impl ServerConnection { username: String, password_old: String, password_new: String, - ) -> Result<()> { + ) -> Result { match self.request_async(Request::UserPasswordUpdate { username, password_old, password_new }).await? { Response::UserPasswordUpdate => Ok(()), other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), diff --git a/src/user/user.rs b/src/user/user.rs index 1231af7f..146b914e 100644 --- a/src/user/user.rs +++ b/src/user/user.rs @@ -28,12 +28,7 @@ pub struct User { } impl User { - pub async fn password_update( - &self, - connection: &Connection, - password_old: String, - password_new: String, - ) -> Result<()> { + pub async fn password_update(&self, connection: &Connection, password_old: String, password_new: String) -> Result { let mut error_buffer = Vec::with_capacity(connection.server_count()); for server_connection in connection.connections() { match server_connection diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index b108c460..fc875962 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -34,74 +34,60 @@ impl UserManager { } pub async fn all(&self) -> Result> { - let mut error_buffer = Vec::with_capacity(self.connection.server_count()); - for server_connection in self.connection.connections() { - match server_connection.all_users().await { - Ok(list) => { - return Ok(list); - } - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), - } - } - Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + self.run_any_node(|server_connection: ServerConnection| async move { server_connection.all_users().await }) + .await } pub async fn contains(&self, username: String) -> Result { - self.run_on_nodes(username, |server_connection, username| async move { - server_connection.contains_user(username).await + self.run_any_node(|server_connection: ServerConnection| { + let username = username.clone(); + async move { server_connection.contains_user(username).await } }) .await } - pub async fn create(&self, username: String, password: String) -> Result<()> { - let mut error_buffer = Vec::with_capacity(self.connection.server_count()); - for server_connection in self.connection.connections() { - match server_connection.create_user(username.clone(), password.clone()).await { - Ok(()) => { - return Ok(()); - } - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), - } - } - Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + pub async fn create(&self, username: String, password: String) -> Result { + self.run_any_node(|server_connection: ServerConnection| { + let username = username.clone(); + let password = password.clone(); + async move { server_connection.create_user(username, password).await } + }) + .await } - pub async fn delete(&self, username: String) -> Result<()> { - self.run_on_nodes(username, |server_connection, username| async move { - server_connection.delete_user(username).await + pub async fn delete(&self, username: String) -> Result { + self.run_any_node(|server_connection: ServerConnection| { + let username = username.clone(); + async move { server_connection.delete_user(username).await } }) .await } pub async fn get(&self, username: String) -> Result> { - self.run_on_nodes( - username, - |server_connection, username| async move { server_connection.get_user(username).await }, - ) + self.run_any_node(|server_connection: ServerConnection| { + let username = username.clone(); + async move { server_connection.get_user(username).await } + }) .await } - pub async fn set_password(&self, username: String, password: String) -> Result<()> { - let mut error_buffer = Vec::with_capacity(self.connection.server_count()); - for server_connection in self.connection.connections() { - match server_connection.set_user_password(username.clone(), password.clone()).await { - Ok(()) => { - return Ok(()); - } - Err(err) => error_buffer.push(format!("- {}: {}", server_connection.address(), err)), - } - } - Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + pub async fn set_password(&self, username: String, password: String) -> Result { + self.run_any_node(|server_connection: ServerConnection| { + let username = username.clone(); + let password = password.clone(); + async move { server_connection.set_user_password(username, password).await } + }) + .await } - async fn run_on_nodes(&self, username: String, task: F) -> Result + async fn run_any_node(&self, task: F) -> Result where - F: Fn(ServerConnection, String) -> P, + F: Fn(ServerConnection) -> P, P: Future>, { let mut error_buffer = Vec::with_capacity(self.connection.server_count()); for server_connection in self.connection.connections() { - match task(server_connection.clone(), username.clone()).await { + match task(server_connection.clone()).await { Ok(res) => { return Ok(res); } diff --git a/tests/integration/user.rs b/tests/integration/user.rs index 0f8adf63..f8f30251 100644 --- a/tests/integration/user.rs +++ b/tests/integration/user.rs @@ -32,16 +32,7 @@ async fn create_and_delete_user() -> TypeDBResult { let connection = common::new_cluster_connection()?; let users = UserManager::new(connection); - try_join_all( - users - .all() - .await - .unwrap() - .into_iter() - .filter(|user| &user.username != "admin") - .map(|user| users.delete(user.username)), - ) - .await?; + cleanup_users(&users).await?; sleep(Duration::from_millis(2000)).await; users.create(String::from("user"), String::from("password")).await?; @@ -57,16 +48,7 @@ async fn create_users_and_purge() -> TypeDBResult { let connection = common::new_cluster_connection()?; let users = UserManager::new(connection); - try_join_all( - users - .all() - .await - .unwrap() - .into_iter() - .filter(|user| &user.username != "admin") - .map(|user| users.delete(user.username)), - ) - .await?; + cleanup_users(&users).await?; sleep(Duration::from_millis(2000)).await; users.create(String::from("user"), String::from("password")).await?; @@ -74,16 +56,7 @@ async fn create_users_and_purge() -> TypeDBResult { users.create(String::from("user2"), String::from("password2")).await?; assert_eq!(3, users.all().await?.len()); - try_join_all( - users - .all() - .await - .unwrap() - .into_iter() - .filter(|user| &user.username != "admin") - .map(|user| users.delete(user.username)), - ) - .await?; + cleanup_users(&users).await?; sleep(Duration::from_millis(2000)).await; assert_eq!(1, users.all().await?.len()); @@ -96,16 +69,7 @@ async fn create_users_reconnect_and_purge() -> TypeDBResult { let connection = common::new_cluster_connection()?; let users = UserManager::new(connection); - try_join_all( - users - .all() - .await - .unwrap() - .into_iter() - .filter(|user| &user.username != "admin") - .map(|user| users.delete(user.username)), - ) - .await?; + cleanup_users(&users).await?; sleep(Duration::from_millis(2000)).await; users.create(String::from("user"), String::from("password")).await?; @@ -116,6 +80,15 @@ async fn create_users_reconnect_and_purge() -> TypeDBResult { let connection = common::new_cluster_connection().unwrap(); let users = UserManager::new(connection); assert_eq!(3, users.all().await?.len()); + + cleanup_users(&users).await?; + sleep(Duration::from_millis(2000)).await; + + assert_eq!(1, users.all().await?.len()); + Ok(()) +} + +async fn cleanup_users(users: &UserManager) -> TypeDBResult { try_join_all( users .all() @@ -126,8 +99,5 @@ async fn create_users_reconnect_and_purge() -> TypeDBResult { .map(|user| users.delete(user.username)), ) .await?; - sleep(Duration::from_millis(2000)).await; - - assert_eq!(1, users.all().await?.len()); Ok(()) } From d360d89aff14147aaee78e7887487a011b816237 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 4 Jul 2023 14:11:01 +0100 Subject: [PATCH 68/84] Fix PR issues --- tests/behaviour/connection/database/steps.rs | 26 ++++---------------- tests/behaviour/connection/user/steps.rs | 4 +-- tests/behaviour/mod.rs | 9 ++++++- tests/behaviour/util.rs | 2 +- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index 0be35636..609432c4 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -33,7 +33,7 @@ use crate::{ assert_with_timeout, behaviour::{ util, - util::{create_database_with_waiting, iter_table}, + util::{create_database_with_timeout, iter_table}, Context, }, generic_step_impl, @@ -42,7 +42,7 @@ use crate::{ generic_step_impl! { #[step(expr = "connection create database: {word}")] pub async fn connection_create_database(context: &mut Context, name: String) { - create_database_with_waiting(&context.databases, name).await; + create_database_with_timeout(&context.databases, name).await; } #[step(expr = "connection create database(s):")] @@ -54,7 +54,7 @@ generic_step_impl! { #[step(expr = "connection create databases in parallel:")] async fn connection_create_databases_in_parallel(context: &mut Context, step: &Step) { - join_all(util::iter_table(step).map(|name| create_database_with_waiting(&context.databases, name.to_string()))) + join_all(util::iter_table(step).map(|name| create_database_with_timeout(&context.databases, name.to_string()))) .await; } @@ -101,15 +101,7 @@ generic_step_impl! { async fn connection_has_databases(context: &mut Context, step: &Step) { let names: HashSet = util::iter_table(step).map(|name| name.to_owned()).collect(); assert_with_timeout!( - context - .databases - .all() - .await - .unwrap() - .into_iter() - .map(|db| db.name().to_owned()) - .collect::>() - == names, + context.all_databases().await == names, "Connection doesn't contain at least one of the databases." ); } @@ -127,15 +119,7 @@ generic_step_impl! { assert_with_timeout!( stream::iter(iter_table(step)) .all(|name| async { - !context - .databases - .all() - .await - .unwrap() - .into_iter() - .map(|db| db.name().to_owned()) - .collect::>() - .contains(name) + !context.all_databases().await.contains(name) }) .await, "Connection contains at least one of the databases.", diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 52a5628d..5ddb57b7 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -50,7 +50,7 @@ generic_step_impl! { #[step(expr = "users contains: {word}")] async fn users_contains(context: &mut Context, username: String) -> TypeDBResult { - assert_with_timeout!(context.users.contains(username.clone()).await?, "User doesn't exist."); + assert_with_timeout!(context.users.contains(username.clone()).await?, "User {username} doesn't exist."); Ok(()) } @@ -61,7 +61,7 @@ generic_step_impl! { #[step(expr = "users not contains: {word}")] async fn users_not_contains(context: &mut Context, username: String) -> TypeDBResult { - assert_with_timeout!(!context.users.contains(username.clone()).await?, "User exists."); + assert_with_timeout!(!context.users.contains(username.clone()).await?, "User {username} exists."); Ok(()) } diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 1d336370..8ca93d4c 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -26,7 +26,10 @@ mod session_tracker; mod typeql; mod util; -use std::{collections::HashMap, path::PathBuf}; +use std::{ + collections::{HashMap, HashSet}, + path::PathBuf, +}; use cucumber::{StatsWriter, World}; use futures::future::try_join_all; @@ -103,6 +106,10 @@ impl Context { self.cleanup_users().await } + pub async fn all_databases(&self) -> HashSet { + self.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>() + } + pub async fn cleanup_databases(&mut self) -> TypeDBResult { try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await?; Ok(()) diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index e5046377..bf515d7c 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -187,7 +187,7 @@ pub async fn match_answer_rule(answer_identifiers: &HashMap<&str, &str>, answer: && then == Variable::Thing(answer.then.clone()) } -pub async fn create_database_with_waiting(databases: &DatabaseManager, name: String) { +pub async fn create_database_with_timeout(databases: &DatabaseManager, name: String) { assert_with_timeout!(databases.create(name.clone()).await.is_ok(), "Database {name} couldn't be created.", name); } From 5cda74a096e18b78702542166d22539405199992 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 4 Jul 2023 14:55:10 +0100 Subject: [PATCH 69/84] Fix PR issues --- src/connection/connection.rs | 2 +- tests/behaviour/connection/database/steps.rs | 6 ++++-- tests/behaviour/connection/user/steps.rs | 12 ++++++------ tests/behaviour/util.rs | 6 +++--- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 3f6269ca..5dab7d04 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -137,7 +137,7 @@ impl Connection { self.server_connections.values() } - pub fn user(&self) -> Option { + pub fn username(&self) -> Option { self.username.clone() } diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index 609432c4..4e36ed6d 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -93,7 +93,8 @@ generic_step_impl! { async fn connection_has_database(context: &mut Context, name: String) { assert_with_timeout!( context.databases.contains(name.clone()).await.unwrap(), - "Connection doesn't contain database {name}." + "Connection doesn't contain database {}.", + name, ); } @@ -110,7 +111,8 @@ generic_step_impl! { async fn connection_does_not_have_database(context: &mut Context, name: String) { assert_with_timeout!( !context.databases.contains(name.clone()).await.unwrap(), - "Connection contains database {name}." + "Connection contains database {}.", + name, ); } diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 5ddb57b7..375a6816 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -50,7 +50,7 @@ generic_step_impl! { #[step(expr = "users contains: {word}")] async fn users_contains(context: &mut Context, username: String) -> TypeDBResult { - assert_with_timeout!(context.users.contains(username.clone()).await?, "User {username} doesn't exist."); + assert_with_timeout!(context.users.contains(username.clone()).await?, "User {} doesn't exist.", username); Ok(()) } @@ -61,7 +61,7 @@ generic_step_impl! { #[step(expr = "users not contains: {word}")] async fn users_not_contains(context: &mut Context, username: String) -> TypeDBResult { - assert_with_timeout!(!context.users.contains(username.clone()).await?, "User {username} exists."); + assert_with_timeout!(!context.users.contains(username.clone()).await?, "User {} exists.", username); Ok(()) } @@ -92,7 +92,7 @@ generic_step_impl! { #[step(expr = "user password update: {word}, {word}")] async fn user_password_update(context: &mut Context, password_old: String, password_new: String) -> TypeDBResult { - let connected_user = context.connection.user(); + let connected_user = context.connection.username(); assert!(connected_user.is_some()); context.users.get(connected_user.unwrap()).await?.unwrap() .password_update(&context.connection, password_old, password_new).await @@ -115,13 +115,13 @@ generic_step_impl! { #[step(expr = "user expiry-seconds")] async fn user_expiry_seconds(context: &mut Context) -> TypeDBResult { - assert!(context.connection.user().is_some()); - assert!(context.users.get(context.connection.user().unwrap()).await?.unwrap().password_expiry_seconds.is_some()); + assert!(context.connection.username().is_some()); + assert!(context.users.get(context.connection.username().unwrap()).await?.unwrap().password_expiry_seconds.is_some()); Ok(()) } #[step(expr = "get connected user")] async fn get_connected_user(context: &mut Context) { - assert!(context.connection.user().is_some()); + assert!(context.connection.username().is_some()); } } diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index bf515d7c..70316908 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -188,12 +188,12 @@ pub async fn match_answer_rule(answer_identifiers: &HashMap<&str, &str>, answer: } pub async fn create_database_with_timeout(databases: &DatabaseManager, name: String) { - assert_with_timeout!(databases.create(name.clone()).await.is_ok(), "Database {name} couldn't be created.", name); + assert_with_timeout!(databases.create(name.clone()).await.is_ok(), "Database {} couldn't be created.", name); } #[macro_export] macro_rules! assert_with_timeout { - ($expr:expr, $message:expr $(, $($arg:expr),*)?) => {{ + ($expr:expr, $message:expr $(, $($arg:expr),+)? $(,)?) => {{ 't: { for _ in 0..Context::STEP_CHECKS_ITERATIONS_LIMIT { if $expr { @@ -201,7 +201,7 @@ macro_rules! assert_with_timeout { } sleep(Context::PAUSE_BETWEEN_STEP_CHECKS).await; } - panic!($message); + panic!(concat!("Timed out: ", $message) $(, $($arg),+)?); } }}; } From d3c7ea7baeb25ac569852869e6d91b8fa120f90a Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 5 Jul 2023 08:17:42 +0100 Subject: [PATCH 70/84] Testing connection (wip) --- src/connection/connection.rs | 36 +++++++++++++++++++++-------------- tests/integration/user.rs | 37 +++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 5dab7d04..154cc131 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -73,23 +73,31 @@ impl Connection { let addresses = Self::fetch_current_addresses(background_runtime.clone(), init_addresses, credential.clone())?; // FIXME: switch to connecting to all servers (not at least one) when the issues with auth in Cluster will be fixed (PR#482) - let mut server_connections = HashMap::new(); - let mut error_buffer = Vec::new(); + // let mut server_connections = HashMap::new(); + // let mut error_buffer = Vec::new(); + // for address in addresses { + // let server_connection = + // ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone()); + // match server_connection { + // Ok(server_connection) => { + // server_connections.insert(address, server_connection); + // } + // Err(err) => { + // error_buffer.push(format!("- {}: {}", address, err)); + // } + // } + // } + // if server_connections.is_empty() { + // Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + // } + let mut server_connections = HashMap::with_capacity(addresses.len()); for address in addresses { let server_connection = - ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone()); - match server_connection { - Ok(server_connection) => { - server_connections.insert(address, server_connection); - } - Err(err) => { - error_buffer.push(format!("- {}: {}", address, err)); - } - } - } - if server_connections.is_empty() { - Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? + ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone())?; + server_connections.insert(address, server_connection); + if server_connection. } + Ok(Self { server_connections, background_runtime, username: Some(credential.username().to_string()) }) } diff --git a/tests/integration/user.rs b/tests/integration/user.rs index f8f30251..4d5681e6 100644 --- a/tests/integration/user.rs +++ b/tests/integration/user.rs @@ -19,10 +19,11 @@ * under the License. */ +use std::path::PathBuf; use futures::future::try_join_all; use serial_test::serial; use tokio::time::{sleep, Duration}; -use typedb_client::{Result as TypeDBResult, UserManager}; +use typedb_client::{Connection, Credential, Result as TypeDBResult, UserManager}; use super::common; @@ -88,6 +89,40 @@ async fn create_users_reconnect_and_purge() -> TypeDBResult { Ok(()) } +#[tokio::test] +#[serial] +async fn create_user_and_connect() -> TypeDBResult { + let connection = common::new_cluster_connection()?; + let users = UserManager::new(connection); + + cleanup_users(&users).await?; + sleep(Duration::from_millis(2000)).await; + + users.create(String::from("user"), String::from("password")).await?; + assert_eq!(2, users.all().await?.len()); + + let connection = Connection::new_encrypted( + &["localhost:11729", "localhost:21729", "localhost:31729"], + Credential::with_tls( + "user", + "password", + Some(&PathBuf::from( + std::env::var("ROOT_CA") + .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), + )), + )?, + )?; + let users = UserManager::new(connection); + + let connection = common::new_cluster_connection()?; + let users = UserManager::new(connection); + cleanup_users(&users).await?; + sleep(Duration::from_millis(2000)).await; + + assert_eq!(1, users.all().await?.len()); + Ok(()) +} + async fn cleanup_users(users: &UserManager) -> TypeDBResult { try_join_all( users From c4606d70076e20e93f1b79a66759fdbfc9ef2eb6 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 5 Jul 2023 10:07:02 +0100 Subject: [PATCH 71/84] ConnectionError only if all connections fail, but store all server connections --- src/connection/connection.rs | 34 +++++++++++++--------------------- src/connection/network/stub.rs | 5 ++--- tests/integration/user.rs | 3 +-- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 154cc131..8c1a32fb 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -72,33 +72,18 @@ impl Connection { let init_addresses = init_addresses.iter().map(|addr| addr.as_ref().parse()).try_collect()?; let addresses = Self::fetch_current_addresses(background_runtime.clone(), init_addresses, credential.clone())?; - // FIXME: switch to connecting to all servers (not at least one) when the issues with auth in Cluster will be fixed (PR#482) - // let mut server_connections = HashMap::new(); - // let mut error_buffer = Vec::new(); - // for address in addresses { - // let server_connection = - // ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone()); - // match server_connection { - // Ok(server_connection) => { - // server_connections.insert(address, server_connection); - // } - // Err(err) => { - // error_buffer.push(format!("- {}: {}", address, err)); - // } - // } - // } - // if server_connections.is_empty() { - // Err(ConnectionError::ClusterAllNodesFailed(error_buffer.join("\n")))? - // } let mut server_connections = HashMap::with_capacity(addresses.len()); for address in addresses { let server_connection = ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone())?; server_connections.insert(address, server_connection); - if server_connection. } - - Ok(Self { server_connections, background_runtime, username: Some(credential.username().to_string()) }) + let (ok, errors): (Vec<_>, Vec<_>) = server_connections.clone().into_iter().map(|(_addr, conn)| conn.validate()).partition(Result::is_ok); + if ok.is_empty() { + Err(ConnectionError::ClusterAllNodesFailed(errors.into_iter().map(|err| err.unwrap_err().to_string()).collect::>().join("\n")))? + } else { + Ok(Self { server_connections, background_runtime, username: Some(credential.username().to_string()) }) + } } fn fetch_current_addresses( @@ -219,6 +204,13 @@ impl ServerConnection { } } + pub(crate) fn validate(&self) -> Result { + match self.request_blocking(Request::DatabasesAll)? { + Response::DatabasesAll { databases: _ } => Ok(()), + _other => Err(ConnectionError::UnableToConnect().into()), + } + } + pub(crate) async fn database_exists(&self, database_name: String) -> Result { match self.request_async(Request::DatabasesContains { database_name }).await? { Response::DatabasesContains { contains } => Ok(contains), diff --git a/src/connection/network/stub.rs b/src/connection/network/stub.rs index 3589cef8..80fcab99 100644 --- a/src/connection/network/stub.rs +++ b/src/connection/network/stub.rs @@ -44,9 +44,8 @@ pub(super) struct RPCStub { impl RPCStub { pub(super) async fn new(channel: Channel, call_credentials: Option>) -> Result { - let this = Self { grpc: GRPC::new(channel), call_credentials }; - let mut this = this.validated().await?; - this.renew_token().await?; + let mut this = Self { grpc: GRPC::new(channel), call_credentials }; + let _ = this.renew_token().await.is_ok(); Ok(this) } diff --git a/tests/integration/user.rs b/tests/integration/user.rs index 4d5681e6..ffc990d1 100644 --- a/tests/integration/user.rs +++ b/tests/integration/user.rs @@ -101,7 +101,7 @@ async fn create_user_and_connect() -> TypeDBResult { users.create(String::from("user"), String::from("password")).await?; assert_eq!(2, users.all().await?.len()); - let connection = Connection::new_encrypted( + Connection::new_encrypted( &["localhost:11729", "localhost:21729", "localhost:31729"], Credential::with_tls( "user", @@ -112,7 +112,6 @@ async fn create_user_and_connect() -> TypeDBResult { )), )?, )?; - let users = UserManager::new(connection); let connection = common::new_cluster_connection()?; let users = UserManager::new(connection); From 17cb0d8d4c104671824ef67048d759eb315e3b91 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 5 Jul 2023 10:19:44 +0100 Subject: [PATCH 72/84] fmt --- src/connection/connection.rs | 7 +++++-- tests/integration/user.rs | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 8c1a32fb..030403a3 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -78,9 +78,12 @@ impl Connection { ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone())?; server_connections.insert(address, server_connection); } - let (ok, errors): (Vec<_>, Vec<_>) = server_connections.clone().into_iter().map(|(_addr, conn)| conn.validate()).partition(Result::is_ok); + let (ok, errors): (Vec<_>, Vec<_>) = + server_connections.clone().into_iter().map(|(_addr, conn)| conn.validate()).partition(Result::is_ok); if ok.is_empty() { - Err(ConnectionError::ClusterAllNodesFailed(errors.into_iter().map(|err| err.unwrap_err().to_string()).collect::>().join("\n")))? + Err(ConnectionError::ClusterAllNodesFailed( + errors.into_iter().map(|err| err.unwrap_err().to_string()).collect::>().join("\n"), + ))? } else { Ok(Self { server_connections, background_runtime, username: Some(credential.username().to_string()) }) } diff --git a/tests/integration/user.rs b/tests/integration/user.rs index ffc990d1..f831d7f8 100644 --- a/tests/integration/user.rs +++ b/tests/integration/user.rs @@ -20,6 +20,7 @@ */ use std::path::PathBuf; + use futures::future::try_join_all; use serial_test::serial; use tokio::time::{sleep, Duration}; From a63a5d491939c8ce94cb124f1409e0630b900fcb Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 5 Jul 2023 10:49:50 +0100 Subject: [PATCH 73/84] UserManager::current_user() --- src/connection/connection.rs | 2 +- src/user/user_manager.rs | 7 +++++++ tests/behaviour/connection/user/steps.rs | 15 ++++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 030403a3..49f7318f 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -133,7 +133,7 @@ impl Connection { self.server_connections.values() } - pub fn username(&self) -> Option { + pub(crate) fn username(&self) -> Option { self.username.clone() } diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index fc875962..859631ab 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -33,6 +33,13 @@ impl UserManager { Self { connection } } + pub async fn current_user(&self) -> Result> { + match self.connection.username() { + Some(username) => self.get(username).await, + None => Ok(None), + } + } + pub async fn all(&self) -> Result> { self.run_any_node(|server_connection: ServerConnection| async move { server_connection.all_users().await }) .await diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 375a6816..68ce0ef6 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -92,10 +92,9 @@ generic_step_impl! { #[step(expr = "user password update: {word}, {word}")] async fn user_password_update(context: &mut Context, password_old: String, password_new: String) -> TypeDBResult { - let connected_user = context.connection.username(); + let connected_user = context.users.current_user().await?; assert!(connected_user.is_some()); - context.users.get(connected_user.unwrap()).await?.unwrap() - .password_update(&context.connection, password_old, password_new).await + connected_user.unwrap().password_update(&context.connection, password_old, password_new).await } #[step(expr = "user password update: {word}, {word}; throws exception")] @@ -115,13 +114,15 @@ generic_step_impl! { #[step(expr = "user expiry-seconds")] async fn user_expiry_seconds(context: &mut Context) -> TypeDBResult { - assert!(context.connection.username().is_some()); - assert!(context.users.get(context.connection.username().unwrap()).await?.unwrap().password_expiry_seconds.is_some()); + let connected_user = context.users.current_user().await?; + assert!(connected_user.is_some()); + assert!(connected_user.unwrap().password_expiry_seconds.is_some()); Ok(()) } #[step(expr = "get connected user")] - async fn get_connected_user(context: &mut Context) { - assert!(context.connection.username().is_some()); + async fn get_connected_user(context: &mut Context) -> TypeDBResult { + assert!(context.users.current_user().await?.is_some()); + Ok(()) } } From 3d24e2986ae7cd84efd0f0d2a9a3a73f4cc6a56c Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 5 Jul 2023 11:05:40 +0100 Subject: [PATCH 74/84] Refactor assert_with_timeout!() --- tests/behaviour/connection/database/steps.rs | 8 +++----- tests/behaviour/connection/user/steps.rs | 4 ++-- tests/behaviour/util.rs | 6 +++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index 4e36ed6d..3fddf8f5 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -93,8 +93,7 @@ generic_step_impl! { async fn connection_has_database(context: &mut Context, name: String) { assert_with_timeout!( context.databases.contains(name.clone()).await.unwrap(), - "Connection doesn't contain database {}.", - name, + "Connection doesn't contain database {name}.", ); } @@ -103,7 +102,7 @@ generic_step_impl! { let names: HashSet = util::iter_table(step).map(|name| name.to_owned()).collect(); assert_with_timeout!( context.all_databases().await == names, - "Connection doesn't contain at least one of the databases." + "Connection doesn't contain at least one of the databases.", ); } @@ -111,8 +110,7 @@ generic_step_impl! { async fn connection_does_not_have_database(context: &mut Context, name: String) { assert_with_timeout!( !context.databases.contains(name.clone()).await.unwrap(), - "Connection contains database {}.", - name, + "Connection contains database {name}.", ); } diff --git a/tests/behaviour/connection/user/steps.rs b/tests/behaviour/connection/user/steps.rs index 68ce0ef6..c674ea3b 100644 --- a/tests/behaviour/connection/user/steps.rs +++ b/tests/behaviour/connection/user/steps.rs @@ -50,7 +50,7 @@ generic_step_impl! { #[step(expr = "users contains: {word}")] async fn users_contains(context: &mut Context, username: String) -> TypeDBResult { - assert_with_timeout!(context.users.contains(username.clone()).await?, "User {} doesn't exist.", username); + assert_with_timeout!(context.users.contains(username.clone()).await?, "User {username} doesn't exist."); Ok(()) } @@ -61,7 +61,7 @@ generic_step_impl! { #[step(expr = "users not contains: {word}")] async fn users_not_contains(context: &mut Context, username: String) -> TypeDBResult { - assert_with_timeout!(!context.users.contains(username.clone()).await?, "User {} exists.", username); + assert_with_timeout!(!context.users.contains(username.clone()).await?, "User {username} exists."); Ok(()) } diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index 70316908..b636b09a 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -188,12 +188,12 @@ pub async fn match_answer_rule(answer_identifiers: &HashMap<&str, &str>, answer: } pub async fn create_database_with_timeout(databases: &DatabaseManager, name: String) { - assert_with_timeout!(databases.create(name.clone()).await.is_ok(), "Database {} couldn't be created.", name); + assert_with_timeout!(databases.create(name.clone()).await.is_ok(), "Database {name} couldn't be created."); } #[macro_export] macro_rules! assert_with_timeout { - ($expr:expr, $message:expr $(, $($arg:expr),+)? $(,)?) => {{ + ($expr:expr, $message:expr $(, $arg:expr)* $(,)?) => {{ 't: { for _ in 0..Context::STEP_CHECKS_ITERATIONS_LIMIT { if $expr { @@ -201,7 +201,7 @@ macro_rules! assert_with_timeout { } sleep(Context::PAUSE_BETWEEN_STEP_CHECKS).await; } - panic!(concat!("Timed out: ", $message) $(, $($arg),+)?); + panic!($message $(, $arg)*); } }}; } From f794775ac1812b219567f1c3eeb6eb8af155cfd9 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 5 Jul 2023 11:17:18 +0100 Subject: [PATCH 75/84] Ignore errors on cleanup --- tests/behaviour/connection/steps.rs | 22 ++++++++++------------ tests/behaviour/mod.rs | 23 ++++++++++------------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/tests/behaviour/connection/steps.rs b/tests/behaviour/connection/steps.rs index f358e117..db51fa98 100644 --- a/tests/behaviour/connection/steps.rs +++ b/tests/behaviour/connection/steps.rs @@ -50,18 +50,16 @@ generic_step_impl! { #[step("connection does not have any database")] async fn connection_does_not_have_any_database(context: &mut Context) -> TypeDBResult { - assert_with_timeout!( - { - if context.databases.all().await.unwrap().is_empty() { - true - } else { - context.cleanup_databases().await?; - context.cleanup_users().await?; - false - } - }, - "Connection has at least one database.", - ); + if context.databases.all().await?.is_empty() { + assert_with_timeout!( + { + context.cleanup_databases().await; + context.cleanup_users().await; + context.databases.all().await?.is_empty() + }, + "Connection has at least one database.", + ); + } Ok(()) } diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 8ca93d4c..04336b39 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -97,26 +97,24 @@ impl Context { self.set_connection( Connection::new_encrypted( &["localhost:11729", "localhost:21729", "localhost:31729"], - Credential::with_tls(&Context::ADMIN_USERNAME, &Context::ADMIN_PASSWORD, Some(&self.tls_root_ca)) - .unwrap(), - ) - .unwrap(), + Credential::with_tls(&Context::ADMIN_USERNAME, &Context::ADMIN_PASSWORD, Some(&self.tls_root_ca))?, + )?, ); - self.cleanup_databases().await?; - self.cleanup_users().await + self.cleanup_databases().await; + self.cleanup_users().await; + Ok(()) } pub async fn all_databases(&self) -> HashSet { self.databases.all().await.unwrap().into_iter().map(|db| db.name().to_owned()).collect::>() } - pub async fn cleanup_databases(&mut self) -> TypeDBResult { - try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await?; - Ok(()) + pub async fn cleanup_databases(&mut self) { + let _ = try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await; } - pub async fn cleanup_users(&mut self) -> TypeDBResult { - try_join_all( + pub async fn cleanup_users(&mut self) { + let _ = try_join_all( self.users .all() .await @@ -125,8 +123,7 @@ impl Context { .filter(|user| user.username != Context::ADMIN_USERNAME) .map(|user| self.users.delete(user.username)), ) - .await?; - Ok(()) + .await; } pub fn transaction(&self) -> &Transaction { From a36c1f74483a0768ceeea93e4a167c1a16aed693 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 5 Jul 2023 11:35:58 +0100 Subject: [PATCH 76/84] Fix PR issues --- src/connection/connection.rs | 4 ++-- src/connection/network/stub.rs | 8 +++++--- src/connection/network/transmitter/rpc.rs | 4 ++-- src/user/user_manager.rs | 2 +- tests/behaviour/mod.rs | 10 ++++------ 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 49f7318f..51d7eeb7 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -133,8 +133,8 @@ impl Connection { self.server_connections.values() } - pub(crate) fn username(&self) -> Option { - self.username.clone() + pub(crate) fn username(&self) -> Option<&str> { + self.username.as_ref().and_then(|s| Some(s.as_ref())) } pub(crate) fn unable_to_connect_error(&self) -> Error { diff --git a/src/connection/network/stub.rs b/src/connection/network/stub.rs index 80fcab99..eac97a8a 100644 --- a/src/connection/network/stub.rs +++ b/src/connection/network/stub.rs @@ -43,10 +43,12 @@ pub(super) struct RPCStub { } impl RPCStub { - pub(super) async fn new(channel: Channel, call_credentials: Option>) -> Result { + pub(super) async fn new(channel: Channel, call_credentials: Option>) -> Self { let mut this = Self { grpc: GRPC::new(channel), call_credentials }; - let _ = this.renew_token().await.is_ok(); - Ok(this) + if let Err(err) = this.renew_token().await { + debug!("{err:?}"); + } + this } pub(super) async fn validated(mut self) -> Result { diff --git a/src/connection/network/transmitter/rpc.rs b/src/connection/network/transmitter/rpc.rs index cf48fcbc..df13e27e 100644 --- a/src/connection/network/transmitter/rpc.rs +++ b/src/connection/network/transmitter/rpc.rs @@ -58,7 +58,7 @@ impl RPCTransmitter { let (shutdown_sink, shutdown_source) = unbounded_async(); runtime.run_blocking(async move { let channel = open_plaintext_channel(address); - let rpc = RPCStub::new(channel, None).await?; + let rpc = RPCStub::new(channel, None).await; tokio::spawn(Self::dispatcher_loop(rpc, request_source, shutdown_source)); Ok::<(), Error>(()) })?; @@ -74,7 +74,7 @@ impl RPCTransmitter { let (shutdown_sink, shutdown_source) = unbounded_async(); runtime.run_blocking(async move { let (channel, call_credentials) = open_encrypted_channel(address, credential)?; - let rpc = RPCStub::new(channel, Some(call_credentials)).await?; + let rpc = RPCStub::new(channel, Some(call_credentials)).await; tokio::spawn(Self::dispatcher_loop(rpc, request_source, shutdown_source)); Ok::<(), Error>(()) })?; diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index 859631ab..eddd839a 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -35,7 +35,7 @@ impl UserManager { pub async fn current_user(&self) -> Result> { match self.connection.username() { - Some(username) => self.get(username).await, + Some(username) => self.get(username.to_string()).await, None => Ok(None), } } diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 04336b39..a66c8b8c 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -94,12 +94,10 @@ impl Context { pub async fn after_scenario(&mut self) -> TypeDBResult { sleep(Context::PAUSE_BETWEEN_STEP_CHECKS).await; - self.set_connection( - Connection::new_encrypted( - &["localhost:11729", "localhost:21729", "localhost:31729"], - Credential::with_tls(&Context::ADMIN_USERNAME, &Context::ADMIN_PASSWORD, Some(&self.tls_root_ca))?, - )?, - ); + self.set_connection(Connection::new_encrypted( + &["localhost:11729", "localhost:21729", "localhost:31729"], + Credential::with_tls(&Context::ADMIN_USERNAME, &Context::ADMIN_PASSWORD, Some(&self.tls_root_ca))?, + )?); self.cleanup_databases().await; self.cleanup_users().await; Ok(()) From e73fccfc5c232699b188151c3d98eee6c149537d Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 5 Jul 2023 11:55:21 +0100 Subject: [PATCH 77/84] Fix PR issues --- src/connection/connection.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 51d7eeb7..3444d067 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -72,17 +72,19 @@ impl Connection { let init_addresses = init_addresses.iter().map(|addr| addr.as_ref().parse()).try_collect()?; let addresses = Self::fetch_current_addresses(background_runtime.clone(), init_addresses, credential.clone())?; - let mut server_connections = HashMap::with_capacity(addresses.len()); - for address in addresses { - let server_connection = - ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone())?; - server_connections.insert(address, server_connection); - } - let (ok, errors): (Vec<_>, Vec<_>) = - server_connections.clone().into_iter().map(|(_addr, conn)| conn.validate()).partition(Result::is_ok); - if ok.is_empty() { + let server_connections: HashMap = addresses + .into_iter() + .map(|address| { + ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone()) + .map(|result| (address, result)) + }) + .try_collect()?; + + let errors: Vec = + server_connections.iter().map(|(_addr, conn)| conn.validate()).filter_map(Result::err).collect(); + if errors.len() == server_connections.len() { Err(ConnectionError::ClusterAllNodesFailed( - errors.into_iter().map(|err| err.unwrap_err().to_string()).collect::>().join("\n"), + errors.into_iter().map(|err| err.to_string()).collect::>().join("\n"), ))? } else { Ok(Self { server_connections, background_runtime, username: Some(credential.username().to_string()) }) From 6b20cd03ee08cb7a774d21c1ab8eff9fe76e4658 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 5 Jul 2023 14:23:26 +0100 Subject: [PATCH 78/84] Run integration tests in serial --- tests/integration/common.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/common.rs b/tests/integration/common.rs index 14011dc6..68d7cd6e 100644 --- a/tests/integration/common.rs +++ b/tests/integration/common.rs @@ -87,7 +87,7 @@ macro_rules! test_for_each_arg { use super::*; $( #[tokio::test] - #[serial($mod)] + #[serial] $( #[ $extra_anno ] )* pub async fn $test() { _impl::$test($arg).await.unwrap(); From 63f0ec31a38d1deb2145fabe7a438cd8aad8cdad Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 5 Jul 2023 14:28:56 +0100 Subject: [PATCH 79/84] Refactoring --- tests/behaviour/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index a66c8b8c..ca8dc7ab 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -108,11 +108,11 @@ impl Context { } pub async fn cleanup_databases(&mut self) { - let _ = try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await; + try_join_all(self.databases.all().await.unwrap().into_iter().map(Database::delete)).await.ok(); } pub async fn cleanup_users(&mut self) { - let _ = try_join_all( + try_join_all( self.users .all() .await @@ -121,7 +121,8 @@ impl Context { .filter(|user| user.username != Context::ADMIN_USERNAME) .map(|user| self.users.delete(user.username)), ) - .await; + .await + .ok(); } pub fn transaction(&self) -> &Transaction { From 0a2f4619f85c18c007efd803a9c13e90e91c6e31 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 5 Jul 2023 14:40:11 +0100 Subject: [PATCH 80/84] Cleanup --- src/connection/connection.rs | 6 +++--- src/connection/network/stub.rs | 9 ++------- src/user/user_manager.rs | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 3444d067..3397ba03 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -76,12 +76,12 @@ impl Connection { .into_iter() .map(|address| { ServerConnection::new_encrypted(background_runtime.clone(), address.clone(), credential.clone()) - .map(|result| (address, result)) + .map(|server_connection| (address, server_connection)) }) .try_collect()?; let errors: Vec = - server_connections.iter().map(|(_addr, conn)| conn.validate()).filter_map(Result::err).collect(); + server_connections.values().map(|conn| conn.validate()).filter_map(Result::err).collect(); if errors.len() == server_connections.len() { Err(ConnectionError::ClusterAllNodesFailed( errors.into_iter().map(|err| err.to_string()).collect::>().join("\n"), @@ -136,7 +136,7 @@ impl Connection { } pub(crate) fn username(&self) -> Option<&str> { - self.username.as_ref().and_then(|s| Some(s.as_ref())) + self.username.as_ref().map(|s| s.as_ref()) } pub(crate) fn unable_to_connect_error(&self) -> Error { diff --git a/src/connection/network/stub.rs b/src/connection/network/stub.rs index eac97a8a..94e6cbd1 100644 --- a/src/connection/network/stub.rs +++ b/src/connection/network/stub.rs @@ -22,7 +22,7 @@ use std::sync::Arc; use futures::{future::BoxFuture, FutureExt, TryFutureExt}; -use log::{debug, trace}; +use log::{debug, trace, warn}; use tokio::sync::mpsc::{unbounded_channel as unbounded_async, UnboundedSender}; use tokio_stream::wrappers::UnboundedReceiverStream; use tonic::{Response, Status, Streaming}; @@ -46,16 +46,11 @@ impl RPCStub { pub(super) async fn new(channel: Channel, call_credentials: Option>) -> Self { let mut this = Self { grpc: GRPC::new(channel), call_credentials }; if let Err(err) = this.renew_token().await { - debug!("{err:?}"); + warn!("{err:?}"); } this } - pub(super) async fn validated(mut self) -> Result { - self.databases_all(database_manager::all::Req {}).await?; - Ok(self) - } - async fn call_with_auto_renew_token(&mut self, call: F) -> Result where for<'a> F: Fn(&'a mut Self) -> BoxFuture<'a, Result>, diff --git a/src/user/user_manager.rs b/src/user/user_manager.rs index eddd839a..9db24f26 100644 --- a/src/user/user_manager.rs +++ b/src/user/user_manager.rs @@ -35,7 +35,7 @@ impl UserManager { pub async fn current_user(&self) -> Result> { match self.connection.username() { - Some(username) => self.get(username.to_string()).await, + Some(username) => self.get(username.to_owned()).await, None => Ok(None), } } From 0eb99a033d48273525496a896fc43df2ab844d51 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 6 Jul 2023 11:57:01 +0100 Subject: [PATCH 81/84] Remove redundant flag --- .factory/automation.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.factory/automation.yml b/.factory/automation.yml index c3db3d0a..e89da045 100644 --- a/.factory/automation.yml +++ b/.factory/automation.yml @@ -58,7 +58,7 @@ build: bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh bazel run @vaticle_dependencies//distribution/artifact:create-netrc tools/start-core-server.sh - bazel test //tests --test_arg=-- --test_arg=integration::queries::core --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + bazel test //tests --test_arg=-- --test_arg=integration::queries::core --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 tools/stop-core-server.sh exit $TEST_SUCCESS test-integration-cluster: @@ -72,7 +72,7 @@ build: bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh bazel run @vaticle_dependencies//distribution/artifact:create-netrc source tools/start-cluster-servers.sh # use source to receive export vars - bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=integration::queries::cluster --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=integration::queries::cluster --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 tools/stop-cluster-servers.sh exit $TEST_SUCCESS test-integration-runtimes: @@ -85,7 +85,7 @@ build: bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh bazel run @vaticle_dependencies//distribution/artifact:create-netrc source tools/start-cluster-servers.sh # use source to receive export vars - bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=integration::runtimes --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=integration::runtimes --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 tools/stop-cluster-servers.sh exit $TEST_SUCCESS test-behaviour-concept: From 04febddeefe19e555ace1f11d1ea0f9dce28ed5a Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 6 Jul 2023 13:00:53 +0100 Subject: [PATCH 82/84] Rename constants --- tests/behaviour/mod.rs | 6 +++--- tests/behaviour/util.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index ca8dc7ab..186ca88f 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -63,8 +63,8 @@ impl Context { const DEFAULT_DATABASE: &'static str = "test"; const ADMIN_USERNAME: &'static str = "admin"; const ADMIN_PASSWORD: &'static str = "password"; - const PAUSE_BETWEEN_STEP_CHECKS: Duration = Duration::from_millis(250); - const STEP_CHECKS_ITERATIONS_LIMIT: u32 = 20; + const STEP_REATTEMPT_SLEEP: Duration = Duration::from_millis(250); + const STEP_REATTEMPT_LIMIT: u32 = 20; async fn test(glob: &'static str) -> bool { let default_panic = std::panic::take_hook(); @@ -93,7 +93,7 @@ impl Context { } pub async fn after_scenario(&mut self) -> TypeDBResult { - sleep(Context::PAUSE_BETWEEN_STEP_CHECKS).await; + sleep(Context::STEP_REATTEMPT_SLEEP).await; self.set_connection(Connection::new_encrypted( &["localhost:11729", "localhost:21729", "localhost:31729"], Credential::with_tls(&Context::ADMIN_USERNAME, &Context::ADMIN_PASSWORD, Some(&self.tls_root_ca))?, diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index b636b09a..145fabfa 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -195,11 +195,11 @@ pub async fn create_database_with_timeout(databases: &DatabaseManager, name: Str macro_rules! assert_with_timeout { ($expr:expr, $message:expr $(, $arg:expr)* $(,)?) => {{ 't: { - for _ in 0..Context::STEP_CHECKS_ITERATIONS_LIMIT { + for _ in 0..Context::STEP_REATTEMPT_LIMIT { if $expr { break 't; } - sleep(Context::PAUSE_BETWEEN_STEP_CHECKS).await; + sleep(Context::STEP_REATTEMPT_SLEEP).await; } panic!($message $(, $arg)*); } From c1546265d80e29a53fc8291c059f44e7fe6a54d4 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 6 Jul 2023 13:05:12 +0100 Subject: [PATCH 83/84] Delete user integration test --- tests/integration/mod.rs | 1 - tests/integration/user.rs | 138 -------------------------------------- 2 files changed, 139 deletions(-) delete mode 100644 tests/integration/user.rs diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs index dc60ecb2..5a87fc7d 100644 --- a/tests/integration/mod.rs +++ b/tests/integration/mod.rs @@ -23,4 +23,3 @@ mod common; mod logic; mod queries; mod runtimes; -mod user; diff --git a/tests/integration/user.rs b/tests/integration/user.rs deleted file mode 100644 index f831d7f8..00000000 --- a/tests/integration/user.rs +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2022 Vaticle - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -use std::path::PathBuf; - -use futures::future::try_join_all; -use serial_test::serial; -use tokio::time::{sleep, Duration}; -use typedb_client::{Connection, Credential, Result as TypeDBResult, UserManager}; - -use super::common; - -#[tokio::test] -#[serial] -async fn create_and_delete_user() -> TypeDBResult { - let connection = common::new_cluster_connection()?; - let users = UserManager::new(connection); - - cleanup_users(&users).await?; - sleep(Duration::from_millis(2000)).await; - - users.create(String::from("user"), String::from("password")).await?; - assert_eq!(2, users.all().await?.len()); - users.delete(String::from("user")).await?; - assert_eq!(1, users.all().await?.len()); - Ok(()) -} - -#[tokio::test] -#[serial] -async fn create_users_and_purge() -> TypeDBResult { - let connection = common::new_cluster_connection()?; - let users = UserManager::new(connection); - - cleanup_users(&users).await?; - sleep(Duration::from_millis(2000)).await; - - users.create(String::from("user"), String::from("password")).await?; - assert_eq!(2, users.all().await?.len()); - users.create(String::from("user2"), String::from("password2")).await?; - assert_eq!(3, users.all().await?.len()); - - cleanup_users(&users).await?; - sleep(Duration::from_millis(2000)).await; - - assert_eq!(1, users.all().await?.len()); - Ok(()) -} - -#[tokio::test] -#[serial] -async fn create_users_reconnect_and_purge() -> TypeDBResult { - let connection = common::new_cluster_connection()?; - let users = UserManager::new(connection); - - cleanup_users(&users).await?; - sleep(Duration::from_millis(2000)).await; - - users.create(String::from("user"), String::from("password")).await?; - assert_eq!(2, users.all().await?.len()); - users.create(String::from("user2"), String::from("password2")).await?; - assert_eq!(3, users.all().await?.len()); - - let connection = common::new_cluster_connection().unwrap(); - let users = UserManager::new(connection); - assert_eq!(3, users.all().await?.len()); - - cleanup_users(&users).await?; - sleep(Duration::from_millis(2000)).await; - - assert_eq!(1, users.all().await?.len()); - Ok(()) -} - -#[tokio::test] -#[serial] -async fn create_user_and_connect() -> TypeDBResult { - let connection = common::new_cluster_connection()?; - let users = UserManager::new(connection); - - cleanup_users(&users).await?; - sleep(Duration::from_millis(2000)).await; - - users.create(String::from("user"), String::from("password")).await?; - assert_eq!(2, users.all().await?.len()); - - Connection::new_encrypted( - &["localhost:11729", "localhost:21729", "localhost:31729"], - Credential::with_tls( - "user", - "password", - Some(&PathBuf::from( - std::env::var("ROOT_CA") - .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), - )), - )?, - )?; - - let connection = common::new_cluster_connection()?; - let users = UserManager::new(connection); - cleanup_users(&users).await?; - sleep(Duration::from_millis(2000)).await; - - assert_eq!(1, users.all().await?.len()); - Ok(()) -} - -async fn cleanup_users(users: &UserManager) -> TypeDBResult { - try_join_all( - users - .all() - .await - .unwrap() - .into_iter() - .filter(|user| &user.username != "admin") - .map(|user| users.delete(user.username)), - ) - .await?; - Ok(()) -} From b71047b088a2dc406378054a99bf2a1411e80e1f Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Fri, 7 Jul 2023 10:10:27 +0100 Subject: [PATCH 84/84] Remove redundant flag --- .factory/automation.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.factory/automation.yml b/.factory/automation.yml index e89da045..abe5254b 100644 --- a/.factory/automation.yml +++ b/.factory/automation.yml @@ -98,7 +98,7 @@ build: bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh bazel run @vaticle_dependencies//distribution/artifact:create-netrc source tools/start-cluster-servers.sh # use source to receive export vars - bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=behaviour::concept --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=behaviour::concept --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 tools/stop-cluster-servers.sh exit $TEST_SUCCESS test-behaviour-connection: @@ -111,7 +111,7 @@ build: bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh bazel run @vaticle_dependencies//distribution/artifact:create-netrc source tools/start-cluster-servers.sh # use source to receive export vars - bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=behaviour::connection --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=behaviour::connection --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 tools/stop-cluster-servers.sh exit $TEST_SUCCESS test-behaviour-typeql: @@ -125,7 +125,7 @@ build: bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh bazel run @vaticle_dependencies//distribution/artifact:create-netrc source tools/start-cluster-servers.sh # use source to receive export vars - bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=behaviour::typeql --test_arg=--test-threads=1 --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + bazel test //tests --test_env=ROOT_CA=$ROOT_CA --test_arg=-- --test_arg=behaviour::typeql --test_output=streamed && export TEST_SUCCESS=0 || export TEST_SUCCESS=1 tools/stop-cluster-servers.sh exit $TEST_SUCCESS deploy-crate-snapshot: