From 0f63919e0ccdf45c31d41591ca1047698961b77a Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 31 May 2023 18:27:36 +0400 Subject: [PATCH 01/64] 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 e758c812..851026d0 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 36df93f5..4843e6c8 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 79d06f2d6a86417e4f735a232d7afe0c209367f6 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 31 May 2023 18:56:37 +0400 Subject: [PATCH 02/64] 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 851026d0..e758c812 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 4843e6c8..36df93f5 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 02c0051b0c04d5f250032d3771657b75d3ec9208 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 1 Jun 2023 20:22:45 +0400 Subject: [PATCH 03/64] wip: get_rule(s), put_rule --- src/connection/message.rs | 18 ++++++++ src/connection/network/proto/logic.rs | 56 +++++++++++++++++++++++++ src/connection/network/proto/message.rs | 49 +++++++++++++++++++--- src/connection/network/proto/mod.rs | 1 + src/connection/transaction_stream.rs | 43 ++++++++++++++++++- src/lib.rs | 5 ++- src/logic/logic.rs | 49 ++++++++++++++++++++++ src/logic/mod.rs | 25 +++++++++++ src/logic/rule.rs | 35 ++++++++++++++++ src/transaction/mod.rs | 8 ++++ tests/behaviour/typeql/mod.rs | 2 +- tests/behaviour/typeql/steps.rs | 8 ++++ 12 files changed, 290 insertions(+), 9 deletions(-) create mode 100644 src/connection/network/proto/logic.rs create mode 100644 src/logic/logic.rs create mode 100644 src/logic/mod.rs create mode 100644 src/logic/rule.rs diff --git a/src/connection/message.rs b/src/connection/message.rs index 631d2064..c6cadbe4 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -24,6 +24,7 @@ use std::time::Duration; use tokio::sync::mpsc::UnboundedSender; use tonic::Streaming; use typedb_protocol::transaction; +use typeql_lang::pattern::{Conjunction, Variable}; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, @@ -33,6 +34,7 @@ use crate::{ Thing, ThingType, Transitivity, Value, ValueType, }, Options, SessionType, TransactionType, + Rule, SchemaException, }; #[derive(Debug)] @@ -108,6 +110,7 @@ pub(super) enum TransactionRequest { RoleType(RoleTypeRequest), Thing(ThingRequest), Stream { request_id: RequestID }, + Logic(LogicRequest), } #[derive(Debug)] @@ -120,6 +123,7 @@ pub(super) enum TransactionResponse { ThingType(ThingTypeResponse), RoleType(RoleTypeResponse), Thing(ThingResponse), + Logic(LogicResponse), } #[derive(Debug)] @@ -457,3 +461,17 @@ pub(super) enum ThingResponse { AttributeGetOwners { owners: Vec }, } + +#[derive(Debug)] +pub(super) enum LogicRequest { + PutRule { label: String, when: Conjunction, then: Variable }, + GetRule { label: String }, + GetRules, +} + +#[derive(Debug)] +pub(super) enum LogicResponse { + PutRule { rule: Rule }, + GetRule { rule: Rule }, + GetRules { rules: Vec }, +} diff --git a/src/connection/network/proto/logic.rs b/src/connection/network/proto/logic.rs new file mode 100644 index 00000000..def7ab49 --- /dev/null +++ b/src/connection/network/proto/logic.rs @@ -0,0 +1,56 @@ +/* + * 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::{ + Rule as RuleProto, +}; +use typeql_lang::{parse_pattern, parse_variable}; +use typeql_lang::pattern::{Pattern, Variable}; +use crate::{common::Result, Error, error::InternalError, Rule}; +use crate::error::ConnectionError; +use super::{FromProto, IntoProto, TryFromProto}; + +impl TryFromProto for Rule { + fn try_from_proto(proto: RuleProto) -> Result { + let when = match parse_pattern(&proto.when) { + Ok(Pattern::Conjunction(conjunction)) => conjunction, + Ok(other) => return Err(Error::Other(format!("When parse error: {other:?}"))), + Err(error) => return Err(Error::Other(format!("{error:?}"))), + }; + let then = match parse_variable(&proto.then) { + Ok(Variable::Thing(thing)) => thing, + Ok(other) => return Err(Error::Other(format!("Then parse error: {other:?}"))), + Err(error) => return Err(Error::Other(format!("{error:?}"))), + }; + Ok(Self::new(proto.label, when, then)) + } +} + +impl IntoProto for Rule { + fn into_proto(self) -> RuleProto { + RuleProto { + label: self.label, + when: self.when.to_string(), + then: self.then.to_string() , + } + } +} + diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index e758c812..9e1d1a00 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -22,10 +22,7 @@ use std::time::Duration; use itertools::Itertools; -use typedb_protocol::{ - attribute, attribute_type, concept_manager, database, database_manager, entity_type, query_manager, r#type, - relation, relation_type, role_type, server_manager, session, thing, thing_type, transaction, -}; +use typedb_protocol::{attribute, attribute_type, concept_manager, database, database_manager, entity_type, logic_manager, query_manager, r#type, relation, relation_type, role_type, server_manager, session, thing, thing_type, transaction}; use super::{FromProto, IntoProto, TryFromProto, TryIntoProto}; use crate::{ @@ -36,11 +33,12 @@ use crate::{ Thing, ThingType, ValueType, }, connection::message::{ - ConceptRequest, ConceptResponse, QueryRequest, QueryResponse, Request, Response, RoleTypeRequest, + ConceptRequest, ConceptResponse, LogicRequest, LogicResponse, QueryRequest, QueryResponse, Request, Response, RoleTypeRequest, RoleTypeResponse, ThingRequest, ThingResponse, ThingTypeRequest, ThingTypeResponse, TransactionRequest, TransactionResponse, }, error::{ConnectionError, InternalError}, + Rule, SchemaException, }; impl TryIntoProto for Request { @@ -270,6 +268,7 @@ impl IntoProto for TransactionRequest { request_id = Some(req_id); transaction::req::Req::StreamReq(transaction::stream::Req {}) } + Self::Logic(logic_request) => transaction::req::Req::LogicManagerReq(logic_request.into_proto()), }; transaction::Req { @@ -297,6 +296,7 @@ impl TryFromProto for TransactionResponse { Ok(Self::RoleType(RoleTypeResponse::try_from_proto(res)?)) } Some(transaction::res::Res::ThingRes(res)) => Ok(Self::Thing(ThingResponse::try_from_proto(res)?)), + Some(transaction::res::Res::LogicManagerRes(res)) => Ok(Self::Logic(LogicResponse::try_from_proto(res)?)), Some(_) => todo!(), None => Err(ConnectionError::MissingResponseField("res").into()), } @@ -1104,3 +1104,42 @@ impl TryFromProto for ThingResponse { } } } + +impl IntoProto for LogicRequest { + 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 { + Some(logic_manager::res::Res::PutRuleRes(logic_manager::put_rule::Res { rule})) => + Ok(Self::PutRule { rule: Rule::try_from_proto(rule.unwrap()).unwrap() }), + Some(logic_manager::res::Res::GetRuleRes(logic_manager::get_rule::Res { rule })) => + Ok(Self::GetRule { rule: Rule::try_from_proto(rule.unwrap()).unwrap() }), + None => Err(ConnectionError::MissingResponseField("res").into()), + } + } +} + +impl TryFromProto for LogicResponse { + fn try_from_proto(proto: logic_manager::ResPart) -> Result { + Ok(Self::GetRules { rules: proto.get_rules_res_part.unwrap().rules.into_iter().map(Rule::try_from_proto).try_collect()? }) + } +} \ No newline at end of file diff --git a/src/connection/network/proto/mod.rs b/src/connection/network/proto/mod.rs index fc805bb2..be389929 100644 --- a/src/connection/network/proto/mod.rs +++ b/src/connection/network/proto/mod.rs @@ -22,6 +22,7 @@ mod common; mod concept; mod database; +mod logic; mod message; use crate::Result; diff --git a/src/connection/transaction_stream.rs b/src/connection/transaction_stream.rs index 3d4d2d12..35c77239 100644 --- a/src/connection/transaction_stream.rs +++ b/src/connection/transaction_stream.rs @@ -22,6 +22,7 @@ use std::{fmt, iter}; use futures::{stream, Stream, StreamExt}; +use typeql_lang::pattern::{Conjunction, Variable}; use super::{ message::{RoleTypeRequest, RoleTypeResponse, ThingRequest, ThingResponse}, @@ -39,8 +40,9 @@ use crate::{ TransactionRequest, TransactionResponse, }, error::InternalError, - Options, TransactionType, + Options, Rule, SchemaException, TransactionType, }; +use crate::connection::message::{LogicRequest, LogicResponse}; pub(crate) struct TransactionStream { type_: TransactionType, @@ -929,6 +931,29 @@ impl TransactionStream { })) } + pub(crate) async fn put_rule(&self, label: String, when: Conjunction, then: Variable) -> Result { + match self.logic_single(LogicRequest::PutRule { label, when, then } ).await? { + LogicResponse::PutRule { rule } => Ok(rule), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } + + pub(crate) async fn get_rule(&self, label: String) -> Result { + match self.logic_single(LogicRequest::GetRule { label }).await? { + LogicResponse::GetRule { rule } => Ok(rule), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } + + pub(crate) fn get_rules(&self) -> Result>> { + let stream = self.logic_stream(LogicRequest::GetRules)?; + Ok(stream.flat_map(|result| match result { + Ok(LogicResponse::GetRules { rules }) => stream_iter(rules.into_iter().map(Ok)), + Ok(other) => stream_once(Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into())), + Err(err) => stream_once(Err(err)), + })) + } + async fn single(&self, req: TransactionRequest) -> Result { self.transaction_transmitter.single(req).await } @@ -968,6 +993,13 @@ impl TransactionStream { } } + async fn logic_single(&self, req: LogicRequest) -> Result { + match self.single(TransactionRequest::Logic(req)).await? { + TransactionResponse::Logic(res) => Ok(res), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } + fn stream(&self, req: TransactionRequest) -> Result>> { self.transaction_transmitter.stream(req) } @@ -1011,6 +1043,15 @@ impl TransactionStream { Err(err) => Err(err), })) } + + fn logic_stream(&self, req: LogicRequest) -> Result>> { + Ok(self.stream(TransactionRequest::Logic(req))?.map(|response| match response { + Ok(TransactionResponse::Logic(res)) => Ok(res), + Ok(other) => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + Err(err) => Err(err), + })) + } + } impl fmt::Debug for TransactionStream { diff --git a/src/lib.rs b/src/lib.rs index 8de9067a..d4aea87e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,10 +27,11 @@ pub mod concept; mod connection; mod database; pub mod transaction; +mod logic; pub use self::{ common::{error, Credential, Error, Options, Result, SessionType, TransactionType}, connection::Connection, - database::{Database, DatabaseManager, Session}, - transaction::Transaction, + database::{Database, DatabaseManager, Session, Transaction}, + logic::{LogicManager, Rule}, }; diff --git a/src/logic/logic.rs b/src/logic/logic.rs new file mode 100644 index 00000000..209506bd --- /dev/null +++ b/src/logic/logic.rs @@ -0,0 +1,49 @@ +/* + * 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 std::sync::Arc; +use futures::Stream; +use typeql_lang::pattern::{Conjunction, Variable}; +use crate::{common::Result, connection::TransactionStream, Rule}; + +#[derive(Clone, Debug)] +pub struct LogicManager { + transaction_stream: Arc, +} + +impl LogicManager { + pub(crate) fn new(transaction_stream: Arc) -> Self { + Self { transaction_stream } + } + + pub async fn put_rule(&self, label: String, when: Conjunction, then: Variable) -> Result { + self.transaction_stream.put_rule(label, when, then).await + } + + pub async fn get_rule(&self, label: String) -> Result { + self.transaction_stream.get_rule(label).await + } + + pub async fn get_rules(&self) -> Result>> { + self.transaction_stream.get_rules() + } +} \ No newline at end of file diff --git a/src/logic/mod.rs b/src/logic/mod.rs new file mode 100644 index 00000000..9233439c --- /dev/null +++ b/src/logic/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 logic; +mod rule; + +pub use self::{logic::LogicManager, rule::Rule}; diff --git a/src/logic/rule.rs b/src/logic/rule.rs new file mode 100644 index 00000000..526152bc --- /dev/null +++ b/src/logic/rule.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. + */ + +use typeql_lang::pattern::{Conjunction, ThingVariable}; + +#[derive(Debug)] +pub struct Rule { + pub label: String, + pub when: Conjunction, + pub then: ThingVariable, +} + +impl Rule { + pub(crate) fn new(label: String, when: Conjunction, then: ThingVariable) -> Self { + Self { label, when, then } + } +} \ No newline at end of file diff --git a/src/transaction/mod.rs b/src/transaction/mod.rs index c10f5d7d..bc69cbc2 100644 --- a/src/transaction/mod.rs +++ b/src/transaction/mod.rs @@ -28,6 +28,7 @@ use self::{concept::ConceptManager, query::QueryManager}; use crate::{ common::{Result, TransactionType}, connection::TransactionStream, + LogicManager, Options, }; @@ -37,9 +38,11 @@ pub struct Transaction<'a> { query: QueryManager, concept: ConceptManager, + logic: LogicManager, transaction_stream: Arc, _lifetime_guard: PhantomData<&'a ()>, + } impl Transaction<'_> { @@ -50,6 +53,7 @@ impl Transaction<'_> { options: transaction_stream.options().clone(), query: QueryManager::new(transaction_stream.clone()), concept: ConceptManager::new(transaction_stream.clone()), + logic: LogicManager::new(transaction_stream.clone()), transaction_stream, _lifetime_guard: PhantomData::default(), } @@ -78,6 +82,10 @@ impl Transaction<'_> { pub async fn rollback(&self) -> Result { self.transaction_stream.rollback().await } + + pub fn logic(&self) -> &LogicManager { + &self.logic + } } impl fmt::Debug for Transaction<'_> { diff --git a/tests/behaviour/typeql/mod.rs b/tests/behaviour/typeql/mod.rs index c57da845..22efafd0 100644 --- a/tests/behaviour/typeql/mod.rs +++ b/tests/behaviour/typeql/mod.rs @@ -26,5 +26,5 @@ mod insert; mod match_; mod rule_validation; mod steps; -// mod undefine; +mod undefine; mod update; diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index 66255573..671d6f75 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -356,4 +356,12 @@ generic_step_impl! { matched entries of given {actual_answers}." ); } + + #[step(expr = "rules contain: {word}")] + async fn rules_contain(context: &mut Context, rule_label: String) { + let res = context.transaction().logic().get_rule(rule_label).await; + assert!(res.is_ok(), "{res:?}") + // assert (tx().logic().getRules().anyMatch(rule -> rule.getLabel().equals(ruleLabel))); + } + } From ede798339466bf8411de2ddf47421d315851e251 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Fri, 2 Jun 2023 13:19:23 +0400 Subject: [PATCH 04/64] get_rules isn't async --- src/connection/transaction_stream.rs | 2 +- src/logic/logic.rs | 2 +- tests/behaviour/typeql/steps.rs | 10 ++++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/connection/transaction_stream.rs b/src/connection/transaction_stream.rs index 35c77239..e4bad527 100644 --- a/src/connection/transaction_stream.rs +++ b/src/connection/transaction_stream.rs @@ -946,7 +946,7 @@ impl TransactionStream { } pub(crate) fn get_rules(&self) -> Result>> { - let stream = self.logic_stream(LogicRequest::GetRules)?; + let stream = self.logic_stream(LogicRequest::GetRules {})?; Ok(stream.flat_map(|result| match result { Ok(LogicResponse::GetRules { rules }) => stream_iter(rules.into_iter().map(Ok)), Ok(other) => stream_once(Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into())), diff --git a/src/logic/logic.rs b/src/logic/logic.rs index 209506bd..4da26d04 100644 --- a/src/logic/logic.rs +++ b/src/logic/logic.rs @@ -43,7 +43,7 @@ impl LogicManager { self.transaction_stream.get_rule(label).await } - pub async fn get_rules(&self) -> Result>> { + pub fn get_rules(&self) -> Result>> { self.transaction_stream.get_rules() } } \ No newline at end of file diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index 671d6f75..310cb2df 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -20,7 +20,7 @@ */ use cucumber::{gherkin::Step, given, then, when}; -use futures::TryStreamExt; +use futures::{StreamExt, TryFutureExt, TryStreamExt}; use typedb_client::{answer::Numeric, Result as TypeDBResult}; use typeql_lang::parse_query; use util::{ @@ -32,6 +32,7 @@ use crate::{ behaviour::{util, Context}, generic_step_impl, }; +use crate::behaviour::parameter::Comparable; generic_step_impl! { #[step(expr = "typeql define")] @@ -361,7 +362,12 @@ generic_step_impl! { async fn rules_contain(context: &mut Context, rule_label: String) { let res = context.transaction().logic().get_rule(rule_label).await; assert!(res.is_ok(), "{res:?}") - // assert (tx().logic().getRules().anyMatch(rule -> rule.getLabel().equals(ruleLabel))); + // let stream = context.transaction().logic().get_rules(); + // assert!(stream.is_ok(), "{:?}", stream.err()); + // let res = stream.unwrap().try_collect::>().await; + // assert!(res.is_ok(), "{:?}", res.err()); + // let filtered: Vec<_> = res.unwrap().into_iter().filter(|rule| rule.label == rule_label).collect(); + // assert!(filtered.len() > 0); } } From 80cf00c0ac5a7e7645568ca509f73a23f8e35f46 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Fri, 2 Jun 2023 20:05:23 +0400 Subject: [PATCH 05/64] "rules do not contain" step --- tests/behaviour/typeql/steps.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index 310cb2df..4cf3bb56 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -361,7 +361,7 @@ generic_step_impl! { #[step(expr = "rules contain: {word}")] async fn rules_contain(context: &mut Context, rule_label: String) { let res = context.transaction().logic().get_rule(rule_label).await; - assert!(res.is_ok(), "{res:?}") + assert!(res.is_ok(), "{res:?}"); // let stream = context.transaction().logic().get_rules(); // assert!(stream.is_ok(), "{:?}", stream.err()); // let res = stream.unwrap().try_collect::>().await; @@ -370,4 +370,10 @@ generic_step_impl! { // assert!(filtered.len() > 0); } + #[step(expr = "rules do not contain: {word}")] + async fn rules_do_not_contain(context: &mut Context, rule_label: String) { + let res = context.transaction().logic().get_rule(rule_label).await; + assert!(res.is_err(), "{res:?}"); + } + } From d10f36f573f46d08712ad74ab6e15daf77c5981a Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 5 Jun 2023 19:19:32 +0400 Subject: [PATCH 06/64] Explanations --- src/answer/concept_map.rs | 51 ++++++++++++++++++- src/answer/mod.rs | 3 +- src/connection/message.rs | 3 +- src/connection/network/proto/concept.rs | 68 +++++++++++++++++++++---- src/connection/network/proto/logic.rs | 2 +- src/connection/transaction_stream.rs | 10 ++++ src/logic/explanation.rs | 43 ++++++++++++++++ src/logic/logic.rs | 1 - src/logic/mod.rs | 3 +- src/logic/rule.rs | 2 +- src/transaction/query.rs | 9 ++++ tests/behaviour/typeql/steps.rs | 5 ++ 12 files changed, 183 insertions(+), 17 deletions(-) create mode 100644 src/logic/explanation.rs diff --git a/src/answer/concept_map.rs b/src/answer/concept_map.rs index 8d665ba7..891cedef 100644 --- a/src/answer/concept_map.rs +++ b/src/answer/concept_map.rs @@ -29,6 +29,20 @@ use crate::concept::Concept; #[derive(Debug)] pub struct ConceptMap { pub map: HashMap, + pub explainables: Option, +} + +#[derive(Debug)] +pub struct Explainables { + pub relations: HashMap, + pub attributes: HashMap, + pub ownerships: HashMap<(String, String), Explainable>, +} + +#[derive(Clone, Debug)] +pub struct Explainable { + pub conjunction: String, + pub id: i64, } impl ConceptMap { @@ -51,7 +65,7 @@ impl Clone for ConceptMap { for (k, v) in &self.map { map.insert(k.clone(), v.clone()); } - Self { map } + Self { map, explainables: self.explainables.clone() } } } @@ -77,3 +91,38 @@ impl IntoIterator for ConceptMap { self.map.into_iter() } } + +impl Explainables { + pub(crate) fn new( + relations: HashMap, + attributes:HashMap, + ownerships: HashMap<(String, String), Explainable>, + ) -> Self { + Self { relations, attributes, ownerships } + } +} + +impl Explainable { + pub(crate) fn new(conjunction: String, id: i64) -> Self { + Self { conjunction, id } + } +} + +impl Clone for Explainables { + fn clone(&self) -> Self { + let mut relations = HashMap::with_capacity(self.relations.len()); + for (k, v) in &self.relations { + relations.insert(k.clone(), v.clone()); + } + let mut attributes = HashMap::with_capacity(self.attributes.len()); + for (k, v) in &self.attributes { + attributes.insert(k.clone(), v.clone()); + } + let mut ownerships = HashMap::with_capacity(self.ownerships.len()); + for (k, v) in &self.ownerships { + ownerships.insert(k.clone(), v.clone()); + } + + Self { relations, attributes, ownerships } + } +} diff --git a/src/answer/mod.rs b/src/answer/mod.rs index 0060eab3..64343862 100644 --- a/src/answer/mod.rs +++ b/src/answer/mod.rs @@ -25,5 +25,6 @@ mod numeric; mod numeric_group; pub use self::{ - concept_map::ConceptMap, concept_map_group::ConceptMapGroup, numeric::Numeric, numeric_group::NumericGroup, + concept_map::ConceptMap, concept_map_group::ConceptMapGroup, concept_map::Explainable, concept_map::Explainables, + numeric::Numeric, numeric_group::NumericGroup, }; diff --git a/src/connection/message.rs b/src/connection/message.rs index c6cadbe4..cbdf49d8 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -36,6 +36,7 @@ use crate::{ Options, SessionType, TransactionType, Rule, SchemaException, }; +use crate::logic::Explanation; #[derive(Debug)] pub(super) enum Request { @@ -156,7 +157,7 @@ pub(super) enum QueryResponse { MatchAggregate { answer: Numeric }, - Explain {}, // TODO: explanations + Explain { answers: Vec }, MatchGroup { answers: Vec }, MatchGroupAggregate { answers: Vec }, diff --git a/src/connection/network/proto/concept.rs b/src/connection/network/proto/concept.rs index 104e74ae..21bb046c 100644 --- a/src/connection/network/proto/concept.rs +++ b/src/connection/network/proto/concept.rs @@ -25,27 +25,28 @@ use chrono::NaiveDateTime; use itertools::Itertools; use typedb_protocol::{ attribute::{value::Value as ValueProtoInner, Value as ValueProto}, - attribute_type::ValueType as ValueTypeProto, - concept, - numeric::Value as NumericValue, + attribute_type::ValueType as ValueTypeProto, concept, numeric::Value as NumericValue, r#type::{annotation, Annotation as AnnotationProto, Transitivity as TransitivityProto}, - thing, thing_type, Attribute as AttributeProto, AttributeType as AttributeTypeProto, Concept as ConceptProto, - ConceptMap as ConceptMapProto, ConceptMapGroup as ConceptMapGroupProto, Entity as EntityProto, - EntityType as EntityTypeProto, Numeric as NumericProto, NumericGroup as NumericGroupProto, - Relation as RelationProto, RelationType as RelationTypeProto, RoleType as RoleTypeProto, Thing as ThingProto, - ThingType as ThingTypeProto, + thing, thing_type, Attribute as AttributeProto, AttributeType as AttributeTypeProto, + Concept as ConceptProto, ConceptMap as ConceptMapProto, ConceptMapGroup as ConceptMapGroupProto, + Entity as EntityProto, EntityType as EntityTypeProto, Explainable as ExplainableProto, Explainables as ExplainablesProto, + Explanation as ExplanationProto, + Numeric as NumericProto, NumericGroup as NumericGroupProto, Relation as RelationProto, RelationType as RelationTypeProto, + RoleType as RoleTypeProto, Thing as ThingProto, ThingType as ThingTypeProto, }; use super::{FromProto, IntoProto, TryFromProto}; use crate::{ - answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, + answer::{ConceptMap, ConceptMapGroup, Explainable, Explainables, Numeric, NumericGroup}, concept::{ Annotation, Attribute, AttributeType, Concept, Entity, EntityType, Relation, RelationType, RoleType, RootThingType, ScopedLabel, Thing, ThingType, Transitivity, Value, ValueType, }, error::{ConnectionError, InternalError}, Result, + Rule, }; +use crate::logic::Explanation; impl IntoProto for Transitivity { fn into_proto(self) -> i32 { @@ -101,7 +102,7 @@ impl TryFromProto for ConceptMap { for (k, v) in proto.map { map.insert(k, Concept::try_from_proto(v)?); } - Ok(Self { map }) + Ok(Self { map, explainables: proto.explainables.map(Explainables::from_proto) }) } } @@ -369,3 +370,50 @@ impl IntoProto for Value { } } } + +impl FromProto for Explainables { + fn from_proto(proto: ExplainablesProto) -> Self { + let mut relations = HashMap::with_capacity(proto.relations.len()); + for (k, v) in proto.relations { + relations.insert(k, Explainable::from_proto(v)); + } + let mut attributes = HashMap::with_capacity(proto.attributes.len()); + for (k, v) in proto.attributes { + attributes.insert(k, Explainable::from_proto(v)); + } + let mut ownerships = HashMap::new(); + for (k1, owned) in proto.ownerships { + for (k2, v) in owned.owned { + ownerships.insert((k1.clone(), k2), Explainable::from_proto(v)); + } + } + + Self::new( relations, attributes, ownerships) + } +} + +impl FromProto for Explainable { + fn from_proto(proto: ExplainableProto) -> Self { + let ExplainableProto { conjunction, id } = proto; + Self::new(conjunction, id) + } +} + +impl TryFromProto for Explanation { + fn try_from_proto(proto: ExplanationProto) -> Result { + let ExplanationProto { rule, conclusion, condition, var_mapping } = proto; + let mut variable_mapping = HashMap::with_capacity(var_mapping.len()); + for (k, v) in var_mapping { + variable_mapping.insert(k, v.vars.clone()); + } + + Ok(Self { + rule: Rule::try_from_proto(rule.ok_or(ConnectionError::MissingResponseField("rule"))?)?, + conclusion: ConceptMap::try_from_proto(conclusion.ok_or(ConnectionError::MissingResponseField("conclusion"))?)?, + condition: ConceptMap::try_from_proto(condition.ok_or(ConnectionError::MissingResponseField("condition"))?)?, + variable_mapping, + }) + } +} + + diff --git a/src/connection/network/proto/logic.rs b/src/connection/network/proto/logic.rs index def7ab49..4f614369 100644 --- a/src/connection/network/proto/logic.rs +++ b/src/connection/network/proto/logic.rs @@ -26,7 +26,7 @@ use typeql_lang::{parse_pattern, parse_variable}; use typeql_lang::pattern::{Pattern, Variable}; use crate::{common::Result, Error, error::InternalError, Rule}; use crate::error::ConnectionError; -use super::{FromProto, IntoProto, TryFromProto}; +use super::{IntoProto, TryFromProto}; impl TryFromProto for Rule { fn try_from_proto(proto: RuleProto) -> Result { diff --git a/src/connection/transaction_stream.rs b/src/connection/transaction_stream.rs index e4bad527..f0ffb510 100644 --- a/src/connection/transaction_stream.rs +++ b/src/connection/transaction_stream.rs @@ -43,6 +43,7 @@ use crate::{ Options, Rule, SchemaException, TransactionType, }; use crate::connection::message::{LogicRequest, LogicResponse}; +use crate::logic::Explanation; pub(crate) struct TransactionStream { type_: TransactionType, @@ -1000,6 +1001,15 @@ impl TransactionStream { } } + pub(crate) fn explain(&self, explainable_id: i64, options: Options) -> Result>> { + let stream = self.query_stream(QueryRequest::Explain { explainable_id, options })?; + Ok(stream.flat_map(|result| match result { + Ok(QueryResponse::Explain { answers }) => stream_iter(answers.into_iter().map(Ok)), + Ok(other) => stream_once(Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into())), + Err(err) => stream_once(Err(err)), + })) + } + fn stream(&self, req: TransactionRequest) -> Result>> { self.transaction_transmitter.stream(req) } diff --git a/src/logic/explanation.rs b/src/logic/explanation.rs new file mode 100644 index 00000000..1a3f9cb6 --- /dev/null +++ b/src/logic/explanation.rs @@ -0,0 +1,43 @@ +/* + * 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::collections::HashMap; +use crate::Rule; +use crate::answer::ConceptMap; + +#[derive(Debug)] +pub struct Explanation { + pub rule: Rule, + pub conclusion: ConceptMap, + pub condition: ConceptMap, + pub variable_mapping: HashMap>, +} + +// impl Explanation { +// pub(crate) fn new( +// rule: Rule, +// conclusion: ConceptMap, +// condition: ConceptMap, +// variable_mapping: HashMap>, +// ) -> Self { +// Self { rule, conclusion, condition, variable_mapping } +// } +// } diff --git a/src/logic/logic.rs b/src/logic/logic.rs index 4da26d04..2dce3412 100644 --- a/src/logic/logic.rs +++ b/src/logic/logic.rs @@ -19,7 +19,6 @@ * under the License. */ -use std::future::Future; use std::sync::Arc; use futures::Stream; use typeql_lang::pattern::{Conjunction, Variable}; diff --git a/src/logic/mod.rs b/src/logic/mod.rs index 9233439c..c5278375 100644 --- a/src/logic/mod.rs +++ b/src/logic/mod.rs @@ -21,5 +21,6 @@ mod logic; mod rule; +mod explanation; -pub use self::{logic::LogicManager, rule::Rule}; +pub use self::{explanation::Explanation, logic::LogicManager, rule::Rule}; diff --git a/src/logic/rule.rs b/src/logic/rule.rs index 526152bc..b9b8d1be 100644 --- a/src/logic/rule.rs +++ b/src/logic/rule.rs @@ -32,4 +32,4 @@ impl Rule { pub(crate) fn new(label: String, when: Conjunction, then: ThingVariable) -> Self { Self { label, when, then } } -} \ No newline at end of file +} diff --git a/src/transaction/query.rs b/src/transaction/query.rs index 36df93f5..27a98023 100644 --- a/src/transaction/query.rs +++ b/src/transaction/query.rs @@ -29,6 +29,7 @@ use crate::{ connection::TransactionStream, Options, }; +use crate::logic::Explanation; #[derive(Debug)] pub struct QueryManager { @@ -119,4 +120,12 @@ impl QueryManager { ) -> Result>> { self.transaction_stream.match_group_aggregate(query.to_string(), options) } + + pub fn explain(&self, explainable_id: i64) -> Result>> { + self.explain_with_options(explainable_id, Options::new()) + } + + pub fn explain_with_options(&self, explainable_id: i64, options: Options) -> Result>> { + self.transaction_stream.explain(explainable_id, options) + } } diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index 4cf3bb56..b0bf4b62 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -377,3 +377,8 @@ generic_step_impl! { } } + +/* +"answers contain explanation tree" + + */ \ No newline at end of file From e1064fcaa45ed759f7531a9ce4ada1398a29e4da Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 5 Jun 2023 21:14:17 +0400 Subject: [PATCH 07/64] `rules are` step --- src/connection/network/proto/message.rs | 10 ++++-- tests/behaviour/typeql/steps.rs | 44 ++++++++++++++++++------- tests/behaviour/util.rs | 21 +++++++++++- 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 9e1d1a00..ff08d086 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -40,6 +40,7 @@ use crate::{ error::{ConnectionError, InternalError}, Rule, SchemaException, }; +use crate::logic::Explanation; impl TryIntoProto for Request { fn try_into_proto(self) -> Result { @@ -316,6 +317,7 @@ impl TryFromProto for TransactionResponse { res: Some(r#type::res_part::Res::RoleTypeResPart(res)), })) => Ok(Self::RoleType(RoleTypeResponse::try_from_proto(res)?)), Some(transaction::res_part::Res::ThingResPart(res)) => Ok(Self::Thing(ThingResponse::try_from_proto(res)?)), + Some(transaction::res_part::Res::LogicManagerResPart(res)) => Ok(Self::Logic(LogicResponse::try_from_proto(res)?)), Some(_) => todo!(), None => Err(ConnectionError::MissingResponseField("res").into()), } @@ -357,7 +359,9 @@ impl IntoProto for QueryRequest { options, ), - _ => todo!(), + Self::Explain { explainable_id, options } => { + (query_manager::req::Req::ExplainReq(query_manager::explain::Req { explainable_id }), options) + } }; query_manager::Req { req: Some(req), options: Some(options.into_proto()) } } @@ -395,7 +399,9 @@ impl TryFromProto for QueryResponse { Some(query_manager::res_part::Res::MatchGroupAggregateResPart(res)) => Ok(Self::MatchGroupAggregate { answers: res.answers.into_iter().map(NumericGroup::try_from_proto).try_collect()?, }), - Some(_) => todo!(), + Some(query_manager::res_part::Res::ExplainResPart(res)) => { + Ok(Self::Explain { answers: res.explanations.into_iter().map(Explanation::try_from_proto).try_collect()? }) + } None => Err(ConnectionError::MissingResponseField("res").into()), } } diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index b0bf4b62..ca1e1efd 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -24,7 +24,7 @@ use futures::{StreamExt, TryFutureExt, TryStreamExt}; use typedb_client::{answer::Numeric, Result as TypeDBResult}; use typeql_lang::parse_query; use util::{ - equals_approximate, iter_table_map, match_answer_concept, match_answer_concept_map, match_templated_answer, + equals_approximate, iter_table_map, match_answer_concept, match_answer_concept_map, match_answer_rule, match_templated_answer, }; use crate::{ @@ -362,12 +362,6 @@ generic_step_impl! { async fn rules_contain(context: &mut Context, rule_label: String) { let res = context.transaction().logic().get_rule(rule_label).await; assert!(res.is_ok(), "{res:?}"); - // let stream = context.transaction().logic().get_rules(); - // assert!(stream.is_ok(), "{:?}", stream.err()); - // let res = stream.unwrap().try_collect::>().await; - // assert!(res.is_ok(), "{:?}", res.err()); - // let filtered: Vec<_> = res.unwrap().into_iter().filter(|rule| rule.label == rule_label).collect(); - // assert!(filtered.len() > 0); } #[step(expr = "rules do not contain: {word}")] @@ -376,9 +370,35 @@ generic_step_impl! { assert!(res.is_err(), "{res:?}"); } -} - -/* -"answers contain explanation tree" + #[step(expr = "rules are")] + async fn rules_are(context: &mut Context, step: &Step) { + let stream = context.transaction().logic().get_rules(); + assert!(stream.is_ok(), "{:?}", stream.err()); + let res = stream.unwrap().try_collect::>().await; + assert!(res.is_ok(), "{:?}", res.err()); + let answers = res.unwrap(); + let step_table = iter_map_table(step).collect::>(); + let expected_answers = step_table.len(); + let actual_answers = answers.len(); + assert_eq!( + actual_answers, expected_answers, + "The number of identifier entries (rows) should match the number of answers, \ + but found {expected_answers} identifier entries and {actual_answers} answers." + ); + let mut matched_rows = 0; + for ans_row in &answers { + for table_row in &step_table { + if match_answer_rule(table_row, ans_row).await { + matched_rows += 1; + break; + } + } + } + assert_eq!( + matched_rows, actual_answers, + "An identifier entry (row) should match 1-to-1 to an answer, but there are only {matched_rows} \ + matched entries of given {actual_answers}." + ); + } - */ \ No newline at end of file +} diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index 33adab9d..d37a6b0b 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -33,8 +33,10 @@ use typedb_client::{ concept::{Annotation, Attribute, Concept, Entity, Relation, Value}, transaction::concept::api::ThingAPI, Result as TypeDBResult, + Rule, }; -use typeql_lang::parse_query; +use typeql_lang::{parse_pattern, parse_query, parse_rule}; +use typeql_lang::pattern::{ThingVariable, Variable}; use crate::behaviour::Context; @@ -174,3 +176,20 @@ fn get_iid(concept: &Concept) -> String { }; iid.to_string() } + +pub async fn match_answer_rule( + answer_identifiers: &HashMap<&String, &String>, + answer: &Rule, +) -> bool { + let when_clause = answer_identifiers.get(&String::from("when")).unwrap().trim_end_matches(";").to_string(); + let when = parse_pattern(when_clause.as_str()).unwrap().into_conjunction(); + let then_clause = answer_identifiers.get(&String::from("then")).unwrap().trim_end_matches(['}', ';', ' ']).trim_start_matches("{").to_string(); + let then_var = parse_pattern(then_clause.as_str()).unwrap().into_variable(); + if let Variable::Thing(then) = then_var { + answer_identifiers.get(&String::from("label")).unwrap().to_string() == answer.label + && when == answer.when + && then == answer.then + } else { + false + } +} From 6621ca973d7abee3ba8c533f7c31960ee1043b68 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 7 Jun 2023 19:30:36 +0400 Subject: [PATCH 08/64] Add `inferred` field --- src/concept/thing.rs | 3 +++ src/connection/network/proto/concept.rs | 13 ++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/concept/thing.rs b/src/concept/thing.rs index 2bfadd4d..460a601a 100644 --- a/src/concept/thing.rs +++ b/src/concept/thing.rs @@ -47,12 +47,14 @@ impl Thing { pub struct Entity { pub iid: IID, pub type_: EntityType, + pub inferred: bool, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Relation { pub iid: IID, pub type_: RelationType, + pub inferred: bool, } #[derive(Clone, Debug, PartialEq)] @@ -60,6 +62,7 @@ pub struct Attribute { pub iid: IID, pub type_: AttributeType, pub value: Value, + pub inferred: bool, } #[derive(Clone, Debug, PartialEq)] diff --git a/src/connection/network/proto/concept.rs b/src/connection/network/proto/concept.rs index 21bb046c..0f212562 100644 --- a/src/connection/network/proto/concept.rs +++ b/src/connection/network/proto/concept.rs @@ -278,10 +278,11 @@ impl IntoProto for Thing { impl TryFromProto for Entity { fn try_from_proto(proto: EntityProto) -> Result { - let EntityProto { iid, entity_type, inferred: _ } = proto; + let EntityProto { iid, entity_type, inferred } = proto; Ok(Self { iid: iid.into(), type_: EntityType::from_proto(entity_type.ok_or(ConnectionError::MissingResponseField("entity_type"))?), + inferred, }) } } @@ -291,19 +292,20 @@ impl IntoProto for Entity { EntityProto { iid: self.iid.into(), entity_type: Some(self.type_.into_proto()), - inferred: false, // FIXME + inferred: self.inferred, } } } impl TryFromProto for Relation { fn try_from_proto(proto: RelationProto) -> Result { - let RelationProto { iid, relation_type, inferred: _ } = proto; + let RelationProto { iid, relation_type, inferred } = proto; Ok(Self { iid: iid.into(), type_: RelationType::from_proto( relation_type.ok_or(ConnectionError::MissingResponseField("relation_type"))?, ), + inferred, }) } } @@ -313,20 +315,21 @@ impl IntoProto for Relation { RelationProto { iid: self.iid.into(), relation_type: Some(self.type_.into_proto()), - inferred: false, // FIXME + inferred: self.inferred, } } } impl TryFromProto for Attribute { fn try_from_proto(proto: AttributeProto) -> Result { - let AttributeProto { iid, attribute_type, value, inferred: _ } = proto; + let AttributeProto { iid, attribute_type, value, inferred } = proto; Ok(Self { iid: iid.into(), type_: AttributeType::try_from_proto( attribute_type.ok_or(ConnectionError::MissingResponseField("attribute_type"))?, )?, value: Value::try_from_proto(value.ok_or(ConnectionError::MissingResponseField("value"))?)?, + inferred, }) } } From 2931c5a932038e98c9b44badc682bfc45751c5e0 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 7 Jun 2023 22:00:45 +0400 Subject: [PATCH 09/64] explanation test (wip) --- src/answer/mod.rs | 2 +- src/lib.rs | 2 +- src/logic/mod.rs | 2 +- tests/integration/logic.rs | 220 +++++++++++++++++++++++++++++++++++++ tests/integration/mod.rs | 1 + 5 files changed, 224 insertions(+), 3 deletions(-) create mode 100644 tests/integration/logic.rs diff --git a/src/answer/mod.rs b/src/answer/mod.rs index 64343862..d1530b24 100644 --- a/src/answer/mod.rs +++ b/src/answer/mod.rs @@ -19,7 +19,7 @@ * under the License. */ -mod concept_map; +pub mod concept_map; mod concept_map_group; mod numeric; mod numeric_group; diff --git a/src/lib.rs b/src/lib.rs index d4aea87e..e4ce756d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ pub mod concept; mod connection; mod database; pub mod transaction; -mod logic; +pub mod logic; pub use self::{ common::{error, Credential, Error, Options, Result, SessionType, TransactionType}, diff --git a/src/logic/mod.rs b/src/logic/mod.rs index c5278375..4771d527 100644 --- a/src/logic/mod.rs +++ b/src/logic/mod.rs @@ -21,6 +21,6 @@ mod logic; mod rule; -mod explanation; +pub mod explanation; pub use self::{explanation::Explanation, logic::LogicManager, rule::Rule}; diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs new file mode 100644 index 00000000..a29f5fd6 --- /dev/null +++ b/tests/integration/logic.rs @@ -0,0 +1,220 @@ +/* + * 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::{sync::Arc, time::Instant}; +use std::collections::HashMap; + +use chrono::{NaiveDate, NaiveDateTime}; +use futures::{StreamExt, TryFutureExt, TryStreamExt}; +use regex::internal::Input; +use serial_test::serial; +use tokio::sync::mpsc; +use typedb_client::{concept::{Attribute, Concept, Value}, error::ConnectionError, Connection, DatabaseManager, Error, Options, Session, SessionType::{Data, Schema}, TransactionType::{Read, Write}, answer::ConceptMap, Transaction}; +use typedb_client::logic::Explanation; + +use super::common; + +macro_rules! test_for_each_arg { + { + $perm_args:tt + $( $( #[ $extra_anno:meta ] )* $async:ident fn $test:ident $args:tt -> $ret:ty $test_impl:block )+ + } => { + test_for_each_arg!{ @impl $( $async fn $test $args $ret $test_impl )+ } + test_for_each_arg!{ @impl_per $perm_args { $( $( #[ $extra_anno ] )* $async fn $test )+ } } + }; + + { @impl $( $async:ident fn $test:ident $args:tt $ret:ty $test_impl:block )+ } => { + mod _impl { + use super::*; + $( pub $async fn $test $args -> $ret $test_impl )+ + } + }; + + { @impl_per { $($mod:ident => $arg:expr),+ $(,)? } $fns:tt } => { + $(test_for_each_arg!{ @impl_mod { $mod => $arg } $fns })+ + }; + + { @impl_mod { $mod:ident => $arg:expr } { $( $( #[ $extra_anno:meta ] )* async fn $test:ident )+ } } => { + mod $mod { + use super::*; + $( + #[tokio::test] + #[serial($mod)] + $( #[ $extra_anno ] )* + pub async fn $test() { + _impl::$test($arg).await.unwrap(); + } + )+ + } + }; +} + +test_for_each_arg! { + { + core => common::new_core_connection().unwrap(), + } + + async fn test_disjunction_explainable(connection: Connection) -> typedb_client::Result { + let schema = r#"define + person sub entity, + owns name, + plays friendship:friend, + plays marriage:husband, + plays marriage:wife; + name sub attribute, value string; + friendship sub relation, + relates friend; + marriage sub relation, + relates husband, relates wife;"#; + common::create_test_database_with_schema(connection.clone(), schema).await?; + + // let databases = DatabaseManager::new(connection); + // if databases.contains(common::TEST_DATABASE).await? { + // databases.get(common::TEST_DATABASE).and_then(Database::delete).await?; + // } + // databases.create(common::TEST_DATABASE).await?; + // let database = databases.get(common::TEST_DATABASE).await?; + // let session = Session::new(database, Schema).await?; + // let transaction = session.transaction(Write).await?; + // + // let person = transaction.concept().put_entity_type(str!{"person"}).await?; + // let name = transaction.concept().put_attribute_type("name", AttributeType.ValueType.STRING); + // person.set_owns(name).await?; + // let friendship = transaction.concept().put_relation_type("friendship").await?; + // friendship.set_relates("friend").await?; + // let marriage = transaction.concept().put_relation_type("marriage").await?; + // marriage.set_relates("husband").await?; + // marriage.set_relates("wife").await?; + // person.set_plays(friendship.get_relates("friend")).await?; + // person.set_plays(marriage.get_relates("husband")).await?; + // person.set_plays(marriage.get_relates("wife")).await?; + + let databases = DatabaseManager::new(connection); + + { + let session = Session::new(databases.get(common::TEST_DATABASE).await?, Schema).await?; + let transaction = session.transaction(Write).await?; + transaction.logic().put_rule( + "marriage-is-friendship".to_string(), + typeql_lang::parse_pattern("{ $x isa person; $y isa person; (husband: $x, wife: $y) isa marriage; }") + .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))? + .into_conjunction(), + typeql_lang::parse_variable("(friend: $x, friend: $y) isa friendship") + .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))?, + ).await?; + transaction.commit().await?; + } + + let session = Session::new(databases.get(common::TEST_DATABASE).await?, Data).await?; + let transaction = session.transaction(Write).await?; + let data = r#"insert $x isa person, has name 'Zack'; + $y isa person, has name 'Yasmin'; + (husband: $x, wife: $y) isa marriage;"#; + let _ = transaction.query().insert(data)?; + transaction.commit().await?; + + let with_inference_and_explanation = Options::new().infer(true).explain(true); + let transaction = session.transaction_with_options(Read, with_inference_and_explanation).await?; + let answer_stream = transaction.query().match_( + r#"match $p1 isa person; + { (friend: $p1, friend: $p2) isa friendship;} + or { $p1 has name 'Zack'; };"#, + )?; + let answers = answer_stream.try_collect::>().await?; + + assert_eq!(3, answers.len()); + + let mut without_explainable = answers.get(0).unwrap(); + let mut with_explainable = answers.get(1).unwrap(); + if without_explainable.map.contains_key("p2") { + (without_explainable, with_explainable) = (with_explainable, without_explainable); + }; + + assert_eq!(3, with_explainable.map.len()); + assert_eq!(2, without_explainable.map.len()); + + assert!(with_explainable.explainables.is_some()); + // assert!(without_explainable.explainables.is_none()); + + assert_single_explainable_explanations(with_explainable, 1, 1, &transaction).await; + + Ok(()) + } +} + +async fn assert_single_explainable_explanations( + ans: &ConceptMap, + explainables_count: usize, + explanations_count: usize, + transaction: &Transaction<'_>, +) { + check_explainable_vars(ans); + let explainables = ans.clone().explainables.unwrap(); + let mut all_explainables = explainables.attributes.values().collect::>(); + all_explainables.extend(explainables.relations.values().collect::>()); + all_explainables.extend(explainables.ownerships.values().collect::>()); + assert_eq!(explainables_count, all_explainables.len()); + let explainable = all_explainables.get(0).unwrap(); + assert!(explainable.id >= 0); + let stream = transaction.query().explain(explainable.id); + assert!(stream.is_ok()); + let result = stream.unwrap().try_collect::>().await; + assert!(result.is_ok()); + let explanations = result.unwrap(); + assert_eq!(explanations_count, explanations.len()); + for explanation in explanations { + let mapping = explanation.variable_mapping; + + } + // explanations.forEach(explanation -> { + // Map> mapping = explanation.variableMapping(); + // Map> retrievableMapping = new HashMap<>(); + // mapping.forEach((k, v) -> retrievableMapping.put( + // k, iterate(v).filter(Identifier::isRetrievable).map(Variable::asRetrievable).toSet() + // )); + // ConceptMap projected = applyMapping(retrievableMapping, ans); + // projected.concepts().forEach((var, concept) -> { + // assertTrue(explanation.conclusionAnswer().concepts().containsKey(var)); + // assertEquals(explanation.conclusionAnswer().concepts().get(var), concept); + // }); + // }); + // return explanations; +} + +fn check_explainable_vars(ans: &ConceptMap) { + ans.clone().explainables.unwrap().relations.into_keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); + ans.clone().explainables.unwrap().attributes.into_keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); + ans.clone().explainables.unwrap().ownerships.into_keys() + .for_each(|(k1, k2)| assert!(ans.map.contains_key(k1.as_str()) && ans.map.contains_key(k2.as_str()))); +} + +fn apply_mapping(mapping: HashMap>, complete_map: ConceptMap) -> ConceptMap { + let concepts: HashMap> = HashMap::new(); + for key in mapping.keys() { + assert!(complete_map.map.contains_key(key)); + let concept = complete_map.get(key).unwrap(); + for mapped in mapping.get(key).unwrap() { + assert!(!concepts.contains_key(mapped) || concepts.get(mapped).unwrap().equals(concept)); + concepts.insert(mapped, concept); + } + } + return ConceptMap::from(concepts); +} \ No newline at end of file diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs index 036bff3c..5a87fc7d 100644 --- a/tests/integration/mod.rs +++ b/tests/integration/mod.rs @@ -20,5 +20,6 @@ */ mod common; +mod logic; mod queries; mod runtimes; From 87cc6ca5c54421fc08bf0d08cd7411bc3443e79f Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 8 Jun 2023 13:45:52 +0400 Subject: [PATCH 10/64] PartialEq trait for Concept --- src/concept/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/concept/mod.rs b/src/concept/mod.rs index 6e6931b8..f1f5b97e 100644 --- a/src/concept/mod.rs +++ b/src/concept/mod.rs @@ -29,7 +29,7 @@ pub use self::{ }, }; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum Concept { RootThingType(RootThingType), From 68f82a11967f3ad218223411397df24e2ea78a9a Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 8 Jun 2023 13:47:33 +0400 Subject: [PATCH 11/64] test_disjunction_explainable --- tests/integration/logic.rs | 58 ++++++++++---------------------------- 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index a29f5fd6..08bda66e 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -86,29 +86,7 @@ test_for_each_arg! { relates husband, relates wife;"#; common::create_test_database_with_schema(connection.clone(), schema).await?; - // let databases = DatabaseManager::new(connection); - // if databases.contains(common::TEST_DATABASE).await? { - // databases.get(common::TEST_DATABASE).and_then(Database::delete).await?; - // } - // databases.create(common::TEST_DATABASE).await?; - // let database = databases.get(common::TEST_DATABASE).await?; - // let session = Session::new(database, Schema).await?; - // let transaction = session.transaction(Write).await?; - // - // let person = transaction.concept().put_entity_type(str!{"person"}).await?; - // let name = transaction.concept().put_attribute_type("name", AttributeType.ValueType.STRING); - // person.set_owns(name).await?; - // let friendship = transaction.concept().put_relation_type("friendship").await?; - // friendship.set_relates("friend").await?; - // let marriage = transaction.concept().put_relation_type("marriage").await?; - // marriage.set_relates("husband").await?; - // marriage.set_relates("wife").await?; - // person.set_plays(friendship.get_relates("friend")).await?; - // person.set_plays(marriage.get_relates("husband")).await?; - // person.set_plays(marriage.get_relates("wife")).await?; - let databases = DatabaseManager::new(connection); - { let session = Session::new(databases.get(common::TEST_DATABASE).await?, Schema).await?; let transaction = session.transaction(Write).await?; @@ -182,21 +160,12 @@ async fn assert_single_explainable_explanations( assert_eq!(explanations_count, explanations.len()); for explanation in explanations { let mapping = explanation.variable_mapping; - - } - // explanations.forEach(explanation -> { - // Map> mapping = explanation.variableMapping(); - // Map> retrievableMapping = new HashMap<>(); - // mapping.forEach((k, v) -> retrievableMapping.put( - // k, iterate(v).filter(Identifier::isRetrievable).map(Variable::asRetrievable).toSet() - // )); - // ConceptMap projected = applyMapping(retrievableMapping, ans); - // projected.concepts().forEach((var, concept) -> { - // assertTrue(explanation.conclusionAnswer().concepts().containsKey(var)); - // assertEquals(explanation.conclusionAnswer().concepts().get(var), concept); - // }); - // }); - // return explanations; + let projected = apply_mapping(&mapping, ans); + for var in projected.map.keys() { + assert!(explanation.conclusion.map.contains_key(var)); + assert_eq!(explanation.conclusion.map.get(var), projected.map.get(var)); + }; + }; } fn check_explainable_vars(ans: &ConceptMap) { @@ -206,15 +175,18 @@ fn check_explainable_vars(ans: &ConceptMap) { .for_each(|(k1, k2)| assert!(ans.map.contains_key(k1.as_str()) && ans.map.contains_key(k2.as_str()))); } -fn apply_mapping(mapping: HashMap>, complete_map: ConceptMap) -> ConceptMap { - let concepts: HashMap> = HashMap::new(); +fn apply_mapping(mapping: &HashMap>, complete_map: &ConceptMap) -> ConceptMap { + let mut concepts: HashMap = HashMap::new(); for key in mapping.keys() { assert!(complete_map.map.contains_key(key)); let concept = complete_map.get(key).unwrap(); for mapped in mapping.get(key).unwrap() { - assert!(!concepts.contains_key(mapped) || concepts.get(mapped).unwrap().equals(concept)); - concepts.insert(mapped, concept); + assert!(!concepts.contains_key(mapped) || concepts.get(mapped).unwrap() == concept); + concepts.insert(mapped.to_string(), concept.clone()); } } - return ConceptMap::from(concepts); -} \ No newline at end of file + ConceptMap { + map: concepts, + explainables: None, + } +} From 1a9b4f27a8722843c5847c51348c1353db12707a Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 8 Jun 2023 14:12:34 +0400 Subject: [PATCH 12/64] Empty Explainables -> None --- src/connection/network/proto/concept.rs | 13 ++++++++++++- tests/integration/logic.rs | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/connection/network/proto/concept.rs b/src/connection/network/proto/concept.rs index 0f212562..61d9daad 100644 --- a/src/connection/network/proto/concept.rs +++ b/src/connection/network/proto/concept.rs @@ -102,7 +102,18 @@ impl TryFromProto for ConceptMap { for (k, v) in proto.map { map.insert(k, Concept::try_from_proto(v)?); } - Ok(Self { map, explainables: proto.explainables.map(Explainables::from_proto) }) + let explainables = match proto.explainables { + Some(expl) => { + if expl.attributes.len() > 0 || expl.relations.len() > 0 || expl.ownerships.len() > 0 { + Some(Explainables::from_proto(expl)) + } + else { + None + } + } + None => return Err(ConnectionError::MissingResponseField("explainables").into()), + }; + Ok(Self { map, explainables }) } } diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 08bda66e..7fd9328e 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -130,7 +130,7 @@ test_for_each_arg! { assert_eq!(2, without_explainable.map.len()); assert!(with_explainable.explainables.is_some()); - // assert!(without_explainable.explainables.is_none()); + assert!(without_explainable.explainables.is_none()); assert_single_explainable_explanations(with_explainable, 1, 1, &transaction).await; From 189a2d46455a14627a6abe8938c43d642938fd42 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 8 Jun 2023 14:19:38 +0400 Subject: [PATCH 13/64] test_relation_explainable --- tests/integration/logic.rs | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 7fd9328e..d938bfe6 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -136,6 +136,61 @@ test_for_each_arg! { Ok(()) } + + async fn test_relation_explainable(connection: Connection) -> typedb_client::Result { + let schema = r#"define + person sub entity, + owns name, + plays friendship:friend, + plays marriage:husband, + plays marriage:wife; + name sub attribute, value string; + friendship sub relation, + relates friend; + marriage sub relation, + relates husband, relates wife;"#; + common::create_test_database_with_schema(connection.clone(), schema).await?; + + let databases = DatabaseManager::new(connection); + { + let session = Session::new(databases.get(common::TEST_DATABASE).await?, Schema).await?; + let transaction = session.transaction(Write).await?; + transaction.logic().put_rule( + "marriage-is-friendship".to_string(), + typeql_lang::parse_pattern("{ $x isa person; $y isa person; (husband: $x, wife: $y) isa marriage; }") + .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))? + .into_conjunction(), + typeql_lang::parse_variable("(friend: $x, friend: $y) isa friendship") + .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))?, + ).await?; + transaction.commit().await?; + } + + let session = Session::new(databases.get(common::TEST_DATABASE).await?, Data).await?; + let transaction = session.transaction(Write).await?; + let data = r#"insert $x isa person, has name 'Zack'; + $y isa person, has name 'Yasmin'; + (husband: $x, wife: $y) isa marriage;"#; + let _ = transaction.query().insert(data)?; + transaction.commit().await?; + + let with_inference_and_explanation = Options::new().infer(true).explain(true); + let transaction = session.transaction_with_options(Read, with_inference_and_explanation).await?; + let answer_stream = transaction.query().match_( + r#"match (friend: $p1, friend: $p2) isa friendship; $p1 has name $na;"#, + )?; + let answers = answer_stream.try_collect::>().await?; + + assert_eq!(2, answers.len()); + + assert!(answers.get(0).unwrap().explainables.is_some()); + assert!(answers.get(1).unwrap().explainables.is_some()); + + assert_single_explainable_explanations(answers.get(0).unwrap(), 1, 1, &transaction).await; + assert_single_explainable_explanations(answers.get(1).unwrap(), 1, 1, &transaction).await; + + Ok(()) + } } async fn assert_single_explainable_explanations( From 04dce460aced208dd6da05f381d3c076a36419d3 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 8 Jun 2023 14:25:16 +0400 Subject: [PATCH 14/64] test_relation_explainable_multiple_ways --- tests/integration/logic.rs | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index d938bfe6..1353b44d 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -191,6 +191,69 @@ test_for_each_arg! { Ok(()) } + + async fn test_relation_explainable_multiple_ways(connection: Connection) -> typedb_client::Result { + let schema = r#"define + person sub entity, + owns name, + plays friendship:friend, + plays marriage:husband, + plays marriage:wife; + name sub attribute, value string; + friendship sub relation, + relates friend; + marriage sub relation, + relates husband, relates wife;"#; + common::create_test_database_with_schema(connection.clone(), schema).await?; + + let databases = DatabaseManager::new(connection); + { + let session = Session::new(databases.get(common::TEST_DATABASE).await?, Schema).await?; + let transaction = session.transaction(Write).await?; + transaction.logic().put_rule( + "marriage-is-friendship".to_string(), + typeql_lang::parse_pattern("{ $x isa person; $y isa person; (husband: $x, wife: $y) isa marriage; }") + .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))? + .into_conjunction(), + typeql_lang::parse_variable("(friend: $x, friend: $y) isa friendship") + .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))?, + ).await?; + transaction.logic().put_rule( + "everyone-is-friends".to_string(), + typeql_lang::parse_pattern("{ $x isa person; $y isa person; not { $x is $y; }; }") + .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))? + .into_conjunction(), + typeql_lang::parse_variable("(friend: $x, friend: $y) isa friendship") + .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))?, + ).await?; + transaction.commit().await?; + } + + let session = Session::new(databases.get(common::TEST_DATABASE).await?, Data).await?; + let transaction = session.transaction(Write).await?; + let data = r#"insert $x isa person, has name 'Zack'; + $y isa person, has name 'Yasmin'; + (husband: $x, wife: $y) isa marriage;"#; + let _ = transaction.query().insert(data)?; + transaction.commit().await?; + + let with_inference_and_explanation = Options::new().infer(true).explain(true); + let transaction = session.transaction_with_options(Read, with_inference_and_explanation).await?; + let answer_stream = transaction.query().match_( + r#"match (friend: $p1, friend: $p2) isa friendship; $p1 has name $na;"#, + )?; + let answers = answer_stream.try_collect::>().await?; + + assert_eq!(2, answers.len()); + + assert!(answers.get(0).unwrap().explainables.is_some()); + assert!(answers.get(1).unwrap().explainables.is_some()); + + assert_single_explainable_explanations(answers.get(0).unwrap(), 1, 3, &transaction).await; + assert_single_explainable_explanations(answers.get(1).unwrap(), 1, 3, &transaction).await; + + Ok(()) + } } async fn assert_single_explainable_explanations( From ff2bbf5d9b4464b9e523993784d758d502358de6 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 8 Jun 2023 15:41:29 +0400 Subject: [PATCH 15/64] test_has_explicit_explainable_two_ways --- tests/integration/logic.rs | 76 +++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 1353b44d..569b9efb 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -27,7 +27,8 @@ use futures::{StreamExt, TryFutureExt, TryStreamExt}; use regex::internal::Input; use serial_test::serial; use tokio::sync::mpsc; -use typedb_client::{concept::{Attribute, Concept, Value}, error::ConnectionError, Connection, DatabaseManager, Error, Options, Session, SessionType::{Data, Schema}, TransactionType::{Read, Write}, answer::ConceptMap, Transaction}; +use typedb_client::{concept::{Attribute, AttributeType, Concept, Value}, error::ConnectionError, Connection, DatabaseManager, Error, Options, Session, SessionType::{Data, Schema}, TransactionType::{Read, Write}, answer::ConceptMap, Transaction}; +use typedb_client::concept::Thing; use typedb_client::logic::Explanation; use super::common; @@ -254,6 +255,79 @@ test_for_each_arg! { Ok(()) } + + async fn test_has_explicit_explainable_two_ways(connection: Connection) -> typedb_client::Result { + let schema = r#"define + milk sub entity, + owns age-in-days, + owns is-still-good; + age-in-days sub attribute, value long; + is-still-good sub attribute, value boolean;"#; + common::create_test_database_with_schema(connection.clone(), schema).await?; + + let databases = DatabaseManager::new(connection); + { + let session = Session::new(databases.get(common::TEST_DATABASE).await?, Schema).await?; + let transaction = session.transaction(Write).await?; + transaction.logic().put_rule( + "old-milk-is-not-good".to_string(), + typeql_lang::parse_pattern("{ $x isa milk, has age-in-days <= 10; }") + .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))? + .into_conjunction(), + typeql_lang::parse_variable("$x has is-still-good true") + .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))?, + ).await?; + transaction.logic().put_rule( + "all-milk-is-good".to_string(), + typeql_lang::parse_pattern("{ $x isa milk; }") + .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))? + .into_conjunction(), + typeql_lang::parse_variable("$x has is-still-good true") + .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))?, + ).await?; + transaction.commit().await?; + } + + let session = Session::new(databases.get(common::TEST_DATABASE).await?, Data).await?; + let transaction = session.transaction(Write).await?; + let data = r#"insert $x isa milk, has age-in-days 5;"#; + let _ = transaction.query().insert(data)?; + let data = r#"insert $x isa milk, has age-in-days 10;"#; + let _ = transaction.query().insert(data)?; + let data = r#"insert $x isa milk, has age-in-days 15;"#; + let _ = transaction.query().insert(data)?; + transaction.commit().await?; + + let with_inference_and_explanation = Options::new().infer(true).explain(true); + let transaction = session.transaction_with_options(Read, with_inference_and_explanation).await?; + let answer_stream = transaction.query().match_( + r#"match $x has is-still-good $a;"#, + )?; + let answers = answer_stream.try_collect::>().await?; + + assert_eq!(3, answers.len()); + + assert!(answers.get(0).unwrap().explainables.is_some()); + assert!(answers.get(1).unwrap().explainables.is_some()); + assert!(answers.get(2).unwrap().explainables.is_some()); + + let age_in_days = transaction.concept().get_attribute_type(String::from("age-in-days")).await?.unwrap(); + for ans in answers { + match ans.map.get("x").unwrap() { + Concept::Entity(entity) => { + let attributes: Vec = entity.get_has(&transaction, vec![age_in_days.clone()], vec![])?.try_collect().await?; + if attributes.first().unwrap().value == Value::Long(15) { + assert_single_explainable_explanations(&ans, 1, 1, &transaction).await; + } else { + assert_single_explainable_explanations(&ans, 1, 2, &transaction).await; + } + }, + _ => panic!("Incorrect Concept type: {:?}", ans.map.get("x").unwrap()), + } + } + + Ok(()) + } } async fn assert_single_explainable_explanations( From ac4e714993186b38f06f02ae948d1b55c6dbd9e2 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 8 Jun 2023 15:56:04 +0400 Subject: [PATCH 16/64] fmt --- src/answer/concept_map.rs | 2 +- src/answer/mod.rs | 2 +- src/connection/message.rs | 2 +- src/connection/network/proto/concept.rs | 48 ++++++++++------------ src/connection/network/proto/logic.rs | 19 +++------ src/connection/network/proto/message.rs | 53 +++++++++++++------------ src/connection/transaction_stream.rs | 13 +++--- src/logic/explanation.rs | 4 +- src/logic/logic.rs | 6 +-- src/logic/mod.rs | 2 +- src/transaction/mod.rs | 1 - src/transaction/query.rs | 8 +++- tests/behaviour/typeql/steps.rs | 2 +- tests/behaviour/util.rs | 7 +++- tests/integration/logic.rs | 30 +++++++++----- 15 files changed, 104 insertions(+), 95 deletions(-) diff --git a/src/answer/concept_map.rs b/src/answer/concept_map.rs index 891cedef..77b0225a 100644 --- a/src/answer/concept_map.rs +++ b/src/answer/concept_map.rs @@ -95,7 +95,7 @@ impl IntoIterator for ConceptMap { impl Explainables { pub(crate) fn new( relations: HashMap, - attributes:HashMap, + attributes: HashMap, ownerships: HashMap<(String, String), Explainable>, ) -> Self { Self { relations, attributes, ownerships } diff --git a/src/answer/mod.rs b/src/answer/mod.rs index d1530b24..c1a9bf88 100644 --- a/src/answer/mod.rs +++ b/src/answer/mod.rs @@ -25,6 +25,6 @@ mod numeric; mod numeric_group; pub use self::{ - concept_map::ConceptMap, concept_map_group::ConceptMapGroup, concept_map::Explainable, concept_map::Explainables, + concept_map::ConceptMap, concept_map::Explainable, concept_map::Explainables, concept_map_group::ConceptMapGroup, numeric::Numeric, numeric_group::NumericGroup, }; diff --git a/src/connection/message.rs b/src/connection/message.rs index cbdf49d8..65165781 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -26,6 +26,7 @@ use tonic::Streaming; use typedb_protocol::transaction; use typeql_lang::pattern::{Conjunction, Variable}; +use crate::logic::Explanation; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, common::{address::Address, info::DatabaseInfo, RequestID, SessionID, IID}, @@ -36,7 +37,6 @@ use crate::{ Options, SessionType, TransactionType, Rule, SchemaException, }; -use crate::logic::Explanation; #[derive(Debug)] pub(super) enum Request { diff --git a/src/connection/network/proto/concept.rs b/src/connection/network/proto/concept.rs index 61d9daad..a3158ad8 100644 --- a/src/connection/network/proto/concept.rs +++ b/src/connection/network/proto/concept.rs @@ -25,17 +25,20 @@ use chrono::NaiveDateTime; use itertools::Itertools; use typedb_protocol::{ attribute::{value::Value as ValueProtoInner, Value as ValueProto}, - attribute_type::ValueType as ValueTypeProto, concept, numeric::Value as NumericValue, + attribute_type::ValueType as ValueTypeProto, + concept, + numeric::Value as NumericValue, r#type::{annotation, Annotation as AnnotationProto, Transitivity as TransitivityProto}, - thing, thing_type, Attribute as AttributeProto, AttributeType as AttributeTypeProto, - Concept as ConceptProto, ConceptMap as ConceptMapProto, ConceptMapGroup as ConceptMapGroupProto, - Entity as EntityProto, EntityType as EntityTypeProto, Explainable as ExplainableProto, Explainables as ExplainablesProto, - Explanation as ExplanationProto, - Numeric as NumericProto, NumericGroup as NumericGroupProto, Relation as RelationProto, RelationType as RelationTypeProto, - RoleType as RoleTypeProto, Thing as ThingProto, ThingType as ThingTypeProto, + thing, thing_type, Attribute as AttributeProto, AttributeType as AttributeTypeProto, Concept as ConceptProto, + ConceptMap as ConceptMapProto, ConceptMapGroup as ConceptMapGroupProto, Entity as EntityProto, + EntityType as EntityTypeProto, Explainable as ExplainableProto, Explainables as ExplainablesProto, + Explanation as ExplanationProto, Numeric as NumericProto, NumericGroup as NumericGroupProto, + Relation as RelationProto, RelationType as RelationTypeProto, RoleType as RoleTypeProto, Thing as ThingProto, + ThingType as ThingTypeProto, }; use super::{FromProto, IntoProto, TryFromProto}; +use crate::logic::Explanation; use crate::{ answer::{ConceptMap, ConceptMapGroup, Explainable, Explainables, Numeric, NumericGroup}, concept::{ @@ -43,10 +46,8 @@ use crate::{ RootThingType, ScopedLabel, Thing, ThingType, Transitivity, Value, ValueType, }, error::{ConnectionError, InternalError}, - Result, - Rule, + Result, Rule, }; -use crate::logic::Explanation; impl IntoProto for Transitivity { fn into_proto(self) -> i32 { @@ -106,8 +107,7 @@ impl TryFromProto for ConceptMap { Some(expl) => { if expl.attributes.len() > 0 || expl.relations.len() > 0 || expl.ownerships.len() > 0 { Some(Explainables::from_proto(expl)) - } - else { + } else { None } } @@ -300,11 +300,7 @@ impl TryFromProto for Entity { impl IntoProto for Entity { fn into_proto(self) -> EntityProto { - EntityProto { - iid: self.iid.into(), - entity_type: Some(self.type_.into_proto()), - inferred: self.inferred, - } + EntityProto { iid: self.iid.into(), entity_type: Some(self.type_.into_proto()), inferred: self.inferred } } } @@ -323,11 +319,7 @@ impl TryFromProto for Relation { impl IntoProto for Relation { fn into_proto(self) -> RelationProto { - RelationProto { - iid: self.iid.into(), - relation_type: Some(self.type_.into_proto()), - inferred: self.inferred, - } + RelationProto { iid: self.iid.into(), relation_type: Some(self.type_.into_proto()), inferred: self.inferred } } } @@ -402,7 +394,7 @@ impl FromProto for Explainables { } } - Self::new( relations, attributes, ownerships) + Self::new(relations, attributes, ownerships) } } @@ -423,11 +415,13 @@ impl TryFromProto for Explanation { Ok(Self { rule: Rule::try_from_proto(rule.ok_or(ConnectionError::MissingResponseField("rule"))?)?, - conclusion: ConceptMap::try_from_proto(conclusion.ok_or(ConnectionError::MissingResponseField("conclusion"))?)?, - condition: ConceptMap::try_from_proto(condition.ok_or(ConnectionError::MissingResponseField("condition"))?)?, + conclusion: ConceptMap::try_from_proto( + conclusion.ok_or(ConnectionError::MissingResponseField("conclusion"))?, + )?, + condition: ConceptMap::try_from_proto( + condition.ok_or(ConnectionError::MissingResponseField("condition"))?, + )?, variable_mapping, }) } } - - diff --git a/src/connection/network/proto/logic.rs b/src/connection/network/proto/logic.rs index 4f614369..1311b3d4 100644 --- a/src/connection/network/proto/logic.rs +++ b/src/connection/network/proto/logic.rs @@ -19,14 +19,12 @@ * under the License. */ -use typedb_protocol::{ - Rule as RuleProto, -}; -use typeql_lang::{parse_pattern, parse_variable}; -use typeql_lang::pattern::{Pattern, Variable}; -use crate::{common::Result, Error, error::InternalError, Rule}; -use crate::error::ConnectionError; use super::{IntoProto, TryFromProto}; +use crate::error::ConnectionError; +use crate::{common::Result, error::InternalError, Error, Rule}; +use typedb_protocol::Rule as RuleProto; +use typeql_lang::pattern::{Pattern, Variable}; +use typeql_lang::{parse_pattern, parse_variable}; impl TryFromProto for Rule { fn try_from_proto(proto: RuleProto) -> Result { @@ -46,11 +44,6 @@ impl TryFromProto for Rule { impl IntoProto for Rule { fn into_proto(self) -> RuleProto { - RuleProto { - label: self.label, - when: self.when.to_string(), - then: self.then.to_string() , - } + RuleProto { label: self.label, when: self.when.to_string(), then: self.then.to_string() } } } - diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index ff08d086..91208d56 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -22,9 +22,13 @@ 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, server_manager, session, thing, thing_type, transaction}; +use typedb_protocol::{ + attribute, attribute_type, concept_manager, database, database_manager, entity_type, logic_manager, query_manager, + r#type, relation, relation_type, role_type, server_manager, session, thing, thing_type, transaction, +}; use super::{FromProto, IntoProto, TryFromProto, TryIntoProto}; +use crate::logic::Explanation; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, common::{info::DatabaseInfo, RequestID, Result}, @@ -40,7 +44,6 @@ use crate::{ error::{ConnectionError, InternalError}, Rule, SchemaException, }; -use crate::logic::Explanation; impl TryIntoProto for Request { fn try_into_proto(self) -> Result { @@ -317,7 +320,9 @@ impl TryFromProto for TransactionResponse { res: Some(r#type::res_part::Res::RoleTypeResPart(res)), })) => Ok(Self::RoleType(RoleTypeResponse::try_from_proto(res)?)), Some(transaction::res_part::Res::ThingResPart(res)) => Ok(Self::Thing(ThingResponse::try_from_proto(res)?)), - Some(transaction::res_part::Res::LogicManagerResPart(res)) => Ok(Self::Logic(LogicResponse::try_from_proto(res)?)), + Some(transaction::res_part::Res::LogicManagerResPart(res)) => { + Ok(Self::Logic(LogicResponse::try_from_proto(res)?)) + } Some(_) => todo!(), None => Err(ConnectionError::MissingResponseField("res").into()), } @@ -399,9 +404,9 @@ impl TryFromProto for QueryResponse { Some(query_manager::res_part::Res::MatchGroupAggregateResPart(res)) => Ok(Self::MatchGroupAggregate { answers: res.answers.into_iter().map(NumericGroup::try_from_proto).try_collect()?, }), - Some(query_manager::res_part::Res::ExplainResPart(res)) => { - Ok(Self::Explain { answers: res.explanations.into_iter().map(Explanation::try_from_proto).try_collect()? }) - } + Some(query_manager::res_part::Res::ExplainResPart(res)) => Ok(Self::Explain { + answers: res.explanations.into_iter().map(Explanation::try_from_proto).try_collect()?, + }), None => Err(ConnectionError::MissingResponseField("res").into()), } } @@ -1114,19 +1119,13 @@ impl TryFromProto for ThingResponse { impl IntoProto for LogicRequest { 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 {}) - } + 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) } } @@ -1135,10 +1134,12 @@ impl IntoProto for LogicRequest { impl TryFromProto for LogicResponse { fn try_from_proto(proto: logic_manager::Res) -> Result { match proto.res { - Some(logic_manager::res::Res::PutRuleRes(logic_manager::put_rule::Res { rule})) => - Ok(Self::PutRule { rule: Rule::try_from_proto(rule.unwrap()).unwrap() }), - Some(logic_manager::res::Res::GetRuleRes(logic_manager::get_rule::Res { rule })) => - Ok(Self::GetRule { rule: Rule::try_from_proto(rule.unwrap()).unwrap() }), + Some(logic_manager::res::Res::PutRuleRes(logic_manager::put_rule::Res { rule })) => { + Ok(Self::PutRule { rule: Rule::try_from_proto(rule.unwrap()).unwrap() }) + } + Some(logic_manager::res::Res::GetRuleRes(logic_manager::get_rule::Res { rule })) => { + Ok(Self::GetRule { rule: Rule::try_from_proto(rule.unwrap()).unwrap() }) + } None => Err(ConnectionError::MissingResponseField("res").into()), } } @@ -1146,6 +1147,8 @@ impl TryFromProto for LogicResponse { impl TryFromProto for LogicResponse { fn try_from_proto(proto: logic_manager::ResPart) -> Result { - Ok(Self::GetRules { rules: proto.get_rules_res_part.unwrap().rules.into_iter().map(Rule::try_from_proto).try_collect()? }) + Ok(Self::GetRules { + rules: proto.get_rules_res_part.unwrap().rules.into_iter().map(Rule::try_from_proto).try_collect()?, + }) } -} \ No newline at end of file +} diff --git a/src/connection/transaction_stream.rs b/src/connection/transaction_stream.rs index f0ffb510..93b2860c 100644 --- a/src/connection/transaction_stream.rs +++ b/src/connection/transaction_stream.rs @@ -28,6 +28,8 @@ use super::{ message::{RoleTypeRequest, RoleTypeResponse, ThingRequest, ThingResponse}, network::transmitter::TransactionTransmitter, }; +use crate::connection::message::{LogicRequest, LogicResponse}; +use crate::logic::Explanation; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, common::{Result, IID}, @@ -42,8 +44,6 @@ use crate::{ error::InternalError, Options, Rule, SchemaException, TransactionType, }; -use crate::connection::message::{LogicRequest, LogicResponse}; -use crate::logic::Explanation; pub(crate) struct TransactionStream { type_: TransactionType, @@ -933,7 +933,7 @@ impl TransactionStream { } pub(crate) async fn put_rule(&self, label: String, when: Conjunction, then: Variable) -> Result { - match self.logic_single(LogicRequest::PutRule { label, when, then } ).await? { + match self.logic_single(LogicRequest::PutRule { label, when, then }).await? { LogicResponse::PutRule { rule } => Ok(rule), other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), } @@ -1001,7 +1001,11 @@ impl TransactionStream { } } - pub(crate) fn explain(&self, explainable_id: i64, options: Options) -> Result>> { + pub(crate) fn explain( + &self, + explainable_id: i64, + options: Options, + ) -> Result>> { let stream = self.query_stream(QueryRequest::Explain { explainable_id, options })?; Ok(stream.flat_map(|result| match result { Ok(QueryResponse::Explain { answers }) => stream_iter(answers.into_iter().map(Ok)), @@ -1061,7 +1065,6 @@ impl TransactionStream { Err(err) => Err(err), })) } - } impl fmt::Debug for TransactionStream { diff --git a/src/logic/explanation.rs b/src/logic/explanation.rs index 1a3f9cb6..7a87b63a 100644 --- a/src/logic/explanation.rs +++ b/src/logic/explanation.rs @@ -19,9 +19,9 @@ * under the License. */ -use std::collections::HashMap; -use crate::Rule; use crate::answer::ConceptMap; +use crate::Rule; +use std::collections::HashMap; #[derive(Debug)] pub struct Explanation { diff --git a/src/logic/logic.rs b/src/logic/logic.rs index 2dce3412..8d043c69 100644 --- a/src/logic/logic.rs +++ b/src/logic/logic.rs @@ -19,10 +19,10 @@ * under the License. */ -use std::sync::Arc; +use crate::{common::Result, connection::TransactionStream, Rule}; use futures::Stream; +use std::sync::Arc; use typeql_lang::pattern::{Conjunction, Variable}; -use crate::{common::Result, connection::TransactionStream, Rule}; #[derive(Clone, Debug)] pub struct LogicManager { @@ -45,4 +45,4 @@ impl LogicManager { pub fn get_rules(&self) -> Result>> { self.transaction_stream.get_rules() } -} \ No newline at end of file +} diff --git a/src/logic/mod.rs b/src/logic/mod.rs index 4771d527..1c811a53 100644 --- a/src/logic/mod.rs +++ b/src/logic/mod.rs @@ -19,8 +19,8 @@ * under the License. */ +pub mod explanation; mod logic; mod rule; -pub mod explanation; pub use self::{explanation::Explanation, logic::LogicManager, rule::Rule}; diff --git a/src/transaction/mod.rs b/src/transaction/mod.rs index bc69cbc2..89090faa 100644 --- a/src/transaction/mod.rs +++ b/src/transaction/mod.rs @@ -42,7 +42,6 @@ pub struct Transaction<'a> { transaction_stream: Arc, _lifetime_guard: PhantomData<&'a ()>, - } impl Transaction<'_> { diff --git a/src/transaction/query.rs b/src/transaction/query.rs index 27a98023..8c1a6662 100644 --- a/src/transaction/query.rs +++ b/src/transaction/query.rs @@ -23,13 +23,13 @@ use std::sync::Arc; use futures::Stream; +use crate::logic::Explanation; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, common::Result, connection::TransactionStream, Options, }; -use crate::logic::Explanation; #[derive(Debug)] pub struct QueryManager { @@ -125,7 +125,11 @@ impl QueryManager { self.explain_with_options(explainable_id, Options::new()) } - pub fn explain_with_options(&self, explainable_id: i64, options: Options) -> Result>> { + pub fn explain_with_options( + &self, + explainable_id: i64, + options: Options, + ) -> Result>> { self.transaction_stream.explain(explainable_id, options) } } diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index ca1e1efd..45654356 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -27,12 +27,12 @@ use util::{ equals_approximate, iter_table_map, match_answer_concept, match_answer_concept_map, match_answer_rule, match_templated_answer, }; +use crate::behaviour::parameter::Comparable; use crate::{ assert_err, behaviour::{util, Context}, generic_step_impl, }; -use crate::behaviour::parameter::Comparable; generic_step_impl! { #[step(expr = "typeql define")] diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index d37a6b0b..9d9f1b1c 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -183,7 +183,12 @@ pub async fn match_answer_rule( ) -> bool { let when_clause = answer_identifiers.get(&String::from("when")).unwrap().trim_end_matches(";").to_string(); let when = parse_pattern(when_clause.as_str()).unwrap().into_conjunction(); - let then_clause = answer_identifiers.get(&String::from("then")).unwrap().trim_end_matches(['}', ';', ' ']).trim_start_matches("{").to_string(); + let then_clause = answer_identifiers + .get(&String::from("then")) + .unwrap() + .trim_end_matches(['}', ';', ' ']) + .trim_start_matches("{") + .to_string(); let then_var = parse_pattern(then_clause.as_str()).unwrap().into_variable(); if let Variable::Thing(then) = then_var { answer_identifiers.get(&String::from("label")).unwrap().to_string() == answer.label diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 569b9efb..c88ab609 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -19,17 +19,24 @@ * under the License. */ -use std::{sync::Arc, time::Instant}; use std::collections::HashMap; +use std::{sync::Arc, time::Instant}; use chrono::{NaiveDate, NaiveDateTime}; use futures::{StreamExt, TryFutureExt, TryStreamExt}; use regex::internal::Input; use serial_test::serial; use tokio::sync::mpsc; -use typedb_client::{concept::{Attribute, AttributeType, Concept, Value}, error::ConnectionError, Connection, DatabaseManager, Error, Options, Session, SessionType::{Data, Schema}, TransactionType::{Read, Write}, answer::ConceptMap, Transaction}; -use typedb_client::concept::Thing; -use typedb_client::logic::Explanation; +use typedb_client::{ + answer::ConceptMap, + concept::{Attribute, AttributeType, Concept, Thing, Value}, + error::ConnectionError, + logic::Explanation, + Connection, DatabaseManager, Error, Options, Session, + SessionType::{Data, Schema}, + Transaction, + TransactionType::{Read, Write}, +}; use super::common; @@ -356,14 +363,18 @@ async fn assert_single_explainable_explanations( for var in projected.map.keys() { assert!(explanation.conclusion.map.contains_key(var)); assert_eq!(explanation.conclusion.map.get(var), projected.map.get(var)); - }; - }; + } + } } fn check_explainable_vars(ans: &ConceptMap) { ans.clone().explainables.unwrap().relations.into_keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); ans.clone().explainables.unwrap().attributes.into_keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); - ans.clone().explainables.unwrap().ownerships.into_keys() + ans.clone() + .explainables + .unwrap() + .ownerships + .into_keys() .for_each(|(k1, k2)| assert!(ans.map.contains_key(k1.as_str()) && ans.map.contains_key(k2.as_str()))); } @@ -377,8 +388,5 @@ fn apply_mapping(mapping: &HashMap>, complete_map: &ConceptM concepts.insert(mapped.to_string(), concept.clone()); } } - ConceptMap { - map: concepts, - explainables: None, - } + ConceptMap { map: concepts, explainables: None } } From 6665d13d65e1ee05f22bd5d78e096a87e6d98bd3 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 8 Jun 2023 15:59:46 +0400 Subject: [PATCH 17/64] Cleanup --- src/connection/network/proto/logic.rs | 3 +-- tests/integration/logic.rs | 8 ++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/connection/network/proto/logic.rs b/src/connection/network/proto/logic.rs index 1311b3d4..8be383e6 100644 --- a/src/connection/network/proto/logic.rs +++ b/src/connection/network/proto/logic.rs @@ -20,8 +20,7 @@ */ use super::{IntoProto, TryFromProto}; -use crate::error::ConnectionError; -use crate::{common::Result, error::InternalError, Error, Rule}; +use crate::{common::Result, Error, Rule}; use typedb_protocol::Rule as RuleProto; use typeql_lang::pattern::{Pattern, Variable}; use typeql_lang::{parse_pattern, parse_variable}; diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index c88ab609..3c99eba5 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -20,19 +20,15 @@ */ use std::collections::HashMap; -use std::{sync::Arc, time::Instant}; -use chrono::{NaiveDate, NaiveDateTime}; use futures::{StreamExt, TryFutureExt, TryStreamExt}; use regex::internal::Input; use serial_test::serial; -use tokio::sync::mpsc; use typedb_client::{ answer::ConceptMap, - concept::{Attribute, AttributeType, Concept, Thing, Value}, - error::ConnectionError, + concept::{Attribute, Concept, Value}, logic::Explanation, - Connection, DatabaseManager, Error, Options, Session, + Connection, DatabaseManager, Options, Session, SessionType::{Data, Schema}, Transaction, TransactionType::{Read, Write}, From 77488de3965ff0366e8165b5f3f43cfb9f8264bb Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 8 Jun 2023 17:34:20 +0400 Subject: [PATCH 18/64] fmt --- src/answer/mod.rs | 6 ++++-- src/connection/message.rs | 5 ++--- src/connection/network/proto/concept.rs | 2 +- src/connection/network/proto/logic.rs | 9 ++++++--- src/connection/network/proto/message.rs | 2 +- src/connection/transaction_stream.rs | 7 +++---- src/logic/explanation.rs | 4 ++-- src/logic/logic.rs | 6 ++++-- src/transaction/query.rs | 2 +- tests/behaviour/typeql/steps.rs | 3 +-- tests/behaviour/util.rs | 6 ++++-- 11 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/answer/mod.rs b/src/answer/mod.rs index c1a9bf88..ad9c9a1c 100644 --- a/src/answer/mod.rs +++ b/src/answer/mod.rs @@ -25,6 +25,8 @@ mod numeric; mod numeric_group; pub use self::{ - concept_map::ConceptMap, concept_map::Explainable, concept_map::Explainables, concept_map_group::ConceptMapGroup, - numeric::Numeric, numeric_group::NumericGroup, + concept_map::{ConceptMap, Explainable, Explainables}, + concept_map_group::ConceptMapGroup, + numeric::Numeric, + numeric_group::NumericGroup, }; diff --git a/src/connection/message.rs b/src/connection/message.rs index 65165781..eabf579f 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -26,7 +26,6 @@ use tonic::Streaming; use typedb_protocol::transaction; use typeql_lang::pattern::{Conjunction, Variable}; -use crate::logic::Explanation; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, common::{address::Address, info::DatabaseInfo, RequestID, SessionID, IID}, @@ -34,8 +33,8 @@ use crate::{ Annotation, Attribute, AttributeType, Entity, EntityType, Relation, RelationType, RoleType, SchemaException, Thing, ThingType, Transitivity, Value, ValueType, }, - Options, SessionType, TransactionType, - Rule, SchemaException, + logic::Explanation, + Options, Rule, SchemaException, SessionType, TransactionType, }; #[derive(Debug)] diff --git a/src/connection/network/proto/concept.rs b/src/connection/network/proto/concept.rs index a3158ad8..a3f1a4ef 100644 --- a/src/connection/network/proto/concept.rs +++ b/src/connection/network/proto/concept.rs @@ -38,7 +38,6 @@ use typedb_protocol::{ }; use super::{FromProto, IntoProto, TryFromProto}; -use crate::logic::Explanation; use crate::{ answer::{ConceptMap, ConceptMapGroup, Explainable, Explainables, Numeric, NumericGroup}, concept::{ @@ -46,6 +45,7 @@ use crate::{ RootThingType, ScopedLabel, Thing, ThingType, Transitivity, Value, ValueType, }, error::{ConnectionError, InternalError}, + logic::Explanation, Result, Rule, }; diff --git a/src/connection/network/proto/logic.rs b/src/connection/network/proto/logic.rs index 8be383e6..b04d5813 100644 --- a/src/connection/network/proto/logic.rs +++ b/src/connection/network/proto/logic.rs @@ -19,11 +19,14 @@ * under the License. */ +use typedb_protocol::Rule as RuleProto; +use typeql_lang::{ + parse_pattern, parse_variable, + pattern::{Pattern, Variable}, +}; + use super::{IntoProto, TryFromProto}; use crate::{common::Result, Error, Rule}; -use typedb_protocol::Rule as RuleProto; -use typeql_lang::pattern::{Pattern, Variable}; -use typeql_lang::{parse_pattern, parse_variable}; impl TryFromProto for Rule { fn try_from_proto(proto: RuleProto) -> Result { diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 91208d56..9f3d5d74 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::logic::Explanation; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, common::{info::DatabaseInfo, RequestID, Result}, @@ -42,6 +41,7 @@ use crate::{ TransactionResponse, }, error::{ConnectionError, InternalError}, + logic::Explanation, Rule, SchemaException, }; diff --git a/src/connection/transaction_stream.rs b/src/connection/transaction_stream.rs index 93b2860c..1dc87a00 100644 --- a/src/connection/transaction_stream.rs +++ b/src/connection/transaction_stream.rs @@ -28,8 +28,6 @@ use super::{ message::{RoleTypeRequest, RoleTypeResponse, ThingRequest, ThingResponse}, network::transmitter::TransactionTransmitter, }; -use crate::connection::message::{LogicRequest, LogicResponse}; -use crate::logic::Explanation; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, common::{Result, IID}, @@ -38,10 +36,11 @@ use crate::{ Thing, ThingType, Transitivity, Value, ValueType, }, connection::message::{ - ConceptRequest, ConceptResponse, QueryRequest, QueryResponse, ThingTypeRequest, ThingTypeResponse, - TransactionRequest, TransactionResponse, + ConceptRequest, ConceptResponse, LogicRequest, LogicResponse, QueryRequest, QueryResponse, ThingTypeRequest, + ThingTypeResponse, TransactionRequest, TransactionResponse, }, error::InternalError, + logic::Explanation, Options, Rule, SchemaException, TransactionType, }; diff --git a/src/logic/explanation.rs b/src/logic/explanation.rs index 7a87b63a..0f6dacf2 100644 --- a/src/logic/explanation.rs +++ b/src/logic/explanation.rs @@ -19,10 +19,10 @@ * under the License. */ -use crate::answer::ConceptMap; -use crate::Rule; use std::collections::HashMap; +use crate::{answer::ConceptMap, Rule}; + #[derive(Debug)] pub struct Explanation { pub rule: Rule, diff --git a/src/logic/logic.rs b/src/logic/logic.rs index 8d043c69..9e5f8d56 100644 --- a/src/logic/logic.rs +++ b/src/logic/logic.rs @@ -19,11 +19,13 @@ * under the License. */ -use crate::{common::Result, connection::TransactionStream, Rule}; -use futures::Stream; use std::sync::Arc; + +use futures::Stream; use typeql_lang::pattern::{Conjunction, Variable}; +use crate::{common::Result, connection::TransactionStream, Rule}; + #[derive(Clone, Debug)] pub struct LogicManager { transaction_stream: Arc, diff --git a/src/transaction/query.rs b/src/transaction/query.rs index 8c1a6662..e19378cd 100644 --- a/src/transaction/query.rs +++ b/src/transaction/query.rs @@ -23,11 +23,11 @@ use std::sync::Arc; use futures::Stream; -use crate::logic::Explanation; use crate::{ answer::{ConceptMap, ConceptMapGroup, Numeric, NumericGroup}, common::Result, connection::TransactionStream, + logic::Explanation, Options, }; diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index 45654356..31bc2f0d 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -27,10 +27,9 @@ use util::{ equals_approximate, iter_table_map, match_answer_concept, match_answer_concept_map, match_answer_rule, match_templated_answer, }; -use crate::behaviour::parameter::Comparable; use crate::{ assert_err, - behaviour::{util, Context}, + behaviour::{parameter::Comparable, util, Context}, generic_step_impl, }; diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index 9d9f1b1c..0b64dbe4 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -35,8 +35,10 @@ use typedb_client::{ Result as TypeDBResult, Rule, }; -use typeql_lang::{parse_pattern, parse_query, parse_rule}; -use typeql_lang::pattern::{ThingVariable, Variable}; +use typeql_lang::{ + parse_pattern, parse_query, parse_rule, + pattern::{ThingVariable, Variable}, +}; use crate::behaviour::Context; From ebddff0068d353d10008fa134ed0a1885ee09ad7 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 8 Jun 2023 18:44:31 +0400 Subject: [PATCH 19/64] Run logic tests on Cluster --- tests/integration/common.rs | 22 +++++++++++++++------- tests/integration/logic.rs | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/tests/integration/common.rs b/tests/integration/common.rs index 44655c73..45ee4b75 100644 --- a/tests/integration/common.rs +++ b/tests/integration/common.rs @@ -33,16 +33,24 @@ pub fn new_core_connection() -> typedb_client::Result { } pub fn new_cluster_connection() -> typedb_client::Result { + // TODO: change back to encrypted connection after fixing problems with Cluster + // Connection::new_encrypted( + // &["127.0.0.1:11729", "127.0.0.1:21729", "127.0.0.1:31729"], + // Credential::with_tls( + // "admin", + // "password", + // Some(&PathBuf::from( + // std::env::var("ROOT_CA") + // .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), + // )), + // )?, + // ) Connection::new_encrypted( - &["localhost:11729", "localhost:21729", "localhost:31729"], - Credential::with_tls( + &["127.0.0.1:11729", "127.0.0.1:21729", "127.0.0.1:31729"], + Credential::without_tls( "admin", "password", - Some(&PathBuf::from( - std::env::var("ROOT_CA") - .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), - )), - )?, + ), ) } diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 3c99eba5..e963dcb4 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -74,6 +74,7 @@ macro_rules! test_for_each_arg { test_for_each_arg! { { core => common::new_core_connection().unwrap(), + cluster => common::new_cluster_connection().unwrap(), } async fn test_disjunction_explainable(connection: Connection) -> typedb_client::Result { From b6880977f0c5ef94d1e57871c62f6c540ba24591 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 8 Jun 2023 18:52:21 +0400 Subject: [PATCH 20/64] 127.0.0.1 -> localhost in the Cluster connection --- tests/integration/common.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/tests/integration/common.rs b/tests/integration/common.rs index 45ee4b75..44655c73 100644 --- a/tests/integration/common.rs +++ b/tests/integration/common.rs @@ -33,24 +33,16 @@ pub fn new_core_connection() -> typedb_client::Result { } pub fn new_cluster_connection() -> typedb_client::Result { - // TODO: change back to encrypted connection after fixing problems with Cluster - // Connection::new_encrypted( - // &["127.0.0.1:11729", "127.0.0.1:21729", "127.0.0.1:31729"], - // Credential::with_tls( - // "admin", - // "password", - // Some(&PathBuf::from( - // std::env::var("ROOT_CA") - // .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), - // )), - // )?, - // ) Connection::new_encrypted( - &["127.0.0.1:11729", "127.0.0.1:21729", "127.0.0.1:31729"], - Credential::without_tls( + &["localhost:11729", "localhost:21729", "localhost:31729"], + Credential::with_tls( "admin", "password", - ), + Some(&PathBuf::from( + std::env::var("ROOT_CA") + .expect("ROOT_CA environment variable needs to be set for cluster tests to run"), + )), + )?, ) } From 165489fd623d95d8ac53507a4386dd31646c7249 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Fri, 9 Jun 2023 14:50:54 +0400 Subject: [PATCH 21/64] Reasoner steps --- src/answer/concept_map.rs | 6 +-- tests/BUILD | 11 +++++ tests/behaviour/mod.rs | 1 + tests/behaviour/typeql/steps.rs | 74 ++++++++++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/answer/concept_map.rs b/src/answer/concept_map.rs index 77b0225a..8d9399c9 100644 --- a/src/answer/concept_map.rs +++ b/src/answer/concept_map.rs @@ -26,20 +26,20 @@ use std::{ use crate::concept::Concept; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct ConceptMap { pub map: HashMap, pub explainables: Option, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct Explainables { pub relations: HashMap, pub attributes: HashMap, pub ownerships: HashMap<(String, String), Explainable>, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Explainable { pub conjunction: String, pub id: i64, diff --git a/tests/BUILD b/tests/BUILD index 6c241689..7b3a43a6 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -59,6 +59,17 @@ rust_test( "@vaticle_typedb_behaviour//typeql/language:match.feature", "@vaticle_typedb_behaviour//typeql/language:get.feature", "@vaticle_typedb_behaviour//typeql/language:rule-validation.feature", + "@vaticle_typedb_behaviour//typeql/reasoner:attribute-attachment.feature", + "@vaticle_typedb_behaviour//typeql/reasoner:compound-queries.feature", + "@vaticle_typedb_behaviour//typeql/reasoner:concept-inequality.feature", + "@vaticle_typedb_behaviour//typeql/reasoner:negation.feature", + "@vaticle_typedb_behaviour//typeql/reasoner:recursion.feature", + "@vaticle_typedb_behaviour//typeql/reasoner:relation-inference.feature", + "@vaticle_typedb_behaviour//typeql/reasoner:rule-interaction.feature", + "@vaticle_typedb_behaviour//typeql/reasoner:schema-queries.feature", + "@vaticle_typedb_behaviour//typeql/reasoner:type-hierarchy.feature", + "@vaticle_typedb_behaviour//typeql/reasoner:value-predicate.feature", + "@vaticle_typedb_behaviour//typeql/reasoner:variable-roles.feature", ], ) diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index d9d52618..4f98efcc 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -53,6 +53,7 @@ pub struct Context { impl Context { const GROUP_COLUMN_NAME: &'static str = "owner"; const VALUE_COLUMN_NAME: &'static str = "value"; + const DEFAULT_DATABASE: &'static str = "test"; async fn test(glob: &'static str) -> bool { let default_panic = std::panic::take_hook(); diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index 31bc2f0d..314a345a 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -19,9 +19,11 @@ * under the License. */ +use std::collections::{HashMap, HashSet}; + use cucumber::{gherkin::Step, given, then, when}; use futures::{StreamExt, TryFutureExt, TryStreamExt}; -use typedb_client::{answer::Numeric, Result as TypeDBResult}; +use typedb_client::{answer::Numeric, Result as TypeDBResult, Session, SessionType, TransactionType}; use typeql_lang::parse_query; use util::{ equals_approximate, iter_table_map, match_answer_concept, match_answer_concept_map, match_answer_rule, match_templated_answer, @@ -116,6 +118,7 @@ generic_step_impl! { } #[step(expr = "answer size is: {int}")] + #[step(expr = "verify answer size is: {int}")] async fn answer_size(context: &mut Context, expected_answers: usize) { let actual_answers = context.answer.len(); assert_eq!( @@ -400,4 +403,73 @@ generic_step_impl! { ); } + #[step(expr = "reasoning schema")] + async fn reasoning_schema(context: &mut Context, step: &Step) { + if context.databases.all().await.unwrap().is_empty() { + context.databases.create(Context::DEFAULT_DATABASE).await.unwrap(); + } + context + .session_trackers + .push(Session::new(context.databases.get(Context::DEFAULT_DATABASE).await.unwrap(), SessionType::Schema).await.unwrap().into()); + for session_tracker in &mut context.session_trackers { + session_tracker.open_transaction(TransactionType::Write).await.unwrap(); + } + typeql_define(context, step).await; + context.take_transaction().commit().await.unwrap(); + context.session_trackers.clear(); + } + + #[step(expr = "reasoning data")] + async fn reasoning_data(context: &mut Context, step: &Step) { + context + .session_trackers + .push(Session::new(context.databases.get(Context::DEFAULT_DATABASE).await.unwrap(), SessionType::Data).await.unwrap().into()); + for session_tracker in &mut context.session_trackers { + session_tracker.open_transaction(TransactionType::Write).await.unwrap(); + } + typeql_insert(context, step).await; + context.take_transaction().commit().await.unwrap(); + context.session_trackers.clear(); + } + + #[step(expr = "reasoning query")] + async fn reasoning_query(context: &mut Context, step: &Step) { + context + .session_trackers + .push(Session::new(context.databases.get(Context::DEFAULT_DATABASE).await.unwrap(), SessionType::Data).await.unwrap().into()); + for session_tracker in &mut context.session_trackers { + session_tracker.open_transaction(TransactionType::Read).await.unwrap(); + } + get_answers_typeql_match(context, step).await; + context.session_trackers.clear(); + } + + #[step(expr = "verify answer set is equivalent for query")] + async fn verify_answer_set_is_equivalent_for_query(context: &mut Context, step: &Step) { + let prev_answer = context.answer.clone(); + reasoning_query(context, step).await; + let total_rows = context.answer.len(); + let mut matched_rows = 0; + for row_curr in &context.answer { + for row_prev in &prev_answer { + if row_curr == row_prev { + matched_rows += 1; + break; + } + } + } + assert_eq!( + matched_rows, total_rows, + "There are only {matched_rows} matched entries of given {total_rows}." + ); + } + + #[step(expr = "verifier is initialised")] + #[step(expr = "verify answers are sound")] + #[step(expr = "verify answers are complete")] + async fn do_nothing(context: &mut Context) {} + + #[step(expr = "verify answers are consistent across {int} executions")] + async fn verify_answers_are_consistent_across_executions(context: &mut Context, executions: usize) {} + } From 56af7eefcc00f9910c0f0468894990950679e08f Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Fri, 9 Jun 2023 20:10:32 +0400 Subject: [PATCH 22/64] Fix imports after rebase --- src/connection/message.rs | 2 +- src/connection/network/proto/message.rs | 8 ++++---- src/connection/transaction_stream.rs | 2 +- src/lib.rs | 5 +++-- src/transaction/mod.rs | 3 +-- tests/integration/logic.rs | 1 + 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/connection/message.rs b/src/connection/message.rs index eabf579f..b09f77fe 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -34,7 +34,7 @@ use crate::{ Thing, ThingType, Transitivity, Value, ValueType, }, logic::Explanation, - Options, Rule, SchemaException, SessionType, TransactionType, + Options, Rule, SessionType, TransactionType, }; #[derive(Debug)] diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 9f3d5d74..924e2641 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -36,13 +36,13 @@ use crate::{ Thing, ThingType, ValueType, }, connection::message::{ - ConceptRequest, ConceptResponse, LogicRequest, LogicResponse, QueryRequest, QueryResponse, Request, Response, RoleTypeRequest, - RoleTypeResponse, ThingRequest, ThingResponse, ThingTypeRequest, ThingTypeResponse, TransactionRequest, - TransactionResponse, + ConceptRequest, ConceptResponse, LogicRequest, LogicResponse, QueryRequest, QueryResponse, Request, Response, + RoleTypeRequest, RoleTypeResponse, ThingRequest, ThingResponse, ThingTypeRequest, ThingTypeResponse, + TransactionRequest, TransactionResponse, }, error::{ConnectionError, InternalError}, logic::Explanation, - Rule, SchemaException, + Rule, }; impl TryIntoProto for Request { diff --git a/src/connection/transaction_stream.rs b/src/connection/transaction_stream.rs index 1dc87a00..2e5d8d29 100644 --- a/src/connection/transaction_stream.rs +++ b/src/connection/transaction_stream.rs @@ -41,7 +41,7 @@ use crate::{ }, error::InternalError, logic::Explanation, - Options, Rule, SchemaException, TransactionType, + Options, Rule, TransactionType, }; pub(crate) struct TransactionStream { diff --git a/src/lib.rs b/src/lib.rs index e4ce756d..12c4b019 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,12 +26,13 @@ mod common; pub mod concept; mod connection; mod database; -pub mod transaction; pub mod logic; +pub mod transaction; pub use self::{ common::{error, Credential, Error, Options, Result, SessionType, TransactionType}, connection::Connection, - database::{Database, DatabaseManager, Session, Transaction}, + database::{Database, DatabaseManager, Session}, logic::{LogicManager, Rule}, + transaction::Transaction, }; diff --git a/src/transaction/mod.rs b/src/transaction/mod.rs index 89090faa..eb11c066 100644 --- a/src/transaction/mod.rs +++ b/src/transaction/mod.rs @@ -28,8 +28,7 @@ use self::{concept::ConceptManager, query::QueryManager}; use crate::{ common::{Result, TransactionType}, connection::TransactionStream, - LogicManager, - Options, + LogicManager, Options, }; pub struct Transaction<'a> { diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index e963dcb4..46bbdffc 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -28,6 +28,7 @@ use typedb_client::{ answer::ConceptMap, concept::{Attribute, Concept, Value}, logic::Explanation, + transaction::concept::api::ThingAPI, Connection, DatabaseManager, Options, Session, SessionType::{Data, Schema}, Transaction, From 2bc131e88346b5d65357d6931a113c02fb3a9c11 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Fri, 9 Jun 2023 21:01:01 +0400 Subject: [PATCH 23/64] rename logic.rs to logic_manager.rs --- src/logic/{logic.rs => logic_manager.rs} | 0 src/logic/mod.rs | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/logic/{logic.rs => logic_manager.rs} (100%) diff --git a/src/logic/logic.rs b/src/logic/logic_manager.rs similarity index 100% rename from src/logic/logic.rs rename to src/logic/logic_manager.rs diff --git a/src/logic/mod.rs b/src/logic/mod.rs index 1c811a53..91357bb3 100644 --- a/src/logic/mod.rs +++ b/src/logic/mod.rs @@ -20,7 +20,7 @@ */ pub mod explanation; -mod logic; +mod logic_manager; mod rule; -pub use self::{explanation::Explanation, logic::LogicManager, rule::Rule}; +pub use self::{explanation::Explanation, logic_manager::LogicManager, rule::Rule}; From 3891d3afeebf98713d5215af79b43d1bc413dd1a Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 13 Jun 2023 17:23:12 +0400 Subject: [PATCH 24/64] Refactoring --- src/answer/concept_map.rs | 57 ++++++++++----------------------------- 1 file changed, 14 insertions(+), 43 deletions(-) diff --git a/src/answer/concept_map.rs b/src/answer/concept_map.rs index 8d9399c9..bd45c36e 100644 --- a/src/answer/concept_map.rs +++ b/src/answer/concept_map.rs @@ -26,25 +26,12 @@ use std::{ use crate::concept::Concept; -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct ConceptMap { pub map: HashMap, pub explainables: Option, } -#[derive(Debug, PartialEq)] -pub struct Explainables { - pub relations: HashMap, - pub attributes: HashMap, - pub ownerships: HashMap<(String, String), Explainable>, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct Explainable { - pub conjunction: String, - pub id: i64, -} - impl ConceptMap { pub fn get(&self, var_name: &str) -> Option<&Concept> { self.map.get(var_name) @@ -59,16 +46,6 @@ impl ConceptMap { } } -impl Clone for ConceptMap { - fn clone(&self) -> Self { - let mut map = HashMap::with_capacity(self.map.len()); - for (k, v) in &self.map { - map.insert(k.clone(), v.clone()); - } - Self { map, explainables: self.explainables.clone() } - } -} - impl From for HashMap { fn from(cm: ConceptMap) -> Self { cm.map @@ -92,6 +69,13 @@ impl IntoIterator for ConceptMap { } } +#[derive(Clone, Debug, PartialEq)] +pub struct Explainables { + pub relations: HashMap, + pub attributes: HashMap, + pub ownerships: HashMap<(String, String), Explainable>, +} + impl Explainables { pub(crate) fn new( relations: HashMap, @@ -102,27 +86,14 @@ impl Explainables { } } +#[derive(Clone, Debug, PartialEq)] +pub struct Explainable { + pub conjunction: String, + pub id: i64, +} + impl Explainable { pub(crate) fn new(conjunction: String, id: i64) -> Self { Self { conjunction, id } } } - -impl Clone for Explainables { - fn clone(&self) -> Self { - let mut relations = HashMap::with_capacity(self.relations.len()); - for (k, v) in &self.relations { - relations.insert(k.clone(), v.clone()); - } - let mut attributes = HashMap::with_capacity(self.attributes.len()); - for (k, v) in &self.attributes { - attributes.insert(k.clone(), v.clone()); - } - let mut ownerships = HashMap::with_capacity(self.ownerships.len()); - for (k, v) in &self.ownerships { - ownerships.insert(k.clone(), v.clone()); - } - - Self { relations, attributes, ownerships } - } -} From 113b2c3fd8fd8989e248548506764e4f33244914 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 13 Jun 2023 20:07:46 +0400 Subject: [PATCH 25/64] Cleanup --- src/logic/explanation.rs | 11 ----------- tests/integration/logic.rs | 3 +-- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/logic/explanation.rs b/src/logic/explanation.rs index 0f6dacf2..540e6210 100644 --- a/src/logic/explanation.rs +++ b/src/logic/explanation.rs @@ -30,14 +30,3 @@ pub struct Explanation { pub condition: ConceptMap, pub variable_mapping: HashMap>, } - -// impl Explanation { -// pub(crate) fn new( -// rule: Rule, -// conclusion: ConceptMap, -// condition: ConceptMap, -// variable_mapping: HashMap>, -// ) -> Self { -// Self { rule, conclusion, condition, variable_mapping } -// } -// } diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 46bbdffc..21ca34f0 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -21,8 +21,7 @@ use std::collections::HashMap; -use futures::{StreamExt, TryFutureExt, TryStreamExt}; -use regex::internal::Input; +use futures::{TryFutureExt, TryStreamExt}; use serial_test::serial; use typedb_client::{ answer::ConceptMap, From 6372a677a855239d81864ce2fd10ebdc653e51ba Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 13 Jun 2023 20:12:05 +0400 Subject: [PATCH 26/64] Refactor --- tests/integration/common.rs | 36 ++++++++++++++++++++++++++++++++++++ tests/integration/logic.rs | 36 +----------------------------------- tests/integration/queries.rs | 36 +----------------------------------- 3 files changed, 38 insertions(+), 70 deletions(-) diff --git a/tests/integration/common.rs b/tests/integration/common.rs index 44655c73..14011dc6 100644 --- a/tests/integration/common.rs +++ b/tests/integration/common.rs @@ -60,3 +60,39 @@ pub async fn create_test_database_with_schema(connection: Connection, schema: &s transaction.commit().await?; Ok(()) } + +#[macro_export] +macro_rules! test_for_each_arg { + { + $perm_args:tt + $( $( #[ $extra_anno:meta ] )* $async:ident fn $test:ident $args:tt -> $ret:ty $test_impl:block )+ + } => { + test_for_each_arg!{ @impl $( $async fn $test $args $ret $test_impl )+ } + test_for_each_arg!{ @impl_per $perm_args { $( $( #[ $extra_anno ] )* $async fn $test )+ } } + }; + + { @impl $( $async:ident fn $test:ident $args:tt $ret:ty $test_impl:block )+ } => { + mod _impl { + use super::*; + $( pub $async fn $test $args -> $ret $test_impl )+ + } + }; + + { @impl_per { $($mod:ident => $arg:expr),+ $(,)? } $fns:tt } => { + $(test_for_each_arg!{ @impl_mod { $mod => $arg } $fns })+ + }; + + { @impl_mod { $mod:ident => $arg:expr } { $( $( #[ $extra_anno:meta ] )* async fn $test:ident )+ } } => { + mod $mod { + use super::*; + $( + #[tokio::test] + #[serial($mod)] + $( #[ $extra_anno ] )* + pub async fn $test() { + _impl::$test($arg).await.unwrap(); + } + )+ + } + }; +} diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 21ca34f0..84cad90c 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -34,43 +34,9 @@ use typedb_client::{ TransactionType::{Read, Write}, }; +use crate::test_for_each_arg; use super::common; -macro_rules! test_for_each_arg { - { - $perm_args:tt - $( $( #[ $extra_anno:meta ] )* $async:ident fn $test:ident $args:tt -> $ret:ty $test_impl:block )+ - } => { - test_for_each_arg!{ @impl $( $async fn $test $args $ret $test_impl )+ } - test_for_each_arg!{ @impl_per $perm_args { $( $( #[ $extra_anno ] )* $async fn $test )+ } } - }; - - { @impl $( $async:ident fn $test:ident $args:tt $ret:ty $test_impl:block )+ } => { - mod _impl { - use super::*; - $( pub $async fn $test $args -> $ret $test_impl )+ - } - }; - - { @impl_per { $($mod:ident => $arg:expr),+ $(,)? } $fns:tt } => { - $(test_for_each_arg!{ @impl_mod { $mod => $arg } $fns })+ - }; - - { @impl_mod { $mod:ident => $arg:expr } { $( $( #[ $extra_anno:meta ] )* async fn $test:ident )+ } } => { - mod $mod { - use super::*; - $( - #[tokio::test] - #[serial($mod)] - $( #[ $extra_anno ] )* - pub async fn $test() { - _impl::$test($arg).await.unwrap(); - } - )+ - } - }; -} - test_for_each_arg! { { core => common::new_core_connection().unwrap(), diff --git a/tests/integration/queries.rs b/tests/integration/queries.rs index 5f5354f3..4a929d15 100644 --- a/tests/integration/queries.rs +++ b/tests/integration/queries.rs @@ -33,43 +33,9 @@ use typedb_client::{ TransactionType::{Read, Write}, }; +use crate::test_for_each_arg; use super::common; -macro_rules! test_for_each_arg { - { - $perm_args:tt - $( $( #[ $extra_anno:meta ] )* $async:ident fn $test:ident $args:tt -> $ret:ty $test_impl:block )+ - } => { - test_for_each_arg!{ @impl $( $async fn $test $args $ret $test_impl )+ } - test_for_each_arg!{ @impl_per $perm_args { $( $( #[ $extra_anno ] )* $async fn $test )+ } } - }; - - { @impl $( $async:ident fn $test:ident $args:tt $ret:ty $test_impl:block )+ } => { - mod _impl { - use super::*; - $( pub $async fn $test $args -> $ret $test_impl )+ - } - }; - - { @impl_per { $($mod:ident => $arg:expr),+ $(,)? } $fns:tt } => { - $(test_for_each_arg!{ @impl_mod { $mod => $arg } $fns })+ - }; - - { @impl_mod { $mod:ident => $arg:expr } { $( $( #[ $extra_anno:meta ] )* async fn $test:ident )+ } } => { - mod $mod { - use super::*; - $( - #[tokio::test] - #[serial($mod)] - $( #[ $extra_anno ] )* - pub async fn $test() { - _impl::$test($arg).await.unwrap(); - } - )+ - } - }; -} - test_for_each_arg! { { core => common::new_core_connection().unwrap(), From a5f38e2cb5ec4204f88793f73d5e04f767863a82 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 13 Jun 2023 21:02:33 +0400 Subject: [PATCH 27/64] Option -> Empty Explainables --- src/answer/concept_map.rs | 6 ++++- src/connection/network/proto/concept.rs | 13 ++-------- tests/integration/logic.rs | 32 +++++++++++++------------ 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/answer/concept_map.rs b/src/answer/concept_map.rs index bd45c36e..bcbbe267 100644 --- a/src/answer/concept_map.rs +++ b/src/answer/concept_map.rs @@ -29,7 +29,7 @@ use crate::concept::Concept; #[derive(Clone, Debug, PartialEq)] pub struct ConceptMap { pub map: HashMap, - pub explainables: Option, + pub explainables: Explainables, } impl ConceptMap { @@ -84,6 +84,10 @@ impl Explainables { ) -> Self { Self { relations, attributes, ownerships } } + + pub fn is_empty(&self) -> bool { + self.relations.is_empty() && self.attributes.is_empty() && self.ownerships.is_empty() + } } #[derive(Clone, Debug, PartialEq)] diff --git a/src/connection/network/proto/concept.rs b/src/connection/network/proto/concept.rs index a3f1a4ef..6204057f 100644 --- a/src/connection/network/proto/concept.rs +++ b/src/connection/network/proto/concept.rs @@ -103,17 +103,8 @@ impl TryFromProto for ConceptMap { for (k, v) in proto.map { map.insert(k, Concept::try_from_proto(v)?); } - let explainables = match proto.explainables { - Some(expl) => { - if expl.attributes.len() > 0 || expl.relations.len() > 0 || expl.ownerships.len() > 0 { - Some(Explainables::from_proto(expl)) - } else { - None - } - } - None => return Err(ConnectionError::MissingResponseField("explainables").into()), - }; - Ok(Self { map, explainables }) + let Some(proto_explainables) = proto.explainables else { return Err(ConnectionError::MissingResponseField("explainables").into()) }; + Ok(Self { map, explainables: Explainables::from_proto(proto_explainables) }) } } diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 84cad90c..2978c293 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -24,7 +24,7 @@ use std::collections::HashMap; use futures::{TryFutureExt, TryStreamExt}; use serial_test::serial; use typedb_client::{ - answer::ConceptMap, + answer::{ConceptMap, Explainable, Explainables}, concept::{Attribute, Concept, Value}, logic::Explanation, transaction::concept::api::ThingAPI, @@ -100,8 +100,8 @@ test_for_each_arg! { assert_eq!(3, with_explainable.map.len()); assert_eq!(2, without_explainable.map.len()); - assert!(with_explainable.explainables.is_some()); - assert!(without_explainable.explainables.is_none()); + assert!(!with_explainable.explainables.is_empty()); + assert!(without_explainable.explainables.is_empty()); assert_single_explainable_explanations(with_explainable, 1, 1, &transaction).await; @@ -154,8 +154,8 @@ test_for_each_arg! { assert_eq!(2, answers.len()); - assert!(answers.get(0).unwrap().explainables.is_some()); - assert!(answers.get(1).unwrap().explainables.is_some()); + assert!(!answers.get(0).unwrap().explainables.is_empty()); + assert!(!answers.get(1).unwrap().explainables.is_empty()); assert_single_explainable_explanations(answers.get(0).unwrap(), 1, 1, &transaction).await; assert_single_explainable_explanations(answers.get(1).unwrap(), 1, 1, &transaction).await; @@ -217,8 +217,8 @@ test_for_each_arg! { assert_eq!(2, answers.len()); - assert!(answers.get(0).unwrap().explainables.is_some()); - assert!(answers.get(1).unwrap().explainables.is_some()); + assert!(!answers.get(0).unwrap().explainables.is_empty()); + assert!(!answers.get(1).unwrap().explainables.is_empty()); assert_single_explainable_explanations(answers.get(0).unwrap(), 1, 3, &transaction).await; assert_single_explainable_explanations(answers.get(1).unwrap(), 1, 3, &transaction).await; @@ -277,9 +277,9 @@ test_for_each_arg! { assert_eq!(3, answers.len()); - assert!(answers.get(0).unwrap().explainables.is_some()); - assert!(answers.get(1).unwrap().explainables.is_some()); - assert!(answers.get(2).unwrap().explainables.is_some()); + assert!(!answers.get(0).unwrap().explainables.is_empty()); + assert!(!answers.get(1).unwrap().explainables.is_empty()); + assert!(!answers.get(2).unwrap().explainables.is_empty()); let age_in_days = transaction.concept().get_attribute_type(String::from("age-in-days")).await?.unwrap(); for ans in answers { @@ -307,7 +307,7 @@ async fn assert_single_explainable_explanations( transaction: &Transaction<'_>, ) { check_explainable_vars(ans); - let explainables = ans.clone().explainables.unwrap(); + let explainables = ans.clone().explainables; let mut all_explainables = explainables.attributes.values().collect::>(); all_explainables.extend(explainables.relations.values().collect::>()); all_explainables.extend(explainables.ownerships.values().collect::>()); @@ -331,11 +331,10 @@ async fn assert_single_explainable_explanations( } fn check_explainable_vars(ans: &ConceptMap) { - ans.clone().explainables.unwrap().relations.into_keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); - ans.clone().explainables.unwrap().attributes.into_keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); + ans.clone().explainables.relations.into_keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); + ans.clone().explainables.attributes.into_keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); ans.clone() .explainables - .unwrap() .ownerships .into_keys() .for_each(|(k1, k2)| assert!(ans.map.contains_key(k1.as_str()) && ans.map.contains_key(k2.as_str()))); @@ -351,5 +350,8 @@ fn apply_mapping(mapping: &HashMap>, complete_map: &ConceptM concepts.insert(mapped.to_string(), concept.clone()); } } - ConceptMap { map: concepts, explainables: None } + let relations: HashMap = HashMap::new(); + let attributes: HashMap = HashMap::new(); + let ownerships: HashMap<(String, String), Explainable> = HashMap::new(); + ConceptMap { map: concepts, explainables: Explainables {relations, attributes, ownerships} } } From 12f811ae5e941a53792c8552c6773aa089427a2f Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 14 Jun 2023 12:21:25 +0400 Subject: [PATCH 28/64] Default Explainables --- src/answer/concept_map.rs | 2 +- tests/integration/logic.rs | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/answer/concept_map.rs b/src/answer/concept_map.rs index bcbbe267..743bbeac 100644 --- a/src/answer/concept_map.rs +++ b/src/answer/concept_map.rs @@ -69,7 +69,7 @@ impl IntoIterator for ConceptMap { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Default, PartialEq)] pub struct Explainables { pub relations: HashMap, pub attributes: HashMap, diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 2978c293..e0437133 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -19,7 +19,7 @@ * under the License. */ -use std::collections::HashMap; +use std::{collections::HashMap, default::Default}; use futures::{TryFutureExt, TryStreamExt}; use serial_test::serial; @@ -350,8 +350,5 @@ fn apply_mapping(mapping: &HashMap>, complete_map: &ConceptM concepts.insert(mapped.to_string(), concept.clone()); } } - let relations: HashMap = HashMap::new(); - let attributes: HashMap = HashMap::new(); - let ownerships: HashMap<(String, String), Explainable> = HashMap::new(); - ConceptMap { map: concepts, explainables: Explainables {relations, attributes, ownerships} } + ConceptMap { map: concepts, explainables: Default::default() } } From a70465216718a73b50782a60427732ecb335d990 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 14 Jun 2023 12:25:28 +0400 Subject: [PATCH 29/64] Remove concepts_to_vec() --- src/answer/concept_map.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/answer/concept_map.rs b/src/answer/concept_map.rs index 743bbeac..42913bc7 100644 --- a/src/answer/concept_map.rs +++ b/src/answer/concept_map.rs @@ -40,10 +40,6 @@ impl ConceptMap { pub fn concepts(&self) -> impl Iterator { self.map.values() } - - pub fn concepts_to_vec(&self) -> Vec<&Concept> { - self.concepts().collect::>() - } } impl From for HashMap { From 8420aee7844c39c5c44e8a29f7efe9b11213613a Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 14 Jun 2023 12:26:59 +0400 Subject: [PATCH 30/64] inferred -> is_inferred --- src/concept/thing.rs | 6 +++--- src/connection/network/proto/concept.rs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/concept/thing.rs b/src/concept/thing.rs index 460a601a..60df8349 100644 --- a/src/concept/thing.rs +++ b/src/concept/thing.rs @@ -47,14 +47,14 @@ impl Thing { pub struct Entity { pub iid: IID, pub type_: EntityType, - pub inferred: bool, + pub is_inferred: bool, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Relation { pub iid: IID, pub type_: RelationType, - pub inferred: bool, + pub is_inferred: bool, } #[derive(Clone, Debug, PartialEq)] @@ -62,7 +62,7 @@ pub struct Attribute { pub iid: IID, pub type_: AttributeType, pub value: Value, - pub inferred: bool, + pub is_inferred: bool, } #[derive(Clone, Debug, PartialEq)] diff --git a/src/connection/network/proto/concept.rs b/src/connection/network/proto/concept.rs index 6204057f..bc117626 100644 --- a/src/connection/network/proto/concept.rs +++ b/src/connection/network/proto/concept.rs @@ -284,14 +284,14 @@ impl TryFromProto for Entity { Ok(Self { iid: iid.into(), type_: EntityType::from_proto(entity_type.ok_or(ConnectionError::MissingResponseField("entity_type"))?), - inferred, + is_inferred: inferred, }) } } impl IntoProto for Entity { fn into_proto(self) -> EntityProto { - EntityProto { iid: self.iid.into(), entity_type: Some(self.type_.into_proto()), inferred: self.inferred } + EntityProto { iid: self.iid.into(), entity_type: Some(self.type_.into_proto()), inferred: self.is_inferred } } } @@ -303,14 +303,14 @@ impl TryFromProto for Relation { type_: RelationType::from_proto( relation_type.ok_or(ConnectionError::MissingResponseField("relation_type"))?, ), - inferred, + is_inferred: inferred, }) } } impl IntoProto for Relation { fn into_proto(self) -> RelationProto { - RelationProto { iid: self.iid.into(), relation_type: Some(self.type_.into_proto()), inferred: self.inferred } + RelationProto { iid: self.iid.into(), relation_type: Some(self.type_.into_proto()), inferred: self.is_inferred } } } @@ -323,7 +323,7 @@ impl TryFromProto for Attribute { attribute_type.ok_or(ConnectionError::MissingResponseField("attribute_type"))?, )?, value: Value::try_from_proto(value.ok_or(ConnectionError::MissingResponseField("value"))?)?, - inferred, + is_inferred: inferred, }) } } From 7cd4ed9dffae6c95977e1a09aa0d40d7bc91cb88 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 14 Jun 2023 13:24:01 +0400 Subject: [PATCH 31/64] Deconstruction + fmt --- src/connection/network/proto/concept.rs | 39 +++++++++++++++---------- src/connection/network/proto/logic.rs | 10 ++++--- tests/integration/logic.rs | 2 +- tests/integration/queries.rs | 2 +- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/connection/network/proto/concept.rs b/src/connection/network/proto/concept.rs index bc117626..ac03933c 100644 --- a/src/connection/network/proto/concept.rs +++ b/src/connection/network/proto/concept.rs @@ -99,12 +99,13 @@ impl TryFromProto for ConceptMapGroup { impl TryFromProto for ConceptMap { fn try_from_proto(proto: ConceptMapProto) -> Result { - let mut map = HashMap::with_capacity(proto.map.len()); - for (k, v) in proto.map { + let ConceptMapProto { map: map_proto, explainables: explainables_proto } = proto; + let mut map = HashMap::with_capacity(map_proto.len()); + for (k, v) in map_proto { map.insert(k, Concept::try_from_proto(v)?); } - let Some(proto_explainables) = proto.explainables else { return Err(ConnectionError::MissingResponseField("explainables").into()) }; - Ok(Self { map, explainables: Explainables::from_proto(proto_explainables) }) + let Some(explainables) = explainables_proto else { return Err(ConnectionError::MissingResponseField("explainables").into()) }; + Ok(Self { map, explainables: Explainables::from_proto(explainables) }) } } @@ -291,7 +292,8 @@ impl TryFromProto for Entity { impl IntoProto for Entity { fn into_proto(self) -> EntityProto { - EntityProto { iid: self.iid.into(), entity_type: Some(self.type_.into_proto()), inferred: self.is_inferred } + let Self { iid, type_, is_inferred } = self; + EntityProto { iid: iid.into(), entity_type: Some(type_.into_proto()), inferred: is_inferred } } } @@ -310,7 +312,8 @@ impl TryFromProto for Relation { impl IntoProto for Relation { fn into_proto(self) -> RelationProto { - RelationProto { iid: self.iid.into(), relation_type: Some(self.type_.into_proto()), inferred: self.is_inferred } + let Self { iid, type_, is_inferred } = self; + RelationProto { iid: iid.into(), relation_type: Some(type_.into_proto()), inferred: is_inferred } } } @@ -330,11 +333,12 @@ impl TryFromProto for Attribute { impl IntoProto for Attribute { fn into_proto(self) -> AttributeProto { + let Self { iid, type_, value, is_inferred } = self; AttributeProto { - iid: self.iid.into(), - attribute_type: Some(self.type_.into_proto()), - value: Some(self.value.into_proto()), - inferred: false, // FIXME + iid: iid.into(), + attribute_type: Some(type_.into_proto()), + value: Some(value.into_proto()), + inferred: is_inferred, } } } @@ -370,16 +374,21 @@ impl IntoProto for Value { impl FromProto for Explainables { fn from_proto(proto: ExplainablesProto) -> Self { - let mut relations = HashMap::with_capacity(proto.relations.len()); - for (k, v) in proto.relations { + let ExplainablesProto { + relations: relations_proto, + attributes: attributes_proto, + ownerships: ownerships_proto, + } = proto; + let mut relations = HashMap::with_capacity(relations_proto.len()); + for (k, v) in relations_proto { relations.insert(k, Explainable::from_proto(v)); } - let mut attributes = HashMap::with_capacity(proto.attributes.len()); - for (k, v) in proto.attributes { + let mut attributes = HashMap::with_capacity(attributes_proto.len()); + for (k, v) in attributes_proto { attributes.insert(k, Explainable::from_proto(v)); } let mut ownerships = HashMap::new(); - for (k1, owned) in proto.ownerships { + for (k1, owned) in ownerships_proto { for (k2, v) in owned.owned { ownerships.insert((k1.clone(), k2), Explainable::from_proto(v)); } diff --git a/src/connection/network/proto/logic.rs b/src/connection/network/proto/logic.rs index b04d5813..f6a83a0a 100644 --- a/src/connection/network/proto/logic.rs +++ b/src/connection/network/proto/logic.rs @@ -30,22 +30,24 @@ use crate::{common::Result, Error, Rule}; impl TryFromProto for Rule { fn try_from_proto(proto: RuleProto) -> Result { - let when = match parse_pattern(&proto.when) { + let RuleProto { label: label_proto, when: when_proto, then: then_proto } = proto; + let when = match parse_pattern(&when_proto) { Ok(Pattern::Conjunction(conjunction)) => conjunction, Ok(other) => return Err(Error::Other(format!("When parse error: {other:?}"))), Err(error) => return Err(Error::Other(format!("{error:?}"))), }; - let then = match parse_variable(&proto.then) { + let then = match parse_variable(&then_proto) { Ok(Variable::Thing(thing)) => thing, Ok(other) => return Err(Error::Other(format!("Then parse error: {other:?}"))), Err(error) => return Err(Error::Other(format!("{error:?}"))), }; - Ok(Self::new(proto.label, when, then)) + Ok(Self::new(label_proto, when, then)) } } impl IntoProto for Rule { fn into_proto(self) -> RuleProto { - RuleProto { label: self.label, when: self.when.to_string(), then: self.then.to_string() } + let Self { label, when, then } = self; + RuleProto { label, when: when.to_string(), then: then.to_string() } } } diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index e0437133..2f230f4b 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -34,8 +34,8 @@ use typedb_client::{ TransactionType::{Read, Write}, }; -use crate::test_for_each_arg; use super::common; +use crate::test_for_each_arg; test_for_each_arg! { { diff --git a/tests/integration/queries.rs b/tests/integration/queries.rs index 4a929d15..0966a270 100644 --- a/tests/integration/queries.rs +++ b/tests/integration/queries.rs @@ -33,8 +33,8 @@ use typedb_client::{ TransactionType::{Read, Write}, }; -use crate::test_for_each_arg; use super::common; +use crate::test_for_each_arg; test_for_each_arg! { { From 9bc06a9eabb5b26fa24975e0ae37da1e78ce0dce Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 14 Jun 2023 17:46:36 +0400 Subject: [PATCH 32/64] RuleRequest, RuleResponse --- src/connection/message.rs | 14 ++++++++++ src/connection/network/proto/message.rs | 36 ++++++++++++++++++++++--- src/connection/transaction_stream.rs | 25 +++++++++++++++-- src/logic/rule.rs | 2 ++ 4 files changed, 71 insertions(+), 6 deletions(-) diff --git a/src/connection/message.rs b/src/connection/message.rs index b09f77fe..44bc3722 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -110,6 +110,7 @@ pub(super) enum TransactionRequest { RoleType(RoleTypeRequest), Thing(ThingRequest), Stream { request_id: RequestID }, + Rule(RuleRequest), Logic(LogicRequest), } @@ -123,6 +124,7 @@ pub(super) enum TransactionResponse { ThingType(ThingTypeResponse), RoleType(RoleTypeResponse), Thing(ThingResponse), + Rule(RuleResponse), Logic(LogicResponse), } @@ -462,6 +464,18 @@ pub(super) enum ThingResponse { AttributeGetOwners { owners: Vec }, } +#[derive(Debug)] +pub(super) enum RuleRequest { + Delete { label: String }, + SetLabel { current_label: String, new_label: String }, +} + +#[derive(Debug)] +pub(super) enum RuleResponse { + Delete, + SetLabel, +} + #[derive(Debug)] pub(super) enum LogicRequest { PutRule { label: String, when: Conjunction, then: Variable }, diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 924e2641..6db6431e 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, server_manager, session, thing, thing_type, transaction, + r#type, relation, relation_type, role_type, rule, server_manager, session, thing, thing_type, transaction, }; use super::{FromProto, IntoProto, TryFromProto, TryIntoProto}; @@ -37,7 +37,7 @@ use crate::{ }, connection::message::{ ConceptRequest, ConceptResponse, LogicRequest, LogicResponse, QueryRequest, QueryResponse, Request, Response, - RoleTypeRequest, RoleTypeResponse, ThingRequest, ThingResponse, ThingTypeRequest, ThingTypeResponse, + RoleTypeRequest, RoleTypeResponse, RuleRequest, RuleResponse, ThingRequest, ThingResponse, ThingTypeRequest, ThingTypeResponse, TransactionRequest, TransactionResponse, }, error::{ConnectionError, InternalError}, @@ -272,6 +272,7 @@ impl IntoProto for TransactionRequest { request_id = Some(req_id); transaction::req::Req::StreamReq(transaction::stream::Req {}) } + Self::Rule(rule_request) => transaction::req::Req::RuleReq(rule_request.into_proto()), Self::Logic(logic_request) => transaction::req::Req::LogicManagerReq(logic_request.into_proto()), }; @@ -299,9 +300,10 @@ impl TryFromProto for TransactionResponse { Some(transaction::res::Res::TypeRes(r#type::Res { res: Some(r#type::res::Res::RoleTypeRes(res)) })) => { Ok(Self::RoleType(RoleTypeResponse::try_from_proto(res)?)) } + Some(transaction::res::Res::TypeRes(r#type::Res { res: None })) => Err(ConnectionError::MissingResponseField("res").into()), Some(transaction::res::Res::ThingRes(res)) => Ok(Self::Thing(ThingResponse::try_from_proto(res)?)), + Some(transaction::res::Res::RuleRes(res)) => Ok(Self::Rule(RuleResponse::try_from_proto(res)?)), Some(transaction::res::Res::LogicManagerRes(res)) => Ok(Self::Logic(LogicResponse::try_from_proto(res)?)), - Some(_) => todo!(), None => Err(ConnectionError::MissingResponseField("res").into()), } } @@ -319,11 +321,12 @@ impl TryFromProto for TransactionResponse { Some(transaction::res_part::Res::TypeResPart(r#type::ResPart { res: Some(r#type::res_part::Res::RoleTypeResPart(res)), })) => Ok(Self::RoleType(RoleTypeResponse::try_from_proto(res)?)), + Some(transaction::res_part::Res::TypeResPart(r#type::ResPart { res: None })) => Err(ConnectionError::MissingResponseField("res").into()), Some(transaction::res_part::Res::ThingResPart(res)) => Ok(Self::Thing(ThingResponse::try_from_proto(res)?)), Some(transaction::res_part::Res::LogicManagerResPart(res)) => { Ok(Self::Logic(LogicResponse::try_from_proto(res)?)) } - Some(_) => todo!(), + Some(transaction::res_part::Res::StreamResPart(_)) => unreachable!(), None => Err(ConnectionError::MissingResponseField("res").into()), } } @@ -1116,6 +1119,31 @@ impl TryFromProto for ThingResponse { } } +impl IntoProto for RuleRequest { + fn into_proto(self) -> rule::Req { + let (req, label) = match self { + Self::Delete { label } => { + (rule::req::Req::RuleDeleteReq(rule::delete::Req {}), label) + } + Self::SetLabel { current_label, new_label } => ( + rule::req::Req::RuleSetLabelReq(rule::set_label::Req { label: new_label }), + current_label, + ), + }; + rule::Req { label, req: Some(req) } + } +} + +impl TryFromProto for RuleResponse { + fn try_from_proto(proto: rule::Res) -> Result { + match proto.res { + Some(rule::res::Res::RuleDeleteRes(_)) => Ok(Self::Delete), + Some(rule::res::Res::RuleSetLabelRes(_)) => Ok(Self::SetLabel), + None => Err(ConnectionError::MissingResponseField("res").into()), + } + } +} + impl IntoProto for LogicRequest { fn into_proto(self) -> logic_manager::Req { let req = match self { diff --git a/src/connection/transaction_stream.rs b/src/connection/transaction_stream.rs index 2e5d8d29..dbe91ecc 100644 --- a/src/connection/transaction_stream.rs +++ b/src/connection/transaction_stream.rs @@ -36,8 +36,8 @@ use crate::{ Thing, ThingType, Transitivity, Value, ValueType, }, connection::message::{ - ConceptRequest, ConceptResponse, LogicRequest, LogicResponse, QueryRequest, QueryResponse, ThingTypeRequest, - ThingTypeResponse, TransactionRequest, TransactionResponse, + ConceptRequest, ConceptResponse, LogicRequest, LogicResponse, QueryRequest, QueryResponse, RuleRequest, RuleResponse, + ThingTypeRequest, ThingTypeResponse, TransactionRequest, TransactionResponse, }, error::InternalError, logic::Explanation, @@ -931,6 +931,20 @@ impl TransactionStream { })) } + pub(crate) async fn rule_delete(&self, rule: Rule) -> Result { + match self.rule_single(RuleRequest::Delete { label: rule.label }).await? { + RuleResponse::Delete => Ok(()), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } + + pub(crate) async fn rule_set_label(&self, rule: Rule, label: String) -> Result { + match self.rule_single(RuleRequest::SetLabel { current_label: rule.label, new_label: label }).await? { + RuleResponse::SetLabel => Ok(()), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } + pub(crate) async fn put_rule(&self, label: String, when: Conjunction, then: Variable) -> Result { match self.logic_single(LogicRequest::PutRule { label, when, then }).await? { LogicResponse::PutRule { rule } => Ok(rule), @@ -993,6 +1007,13 @@ impl TransactionStream { } } + async fn rule_single(&self, req: RuleRequest) -> Result { + match self.single(TransactionRequest::Rule(req)).await? { + TransactionResponse::Rule(res) => Ok(res), + other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), + } + } + async fn logic_single(&self, req: LogicRequest) -> Result { match self.single(TransactionRequest::Logic(req)).await? { TransactionResponse::Logic(res) => Ok(res), diff --git a/src/logic/rule.rs b/src/logic/rule.rs index b9b8d1be..3c68a742 100644 --- a/src/logic/rule.rs +++ b/src/logic/rule.rs @@ -21,6 +21,8 @@ use typeql_lang::pattern::{Conjunction, ThingVariable}; +use crate::common::Result; + #[derive(Debug)] pub struct Rule { pub label: String, From a09f3a31f7ed698505aa0c54cf66794bc4809bce Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 14 Jun 2023 17:58:30 +0400 Subject: [PATCH 33/64] logic.logic_manager -> transaction.logic.manager --- src/connection/message.rs | 4 ++-- src/connection/network/proto/concept.rs | 4 ++-- src/connection/network/proto/logic.rs | 2 +- src/connection/network/proto/message.rs | 3 +-- src/connection/transaction_stream.rs | 4 ++-- src/lib.rs | 1 - src/logic/explanation.rs | 2 +- src/logic/mod.rs | 3 +-- .../logic/manager.rs} | 2 +- src/transaction/logic/mod.rs | 24 +++++++++++++++++++ src/transaction/mod.rs | 5 ++-- tests/behaviour/util.rs | 2 +- 12 files changed, 39 insertions(+), 17 deletions(-) rename src/{logic/logic_manager.rs => transaction/logic/manager.rs} (95%) create mode 100644 src/transaction/logic/mod.rs diff --git a/src/connection/message.rs b/src/connection/message.rs index 44bc3722..0b508987 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -33,8 +33,8 @@ use crate::{ Annotation, Attribute, AttributeType, Entity, EntityType, Relation, RelationType, RoleType, SchemaException, Thing, ThingType, Transitivity, Value, ValueType, }, - logic::Explanation, - Options, Rule, SessionType, TransactionType, + logic::{Explanation, Rule}, + Options, SessionType, TransactionType, }; #[derive(Debug)] diff --git a/src/connection/network/proto/concept.rs b/src/connection/network/proto/concept.rs index ac03933c..ecfe7111 100644 --- a/src/connection/network/proto/concept.rs +++ b/src/connection/network/proto/concept.rs @@ -45,8 +45,8 @@ use crate::{ RootThingType, ScopedLabel, Thing, ThingType, Transitivity, Value, ValueType, }, error::{ConnectionError, InternalError}, - logic::Explanation, - Result, Rule, + logic::{Explanation, Rule}, + Result, }; impl IntoProto for Transitivity { diff --git a/src/connection/network/proto/logic.rs b/src/connection/network/proto/logic.rs index f6a83a0a..c8cad741 100644 --- a/src/connection/network/proto/logic.rs +++ b/src/connection/network/proto/logic.rs @@ -26,7 +26,7 @@ use typeql_lang::{ }; use super::{IntoProto, TryFromProto}; -use crate::{common::Result, Error, Rule}; +use crate::{common::Result, Error, logic::Rule}; impl TryFromProto for Rule { fn try_from_proto(proto: RuleProto) -> Result { diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 6db6431e..7d3c9ca3 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -41,8 +41,7 @@ use crate::{ TransactionRequest, TransactionResponse, }, error::{ConnectionError, InternalError}, - logic::Explanation, - Rule, + logic::{Explanation, Rule}, }; impl TryIntoProto for Request { diff --git a/src/connection/transaction_stream.rs b/src/connection/transaction_stream.rs index dbe91ecc..36e43147 100644 --- a/src/connection/transaction_stream.rs +++ b/src/connection/transaction_stream.rs @@ -40,8 +40,8 @@ use crate::{ ThingTypeRequest, ThingTypeResponse, TransactionRequest, TransactionResponse, }, error::InternalError, - logic::Explanation, - Options, Rule, TransactionType, + logic::{Explanation, Rule}, + Options, TransactionType, }; pub(crate) struct TransactionStream { diff --git a/src/lib.rs b/src/lib.rs index 12c4b019..e532f4c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,6 +33,5 @@ pub use self::{ common::{error, Credential, Error, Options, Result, SessionType, TransactionType}, connection::Connection, database::{Database, DatabaseManager, Session}, - logic::{LogicManager, Rule}, transaction::Transaction, }; diff --git a/src/logic/explanation.rs b/src/logic/explanation.rs index 540e6210..0137e673 100644 --- a/src/logic/explanation.rs +++ b/src/logic/explanation.rs @@ -21,7 +21,7 @@ use std::collections::HashMap; -use crate::{answer::ConceptMap, Rule}; +use crate::{answer::ConceptMap, logic::Rule}; #[derive(Debug)] pub struct Explanation { diff --git a/src/logic/mod.rs b/src/logic/mod.rs index 91357bb3..f466262c 100644 --- a/src/logic/mod.rs +++ b/src/logic/mod.rs @@ -20,7 +20,6 @@ */ pub mod explanation; -mod logic_manager; mod rule; -pub use self::{explanation::Explanation, logic_manager::LogicManager, rule::Rule}; +pub use self::{explanation::Explanation, rule::Rule}; diff --git a/src/logic/logic_manager.rs b/src/transaction/logic/manager.rs similarity index 95% rename from src/logic/logic_manager.rs rename to src/transaction/logic/manager.rs index 9e5f8d56..0c9155ee 100644 --- a/src/logic/logic_manager.rs +++ b/src/transaction/logic/manager.rs @@ -24,7 +24,7 @@ use std::sync::Arc; use futures::Stream; use typeql_lang::pattern::{Conjunction, Variable}; -use crate::{common::Result, connection::TransactionStream, Rule}; +use crate::{common::Result, connection::TransactionStream, logic::Rule}; #[derive(Clone, Debug)] pub struct LogicManager { diff --git a/src/transaction/logic/mod.rs b/src/transaction/logic/mod.rs new file mode 100644 index 00000000..1edb23ee --- /dev/null +++ b/src/transaction/logic/mod.rs @@ -0,0 +1,24 @@ +/* + * 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 manager; + +pub use self::manager::LogicManager; diff --git a/src/transaction/mod.rs b/src/transaction/mod.rs index eb11c066..4cdc38b2 100644 --- a/src/transaction/mod.rs +++ b/src/transaction/mod.rs @@ -20,15 +20,16 @@ */ pub mod concept; +pub mod logic; mod query; use std::{fmt, marker::PhantomData, sync::Arc}; -use self::{concept::ConceptManager, query::QueryManager}; +use self::{concept::ConceptManager, logic::LogicManager, query::QueryManager}; use crate::{ common::{Result, TransactionType}, connection::TransactionStream, - LogicManager, Options, + Options, }; pub struct Transaction<'a> { diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index 0b64dbe4..e47fbea4 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -31,9 +31,9 @@ use regex::{Captures, Regex}; use typedb_client::{ answer::ConceptMap, concept::{Annotation, Attribute, Concept, Entity, Relation, Value}, + logic::Rule, transaction::concept::api::ThingAPI, Result as TypeDBResult, - Rule, }; use typeql_lang::{ parse_pattern, parse_query, parse_rule, From 2817df9a8309a901b7e582a9fcbcd9ce4408b68d Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 14 Jun 2023 18:27:05 +0400 Subject: [PATCH 34/64] RuleAPI --- src/connection/transaction_stream.rs | 4 +-- src/logic/rule.rs | 2 +- src/transaction/logic/api/mod.rs | 26 ++++++++++++++ src/transaction/logic/api/rule.rs | 53 ++++++++++++++++++++++++++++ src/transaction/logic/manager.rs | 2 +- src/transaction/logic/mod.rs | 1 + 6 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 src/transaction/logic/api/mod.rs create mode 100644 src/transaction/logic/api/rule.rs diff --git a/src/connection/transaction_stream.rs b/src/connection/transaction_stream.rs index 36e43147..c82164b7 100644 --- a/src/connection/transaction_stream.rs +++ b/src/connection/transaction_stream.rs @@ -938,8 +938,8 @@ impl TransactionStream { } } - pub(crate) async fn rule_set_label(&self, rule: Rule, label: String) -> Result { - match self.rule_single(RuleRequest::SetLabel { current_label: rule.label, new_label: label }).await? { + pub(crate) async fn rule_set_label(&self, rule: Rule, new_label: String) -> Result { + match self.rule_single(RuleRequest::SetLabel { current_label: rule.label, new_label }).await? { RuleResponse::SetLabel => Ok(()), other => Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into()), } diff --git a/src/logic/rule.rs b/src/logic/rule.rs index 3c68a742..3a5542dc 100644 --- a/src/logic/rule.rs +++ b/src/logic/rule.rs @@ -23,7 +23,7 @@ use typeql_lang::pattern::{Conjunction, ThingVariable}; use crate::common::Result; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Rule { pub label: String, pub when: Conjunction, diff --git a/src/transaction/logic/api/mod.rs b/src/transaction/logic/api/mod.rs new file mode 100644 index 00000000..037d8c74 --- /dev/null +++ b/src/transaction/logic/api/mod.rs @@ -0,0 +1,26 @@ +/* + * 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 rule; + +pub use self::{ + rule::{RuleAPI}, +}; diff --git a/src/transaction/logic/api/rule.rs b/src/transaction/logic/api/rule.rs new file mode 100644 index 00000000..1b109073 --- /dev/null +++ b/src/transaction/logic/api/rule.rs @@ -0,0 +1,53 @@ +/* + * 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 async_trait::async_trait; +use futures::stream::BoxStream; + +use crate::{ + common::box_stream, + logic::Rule, + Result, Transaction, +}; + +#[async_trait] +pub trait RuleAPI: Clone + Sync + Send { + fn label(&self) -> &String; + async fn delete(&mut self, transaction: &Transaction<'_>) -> Result; + async fn set_label(&mut self, transaction: &Transaction<'_>, new_label: String) -> Result; +} + +#[async_trait] +impl RuleAPI for Rule { + fn label(&self) -> &String { + &self.label + } + + async fn delete(&mut self, transaction: &Transaction<'_>) -> Result { + transaction.logic().transaction_stream.rule_delete(self.clone()).await + } + + async fn set_label(&mut self, transaction: &Transaction<'_>, new_label: String) -> Result { + transaction.logic().transaction_stream.rule_set_label(self.clone(), new_label).await + } +} + + diff --git a/src/transaction/logic/manager.rs b/src/transaction/logic/manager.rs index 0c9155ee..2d8f8f20 100644 --- a/src/transaction/logic/manager.rs +++ b/src/transaction/logic/manager.rs @@ -28,7 +28,7 @@ use crate::{common::Result, connection::TransactionStream, logic::Rule}; #[derive(Clone, Debug)] pub struct LogicManager { - transaction_stream: Arc, + pub(super) transaction_stream: Arc, } impl LogicManager { diff --git a/src/transaction/logic/mod.rs b/src/transaction/logic/mod.rs index 1edb23ee..6f76b3be 100644 --- a/src/transaction/logic/mod.rs +++ b/src/transaction/logic/mod.rs @@ -19,6 +19,7 @@ * under the License. */ +pub mod api; mod manager; pub use self::manager::LogicManager; From 1e79084cf9bfa5029baf7cf0a3b1e582bdeebcf7 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 14 Jun 2023 18:43:47 +0400 Subject: [PATCH 35/64] BDD steps for rules --- tests/behaviour/concept/mod.rs | 1 + tests/behaviour/concept/rule/mod.rs | 22 +++++++++ tests/behaviour/concept/rule/steps.rs | 64 +++++++++++++++++++++++++++ tests/behaviour/mod.rs | 5 +++ 4 files changed, 92 insertions(+) create mode 100644 tests/behaviour/concept/rule/mod.rs create mode 100644 tests/behaviour/concept/rule/steps.rs diff --git a/tests/behaviour/concept/mod.rs b/tests/behaviour/concept/mod.rs index b9d24896..97bfa416 100644 --- a/tests/behaviour/concept/mod.rs +++ b/tests/behaviour/concept/mod.rs @@ -19,5 +19,6 @@ * under the License. */ +mod rule; mod thing; mod type_; diff --git a/tests/behaviour/concept/rule/mod.rs b/tests/behaviour/concept/rule/mod.rs new file mode 100644 index 00000000..d27c1a61 --- /dev/null +++ b/tests/behaviour/concept/rule/mod.rs @@ -0,0 +1,22 @@ +/* + * 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; diff --git a/tests/behaviour/concept/rule/steps.rs b/tests/behaviour/concept/rule/steps.rs new file mode 100644 index 00000000..74a7f048 --- /dev/null +++ b/tests/behaviour/concept/rule/steps.rs @@ -0,0 +1,64 @@ +/* + * 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 futures::TryStreamExt; +use typedb_client::{ + logic::Rule, + transaction::logic::api::{RuleAPI}, + Result as TypeDBResult, +}; + +use crate::{ + assert_err, + behaviour::{ + parameter::{ + ContainmentParam, LabelParam, OptionalAnnotationsParam, OptionalExplicitParam, OptionalOverrideLabelParam, + OptionalOverrideScopedLabelParam, ScopedLabelParam, + }, + util::iter_table, + Context, + }, + generic_step_impl, +}; + +generic_step_impl! { + #[step(expr = "delete rule: {label}")] + async fn delete_rule(context: &mut Context, label: LabelParam) -> TypeDBResult { + let tx = context.transaction(); + context.get_rule(label.name).await?.delete(tx).await + } + + #[step(expr = "delete rule: {label}; throws exception")] + async fn delete_rule_throws(context: &mut Context, type_label: LabelParam) { + assert_err!(delete_rule(context, type_label).await); + } + + #[step(expr = r"rule\(( ){label}( )\) set label: {label}")] + async fn rule_set_label( + context: &mut Context, + current_label: LabelParam, + new_label: LabelParam, + ) -> TypeDBResult { + let tx = context.transaction(); + context.get_rule(current_label.name).await?.set_label(tx, new_label.name).await + } +} diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 4f98efcc..65f1e106 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -33,6 +33,7 @@ use futures::future::try_join_all; 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, }; @@ -156,6 +157,10 @@ impl Context { pub fn insert_attribute(&mut self, var_name: String, attribute: Option) { 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 + } } impl Default for Context { From 4218ffae75d60ae187c31930b8609f5075af13e4 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 14 Jun 2023 18:44:06 +0400 Subject: [PATCH 36/64] fmt --- src/connection/network/proto/logic.rs | 2 +- src/connection/network/proto/message.rs | 21 +++++++++++---------- src/connection/transaction_stream.rs | 4 ++-- src/transaction/logic/api/mod.rs | 4 +--- src/transaction/logic/api/rule.rs | 8 +------- tests/behaviour/concept/rule/steps.rs | 6 +----- 6 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/connection/network/proto/logic.rs b/src/connection/network/proto/logic.rs index c8cad741..0c3bb12b 100644 --- a/src/connection/network/proto/logic.rs +++ b/src/connection/network/proto/logic.rs @@ -26,7 +26,7 @@ use typeql_lang::{ }; use super::{IntoProto, TryFromProto}; -use crate::{common::Result, Error, logic::Rule}; +use crate::{common::Result, logic::Rule, Error}; impl TryFromProto for Rule { fn try_from_proto(proto: RuleProto) -> Result { diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 7d3c9ca3..196d1654 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -37,8 +37,8 @@ use crate::{ }, connection::message::{ ConceptRequest, ConceptResponse, LogicRequest, LogicResponse, QueryRequest, QueryResponse, Request, Response, - RoleTypeRequest, RoleTypeResponse, RuleRequest, RuleResponse, ThingRequest, ThingResponse, ThingTypeRequest, ThingTypeResponse, - TransactionRequest, TransactionResponse, + RoleTypeRequest, RoleTypeResponse, RuleRequest, RuleResponse, ThingRequest, ThingResponse, ThingTypeRequest, + ThingTypeResponse, TransactionRequest, TransactionResponse, }, error::{ConnectionError, InternalError}, logic::{Explanation, Rule}, @@ -299,7 +299,9 @@ impl TryFromProto for TransactionResponse { Some(transaction::res::Res::TypeRes(r#type::Res { res: Some(r#type::res::Res::RoleTypeRes(res)) })) => { Ok(Self::RoleType(RoleTypeResponse::try_from_proto(res)?)) } - Some(transaction::res::Res::TypeRes(r#type::Res { res: None })) => Err(ConnectionError::MissingResponseField("res").into()), + Some(transaction::res::Res::TypeRes(r#type::Res { res: None })) => { + Err(ConnectionError::MissingResponseField("res").into()) + } Some(transaction::res::Res::ThingRes(res)) => Ok(Self::Thing(ThingResponse::try_from_proto(res)?)), Some(transaction::res::Res::RuleRes(res)) => Ok(Self::Rule(RuleResponse::try_from_proto(res)?)), Some(transaction::res::Res::LogicManagerRes(res)) => Ok(Self::Logic(LogicResponse::try_from_proto(res)?)), @@ -320,7 +322,9 @@ impl TryFromProto for TransactionResponse { Some(transaction::res_part::Res::TypeResPart(r#type::ResPart { res: Some(r#type::res_part::Res::RoleTypeResPart(res)), })) => Ok(Self::RoleType(RoleTypeResponse::try_from_proto(res)?)), - Some(transaction::res_part::Res::TypeResPart(r#type::ResPart { res: None })) => Err(ConnectionError::MissingResponseField("res").into()), + Some(transaction::res_part::Res::TypeResPart(r#type::ResPart { res: None })) => { + Err(ConnectionError::MissingResponseField("res").into()) + } Some(transaction::res_part::Res::ThingResPart(res)) => Ok(Self::Thing(ThingResponse::try_from_proto(res)?)), Some(transaction::res_part::Res::LogicManagerResPart(res)) => { Ok(Self::Logic(LogicResponse::try_from_proto(res)?)) @@ -1121,13 +1125,10 @@ impl TryFromProto for ThingResponse { impl IntoProto for RuleRequest { fn into_proto(self) -> rule::Req { let (req, label) = match self { - Self::Delete { label } => { - (rule::req::Req::RuleDeleteReq(rule::delete::Req {}), label) + Self::Delete { label } => (rule::req::Req::RuleDeleteReq(rule::delete::Req {}), label), + Self::SetLabel { current_label, new_label } => { + (rule::req::Req::RuleSetLabelReq(rule::set_label::Req { label: new_label }), current_label) } - Self::SetLabel { current_label, new_label } => ( - rule::req::Req::RuleSetLabelReq(rule::set_label::Req { label: new_label }), - current_label, - ), }; rule::Req { label, req: Some(req) } } diff --git a/src/connection/transaction_stream.rs b/src/connection/transaction_stream.rs index c82164b7..b2184b32 100644 --- a/src/connection/transaction_stream.rs +++ b/src/connection/transaction_stream.rs @@ -36,8 +36,8 @@ use crate::{ Thing, ThingType, Transitivity, Value, ValueType, }, connection::message::{ - ConceptRequest, ConceptResponse, LogicRequest, LogicResponse, QueryRequest, QueryResponse, RuleRequest, RuleResponse, - ThingTypeRequest, ThingTypeResponse, TransactionRequest, TransactionResponse, + ConceptRequest, ConceptResponse, LogicRequest, LogicResponse, QueryRequest, QueryResponse, RuleRequest, + RuleResponse, ThingTypeRequest, ThingTypeResponse, TransactionRequest, TransactionResponse, }, error::InternalError, logic::{Explanation, Rule}, diff --git a/src/transaction/logic/api/mod.rs b/src/transaction/logic/api/mod.rs index 037d8c74..a36834e0 100644 --- a/src/transaction/logic/api/mod.rs +++ b/src/transaction/logic/api/mod.rs @@ -21,6 +21,4 @@ mod rule; -pub use self::{ - rule::{RuleAPI}, -}; +pub use self::rule::RuleAPI; diff --git a/src/transaction/logic/api/rule.rs b/src/transaction/logic/api/rule.rs index 1b109073..3266817f 100644 --- a/src/transaction/logic/api/rule.rs +++ b/src/transaction/logic/api/rule.rs @@ -22,11 +22,7 @@ use async_trait::async_trait; use futures::stream::BoxStream; -use crate::{ - common::box_stream, - logic::Rule, - Result, Transaction, -}; +use crate::{common::box_stream, logic::Rule, Result, Transaction}; #[async_trait] pub trait RuleAPI: Clone + Sync + Send { @@ -49,5 +45,3 @@ impl RuleAPI for Rule { transaction.logic().transaction_stream.rule_set_label(self.clone(), new_label).await } } - - diff --git a/tests/behaviour/concept/rule/steps.rs b/tests/behaviour/concept/rule/steps.rs index 74a7f048..db8d1df8 100644 --- a/tests/behaviour/concept/rule/steps.rs +++ b/tests/behaviour/concept/rule/steps.rs @@ -21,11 +21,7 @@ use cucumber::{gherkin::Step, given, then, when}; use futures::TryStreamExt; -use typedb_client::{ - logic::Rule, - transaction::logic::api::{RuleAPI}, - Result as TypeDBResult, -}; +use typedb_client::{logic::Rule, transaction::logic::api::RuleAPI, Result as TypeDBResult}; use crate::{ assert_err, From 879ace8d68b0ffe233f2fe6ec15773faaa1321ac Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 14 Jun 2023 19:56:37 +0400 Subject: [PATCH 37/64] Rules BDD tests --- dependencies/vaticle/repositories.bzl | 2 +- tests/BUILD | 1 + tests/behaviour/concept/rule/mod.rs | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/dependencies/vaticle/repositories.bzl b/dependencies/vaticle/repositories.bzl index 744ec502..24e23b08 100644 --- a/dependencies/vaticle/repositories.bzl +++ b/dependencies/vaticle/repositories.bzl @@ -46,7 +46,7 @@ def vaticle_typedb_behaviour(): git_repository( name = "vaticle_typedb_behaviour", remote = "https://github.com/vaticle/typedb-behaviour", - commit = "d4947828f570853b71f0eb8aefd34af7b5a485fb", + commit = "d4947828f570853b71f0eb8aefd34af7b5a485fb", # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_behaviour ) def vaticle_typeql(): diff --git a/tests/BUILD b/tests/BUILD index 7b3a43a6..90eaca2a 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -42,6 +42,7 @@ rust_test( "@crates//:tokio", ], data = [ + "@vaticle_typedb_behaviour//concept/rule:rule.feature", "@vaticle_typedb_behaviour//concept/thing:entity.feature", "@vaticle_typedb_behaviour//concept/thing:relation.feature", "@vaticle_typedb_behaviour//concept/thing:attribute.feature", diff --git a/tests/behaviour/concept/rule/mod.rs b/tests/behaviour/concept/rule/mod.rs index d27c1a61..03af19ff 100644 --- a/tests/behaviour/concept/rule/mod.rs +++ b/tests/behaviour/concept/rule/mod.rs @@ -20,3 +20,16 @@ */ 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/concept/rule/rule.feature").await); +} From b3ad7874ce8f875528df4277f65ef129e0ae6242 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 14 Jun 2023 20:04:19 +0400 Subject: [PATCH 38/64] Extract reasoner tests --- tests/behaviour/typeql/mod.rs | 1 + tests/behaviour/typeql/reasoner/mod.rs | 43 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 tests/behaviour/typeql/reasoner/mod.rs diff --git a/tests/behaviour/typeql/mod.rs b/tests/behaviour/typeql/mod.rs index 22efafd0..f68c76ab 100644 --- a/tests/behaviour/typeql/mod.rs +++ b/tests/behaviour/typeql/mod.rs @@ -24,6 +24,7 @@ mod delete; mod get; mod insert; mod match_; +mod reasoner; mod rule_validation; mod steps; mod undefine; diff --git a/tests/behaviour/typeql/reasoner/mod.rs b/tests/behaviour/typeql/reasoner/mod.rs new file mode 100644 index 00000000..31ae1435 --- /dev/null +++ b/tests/behaviour/typeql/reasoner/mod.rs @@ -0,0 +1,43 @@ +/* + * 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 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/typeql/reasoner/attribute-attachment.feature").await); + assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/compound-queries.feature").await); + assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/concept-inequality.feature").await); + assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/negation.feature").await); + assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/recursion.feature").await); + assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/relation-inference.feature").await); + assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/rule-interaction.feature").await); + assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/schema-queries.feature").await); + assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/type-hierarchy.feature").await); + assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/value-predicate.feature").await); + assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/variable-roles.feature").await); +} From 42f488f42c1943392a7ce12cca67a48f90faaa89 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 14 Jun 2023 20:34:48 +0400 Subject: [PATCH 39/64] Reordering --- src/connection/transaction_stream.rs | 26 +++++++++++++------------- src/transaction/mod.rs | 8 ++++---- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/connection/transaction_stream.rs b/src/connection/transaction_stream.rs index b2184b32..4fedc498 100644 --- a/src/connection/transaction_stream.rs +++ b/src/connection/transaction_stream.rs @@ -968,6 +968,19 @@ impl TransactionStream { })) } + pub(crate) fn explain( + &self, + explainable_id: i64, + options: Options, + ) -> Result>> { + let stream = self.query_stream(QueryRequest::Explain { explainable_id, options })?; + Ok(stream.flat_map(|result| match result { + Ok(QueryResponse::Explain { answers }) => stream_iter(answers.into_iter().map(Ok)), + Ok(other) => stream_once(Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into())), + Err(err) => stream_once(Err(err)), + })) + } + async fn single(&self, req: TransactionRequest) -> Result { self.transaction_transmitter.single(req).await } @@ -1021,19 +1034,6 @@ impl TransactionStream { } } - pub(crate) fn explain( - &self, - explainable_id: i64, - options: Options, - ) -> Result>> { - let stream = self.query_stream(QueryRequest::Explain { explainable_id, options })?; - Ok(stream.flat_map(|result| match result { - Ok(QueryResponse::Explain { answers }) => stream_iter(answers.into_iter().map(Ok)), - Ok(other) => stream_once(Err(InternalError::UnexpectedResponseType(format!("{other:?}")).into())), - Err(err) => stream_once(Err(err)), - })) - } - fn stream(&self, req: TransactionRequest) -> Result>> { self.transaction_transmitter.stream(req) } diff --git a/src/transaction/mod.rs b/src/transaction/mod.rs index 4cdc38b2..a37ca63e 100644 --- a/src/transaction/mod.rs +++ b/src/transaction/mod.rs @@ -74,6 +74,10 @@ impl Transaction<'_> { &self.concept } + pub fn logic(&self) -> &LogicManager { + &self.logic + } + pub async fn commit(self) -> Result { self.transaction_stream.commit().await } @@ -81,10 +85,6 @@ impl Transaction<'_> { pub async fn rollback(&self) -> Result { self.transaction_stream.rollback().await } - - pub fn logic(&self) -> &LogicManager { - &self.logic - } } impl fmt::Debug for Transaction<'_> { From 1b2169e2fcd0e17cbca9d7af7eeb310782790946 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 15 Jun 2023 12:35:14 +0400 Subject: [PATCH 40/64] Remove redundant clone() --- tests/integration/logic.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 2f230f4b..23e621fe 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -307,7 +307,7 @@ async fn assert_single_explainable_explanations( transaction: &Transaction<'_>, ) { check_explainable_vars(ans); - let explainables = ans.clone().explainables; + let explainables = &ans.explainables; let mut all_explainables = explainables.attributes.values().collect::>(); all_explainables.extend(explainables.relations.values().collect::>()); all_explainables.extend(explainables.ownerships.values().collect::>()); @@ -331,12 +331,11 @@ async fn assert_single_explainable_explanations( } fn check_explainable_vars(ans: &ConceptMap) { - ans.clone().explainables.relations.into_keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); - ans.clone().explainables.attributes.into_keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); - ans.clone() - .explainables + ans.explainables.relations.keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); + ans.explainables.attributes.keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); + ans.explainables .ownerships - .into_keys() + .keys() .for_each(|(k1, k2)| assert!(ans.map.contains_key(k1.as_str()) && ans.map.contains_key(k2.as_str()))); } From 2b90c9ac6969e430145567063bbe294239ca460f Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 15 Jun 2023 14:22:14 +0400 Subject: [PATCH 41/64] Refactoring --- tests/integration/logic.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 23e621fe..7040233c 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -306,20 +306,26 @@ async fn assert_single_explainable_explanations( explanations_count: usize, transaction: &Transaction<'_>, ) { - check_explainable_vars(ans); + assert_explainables_in_concept_map(ans); + let explainables = &ans.explainables; let mut all_explainables = explainables.attributes.values().collect::>(); all_explainables.extend(explainables.relations.values().collect::>()); all_explainables.extend(explainables.ownerships.values().collect::>()); assert_eq!(explainables_count, all_explainables.len()); + let explainable = all_explainables.get(0).unwrap(); assert!(explainable.id >= 0); + let stream = transaction.query().explain(explainable.id); assert!(stream.is_ok()); + let result = stream.unwrap().try_collect::>().await; assert!(result.is_ok()); + let explanations = result.unwrap(); assert_eq!(explanations_count, explanations.len()); + for explanation in explanations { let mapping = explanation.variable_mapping; let projected = apply_mapping(&mapping, ans); @@ -330,7 +336,7 @@ async fn assert_single_explainable_explanations( } } -fn check_explainable_vars(ans: &ConceptMap) { +fn assert_explainables_in_concept_map(ans: &ConceptMap) { ans.explainables.relations.keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); ans.explainables.attributes.keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); ans.explainables From 1342e75590b59e498157399ca46ac93c21f4409d Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 15 Jun 2023 17:33:35 +0400 Subject: [PATCH 42/64] Fix rebase --- src/logic/rule.rs | 2 -- src/transaction/logic/api/rule.rs | 3 +-- tests/behaviour/concept/rule/steps.rs | 11 +++-------- tests/behaviour/typeql/steps.rs | 18 +++++++++--------- tests/behaviour/util.rs | 4 ++-- tests/integration/logic.rs | 5 ++--- 6 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/logic/rule.rs b/src/logic/rule.rs index 3a5542dc..1bfe7ecc 100644 --- a/src/logic/rule.rs +++ b/src/logic/rule.rs @@ -21,8 +21,6 @@ use typeql_lang::pattern::{Conjunction, ThingVariable}; -use crate::common::Result; - #[derive(Clone, Debug)] pub struct Rule { pub label: String, diff --git a/src/transaction/logic/api/rule.rs b/src/transaction/logic/api/rule.rs index 3266817f..97ee678d 100644 --- a/src/transaction/logic/api/rule.rs +++ b/src/transaction/logic/api/rule.rs @@ -20,9 +20,8 @@ */ use async_trait::async_trait; -use futures::stream::BoxStream; -use crate::{common::box_stream, logic::Rule, Result, Transaction}; +use crate::{logic::Rule, Result, Transaction}; #[async_trait] pub trait RuleAPI: Clone + Sync + Send { diff --git a/tests/behaviour/concept/rule/steps.rs b/tests/behaviour/concept/rule/steps.rs index db8d1df8..a30cdd9b 100644 --- a/tests/behaviour/concept/rule/steps.rs +++ b/tests/behaviour/concept/rule/steps.rs @@ -19,18 +19,13 @@ * under the License. */ -use cucumber::{gherkin::Step, given, then, when}; -use futures::TryStreamExt; -use typedb_client::{logic::Rule, transaction::logic::api::RuleAPI, Result as TypeDBResult}; +use cucumber::{given, then, when}; +use typedb_client::{transaction::logic::api::RuleAPI, Result as TypeDBResult}; use crate::{ assert_err, behaviour::{ - parameter::{ - ContainmentParam, LabelParam, OptionalAnnotationsParam, OptionalExplicitParam, OptionalOverrideLabelParam, - OptionalOverrideScopedLabelParam, ScopedLabelParam, - }, - util::iter_table, + parameter::LabelParam, Context, }, generic_step_impl, diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index 314a345a..7119f693 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -19,10 +19,8 @@ * under the License. */ -use std::collections::{HashMap, HashSet}; - use cucumber::{gherkin::Step, given, then, when}; -use futures::{StreamExt, TryFutureExt, TryStreamExt}; +use futures::{TryStreamExt}; use typedb_client::{answer::Numeric, Result as TypeDBResult, Session, SessionType, TransactionType}; use typeql_lang::parse_query; use util::{ @@ -31,7 +29,7 @@ use util::{ use crate::{ assert_err, - behaviour::{parameter::Comparable, util, Context}, + behaviour::{util, Context}, generic_step_impl, }; @@ -379,7 +377,7 @@ generic_step_impl! { let res = stream.unwrap().try_collect::>().await; assert!(res.is_ok(), "{:?}", res.err()); let answers = res.unwrap(); - let step_table = iter_map_table(step).collect::>(); + let step_table = iter_table_map(step).collect::>(); let expected_answers = step_table.len(); let actual_answers = answers.len(); assert_eq!( @@ -414,7 +412,7 @@ generic_step_impl! { for session_tracker in &mut context.session_trackers { session_tracker.open_transaction(TransactionType::Write).await.unwrap(); } - typeql_define(context, step).await; + assert!(typeql_define(context, step).await.is_ok()); context.take_transaction().commit().await.unwrap(); context.session_trackers.clear(); } @@ -427,7 +425,7 @@ generic_step_impl! { for session_tracker in &mut context.session_trackers { session_tracker.open_transaction(TransactionType::Write).await.unwrap(); } - typeql_insert(context, step).await; + assert!(typeql_insert(context, step).await.is_ok()); context.take_transaction().commit().await.unwrap(); context.session_trackers.clear(); } @@ -440,7 +438,7 @@ generic_step_impl! { for session_tracker in &mut context.session_trackers { session_tracker.open_transaction(TransactionType::Read).await.unwrap(); } - get_answers_typeql_match(context, step).await; + assert!(get_answers_typeql_match(context, step).await.is_ok()); context.session_trackers.clear(); } @@ -470,6 +468,8 @@ generic_step_impl! { async fn do_nothing(context: &mut Context) {} #[step(expr = "verify answers are consistent across {int} executions")] - async fn verify_answers_are_consistent_across_executions(context: &mut Context, executions: usize) {} + async fn verify_answers_are_consistent_across_executions(_context: &mut Context, _executions: usize) { + // We can't execute previous query again because don't remember the query + } } diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index e47fbea4..63477084 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -36,8 +36,8 @@ use typedb_client::{ Result as TypeDBResult, }; use typeql_lang::{ - parse_pattern, parse_query, parse_rule, - pattern::{ThingVariable, Variable}, + parse_pattern, parse_query, + pattern::Variable, }; use crate::behaviour::Context; diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 7040233c..e0b0b143 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -21,12 +21,11 @@ use std::{collections::HashMap, default::Default}; -use futures::{TryFutureExt, TryStreamExt}; +use futures::{TryStreamExt}; use serial_test::serial; use typedb_client::{ - answer::{ConceptMap, Explainable, Explainables}, + answer::ConceptMap, concept::{Attribute, Concept, Value}, - logic::Explanation, transaction::concept::api::ThingAPI, Connection, DatabaseManager, Options, Session, SessionType::{Data, Schema}, From c83c108ae591503c5aaf7deba5b0f76254fa4d0f Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 15 Jun 2023 17:42:07 +0400 Subject: [PATCH 43/64] typeql BDD test regrouping --- .../typeql/{ => language}/define/mod.rs | 0 .../typeql/{ => language}/delete/mod.rs | 0 .../typeql/{ => language}/get/mod.rs | 0 .../typeql/{ => language}/insert/mod.rs | 0 .../typeql/{ => language}/match_/mod.rs | 0 tests/behaviour/typeql/language/mod.rs | 29 +++++++++++++++++++ .../{ => language}/rule_validation/mod.rs | 0 .../typeql/{ => language}/undefine/mod.rs | 0 .../typeql/{ => language}/update/mod.rs | 0 tests/behaviour/typeql/mod.rs | 9 +----- 10 files changed, 30 insertions(+), 8 deletions(-) rename tests/behaviour/typeql/{ => language}/define/mod.rs (100%) rename tests/behaviour/typeql/{ => language}/delete/mod.rs (100%) rename tests/behaviour/typeql/{ => language}/get/mod.rs (100%) rename tests/behaviour/typeql/{ => language}/insert/mod.rs (100%) rename tests/behaviour/typeql/{ => language}/match_/mod.rs (100%) create mode 100644 tests/behaviour/typeql/language/mod.rs rename tests/behaviour/typeql/{ => language}/rule_validation/mod.rs (100%) rename tests/behaviour/typeql/{ => language}/undefine/mod.rs (100%) rename tests/behaviour/typeql/{ => language}/update/mod.rs (100%) diff --git a/tests/behaviour/typeql/define/mod.rs b/tests/behaviour/typeql/language/define/mod.rs similarity index 100% rename from tests/behaviour/typeql/define/mod.rs rename to tests/behaviour/typeql/language/define/mod.rs diff --git a/tests/behaviour/typeql/delete/mod.rs b/tests/behaviour/typeql/language/delete/mod.rs similarity index 100% rename from tests/behaviour/typeql/delete/mod.rs rename to tests/behaviour/typeql/language/delete/mod.rs diff --git a/tests/behaviour/typeql/get/mod.rs b/tests/behaviour/typeql/language/get/mod.rs similarity index 100% rename from tests/behaviour/typeql/get/mod.rs rename to tests/behaviour/typeql/language/get/mod.rs diff --git a/tests/behaviour/typeql/insert/mod.rs b/tests/behaviour/typeql/language/insert/mod.rs similarity index 100% rename from tests/behaviour/typeql/insert/mod.rs rename to tests/behaviour/typeql/language/insert/mod.rs diff --git a/tests/behaviour/typeql/match_/mod.rs b/tests/behaviour/typeql/language/match_/mod.rs similarity index 100% rename from tests/behaviour/typeql/match_/mod.rs rename to tests/behaviour/typeql/language/match_/mod.rs diff --git a/tests/behaviour/typeql/language/mod.rs b/tests/behaviour/typeql/language/mod.rs new file mode 100644 index 00000000..474aaed7 --- /dev/null +++ b/tests/behaviour/typeql/language/mod.rs @@ -0,0 +1,29 @@ +/* + * 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 define; +mod delete; +mod get; +mod insert; +mod match_; +mod rule_validation; +mod undefine; +mod update; diff --git a/tests/behaviour/typeql/rule_validation/mod.rs b/tests/behaviour/typeql/language/rule_validation/mod.rs similarity index 100% rename from tests/behaviour/typeql/rule_validation/mod.rs rename to tests/behaviour/typeql/language/rule_validation/mod.rs diff --git a/tests/behaviour/typeql/undefine/mod.rs b/tests/behaviour/typeql/language/undefine/mod.rs similarity index 100% rename from tests/behaviour/typeql/undefine/mod.rs rename to tests/behaviour/typeql/language/undefine/mod.rs diff --git a/tests/behaviour/typeql/update/mod.rs b/tests/behaviour/typeql/language/update/mod.rs similarity index 100% rename from tests/behaviour/typeql/update/mod.rs rename to tests/behaviour/typeql/language/update/mod.rs diff --git a/tests/behaviour/typeql/mod.rs b/tests/behaviour/typeql/mod.rs index f68c76ab..d15a52b5 100644 --- a/tests/behaviour/typeql/mod.rs +++ b/tests/behaviour/typeql/mod.rs @@ -19,13 +19,6 @@ * under the License. */ -mod define; -mod delete; -mod get; -mod insert; -mod match_; +mod language; mod reasoner; -mod rule_validation; mod steps; -mod undefine; -mod update; From 0a6110cb8ef7325cdfcfd989bb3eaa4678bbfa91 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 15 Jun 2023 17:50:04 +0400 Subject: [PATCH 44/64] Add comment to do_nothing() --- tests/behaviour/typeql/steps.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index 7119f693..3d206798 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -465,7 +465,9 @@ generic_step_impl! { #[step(expr = "verifier is initialised")] #[step(expr = "verify answers are sound")] #[step(expr = "verify answers are complete")] - async fn do_nothing(context: &mut Context) {} + async fn do_nothing(_context: &mut Context) { + // We don't have a verifier + } #[step(expr = "verify answers are consistent across {int} executions")] async fn verify_answers_are_consistent_across_executions(_context: &mut Context, _executions: usize) { From c6094e09d14231b20796683fe894bd2f542c6daa Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 15 Jun 2023 17:50:52 +0400 Subject: [PATCH 45/64] fmt --- tests/behaviour/concept/rule/steps.rs | 5 +---- tests/behaviour/typeql/steps.rs | 5 +++-- tests/behaviour/util.rs | 10 ++-------- tests/integration/logic.rs | 2 +- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/tests/behaviour/concept/rule/steps.rs b/tests/behaviour/concept/rule/steps.rs index a30cdd9b..9511cb5d 100644 --- a/tests/behaviour/concept/rule/steps.rs +++ b/tests/behaviour/concept/rule/steps.rs @@ -24,10 +24,7 @@ use typedb_client::{transaction::logic::api::RuleAPI, Result as TypeDBResult}; use crate::{ assert_err, - behaviour::{ - parameter::LabelParam, - Context, - }, + behaviour::{parameter::LabelParam, Context}, generic_step_impl, }; diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index 3d206798..04c13597 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -20,11 +20,12 @@ */ use cucumber::{gherkin::Step, given, then, when}; -use futures::{TryStreamExt}; +use futures::TryStreamExt; use typedb_client::{answer::Numeric, Result as TypeDBResult, Session, SessionType, TransactionType}; use typeql_lang::parse_query; use util::{ - equals_approximate, iter_table_map, match_answer_concept, match_answer_concept_map, match_answer_rule, match_templated_answer, + equals_approximate, iter_table_map, match_answer_concept, match_answer_concept_map, match_answer_rule, + match_templated_answer, }; use crate::{ diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index 63477084..4cfca5f1 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -35,10 +35,7 @@ use typedb_client::{ transaction::concept::api::ThingAPI, Result as TypeDBResult, }; -use typeql_lang::{ - parse_pattern, parse_query, - pattern::Variable, -}; +use typeql_lang::{parse_pattern, parse_query, pattern::Variable}; use crate::behaviour::Context; @@ -179,10 +176,7 @@ fn get_iid(concept: &Concept) -> String { iid.to_string() } -pub async fn match_answer_rule( - answer_identifiers: &HashMap<&String, &String>, - answer: &Rule, -) -> bool { +pub async fn match_answer_rule(answer_identifiers: &HashMap<&String, &String>, answer: &Rule) -> bool { let when_clause = answer_identifiers.get(&String::from("when")).unwrap().trim_end_matches(";").to_string(); let when = parse_pattern(when_clause.as_str()).unwrap().into_conjunction(); let then_clause = answer_identifiers diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index e0b0b143..4720b85d 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -21,7 +21,7 @@ use std::{collections::HashMap, default::Default}; -use futures::{TryStreamExt}; +use futures::TryStreamExt; use serial_test::serial; use typedb_client::{ answer::ConceptMap, From 8a67c03eb872ece6c663588fd409e01721429ef7 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 15 Jun 2023 18:22:27 +0400 Subject: [PATCH 46/64] Fix error types --- src/common/error.rs | 2 ++ src/connection/network/proto/logic.rs | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/common/error.rs b/src/common/error.rs index 813f7f57..3bd26862 100644 --- a/src/common/error.rs +++ b/src/common/error.rs @@ -44,6 +44,8 @@ error_messages! { ConnectionError 9: "Missing field in message received from server: '{}'.", UnknownRequestId(RequestID) = 10: "Received a response with unknown request id '{}'", + InvalidResponseField(&'static str) = + 11: "Invalid field in message received from server: '{}'.", ClusterUnableToConnect(String) = 12: "Unable to connect to TypeDB Cluster. Attempted connecting to the cluster members, but none are available: '{}'.", ClusterReplicaNotPrimary() = diff --git a/src/connection/network/proto/logic.rs b/src/connection/network/proto/logic.rs index 0c3bb12b..950e3f35 100644 --- a/src/connection/network/proto/logic.rs +++ b/src/connection/network/proto/logic.rs @@ -26,20 +26,20 @@ use typeql_lang::{ }; use super::{IntoProto, TryFromProto}; -use crate::{common::Result, logic::Rule, Error}; +use crate::{common::Result, error::ConnectionError, logic::Rule}; impl TryFromProto for Rule { fn try_from_proto(proto: RuleProto) -> Result { let RuleProto { label: label_proto, when: when_proto, then: then_proto } = proto; let when = match parse_pattern(&when_proto) { Ok(Pattern::Conjunction(conjunction)) => conjunction, - Ok(other) => return Err(Error::Other(format!("When parse error: {other:?}"))), - Err(error) => return Err(Error::Other(format!("{error:?}"))), + Ok(_) => return Err(ConnectionError::InvalidResponseField("when").into()), + Err(error) => return Err(error.into()), }; let then = match parse_variable(&then_proto) { Ok(Variable::Thing(thing)) => thing, - Ok(other) => return Err(Error::Other(format!("Then parse error: {other:?}"))), - Err(error) => return Err(Error::Other(format!("{error:?}"))), + Ok(_) => return Err(ConnectionError::InvalidResponseField("then").into()), + Err(error) => return Err(error.into()), }; Ok(Self::new(label_proto, when, then)) } From 7f681d930d0ea39cabc1808b4047f2e3892f19cd Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 15 Jun 2023 19:39:47 +0400 Subject: [PATCH 47/64] Remove redundant check --- tests/integration/logic.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 4720b85d..643bb8cb 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -314,8 +314,6 @@ async fn assert_single_explainable_explanations( assert_eq!(explainables_count, all_explainables.len()); let explainable = all_explainables.get(0).unwrap(); - assert!(explainable.id >= 0); - let stream = transaction.query().explain(explainable.id); assert!(stream.is_ok()); From 3c200c8b1fa79bdd5609056c60b6269377ee2267 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 15 Jun 2023 20:05:18 +0400 Subject: [PATCH 48/64] explainables_count -> 1 --- tests/integration/logic.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 643bb8cb..1f8437f9 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -102,7 +102,7 @@ test_for_each_arg! { assert!(!with_explainable.explainables.is_empty()); assert!(without_explainable.explainables.is_empty()); - assert_single_explainable_explanations(with_explainable, 1, 1, &transaction).await; + assert_single_explainable_explanations(with_explainable, 1, &transaction).await; Ok(()) } @@ -156,8 +156,8 @@ test_for_each_arg! { assert!(!answers.get(0).unwrap().explainables.is_empty()); assert!(!answers.get(1).unwrap().explainables.is_empty()); - assert_single_explainable_explanations(answers.get(0).unwrap(), 1, 1, &transaction).await; - assert_single_explainable_explanations(answers.get(1).unwrap(), 1, 1, &transaction).await; + assert_single_explainable_explanations(answers.get(0).unwrap(), 1, &transaction).await; + assert_single_explainable_explanations(answers.get(1).unwrap(), 1, &transaction).await; Ok(()) } @@ -219,8 +219,8 @@ test_for_each_arg! { assert!(!answers.get(0).unwrap().explainables.is_empty()); assert!(!answers.get(1).unwrap().explainables.is_empty()); - assert_single_explainable_explanations(answers.get(0).unwrap(), 1, 3, &transaction).await; - assert_single_explainable_explanations(answers.get(1).unwrap(), 1, 3, &transaction).await; + assert_single_explainable_explanations(answers.get(0).unwrap(), 3, &transaction).await; + assert_single_explainable_explanations(answers.get(1).unwrap(), 3, &transaction).await; Ok(()) } @@ -286,9 +286,9 @@ test_for_each_arg! { Concept::Entity(entity) => { let attributes: Vec = entity.get_has(&transaction, vec![age_in_days.clone()], vec![])?.try_collect().await?; if attributes.first().unwrap().value == Value::Long(15) { - assert_single_explainable_explanations(&ans, 1, 1, &transaction).await; + assert_single_explainable_explanations(&ans, 1, &transaction).await; } else { - assert_single_explainable_explanations(&ans, 1, 2, &transaction).await; + assert_single_explainable_explanations(&ans, 2, &transaction).await; } }, _ => panic!("Incorrect Concept type: {:?}", ans.map.get("x").unwrap()), @@ -301,7 +301,6 @@ test_for_each_arg! { async fn assert_single_explainable_explanations( ans: &ConceptMap, - explainables_count: usize, explanations_count: usize, transaction: &Transaction<'_>, ) { @@ -311,7 +310,7 @@ async fn assert_single_explainable_explanations( let mut all_explainables = explainables.attributes.values().collect::>(); all_explainables.extend(explainables.relations.values().collect::>()); all_explainables.extend(explainables.ownerships.values().collect::>()); - assert_eq!(explainables_count, all_explainables.len()); + assert_eq!(1, all_explainables.len()); let explainable = all_explainables.get(0).unwrap(); let stream = transaction.query().explain(explainable.id); From c7ce9077ff995a925c3b90371b30986bbacced01 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 19 Jun 2023 14:50:48 +0400 Subject: [PATCH 49/64] Simplify Co-authored-by: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> --- src/connection/network/proto/logic.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/connection/network/proto/logic.rs b/src/connection/network/proto/logic.rs index 950e3f35..1bce3bb8 100644 --- a/src/connection/network/proto/logic.rs +++ b/src/connection/network/proto/logic.rs @@ -31,10 +31,9 @@ use crate::{common::Result, error::ConnectionError, logic::Rule}; impl TryFromProto for Rule { fn try_from_proto(proto: RuleProto) -> Result { let RuleProto { label: label_proto, when: when_proto, then: then_proto } = proto; - let when = match parse_pattern(&when_proto) { - Ok(Pattern::Conjunction(conjunction)) => conjunction, - Ok(_) => return Err(ConnectionError::InvalidResponseField("when").into()), - Err(error) => return Err(error.into()), + let when = match parse_pattern(&when_proto)? { + Pattern::Conjunction(conjunction) => conjunction, + _ => return Err(ConnectionError::InvalidResponseField("when").into()), }; let then = match parse_variable(&then_proto) { Ok(Variable::Thing(thing)) => thing, From 84d902cc9b76c5cc7a29b0079d75063fd5f466d0 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 19 Jun 2023 14:51:38 +0400 Subject: [PATCH 50/64] Cleanup Co-authored-by: Dmitrii Ubskii <18616863+dmitrii-ubskii@users.noreply.github.com> --- tests/behaviour/typeql/steps.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index 04c13597..f7584dfb 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -474,5 +474,4 @@ generic_step_impl! { async fn verify_answers_are_consistent_across_executions(_context: &mut Context, _executions: usize) { // We can't execute previous query again because don't remember the query } - } From 4dc37277fcd9c98c53ea8585303430c0367e2bc2 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 19 Jun 2023 14:39:00 +0300 Subject: [PATCH 51/64] Refactor --- src/answer/concept_map.rs | 8 -------- src/connection/network/proto/concept.rs | 22 +++++++--------------- tests/behaviour/util.rs | 12 ++++-------- 3 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/answer/concept_map.rs b/src/answer/concept_map.rs index 42913bc7..5545b930 100644 --- a/src/answer/concept_map.rs +++ b/src/answer/concept_map.rs @@ -73,14 +73,6 @@ pub struct Explainables { } impl Explainables { - pub(crate) fn new( - relations: HashMap, - attributes: HashMap, - ownerships: HashMap<(String, String), Explainable>, - ) -> Self { - Self { relations, attributes, ownerships } - } - pub fn is_empty(&self) -> bool { self.relations.is_empty() && self.attributes.is_empty() && self.ownerships.is_empty() } diff --git a/src/connection/network/proto/concept.rs b/src/connection/network/proto/concept.rs index ecfe7111..13740ef5 100644 --- a/src/connection/network/proto/concept.rs +++ b/src/connection/network/proto/concept.rs @@ -100,11 +100,9 @@ impl TryFromProto for ConceptMapGroup { impl TryFromProto for ConceptMap { fn try_from_proto(proto: ConceptMapProto) -> Result { let ConceptMapProto { map: map_proto, explainables: explainables_proto } = proto; - let mut map = HashMap::with_capacity(map_proto.len()); - for (k, v) in map_proto { - map.insert(k, Concept::try_from_proto(v)?); - } - let Some(explainables) = explainables_proto else { return Err(ConnectionError::MissingResponseField("explainables").into()) }; + let map = map_proto.into_iter().map(|(k, v)| Concept::try_from_proto(v).map(|v| (k, v))).try_collect()?; + let explainables = explainables_proto + .ok_or::(ConnectionError::MissingResponseField("explainables").into())?; Ok(Self { map, explainables: Explainables::from_proto(explainables) }) } } @@ -379,14 +377,8 @@ impl FromProto for Explainables { attributes: attributes_proto, ownerships: ownerships_proto, } = proto; - let mut relations = HashMap::with_capacity(relations_proto.len()); - for (k, v) in relations_proto { - relations.insert(k, Explainable::from_proto(v)); - } - let mut attributes = HashMap::with_capacity(attributes_proto.len()); - for (k, v) in attributes_proto { - attributes.insert(k, Explainable::from_proto(v)); - } + let relations = relations_proto.into_iter().map(|(k, v)| (k, Explainable::from_proto(v))).collect(); + let attributes = attributes_proto.into_iter().map(|(k, v)| (k, Explainable::from_proto(v))).collect(); let mut ownerships = HashMap::new(); for (k1, owned) in ownerships_proto { for (k2, v) in owned.owned { @@ -394,7 +386,7 @@ impl FromProto for Explainables { } } - Self::new(relations, attributes, ownerships) + Self { relations, attributes, ownerships } } } @@ -410,7 +402,7 @@ impl TryFromProto for Explanation { let ExplanationProto { rule, conclusion, condition, var_mapping } = proto; let mut variable_mapping = HashMap::with_capacity(var_mapping.len()); for (k, v) in var_mapping { - variable_mapping.insert(k, v.vars.clone()); + variable_mapping.insert(k, v.vars); } Ok(Self { diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index 4cfca5f1..357c81c0 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -185,12 +185,8 @@ pub async fn match_answer_rule(answer_identifiers: &HashMap<&String, &String>, a .trim_end_matches(['}', ';', ' ']) .trim_start_matches("{") .to_string(); - let then_var = parse_pattern(then_clause.as_str()).unwrap().into_variable(); - if let Variable::Thing(then) = then_var { - answer_identifiers.get(&String::from("label")).unwrap().to_string() == answer.label - && when == answer.when - && then == answer.then - } else { - false - } + let then = parse_pattern(then_clause.as_str()).unwrap().into_variable(); + answer_identifiers.get(&String::from("label")).unwrap().to_string() == answer.label + && when == answer.when + && then == Variable::Thing(answer.then.clone()) } From 63a54a4f974ae7d34171f68ec3f131b9ea87dadf Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 19 Jun 2023 14:54:51 +0300 Subject: [PATCH 52/64] Separate reasoner tests --- .../reasoner/attribute_attachment/mod.rs | 33 +++++++++++++++++++ .../typeql/reasoner/compound_queries/mod.rs | 33 +++++++++++++++++++ .../typeql/reasoner/concept_inequality/mod.rs | 33 +++++++++++++++++++ tests/behaviour/typeql/reasoner/mod.rs | 33 +++++++------------ .../behaviour/typeql/reasoner/negation/mod.rs | 33 +++++++++++++++++++ .../typeql/reasoner/recursion/mod.rs | 33 +++++++++++++++++++ .../typeql/reasoner/relation_inference/mod.rs | 33 +++++++++++++++++++ .../typeql/reasoner/rule_interaction/mod.rs | 33 +++++++++++++++++++ .../typeql/reasoner/schema_queries/mod.rs | 33 +++++++++++++++++++ .../typeql/reasoner/type_hierarchy/mod.rs | 33 +++++++++++++++++++ .../typeql/reasoner/value_predicate/mod.rs | 33 +++++++++++++++++++ .../typeql/reasoner/variable_roles/mod.rs | 33 +++++++++++++++++++ 12 files changed, 374 insertions(+), 22 deletions(-) create mode 100644 tests/behaviour/typeql/reasoner/attribute_attachment/mod.rs create mode 100644 tests/behaviour/typeql/reasoner/compound_queries/mod.rs create mode 100644 tests/behaviour/typeql/reasoner/concept_inequality/mod.rs create mode 100644 tests/behaviour/typeql/reasoner/negation/mod.rs create mode 100644 tests/behaviour/typeql/reasoner/recursion/mod.rs create mode 100644 tests/behaviour/typeql/reasoner/relation_inference/mod.rs create mode 100644 tests/behaviour/typeql/reasoner/rule_interaction/mod.rs create mode 100644 tests/behaviour/typeql/reasoner/schema_queries/mod.rs create mode 100644 tests/behaviour/typeql/reasoner/type_hierarchy/mod.rs create mode 100644 tests/behaviour/typeql/reasoner/value_predicate/mod.rs create mode 100644 tests/behaviour/typeql/reasoner/variable_roles/mod.rs diff --git a/tests/behaviour/typeql/reasoner/attribute_attachment/mod.rs b/tests/behaviour/typeql/reasoner/attribute_attachment/mod.rs new file mode 100644 index 00000000..183472e3 --- /dev/null +++ b/tests/behaviour/typeql/reasoner/attribute_attachment/mod.rs @@ -0,0 +1,33 @@ +/* + * 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 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/typeql/reasoner/attribute-attachment.feature").await); +} diff --git a/tests/behaviour/typeql/reasoner/compound_queries/mod.rs b/tests/behaviour/typeql/reasoner/compound_queries/mod.rs new file mode 100644 index 00000000..657619d6 --- /dev/null +++ b/tests/behaviour/typeql/reasoner/compound_queries/mod.rs @@ -0,0 +1,33 @@ +/* + * 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 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/typeql/reasoner/compound-queries.feature").await); +} diff --git a/tests/behaviour/typeql/reasoner/concept_inequality/mod.rs b/tests/behaviour/typeql/reasoner/concept_inequality/mod.rs new file mode 100644 index 00000000..001675b7 --- /dev/null +++ b/tests/behaviour/typeql/reasoner/concept_inequality/mod.rs @@ -0,0 +1,33 @@ +/* + * 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 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/typeql/reasoner/concept-inequality.feature").await); +} diff --git a/tests/behaviour/typeql/reasoner/mod.rs b/tests/behaviour/typeql/reasoner/mod.rs index 31ae1435..6f01420b 100644 --- a/tests/behaviour/typeql/reasoner/mod.rs +++ b/tests/behaviour/typeql/reasoner/mod.rs @@ -19,25 +19,14 @@ * under the License. */ -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/typeql/reasoner/attribute-attachment.feature").await); - assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/compound-queries.feature").await); - assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/concept-inequality.feature").await); - assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/negation.feature").await); - assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/recursion.feature").await); - assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/relation-inference.feature").await); - assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/rule-interaction.feature").await); - assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/schema-queries.feature").await); - assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/type-hierarchy.feature").await); - assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/value-predicate.feature").await); - assert!(Context::test("../vaticle_typedb_behaviour/typeql/reasoner/variable-roles.feature").await); -} +mod attribute_attachment; +mod compound_queries; +mod concept_inequality; +mod negation; +mod recursion; +mod relation_inference; +mod rule_interaction; +mod schema_queries; +mod type_hierarchy; +mod value_predicate; +mod variable_roles; diff --git a/tests/behaviour/typeql/reasoner/negation/mod.rs b/tests/behaviour/typeql/reasoner/negation/mod.rs new file mode 100644 index 00000000..bc5f2379 --- /dev/null +++ b/tests/behaviour/typeql/reasoner/negation/mod.rs @@ -0,0 +1,33 @@ +/* + * 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 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/typeql/reasoner/negation.feature").await); +} diff --git a/tests/behaviour/typeql/reasoner/recursion/mod.rs b/tests/behaviour/typeql/reasoner/recursion/mod.rs new file mode 100644 index 00000000..5e80da69 --- /dev/null +++ b/tests/behaviour/typeql/reasoner/recursion/mod.rs @@ -0,0 +1,33 @@ +/* + * 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 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/typeql/reasoner/recursion.feature").await); +} diff --git a/tests/behaviour/typeql/reasoner/relation_inference/mod.rs b/tests/behaviour/typeql/reasoner/relation_inference/mod.rs new file mode 100644 index 00000000..c8e24dd1 --- /dev/null +++ b/tests/behaviour/typeql/reasoner/relation_inference/mod.rs @@ -0,0 +1,33 @@ +/* + * 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 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/typeql/reasoner/relation-inference.feature").await); +} diff --git a/tests/behaviour/typeql/reasoner/rule_interaction/mod.rs b/tests/behaviour/typeql/reasoner/rule_interaction/mod.rs new file mode 100644 index 00000000..f9734df3 --- /dev/null +++ b/tests/behaviour/typeql/reasoner/rule_interaction/mod.rs @@ -0,0 +1,33 @@ +/* + * 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 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/typeql/reasoner/rule-interaction.feature").await); +} diff --git a/tests/behaviour/typeql/reasoner/schema_queries/mod.rs b/tests/behaviour/typeql/reasoner/schema_queries/mod.rs new file mode 100644 index 00000000..383d0f23 --- /dev/null +++ b/tests/behaviour/typeql/reasoner/schema_queries/mod.rs @@ -0,0 +1,33 @@ +/* + * 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 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/typeql/reasoner/schema-queries.feature").await); +} diff --git a/tests/behaviour/typeql/reasoner/type_hierarchy/mod.rs b/tests/behaviour/typeql/reasoner/type_hierarchy/mod.rs new file mode 100644 index 00000000..73edd887 --- /dev/null +++ b/tests/behaviour/typeql/reasoner/type_hierarchy/mod.rs @@ -0,0 +1,33 @@ +/* + * 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 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/typeql/reasoner/type-hierarchy.feature").await); +} diff --git a/tests/behaviour/typeql/reasoner/value_predicate/mod.rs b/tests/behaviour/typeql/reasoner/value_predicate/mod.rs new file mode 100644 index 00000000..98671615 --- /dev/null +++ b/tests/behaviour/typeql/reasoner/value_predicate/mod.rs @@ -0,0 +1,33 @@ +/* + * 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 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/typeql/reasoner/value-predicate.feature").await); +} diff --git a/tests/behaviour/typeql/reasoner/variable_roles/mod.rs b/tests/behaviour/typeql/reasoner/variable_roles/mod.rs new file mode 100644 index 00000000..1ba1abac --- /dev/null +++ b/tests/behaviour/typeql/reasoner/variable_roles/mod.rs @@ -0,0 +1,33 @@ +/* + * 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 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/typeql/reasoner/variable-roles.feature").await); +} From 7a3ae3e5b8d995d1785082863738d46cdd47a11e Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Mon, 19 Jun 2023 14:57:07 +0300 Subject: [PATCH 53/64] word -> label --- tests/behaviour/typeql/steps.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index f7584dfb..bbfed092 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -359,13 +359,13 @@ generic_step_impl! { ); } - #[step(expr = "rules contain: {word}")] + #[step(expr = "rules contain: {label}")] async fn rules_contain(context: &mut Context, rule_label: String) { let res = context.transaction().logic().get_rule(rule_label).await; assert!(res.is_ok(), "{res:?}"); } - #[step(expr = "rules do not contain: {word}")] + #[step(expr = "rules do not contain: {label}")] async fn rules_do_not_contain(context: &mut Context, rule_label: String) { let res = context.transaction().logic().get_rule(rule_label).await; assert!(res.is_err(), "{res:?}"); From 0e78574ad80913657ea9e058b0cc9eeddfdd59d3 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 20 Jun 2023 16:46:21 +0300 Subject: [PATCH 54/64] str in step table identifiers --- tests/behaviour/typeql/steps.rs | 18 +++++++++--------- tests/behaviour/util.rs | 22 +++++++++------------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index bbfed092..3c919af1 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -30,7 +30,7 @@ use util::{ use crate::{ assert_err, - behaviour::{util, Context}, + behaviour::{parameter::LabelParam, util, Context}, generic_step_impl, }; @@ -288,13 +288,13 @@ generic_step_impl! { for table_row in &step_table { if match_answer_concept( context, - table_row.get(&Context::GROUP_COLUMN_NAME.to_string()).unwrap(), + table_row.get(&Context::GROUP_COLUMN_NAME).unwrap(), &group.owner, ) .await { let mut table_row_wo_owner = table_row.clone(); - table_row_wo_owner.remove(&Context::GROUP_COLUMN_NAME.to_string()); + table_row_wo_owner.remove(&Context::GROUP_COLUMN_NAME); if match_answer_concept_map(context, &table_row_wo_owner, &ans_row).await { matched_rows += 1; break; @@ -333,7 +333,7 @@ generic_step_impl! { for table_row in &step_table { if match_answer_concept( context, - table_row.get(&Context::GROUP_COLUMN_NAME.to_string()).unwrap(), + table_row.get(&Context::GROUP_COLUMN_NAME).unwrap(), &group.owner, ) .await @@ -344,7 +344,7 @@ generic_step_impl! { Numeric::NaN => panic!("Last answer in NaN while expected answer is not."), }; let expected_value: f64 = - table_row.get(&Context::VALUE_COLUMN_NAME.to_string()).unwrap().parse().unwrap(); + table_row.get(&Context::VALUE_COLUMN_NAME).unwrap().parse().unwrap(); if equals_approximate(answer, expected_value) { matched_rows += 1; break; @@ -360,14 +360,14 @@ generic_step_impl! { } #[step(expr = "rules contain: {label}")] - async fn rules_contain(context: &mut Context, rule_label: String) { - let res = context.transaction().logic().get_rule(rule_label).await; + async fn rules_contain(context: &mut Context, rule_label: LabelParam) { + let res = context.transaction().logic().get_rule(rule_label.name).await; assert!(res.is_ok(), "{res:?}"); } #[step(expr = "rules do not contain: {label}")] - async fn rules_do_not_contain(context: &mut Context, rule_label: String) { - let res = context.transaction().logic().get_rule(rule_label).await; + async fn rules_do_not_contain(context: &mut Context, rule_label: LabelParam) { + let res = context.transaction().logic().get_rule(rule_label.name).await; assert!(res.is_err(), "{res:?}"); } diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index 357c81c0..eadc3354 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -43,14 +43,14 @@ pub fn iter_table(step: &Step) -> impl Iterator { step.table().unwrap().rows.iter().flatten().map(String::as_str) } -pub fn iter_table_map(step: &Step) -> impl Iterator> { +pub fn iter_table_map(step: &Step) -> impl Iterator> { let (keys, rows) = step.table().unwrap().rows.split_first().unwrap(); - rows.iter().map(|row| keys.iter().zip(row).collect()) + rows.iter().map(|row| keys.iter().map(String::as_str).zip(row.into_iter().map(String::as_str)).collect()) } pub async fn match_answer_concept_map( context: &Context, - answer_identifiers: &HashMap<&String, &String>, + answer_identifiers: &HashMap<&str, &str>, answer: &ConceptMap, ) -> bool { stream::iter(answer_identifiers.keys()) @@ -61,7 +61,7 @@ pub async fn match_answer_concept_map( .await } -pub async fn match_answer_concept(context: &Context, answer_identifier: &String, answer: &Concept) -> bool { +pub async fn match_answer_concept(context: &Context, answer_identifier: &str, answer: &Concept) -> bool { let identifiers: Vec<&str> = answer_identifier.splitn(2, ":").collect(); match identifiers[0] { "key" => key_values_equal(context, identifiers[1], answer).await, @@ -176,17 +176,13 @@ fn get_iid(concept: &Concept) -> String { iid.to_string() } -pub async fn match_answer_rule(answer_identifiers: &HashMap<&String, &String>, answer: &Rule) -> bool { - let when_clause = answer_identifiers.get(&String::from("when")).unwrap().trim_end_matches(";").to_string(); +pub async fn match_answer_rule(answer_identifiers: &HashMap<&str, &str>, answer: &Rule) -> bool { + let when_clause = answer_identifiers.get("when").unwrap().trim_end_matches(";").to_string(); let when = parse_pattern(when_clause.as_str()).unwrap().into_conjunction(); - let then_clause = answer_identifiers - .get(&String::from("then")) - .unwrap() - .trim_end_matches(['}', ';', ' ']) - .trim_start_matches("{") - .to_string(); + let then_clause = + answer_identifiers.get("then").unwrap().trim_end_matches(['}', ';', ' ']).trim_start_matches("{").to_string(); let then = parse_pattern(then_clause.as_str()).unwrap().into_variable(); - answer_identifiers.get(&String::from("label")).unwrap().to_string() == answer.label + answer_identifiers.get("label").unwrap().to_string() == answer.label && when == answer.when && then == Variable::Thing(answer.then.clone()) } From 59f60222d8649f5b4485c57344cb3cda3f33011b Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 20 Jun 2023 17:44:23 +0300 Subject: [PATCH 55/64] Remove redundant map_err --- tests/integration/logic.rs | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index 1f8437f9..bb699321 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -62,11 +62,9 @@ test_for_each_arg! { let transaction = session.transaction(Write).await?; transaction.logic().put_rule( "marriage-is-friendship".to_string(), - typeql_lang::parse_pattern("{ $x isa person; $y isa person; (husband: $x, wife: $y) isa marriage; }") - .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))? + typeql_lang::parse_pattern("{ $x isa person; $y isa person; (husband: $x, wife: $y) isa marriage; }")? .into_conjunction(), - typeql_lang::parse_variable("(friend: $x, friend: $y) isa friendship") - .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))?, + typeql_lang::parse_variable("(friend: $x, friend: $y) isa friendship")?, ).await?; transaction.commit().await?; } @@ -127,11 +125,9 @@ test_for_each_arg! { let transaction = session.transaction(Write).await?; transaction.logic().put_rule( "marriage-is-friendship".to_string(), - typeql_lang::parse_pattern("{ $x isa person; $y isa person; (husband: $x, wife: $y) isa marriage; }") - .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))? + typeql_lang::parse_pattern("{ $x isa person; $y isa person; (husband: $x, wife: $y) isa marriage; }")? .into_conjunction(), - typeql_lang::parse_variable("(friend: $x, friend: $y) isa friendship") - .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))?, + typeql_lang::parse_variable("(friend: $x, friend: $y) isa friendship")?, ).await?; transaction.commit().await?; } @@ -182,19 +178,15 @@ test_for_each_arg! { let transaction = session.transaction(Write).await?; transaction.logic().put_rule( "marriage-is-friendship".to_string(), - typeql_lang::parse_pattern("{ $x isa person; $y isa person; (husband: $x, wife: $y) isa marriage; }") - .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))? + typeql_lang::parse_pattern("{ $x isa person; $y isa person; (husband: $x, wife: $y) isa marriage; }")? .into_conjunction(), - typeql_lang::parse_variable("(friend: $x, friend: $y) isa friendship") - .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))?, + typeql_lang::parse_variable("(friend: $x, friend: $y) isa friendship")?, ).await?; transaction.logic().put_rule( "everyone-is-friends".to_string(), - typeql_lang::parse_pattern("{ $x isa person; $y isa person; not { $x is $y; }; }") - .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))? + typeql_lang::parse_pattern("{ $x isa person; $y isa person; not { $x is $y; }; }")? .into_conjunction(), - typeql_lang::parse_variable("(friend: $x, friend: $y) isa friendship") - .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))?, + typeql_lang::parse_variable("(friend: $x, friend: $y) isa friendship")?, ).await?; transaction.commit().await?; } @@ -240,19 +232,15 @@ test_for_each_arg! { let transaction = session.transaction(Write).await?; transaction.logic().put_rule( "old-milk-is-not-good".to_string(), - typeql_lang::parse_pattern("{ $x isa milk, has age-in-days <= 10; }") - .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))? + typeql_lang::parse_pattern("{ $x isa milk, has age-in-days <= 10; }")? .into_conjunction(), - typeql_lang::parse_variable("$x has is-still-good true") - .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))?, + typeql_lang::parse_variable("$x has is-still-good true")?, ).await?; transaction.logic().put_rule( "all-milk-is-good".to_string(), - typeql_lang::parse_pattern("{ $x isa milk; }") - .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))? + typeql_lang::parse_pattern("{ $x isa milk; }")? .into_conjunction(), - typeql_lang::parse_variable("$x has is-still-good true") - .map_err(|err| typedb_client::Error::Other(format!("{err:?}")))?, + typeql_lang::parse_variable("$x has is-still-good true")?, ).await?; transaction.commit().await?; } From 38d5a7651540374875bdb90d4f2e83b7db8c6cb7 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 20 Jun 2023 18:09:30 +0300 Subject: [PATCH 56/64] Refactor --- tests/integration/logic.rs | 56 ++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index bb699321..d1ce1ad2 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -88,19 +88,16 @@ test_for_each_arg! { assert_eq!(3, answers.len()); - let mut without_explainable = answers.get(0).unwrap(); - let mut with_explainable = answers.get(1).unwrap(); - if without_explainable.map.contains_key("p2") { - (without_explainable, with_explainable) = (with_explainable, without_explainable); - }; - - assert_eq!(3, with_explainable.map.len()); - assert_eq!(2, without_explainable.map.len()); - - assert!(!with_explainable.explainables.is_empty()); - assert!(without_explainable.explainables.is_empty()); - - assert_single_explainable_explanations(with_explainable, 1, &transaction).await; + for answer in answers { + if answer.map.contains_key("p2") { + assert_eq!(3, answer.map.len()); + assert!(!answer.explainables.is_empty()); + assert_single_explainable_explanations(&answer, 1, &transaction).await; + } else { + assert_eq!(2, answer.map.len()); + assert!(answer.explainables.is_empty()); + } + } Ok(()) } @@ -149,11 +146,10 @@ test_for_each_arg! { assert_eq!(2, answers.len()); - assert!(!answers.get(0).unwrap().explainables.is_empty()); - assert!(!answers.get(1).unwrap().explainables.is_empty()); - - assert_single_explainable_explanations(answers.get(0).unwrap(), 1, &transaction).await; - assert_single_explainable_explanations(answers.get(1).unwrap(), 1, &transaction).await; + for answer in answers { + assert!(!answer.explainables.is_empty()); + assert_single_explainable_explanations(&answer, 1, &transaction).await; + } Ok(()) } @@ -208,11 +204,10 @@ test_for_each_arg! { assert_eq!(2, answers.len()); - assert!(!answers.get(0).unwrap().explainables.is_empty()); - assert!(!answers.get(1).unwrap().explainables.is_empty()); - - assert_single_explainable_explanations(answers.get(0).unwrap(), 3, &transaction).await; - assert_single_explainable_explanations(answers.get(1).unwrap(), 3, &transaction).await; + for answer in answers { + assert!(!answer.explainables.is_empty()); + assert_single_explainable_explanations(&answer, 3, &transaction).await; + } Ok(()) } @@ -264,22 +259,19 @@ test_for_each_arg! { assert_eq!(3, answers.len()); - assert!(!answers.get(0).unwrap().explainables.is_empty()); - assert!(!answers.get(1).unwrap().explainables.is_empty()); - assert!(!answers.get(2).unwrap().explainables.is_empty()); - let age_in_days = transaction.concept().get_attribute_type(String::from("age-in-days")).await?.unwrap(); - for ans in answers { - match ans.map.get("x").unwrap() { + for answer in answers { + assert!(!answer.explainables.is_empty()); + match answer.map.get("x").unwrap() { Concept::Entity(entity) => { let attributes: Vec = entity.get_has(&transaction, vec![age_in_days.clone()], vec![])?.try_collect().await?; if attributes.first().unwrap().value == Value::Long(15) { - assert_single_explainable_explanations(&ans, 1, &transaction).await; + assert_single_explainable_explanations(&answer, 1, &transaction).await; } else { - assert_single_explainable_explanations(&ans, 2, &transaction).await; + assert_single_explainable_explanations(&answer, 2, &transaction).await; } }, - _ => panic!("Incorrect Concept type: {:?}", ans.map.get("x").unwrap()), + _ => panic!("Incorrect Concept type: {:?}", answer.map.get("x").unwrap()), } } From a9854e2a4637852d99f1c83f792053ef09366c8b Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Tue, 20 Jun 2023 19:29:03 +0300 Subject: [PATCH 57/64] Refactor --- tests/integration/logic.rs | 77 +++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/tests/integration/logic.rs b/tests/integration/logic.rs index d1ce1ad2..a29375d3 100644 --- a/tests/integration/logic.rs +++ b/tests/integration/logic.rs @@ -24,10 +24,11 @@ use std::{collections::HashMap, default::Default}; use futures::TryStreamExt; use serial_test::serial; use typedb_client::{ - answer::ConceptMap, + answer::{ConceptMap, Explainable}, concept::{Attribute, Concept, Value}, + logic::Explanation, transaction::concept::api::ThingAPI, - Connection, DatabaseManager, Options, Session, + Connection, DatabaseManager, Options, Result as TypeDBResult, Session, SessionType::{Data, Schema}, Transaction, TransactionType::{Read, Write}, @@ -42,7 +43,7 @@ test_for_each_arg! { cluster => common::new_cluster_connection().unwrap(), } - async fn test_disjunction_explainable(connection: Connection) -> typedb_client::Result { + async fn test_disjunction_explainable(connection: Connection) -> TypeDBResult { let schema = r#"define person sub entity, owns name, @@ -92,7 +93,7 @@ test_for_each_arg! { if answer.map.contains_key("p2") { assert_eq!(3, answer.map.len()); assert!(!answer.explainables.is_empty()); - assert_single_explainable_explanations(&answer, 1, &transaction).await; + assert_explanations_count_and_projection_match(&answer, 1, &transaction).await?; } else { assert_eq!(2, answer.map.len()); assert!(answer.explainables.is_empty()); @@ -102,7 +103,7 @@ test_for_each_arg! { Ok(()) } - async fn test_relation_explainable(connection: Connection) -> typedb_client::Result { + async fn test_relation_explainable(connection: Connection) -> TypeDBResult { let schema = r#"define person sub entity, owns name, @@ -148,13 +149,13 @@ test_for_each_arg! { for answer in answers { assert!(!answer.explainables.is_empty()); - assert_single_explainable_explanations(&answer, 1, &transaction).await; + assert_explanations_count_and_projection_match(&answer, 1, &transaction).await?; } Ok(()) } - async fn test_relation_explainable_multiple_ways(connection: Connection) -> typedb_client::Result { + async fn test_relation_explainable_multiple_ways(connection: Connection) -> TypeDBResult { let schema = r#"define person sub entity, owns name, @@ -206,13 +207,13 @@ test_for_each_arg! { for answer in answers { assert!(!answer.explainables.is_empty()); - assert_single_explainable_explanations(&answer, 3, &transaction).await; + assert_explanations_count_and_projection_match(&answer, 3, &transaction).await?; } Ok(()) } - async fn test_has_explicit_explainable_two_ways(connection: Connection) -> typedb_client::Result { + async fn test_has_explicit_explainable_two_ways(connection: Connection) -> TypeDBResult { let schema = r#"define milk sub entity, owns age-in-days, @@ -266,9 +267,9 @@ test_for_each_arg! { Concept::Entity(entity) => { let attributes: Vec = entity.get_has(&transaction, vec![age_in_days.clone()], vec![])?.try_collect().await?; if attributes.first().unwrap().value == Value::Long(15) { - assert_single_explainable_explanations(&answer, 1, &transaction).await; + assert_explanations_count_and_projection_match(&answer, 1, &transaction).await?; } else { - assert_single_explainable_explanations(&answer, 2, &transaction).await; + assert_explanations_count_and_projection_match(&answer, 2, &transaction).await?; } }, _ => panic!("Incorrect Concept type: {:?}", answer.map.get("x").unwrap()), @@ -279,29 +280,46 @@ test_for_each_arg! { } } -async fn assert_single_explainable_explanations( +async fn assert_explanations_count_and_projection_match( ans: &ConceptMap, explanations_count: usize, transaction: &Transaction<'_>, -) { +) -> TypeDBResult { assert_explainables_in_concept_map(ans); + let explainables = all_explainables(ans); + assert_eq!(1, explainables.len()); - let explainables = &ans.explainables; - let mut all_explainables = explainables.attributes.values().collect::>(); - all_explainables.extend(explainables.relations.values().collect::>()); - all_explainables.extend(explainables.ownerships.values().collect::>()); - assert_eq!(1, all_explainables.len()); + let explanations = get_explanations(explainables[0], transaction).await?; + assert_eq!(explanations_count, explanations.len()); - let explainable = all_explainables.get(0).unwrap(); - let stream = transaction.query().explain(explainable.id); - assert!(stream.is_ok()); + assert_explanation_concepts_match_projection(ans, explanations); + Ok(()) +} - let result = stream.unwrap().try_collect::>().await; - assert!(result.is_ok()); +fn assert_explainables_in_concept_map(ans: &ConceptMap) { + ans.explainables.relations.keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); + ans.explainables.attributes.keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); + ans.explainables + .ownerships + .keys() + .for_each(|(k1, k2)| assert!(ans.map.contains_key(k1.as_str()) && ans.map.contains_key(k2.as_str()))); +} - let explanations = result.unwrap(); - assert_eq!(explanations_count, explanations.len()); +fn all_explainables(ans: &ConceptMap) -> Vec<&Explainable> { + let explainables = &ans.explainables; + explainables + .attributes + .values() + .chain(explainables.relations.values()) + .chain(explainables.ownerships.values()) + .collect::>() +} + +async fn get_explanations(explainable: &Explainable, transaction: &Transaction<'_>) -> TypeDBResult> { + transaction.query().explain(explainable.id)?.try_collect::>().await +} +fn assert_explanation_concepts_match_projection(ans: &ConceptMap, explanations: Vec) { for explanation in explanations { let mapping = explanation.variable_mapping; let projected = apply_mapping(&mapping, ans); @@ -312,15 +330,6 @@ async fn assert_single_explainable_explanations( } } -fn assert_explainables_in_concept_map(ans: &ConceptMap) { - ans.explainables.relations.keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); - ans.explainables.attributes.keys().for_each(|k| assert!(ans.map.contains_key(k.as_str()))); - ans.explainables - .ownerships - .keys() - .for_each(|(k1, k2)| assert!(ans.map.contains_key(k1.as_str()) && ans.map.contains_key(k2.as_str()))); -} - fn apply_mapping(mapping: &HashMap>, complete_map: &ConceptMap) -> ConceptMap { let mut concepts: HashMap = HashMap::new(); for key in mapping.keys() { From 6a7ef99c42f32a8e148600093c93aa0157cedc17 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 21 Jun 2023 11:49:18 +0300 Subject: [PATCH 58/64] Remove trimming --- tests/behaviour/util.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index eadc3354..30f2d9de 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -35,7 +35,7 @@ use typedb_client::{ transaction::concept::api::ThingAPI, Result as TypeDBResult, }; -use typeql_lang::{parse_pattern, parse_query, pattern::Variable}; +use typeql_lang::{parse_patterns, parse_query, pattern::Variable}; use crate::behaviour::Context; @@ -177,11 +177,11 @@ fn get_iid(concept: &Concept) -> String { } pub async fn match_answer_rule(answer_identifiers: &HashMap<&str, &str>, answer: &Rule) -> bool { - let when_clause = answer_identifiers.get("when").unwrap().trim_end_matches(";").to_string(); - let when = parse_pattern(when_clause.as_str()).unwrap().into_conjunction(); + let when_clause = answer_identifiers.get("when").unwrap().to_string(); + let when = parse_patterns(when_clause.as_str()).unwrap()[0].clone().into_conjunction(); let then_clause = - answer_identifiers.get("then").unwrap().trim_end_matches(['}', ';', ' ']).trim_start_matches("{").to_string(); - let then = parse_pattern(then_clause.as_str()).unwrap().into_variable(); + answer_identifiers.get("then").unwrap().to_string(); + let then = parse_patterns(then_clause.as_str()).unwrap()[0].clone().into_variable(); answer_identifiers.get("label").unwrap().to_string() == answer.label && when == answer.when && then == Variable::Thing(answer.then.clone()) From 7506accbcf0f4780bb0ff3358be4a6d711a72b88 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 21 Jun 2023 12:14:22 +0300 Subject: [PATCH 59/64] Refactor --- src/connection/network/proto/concept.rs | 6 +----- tests/behaviour/util.rs | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/connection/network/proto/concept.rs b/src/connection/network/proto/concept.rs index 13740ef5..af81c17d 100644 --- a/src/connection/network/proto/concept.rs +++ b/src/connection/network/proto/concept.rs @@ -400,11 +400,7 @@ impl FromProto for Explainable { impl TryFromProto for Explanation { fn try_from_proto(proto: ExplanationProto) -> Result { let ExplanationProto { rule, conclusion, condition, var_mapping } = proto; - let mut variable_mapping = HashMap::with_capacity(var_mapping.len()); - for (k, v) in var_mapping { - variable_mapping.insert(k, v.vars); - } - + let variable_mapping = var_mapping.iter().map(|(k, v)| (k.clone(), v.clone().vars)).collect(); Ok(Self { rule: Rule::try_from_proto(rule.ok_or(ConnectionError::MissingResponseField("rule"))?)?, conclusion: ConceptMap::try_from_proto( diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index 30f2d9de..9789c3b3 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -45,7 +45,7 @@ pub fn iter_table(step: &Step) -> impl Iterator { pub fn iter_table_map(step: &Step) -> impl Iterator> { let (keys, rows) = step.table().unwrap().rows.split_first().unwrap(); - rows.iter().map(|row| keys.iter().map(String::as_str).zip(row.into_iter().map(String::as_str)).collect()) + rows.iter().map(|row| keys.iter().zip(row).map(|(k, v)| (k.as_str(), v.as_str())).collect()) } pub async fn match_answer_concept_map( From cc7ca76b528a0fd32b1e539d8158c38762986771 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 21 Jun 2023 12:18:51 +0300 Subject: [PATCH 60/64] fmt --- tests/behaviour/util.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/behaviour/util.rs b/tests/behaviour/util.rs index 9789c3b3..d046a963 100644 --- a/tests/behaviour/util.rs +++ b/tests/behaviour/util.rs @@ -179,8 +179,7 @@ fn get_iid(concept: &Concept) -> String { pub async fn match_answer_rule(answer_identifiers: &HashMap<&str, &str>, answer: &Rule) -> bool { let when_clause = answer_identifiers.get("when").unwrap().to_string(); let when = parse_patterns(when_clause.as_str()).unwrap()[0].clone().into_conjunction(); - let then_clause = - answer_identifiers.get("then").unwrap().to_string(); + let then_clause = answer_identifiers.get("then").unwrap().to_string(); let then = parse_patterns(then_clause.as_str()).unwrap()[0].clone().into_variable(); answer_identifiers.get("label").unwrap().to_string() == answer.label && when == answer.when From fd0cc10e5c6208ff5110dc627e83cc895cf187b7 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Wed, 21 Jun 2023 12:31:24 +0300 Subject: [PATCH 61/64] Simplification --- src/connection/network/proto/concept.rs | 4 ++-- tests/behaviour/typeql/steps.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/connection/network/proto/concept.rs b/src/connection/network/proto/concept.rs index af81c17d..a5f16420 100644 --- a/src/connection/network/proto/concept.rs +++ b/src/connection/network/proto/concept.rs @@ -101,8 +101,8 @@ impl TryFromProto for ConceptMap { fn try_from_proto(proto: ConceptMapProto) -> Result { let ConceptMapProto { map: map_proto, explainables: explainables_proto } = proto; let map = map_proto.into_iter().map(|(k, v)| Concept::try_from_proto(v).map(|v| (k, v))).try_collect()?; - let explainables = explainables_proto - .ok_or::(ConnectionError::MissingResponseField("explainables").into())?; + let explainables = + explainables_proto.ok_or::(ConnectionError::MissingResponseField("explainables"))?; Ok(Self { map, explainables: Explainables::from_proto(explainables) }) } } diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/steps.rs index 3c919af1..e3ef8935 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/steps.rs @@ -288,13 +288,13 @@ generic_step_impl! { for table_row in &step_table { if match_answer_concept( context, - table_row.get(&Context::GROUP_COLUMN_NAME).unwrap(), + table_row.get(Context::GROUP_COLUMN_NAME).unwrap(), &group.owner, ) .await { let mut table_row_wo_owner = table_row.clone(); - table_row_wo_owner.remove(&Context::GROUP_COLUMN_NAME); + table_row_wo_owner.remove(Context::GROUP_COLUMN_NAME); if match_answer_concept_map(context, &table_row_wo_owner, &ans_row).await { matched_rows += 1; break; @@ -333,7 +333,7 @@ generic_step_impl! { for table_row in &step_table { if match_answer_concept( context, - table_row.get(&Context::GROUP_COLUMN_NAME).unwrap(), + table_row.get(Context::GROUP_COLUMN_NAME).unwrap(), &group.owner, ) .await @@ -344,7 +344,7 @@ generic_step_impl! { Numeric::NaN => panic!("Last answer in NaN while expected answer is not."), }; let expected_value: f64 = - table_row.get(&Context::VALUE_COLUMN_NAME).unwrap().parse().unwrap(); + table_row.get(Context::VALUE_COLUMN_NAME).unwrap().parse().unwrap(); if equals_approximate(answer, expected_value) { matched_rows += 1; break; From 006972c7cef63cd0114a4f4535b4c3117c8ad700 Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 22 Jun 2023 12:26:56 +0300 Subject: [PATCH 62/64] TransactionRequest reordering --- src/connection/message.rs | 2 +- src/connection/network/proto/message.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/connection/message.rs b/src/connection/message.rs index 0b508987..30da85d7 100644 --- a/src/connection/message.rs +++ b/src/connection/message.rs @@ -109,9 +109,9 @@ pub(super) enum TransactionRequest { ThingType(ThingTypeRequest), RoleType(RoleTypeRequest), Thing(ThingRequest), - Stream { request_id: RequestID }, Rule(RuleRequest), Logic(LogicRequest), + Stream { request_id: RequestID }, } #[derive(Debug)] diff --git a/src/connection/network/proto/message.rs b/src/connection/network/proto/message.rs index 196d1654..493b94ff 100644 --- a/src/connection/network/proto/message.rs +++ b/src/connection/network/proto/message.rs @@ -267,12 +267,12 @@ impl IntoProto for TransactionRequest { Self::ThingType(thing_type_request) => transaction::req::Req::TypeReq(thing_type_request.into_proto()), Self::RoleType(role_type_request) => transaction::req::Req::TypeReq(role_type_request.into_proto()), Self::Thing(thing_request) => transaction::req::Req::ThingReq(thing_request.into_proto()), + Self::Rule(rule_request) => transaction::req::Req::RuleReq(rule_request.into_proto()), + Self::Logic(logic_request) => transaction::req::Req::LogicManagerReq(logic_request.into_proto()), Self::Stream { request_id: req_id } => { request_id = Some(req_id); transaction::req::Req::StreamReq(transaction::stream::Req {}) } - Self::Rule(rule_request) => transaction::req::Req::RuleReq(rule_request.into_proto()), - Self::Logic(logic_request) => transaction::req::Req::LogicManagerReq(logic_request.into_proto()), }; transaction::Req { From f6ec58d9172af581906103481dfc8e7de9c306bd Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 22 Jun 2023 13:30:01 +0300 Subject: [PATCH 63/64] Tests refactoring --- tests/behaviour/connection/database/mod.rs | 2 +- tests/behaviour/connection/database/steps.rs | 2 +- tests/behaviour/connection/mod.rs | 8 +- tests/behaviour/connection/session/mod.rs | 2 +- tests/behaviour/connection/session/steps.rs | 6 +- tests/behaviour/connection/transaction/mod.rs | 2 +- .../behaviour/connection/transaction/steps.rs | 4 +- tests/behaviour/mod.rs | 4 +- tests/behaviour/typeql/language/mod.rs | 1 + .../behaviour/typeql/{ => language}/steps.rs | 84 +------------- tests/behaviour/typeql/mod.rs | 1 - tests/behaviour/typeql/reasoner/mod.rs | 1 + tests/behaviour/typeql/reasoner/steps.rs | 108 ++++++++++++++++++ 13 files changed, 130 insertions(+), 95 deletions(-) rename tests/behaviour/typeql/{ => language}/steps.rs (82%) create mode 100644 tests/behaviour/typeql/reasoner/steps.rs diff --git a/tests/behaviour/connection/database/mod.rs b/tests/behaviour/connection/database/mod.rs index 5683bb7e..ac57625c 100644 --- a/tests/behaviour/connection/database/mod.rs +++ b/tests/behaviour/connection/database/mod.rs @@ -19,7 +19,7 @@ * under the License. */ -mod steps; +pub(crate) mod steps; use serial_test::serial; diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index 2cfc560f..56f82e44 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -32,7 +32,7 @@ use crate::{ generic_step_impl! { #[step(expr = "connection create database: {word}")] - async fn connection_create_database(context: &mut Context, name: String) { + pub async fn connection_create_database(context: &mut Context, name: String) { context.databases.create(name).await.unwrap(); } diff --git a/tests/behaviour/connection/mod.rs b/tests/behaviour/connection/mod.rs index f343d1d1..2ab68731 100644 --- a/tests/behaviour/connection/mod.rs +++ b/tests/behaviour/connection/mod.rs @@ -19,7 +19,7 @@ * under the License. */ -mod database; -mod session; -mod steps; -mod transaction; +pub(crate) mod database; +pub(crate) mod session; +pub(crate) mod steps; +pub(crate) mod transaction; diff --git a/tests/behaviour/connection/session/mod.rs b/tests/behaviour/connection/session/mod.rs index aa456e65..5c37d274 100644 --- a/tests/behaviour/connection/session/mod.rs +++ b/tests/behaviour/connection/session/mod.rs @@ -19,7 +19,7 @@ * under the License. */ -mod steps; +pub(crate) mod steps; use serial_test::serial; diff --git a/tests/behaviour/connection/session/steps.rs b/tests/behaviour/connection/session/steps.rs index 77fcf5a9..358b9759 100644 --- a/tests/behaviour/connection/session/steps.rs +++ b/tests/behaviour/connection/session/steps.rs @@ -30,14 +30,14 @@ use crate::{ generic_step_impl! { #[step(expr = "connection open schema session for database: {word}")] - async fn connection_open_schema_session_for_database(context: &mut Context, name: String) { + pub async fn connection_open_schema_session_for_database(context: &mut Context, name: String) { context .session_trackers .push(Session::new(context.databases.get(name).await.unwrap(), SessionType::Schema).await.unwrap().into()); } #[step(expr = "connection open (data )session for database: {word}")] - async fn connection_open_data_session_for_database(context: &mut Context, name: String) { + pub async fn connection_open_data_session_for_database(context: &mut Context, name: String) { context .session_trackers .push(Session::new(context.databases.get(name).await.unwrap(), SessionType::Data).await.unwrap().into()); @@ -73,7 +73,7 @@ generic_step_impl! { } #[step("connection close all sessions")] - async fn connection_close_all_sessions(context: &mut Context) { + pub async fn connection_close_all_sessions(context: &mut Context) { context.session_trackers.clear(); } diff --git a/tests/behaviour/connection/transaction/mod.rs b/tests/behaviour/connection/transaction/mod.rs index 391652e1..87789a05 100644 --- a/tests/behaviour/connection/transaction/mod.rs +++ b/tests/behaviour/connection/transaction/mod.rs @@ -19,7 +19,7 @@ * under the License. */ -mod steps; +pub(crate) mod steps; use serial_test::serial; diff --git a/tests/behaviour/connection/transaction/steps.rs b/tests/behaviour/connection/transaction/steps.rs index 928d08ca..e32e569c 100644 --- a/tests/behaviour/connection/transaction/steps.rs +++ b/tests/behaviour/connection/transaction/steps.rs @@ -33,7 +33,7 @@ generic_step_impl! { // =============================================// #[step(expr = "(for each )session(,) open(s) transaction(s) of type: {transaction_type}")] - async fn session_opens_transaction_of_type(context: &mut Context, type_: TransactionTypeParam) { + pub async fn session_opens_transaction_of_type(context: &mut Context, type_: TransactionTypeParam) { for session_tracker in &mut context.session_trackers { session_tracker.open_transaction(type_.transaction_type).await.unwrap(); } @@ -88,7 +88,7 @@ generic_step_impl! { } #[step(expr = "transaction commits")] - async fn transaction_commits(context: &mut Context) { + pub async fn transaction_commits(context: &mut Context) { context.take_transaction().commit().await.unwrap(); } diff --git a/tests/behaviour/mod.rs b/tests/behaviour/mod.rs index 65f1e106..e87c6607 100644 --- a/tests/behaviour/mod.rs +++ b/tests/behaviour/mod.rs @@ -182,13 +182,13 @@ impl Default for Context { #[macro_export] macro_rules! generic_step_impl { - {$($(#[step($pattern:expr)])+ $async:ident fn $fn_name:ident $args:tt $(-> $res:ty)? $body:block)+} => { + {$($(#[step($pattern:expr)])+ $vis:vis $async:ident fn $fn_name:ident $args:tt $(-> $res:ty)? $body:block)+} => { $($( #[given($pattern)] #[when($pattern)] #[then($pattern)] )* - $async fn $fn_name $args $(-> $res)? $body + $vis $async fn $fn_name $args $(-> $res)? $body )* }; } diff --git a/tests/behaviour/typeql/language/mod.rs b/tests/behaviour/typeql/language/mod.rs index 474aaed7..8bc83b90 100644 --- a/tests/behaviour/typeql/language/mod.rs +++ b/tests/behaviour/typeql/language/mod.rs @@ -25,5 +25,6 @@ mod get; mod insert; mod match_; mod rule_validation; +pub(crate) mod steps; mod undefine; mod update; diff --git a/tests/behaviour/typeql/steps.rs b/tests/behaviour/typeql/language/steps.rs similarity index 82% rename from tests/behaviour/typeql/steps.rs rename to tests/behaviour/typeql/language/steps.rs index e3ef8935..67c68edb 100644 --- a/tests/behaviour/typeql/steps.rs +++ b/tests/behaviour/typeql/language/steps.rs @@ -21,7 +21,7 @@ use cucumber::{gherkin::Step, given, then, when}; use futures::TryStreamExt; -use typedb_client::{answer::Numeric, Result as TypeDBResult, Session, SessionType, TransactionType}; +use typedb_client::{answer::Numeric, Result as TypeDBResult}; use typeql_lang::parse_query; use util::{ equals_approximate, iter_table_map, match_answer_concept, match_answer_concept_map, match_answer_rule, @@ -36,7 +36,7 @@ use crate::{ generic_step_impl! { #[step(expr = "typeql define")] - async fn typeql_define(context: &mut Context, step: &Step) -> TypeDBResult { + pub async fn typeql_define(context: &mut Context, step: &Step) -> TypeDBResult { let parsed = parse_query(step.docstring().unwrap())?; context.transaction().query().define(&parsed.to_string()).await } @@ -63,7 +63,7 @@ generic_step_impl! { } #[step(expr = "typeql insert")] - async fn typeql_insert(context: &mut Context, step: &Step) -> TypeDBResult { + pub async fn typeql_insert(context: &mut Context, step: &Step) -> TypeDBResult { let parsed = parse_query(step.docstring().unwrap())?; context.transaction().query().insert(&parsed.to_string())?.try_collect::>().await?; Ok(()) @@ -103,7 +103,7 @@ generic_step_impl! { } #[step(expr = "get answers of typeql match")] - async fn get_answers_typeql_match(context: &mut Context, step: &Step) -> TypeDBResult { + pub async fn get_answers_typeql_match(context: &mut Context, step: &Step) -> TypeDBResult { let parsed = parse_query(step.docstring().unwrap())?; context.answer = context.transaction().query().match_(&parsed.to_string())?.try_collect::>().await?; Ok(()) @@ -117,8 +117,7 @@ generic_step_impl! { } #[step(expr = "answer size is: {int}")] - #[step(expr = "verify answer size is: {int}")] - async fn answer_size(context: &mut Context, expected_answers: usize) { + pub async fn answer_size(context: &mut Context, expected_answers: usize) { let actual_answers = context.answer.len(); assert_eq!( actual_answers, expected_answers, @@ -401,77 +400,4 @@ generic_step_impl! { matched entries of given {actual_answers}." ); } - - #[step(expr = "reasoning schema")] - async fn reasoning_schema(context: &mut Context, step: &Step) { - if context.databases.all().await.unwrap().is_empty() { - context.databases.create(Context::DEFAULT_DATABASE).await.unwrap(); - } - context - .session_trackers - .push(Session::new(context.databases.get(Context::DEFAULT_DATABASE).await.unwrap(), SessionType::Schema).await.unwrap().into()); - for session_tracker in &mut context.session_trackers { - session_tracker.open_transaction(TransactionType::Write).await.unwrap(); - } - assert!(typeql_define(context, step).await.is_ok()); - context.take_transaction().commit().await.unwrap(); - context.session_trackers.clear(); - } - - #[step(expr = "reasoning data")] - async fn reasoning_data(context: &mut Context, step: &Step) { - context - .session_trackers - .push(Session::new(context.databases.get(Context::DEFAULT_DATABASE).await.unwrap(), SessionType::Data).await.unwrap().into()); - for session_tracker in &mut context.session_trackers { - session_tracker.open_transaction(TransactionType::Write).await.unwrap(); - } - assert!(typeql_insert(context, step).await.is_ok()); - context.take_transaction().commit().await.unwrap(); - context.session_trackers.clear(); - } - - #[step(expr = "reasoning query")] - async fn reasoning_query(context: &mut Context, step: &Step) { - context - .session_trackers - .push(Session::new(context.databases.get(Context::DEFAULT_DATABASE).await.unwrap(), SessionType::Data).await.unwrap().into()); - for session_tracker in &mut context.session_trackers { - session_tracker.open_transaction(TransactionType::Read).await.unwrap(); - } - assert!(get_answers_typeql_match(context, step).await.is_ok()); - context.session_trackers.clear(); - } - - #[step(expr = "verify answer set is equivalent for query")] - async fn verify_answer_set_is_equivalent_for_query(context: &mut Context, step: &Step) { - let prev_answer = context.answer.clone(); - reasoning_query(context, step).await; - let total_rows = context.answer.len(); - let mut matched_rows = 0; - for row_curr in &context.answer { - for row_prev in &prev_answer { - if row_curr == row_prev { - matched_rows += 1; - break; - } - } - } - assert_eq!( - matched_rows, total_rows, - "There are only {matched_rows} matched entries of given {total_rows}." - ); - } - - #[step(expr = "verifier is initialised")] - #[step(expr = "verify answers are sound")] - #[step(expr = "verify answers are complete")] - async fn do_nothing(_context: &mut Context) { - // We don't have a verifier - } - - #[step(expr = "verify answers are consistent across {int} executions")] - async fn verify_answers_are_consistent_across_executions(_context: &mut Context, _executions: usize) { - // We can't execute previous query again because don't remember the query - } } diff --git a/tests/behaviour/typeql/mod.rs b/tests/behaviour/typeql/mod.rs index d15a52b5..f4051a0e 100644 --- a/tests/behaviour/typeql/mod.rs +++ b/tests/behaviour/typeql/mod.rs @@ -21,4 +21,3 @@ mod language; mod reasoner; -mod steps; diff --git a/tests/behaviour/typeql/reasoner/mod.rs b/tests/behaviour/typeql/reasoner/mod.rs index 6f01420b..c49cb72a 100644 --- a/tests/behaviour/typeql/reasoner/mod.rs +++ b/tests/behaviour/typeql/reasoner/mod.rs @@ -27,6 +27,7 @@ mod recursion; mod relation_inference; mod rule_interaction; mod schema_queries; +mod steps; mod type_hierarchy; mod value_predicate; mod variable_roles; diff --git a/tests/behaviour/typeql/reasoner/steps.rs b/tests/behaviour/typeql/reasoner/steps.rs new file mode 100644 index 00000000..1ea616bf --- /dev/null +++ b/tests/behaviour/typeql/reasoner/steps.rs @@ -0,0 +1,108 @@ +/* + * 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::TransactionType; + +use crate::{ + behaviour::{ + connection::{ + database::steps::connection_create_database, + session::steps::{ + connection_close_all_sessions, connection_open_data_session_for_database, + connection_open_schema_session_for_database, + }, + transaction::steps::{session_opens_transaction_of_type, transaction_commits}, + }, + parameter::TransactionTypeParam, + typeql::language::steps::{answer_size, get_answers_typeql_match, typeql_define, typeql_insert}, + Context, + }, + generic_step_impl, +}; + +generic_step_impl! { + #[step(expr = "reasoning schema")] + async fn reasoning_schema(context: &mut Context, step: &Step) { + if context.databases.all().await.unwrap().is_empty() { + connection_create_database(context, Context::DEFAULT_DATABASE.to_string()).await; + } + connection_open_schema_session_for_database(context, Context::DEFAULT_DATABASE.to_string()).await; + session_opens_transaction_of_type(context, TransactionTypeParam { transaction_type: TransactionType::Write }).await; + assert!(typeql_define(context, step).await.is_ok()); + transaction_commits(context).await; + connection_close_all_sessions(context).await; + } + + #[step(expr = "reasoning data")] + async fn reasoning_data(context: &mut Context, step: &Step) { + connection_open_data_session_for_database(context, Context::DEFAULT_DATABASE.to_string()).await; + session_opens_transaction_of_type(context, TransactionTypeParam { transaction_type: TransactionType::Write }).await; + assert!(typeql_insert(context, step).await.is_ok()); + transaction_commits(context).await; + connection_close_all_sessions(context).await; + } + + #[step(expr = "reasoning query")] + async fn reasoning_query(context: &mut Context, step: &Step) { + connection_open_data_session_for_database(context, Context::DEFAULT_DATABASE.to_string()).await; + session_opens_transaction_of_type(context, TransactionTypeParam { transaction_type: TransactionType::Read }).await; + assert!(get_answers_typeql_match(context, step).await.is_ok()); + connection_close_all_sessions(context).await; + } + + #[step(expr = "verify answer size is: {int}")] + async fn verify_answer_size(context: &mut Context, expected_answers: usize) { + answer_size(context, expected_answers).await; + } + + #[step(expr = "verify answer set is equivalent for query")] + async fn verify_answer_set_is_equivalent_for_query(context: &mut Context, step: &Step) { + let prev_answer = context.answer.clone(); + reasoning_query(context, step).await; + let total_rows = context.answer.len(); + let mut matched_rows = 0; + for row_curr in &context.answer { + for row_prev in &prev_answer { + if row_curr == row_prev { + matched_rows += 1; + break; + } + } + } + assert_eq!( + matched_rows, total_rows, + "There are only {matched_rows} matched entries of given {total_rows}." + ); + } + + #[step(expr = "verifier is initialised")] + #[step(expr = "verify answers are sound")] + #[step(expr = "verify answers are complete")] + async fn do_nothing(_context: &mut Context) { + // We don't have a verifier + } + + #[step(expr = "verify answers are consistent across {int} executions")] + async fn verify_answers_are_consistent_across_executions(_context: &mut Context, _executions: usize) { + // We can't execute previous query again because don't remember the query + } +} From 3f2bb57229660c64070c1864156c45ffcb3ac58d Mon Sep 17 00:00:00 2001 From: Dmitrii Mikhalin Date: Thu, 22 Jun 2023 14:07:34 +0300 Subject: [PATCH 64/64] Check only one db existance --- tests/behaviour/connection/database/steps.rs | 2 +- tests/behaviour/typeql/reasoner/steps.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/behaviour/connection/database/steps.rs b/tests/behaviour/connection/database/steps.rs index 56f82e44..ce39534a 100644 --- a/tests/behaviour/connection/database/steps.rs +++ b/tests/behaviour/connection/database/steps.rs @@ -49,7 +49,7 @@ generic_step_impl! { } #[step(expr = "connection delete database: {word}")] - async fn connection_delete_database(context: &mut Context, name: String) { + pub async fn connection_delete_database(context: &mut Context, name: String) { context.databases.get(name).and_then(Database::delete).await.unwrap(); } diff --git a/tests/behaviour/typeql/reasoner/steps.rs b/tests/behaviour/typeql/reasoner/steps.rs index 1ea616bf..eb74582e 100644 --- a/tests/behaviour/typeql/reasoner/steps.rs +++ b/tests/behaviour/typeql/reasoner/steps.rs @@ -42,7 +42,7 @@ use crate::{ generic_step_impl! { #[step(expr = "reasoning schema")] async fn reasoning_schema(context: &mut Context, step: &Step) { - if context.databases.all().await.unwrap().is_empty() { + if !context.databases.contains(Context::DEFAULT_DATABASE).await.unwrap() { connection_create_database(context, Context::DEFAULT_DATABASE.to_string()).await; } connection_open_schema_session_for_database(context, Context::DEFAULT_DATABASE.to_string()).await;