From b68a1814ead57eaf347148585f1b00af43395274 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Thu, 28 Jul 2022 22:40:38 +0100 Subject: [PATCH] Add initial integration tests for DEFINE statements --- lib/tests/define.rs | 667 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 667 insertions(+) create mode 100644 lib/tests/define.rs diff --git a/lib/tests/define.rs b/lib/tests/define.rs new file mode 100644 index 00000000..3d28afb7 --- /dev/null +++ b/lib/tests/define.rs @@ -0,0 +1,667 @@ +mod parse; +use parse::Parse; +use surrealdb::sql::Value; +use surrealdb::Datastore; +use surrealdb::Error; +use surrealdb::Session; + +#[tokio::test] +async fn define_statement_namespace() -> Result<(), Error> { + let sql = " + DEFINE NAMESPACE test; + INFO FOR KV; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 2); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ns: { test: 'DEFINE NAMESPACE test' }, + }", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_database() -> Result<(), Error> { + let sql = " + DEFINE DATABASE test; + INFO FOR NS; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 2); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + db: { test: 'DEFINE DATABASE test' }, + nl: {}, + nt: {}, + }", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_table_drop() -> Result<(), Error> { + let sql = " + DEFINE TABLE test DROP; + INFO FOR DB; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 2); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + dl: {}, + dt: {}, + sc: {}, + tb: { test: 'DEFINE TABLE test DROP SCHEMALESS' }, + }", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_table_schemaless() -> Result<(), Error> { + let sql = " + DEFINE TABLE test SCHEMALESS; + INFO FOR DB; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 2); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + dl: {}, + dt: {}, + sc: {}, + tb: { test: 'DEFINE TABLE test SCHEMALESS' }, + }", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_table_schemafull() -> Result<(), Error> { + let sql = " + DEFINE TABLE test SCHEMAFULL; + INFO FOR DB; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 2); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + dl: {}, + dt: {}, + sc: {}, + tb: { test: 'DEFINE TABLE test SCHEMAFULL' }, + }", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_event() -> Result<(), Error> { + let sql = " + DEFINE EVENT test ON user WHEN true THEN ( + CREATE activity SET user = $this, value = $after.email, action = $event + ); + DEFINE EVENT test ON TABLE user WHEN true THEN ( + CREATE activity SET user = $this, value = $after.email, action = $event + ); + INFO FOR TABLE user; + UPDATE user:test SET email = 'info@surrealdb.com', updated_at = time::now(); + UPDATE user:test SET email = 'info@surrealdb.com', updated_at = time::now(); + UPDATE user:test SET email = 'test@surrealdb.com', updated_at = time::now(); + SELECT count() FROM activity GROUP BY ALL; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 7); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ev: { test: 'DEFINE EVENT test ON user WHEN true THEN (CREATE activity SET user = $this, value = $after.email, action = $event)' }, + fd: {}, + ft: {}, + ix: {}, + }", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "[{ + count: 3 + }]", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_event_when_event() -> Result<(), Error> { + let sql = " + DEFINE EVENT test ON user WHEN $event = 'CREATE' THEN ( + CREATE activity SET user = $this, value = $after.email, action = $event + ); + DEFINE EVENT test ON TABLE user WHEN $event = 'CREATE' THEN ( + CREATE activity SET user = $this, value = $after.email, action = $event + ); + INFO FOR TABLE user; + UPDATE user:test SET email = 'info@surrealdb.com', updated_at = time::now(); + UPDATE user:test SET email = 'info@surrealdb.com', updated_at = time::now(); + UPDATE user:test SET email = 'test@surrealdb.com', updated_at = time::now(); + SELECT count() FROM activity GROUP BY ALL; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 7); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ev: { test: 'DEFINE EVENT test ON user WHEN $event = \"CREATE\" THEN (CREATE activity SET user = $this, value = $after.email, action = $event)' }, + fd: {}, + ft: {}, + ix: {}, + }", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "[{ + count: 1 + }]", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_event_when_logic() -> Result<(), Error> { + let sql = " + DEFINE EVENT test ON user WHEN $before.email != $after.email THEN ( + CREATE activity SET user = $this, value = $after.email, action = $event + ); + DEFINE EVENT test ON TABLE user WHEN $before.email != $after.email THEN ( + CREATE activity SET user = $this, value = $after.email, action = $event + ); + INFO FOR TABLE user; + UPDATE user:test SET email = 'info@surrealdb.com', updated_at = time::now(); + UPDATE user:test SET email = 'info@surrealdb.com', updated_at = time::now(); + UPDATE user:test SET email = 'test@surrealdb.com', updated_at = time::now(); + SELECT count() FROM activity GROUP BY ALL; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 7); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ev: { test: 'DEFINE EVENT test ON user WHEN $before.email != $after.email THEN (CREATE activity SET user = $this, value = $after.email, action = $event)' }, + fd: {}, + ft: {}, + ix: {}, + }", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "[{ + count: 2 + }]", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_field() -> Result<(), Error> { + let sql = " + DEFINE FIELD test ON user; + DEFINE FIELD test ON TABLE user; + INFO FOR TABLE user; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 3); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ev: {}, + fd: { test: 'DEFINE FIELD test ON user' }, + ft: {}, + ix: {}, + }", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_field_type() -> Result<(), Error> { + let sql = " + DEFINE FIELD test ON user TYPE string; + DEFINE FIELD test ON TABLE user TYPE string; + INFO FOR TABLE user; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 3); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ev: {}, + fd: { test: 'DEFINE FIELD test ON user TYPE string' }, + ft: {}, + ix: {}, + }", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_field_value() -> Result<(), Error> { + let sql = " + DEFINE FIELD test ON user VALUE $value OR 'GBR'; + DEFINE FIELD test ON TABLE user VALUE $value OR 'GBR'; + INFO FOR TABLE user; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 3); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ev: {}, + fd: { test: 'DEFINE FIELD test ON user VALUE $value OR \"GBR\"' }, + ft: {}, + ix: {}, + }", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_field_assert() -> Result<(), Error> { + let sql = " + DEFINE FIELD test ON user ASSERT $value != NONE AND $value = /[A-Z]{3}/; + DEFINE FIELD test ON TABLE user ASSERT $value != NONE AND $value = /[A-Z]{3}/; + INFO FOR TABLE user; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 3); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ev: {}, + fd: { test: 'DEFINE FIELD test ON user ASSERT $value != NONE AND $value = /[A-Z]{3}/' }, + ft: {}, + ix: {}, + }", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_field_type_value_assert() -> Result<(), Error> { + let sql = " + DEFINE FIELD test ON user TYPE string VALUE $value OR 'GBR' ASSERT $value != NONE AND $value = /[A-Z]{3}/; + DEFINE FIELD test ON TABLE user TYPE string VALUE $value OR 'GBR' ASSERT $value != NONE AND $value = /[A-Z]{3}/; + INFO FOR TABLE user; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 3); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ev: {}, + fd: { test: 'DEFINE FIELD test ON user TYPE string VALUE $value OR \"GBR\" ASSERT $value != NONE AND $value = /[A-Z]{3}/' }, + ft: {}, + ix: {}, + }", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_index_single() -> Result<(), Error> { + let sql = " + DEFINE INDEX test ON user FIELDS email; + DEFINE INDEX test ON user COLUMNS email; + INFO FOR TABLE user; + CREATE user:1 SET email = 'test@surrealdb.com'; + CREATE user:2 SET email = 'test@surrealdb.com'; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 5); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ev: {}, + fd: {}, + ft: {}, + ix: { test: 'DEFINE INDEX test ON user FIELDS email' }, + }", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse("[{ id: user:1, email: 'test@surrealdb.com' }]"); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse("[{ id: user:2, email: 'test@surrealdb.com' }]"); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_index_multiple() -> Result<(), Error> { + let sql = " + DEFINE INDEX test ON user FIELDS account, email; + DEFINE INDEX test ON user COLUMNS account, email; + INFO FOR TABLE user; + CREATE user:1 SET account = 'apple', email = 'test@surrealdb.com'; + CREATE user:2 SET account = 'tesla', email = 'test@surrealdb.com'; + CREATE user:3 SET account = 'apple', email = 'test@surrealdb.com'; + CREATE user:4 SET account = 'tesla', email = 'test@surrealdb.com'; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 7); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ev: {}, + fd: {}, + ft: {}, + ix: { test: 'DEFINE INDEX test ON user FIELDS account, email' }, + }", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse("[{ id: user:1, account: 'apple', email: 'test@surrealdb.com' }]"); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse("[{ id: user:2, account: 'tesla', email: 'test@surrealdb.com' }]"); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse("[{ id: user:3, account: 'apple', email: 'test@surrealdb.com' }]"); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse("[{ id: user:4, account: 'tesla', email: 'test@surrealdb.com' }]"); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_index_single_unique() -> Result<(), Error> { + let sql = " + DEFINE INDEX test ON user FIELDS email UNIQUE; + DEFINE INDEX test ON user COLUMNS email UNIQUE; + INFO FOR TABLE user; + CREATE user:1 SET email = 'test@surrealdb.com'; + CREATE user:2 SET email = 'test@surrealdb.com'; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 5); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ev: {}, + fd: {}, + ft: {}, + ix: { test: 'DEFINE INDEX test ON user FIELDS email UNIQUE' }, + }", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse("[{ id: user:1, email: 'test@surrealdb.com' }]"); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result; + assert!(matches!( + tmp.err(), + Some(e) if e.to_string() == "Database index `test` already contains `user:2`" + )); + // + Ok(()) +} + +#[tokio::test] +async fn define_statement_index_multiple_unique() -> Result<(), Error> { + let sql = " + DEFINE INDEX test ON user FIELDS account, email UNIQUE; + DEFINE INDEX test ON user COLUMNS account, email UNIQUE; + INFO FOR TABLE user; + CREATE user:1 SET account = 'apple', email = 'test@surrealdb.com'; + CREATE user:2 SET account = 'tesla', email = 'test@surrealdb.com'; + CREATE user:3 SET account = 'apple', email = 'test@surrealdb.com'; + CREATE user:4 SET account = 'tesla', email = 'test@surrealdb.com'; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 7); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + ev: {}, + fd: {}, + ft: {}, + ix: { test: 'DEFINE INDEX test ON user FIELDS account, email UNIQUE' }, + }", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse("[{ id: user:1, account: 'apple', email: 'test@surrealdb.com' }]"); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse("[{ id: user:2, account: 'tesla', email: 'test@surrealdb.com' }]"); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result; + assert!(matches!( + tmp.err(), + Some(e) if e.to_string() == "Database index `test` already contains `user:3`" + )); + // + let tmp = res.remove(0).result; + assert!(matches!( + tmp.err(), + Some(e) if e.to_string() == "Database index `test` already contains `user:4`" + )); + // + Ok(()) +}