2023-04-25 10:13:04 +00:00
|
|
|
mod parse;
|
|
|
|
use parse::Parse;
|
2023-08-30 18:01:30 +00:00
|
|
|
mod helpers;
|
2024-06-07 16:24:55 +00:00
|
|
|
use crate::helpers::Test;
|
2023-08-30 18:01:30 +00:00
|
|
|
use helpers::new_ds;
|
2023-04-25 10:13:04 +00:00
|
|
|
use surrealdb::dbs::Session;
|
|
|
|
use surrealdb::err::Error;
|
|
|
|
use surrealdb::sql::Value;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn strict_typing_inline() -> Result<(), Error> {
|
|
|
|
let sql = "
|
2024-06-12 09:15:09 +00:00
|
|
|
UPSERT person:test SET age = <int> NONE;
|
|
|
|
UPSERT person:test SET age = <int> '18';
|
|
|
|
UPSERT person:test SET enabled = <bool | int> NONE;
|
|
|
|
UPSERT person:test SET enabled = <bool | int> true;
|
|
|
|
UPSERT person:test SET name = <string> 'Tobie Morgan Hitchcock';
|
|
|
|
UPSERT person:test SET scores = <set<float>> [1,1,2,2,3,3,4,4,5,5];
|
|
|
|
UPSERT person:test SET scores = <array<float>> [1,1,2,2,3,3,4,4,5,5];
|
|
|
|
UPSERT person:test SET scores = <set<float, 5>> [1,1,2,2,3,3,4,4,5,5];
|
|
|
|
UPSERT person:test SET scores = <array<float, 5>> [1,1,2,2,3,3,4,4,5,5];
|
2023-04-25 10:13:04 +00:00
|
|
|
";
|
2023-08-30 18:01:30 +00:00
|
|
|
let dbs = new_ds().await?;
|
2023-07-29 18:47:25 +00:00
|
|
|
let ses = Session::owner().with_ns("test").with_db("test");
|
2023-07-05 21:26:13 +00:00
|
|
|
let res = &mut dbs.execute(sql, &ses, None).await?;
|
2023-04-25 10:13:04 +00:00
|
|
|
assert_eq!(res.len(), 9);
|
|
|
|
//
|
|
|
|
let tmp = res.remove(0).result;
|
|
|
|
assert!(matches!(
|
|
|
|
tmp.err(),
|
2023-06-06 06:12:59 +00:00
|
|
|
Some(e) if e.to_string() == "Expected a int but cannot convert NONE into a int"
|
2023-04-25 10:13:04 +00:00
|
|
|
));
|
|
|
|
//
|
|
|
|
let tmp = res.remove(0).result?;
|
|
|
|
let val = Value::parse(
|
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
age: 18,
|
|
|
|
}
|
|
|
|
]",
|
|
|
|
);
|
|
|
|
assert_eq!(tmp, val);
|
|
|
|
//
|
|
|
|
let tmp = res.remove(0).result;
|
|
|
|
assert!(matches!(
|
|
|
|
tmp.err(),
|
2023-06-06 06:12:59 +00:00
|
|
|
Some(e) if e.to_string() == "Expected a bool | int but cannot convert NONE into a bool | int"
|
2023-04-25 10:13:04 +00:00
|
|
|
));
|
|
|
|
//
|
|
|
|
let tmp = res.remove(0).result?;
|
|
|
|
let val = Value::parse(
|
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
age: 18,
|
|
|
|
enabled: true,
|
|
|
|
}
|
|
|
|
]",
|
|
|
|
);
|
|
|
|
assert_eq!(tmp, val);
|
|
|
|
//
|
|
|
|
let tmp = res.remove(0).result?;
|
|
|
|
let val = Value::parse(
|
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
age: 18,
|
|
|
|
enabled: true,
|
|
|
|
name: 'Tobie Morgan Hitchcock',
|
|
|
|
}
|
|
|
|
]",
|
|
|
|
);
|
|
|
|
assert_eq!(tmp, val);
|
|
|
|
//
|
|
|
|
let tmp = res.remove(0).result?;
|
|
|
|
let val = Value::parse(
|
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
age: 18,
|
|
|
|
enabled: true,
|
|
|
|
name: 'Tobie Morgan Hitchcock',
|
|
|
|
scores: [1.0, 2.0, 3.0, 4.0, 5.0],
|
|
|
|
}
|
|
|
|
]",
|
|
|
|
);
|
|
|
|
assert_eq!(tmp, val);
|
|
|
|
//
|
|
|
|
let tmp = res.remove(0).result?;
|
|
|
|
let val = Value::parse(
|
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
age: 18,
|
|
|
|
enabled: true,
|
|
|
|
name: 'Tobie Morgan Hitchcock',
|
|
|
|
scores: [1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 4.0, 4.0, 5.0, 5.0],
|
|
|
|
}
|
|
|
|
]",
|
|
|
|
);
|
|
|
|
assert_eq!(tmp, val);
|
|
|
|
//
|
|
|
|
let tmp = res.remove(0).result?;
|
|
|
|
let val = Value::parse(
|
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
age: 18,
|
|
|
|
enabled: true,
|
|
|
|
name: 'Tobie Morgan Hitchcock',
|
|
|
|
scores: [1.0, 2.0, 3.0, 4.0, 5.0],
|
|
|
|
}
|
|
|
|
]",
|
|
|
|
);
|
|
|
|
assert_eq!(tmp, val);
|
|
|
|
//
|
2023-06-06 06:12:59 +00:00
|
|
|
let tmp = res.remove(0).result;
|
|
|
|
assert!(matches!(
|
|
|
|
tmp.err(),
|
|
|
|
Some(e) if e.to_string() == "Expected a array<float, 5> but the array had 10 items"
|
|
|
|
));
|
2023-04-25 10:13:04 +00:00
|
|
|
//
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn strict_typing_defined() -> Result<(), Error> {
|
|
|
|
let sql = "
|
|
|
|
DEFINE FIELD age ON person TYPE int;
|
|
|
|
DEFINE FIELD enabled ON person TYPE bool | int;
|
|
|
|
DEFINE FIELD name ON person TYPE string;
|
|
|
|
DEFINE FIELD scores ON person TYPE set<float, 5>;
|
2024-06-12 09:15:09 +00:00
|
|
|
UPSERT person:test SET age = NONE, enabled = NONE, name = NONE, scored = [1,1,2,2,3,3,4,4,5,5];
|
|
|
|
UPSERT person:test SET age = 18, enabled = NONE, name = NONE, scored = [1,1,2,2,3,3,4,4,5,5];
|
|
|
|
UPSERT person:test SET age = 18, enabled = true, name = NONE, scored = [1,1,2,2,3,3,4,4,5,5];
|
|
|
|
UPSERT person:test SET age = 18, enabled = true, name = 'Tobie Morgan Hitchcock', scores = [1,1,2,2,3,3,4,4,5,5];
|
2023-04-25 10:13:04 +00:00
|
|
|
";
|
2023-08-30 18:01:30 +00:00
|
|
|
let dbs = new_ds().await?;
|
2023-07-29 18:47:25 +00:00
|
|
|
let ses = Session::owner().with_ns("test").with_db("test");
|
2023-07-05 21:26:13 +00:00
|
|
|
let res = &mut dbs.execute(sql, &ses, None).await?;
|
2023-04-25 10:13:04 +00:00
|
|
|
assert_eq!(res.len(), 8);
|
|
|
|
//
|
|
|
|
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;
|
|
|
|
assert!(tmp.is_ok());
|
|
|
|
//
|
|
|
|
let tmp = res.remove(0).result;
|
|
|
|
assert!(matches!(
|
|
|
|
tmp.err(),
|
|
|
|
Some(e) if e.to_string() == "Found NONE for field `age`, with record `person:test`, but expected a int"
|
|
|
|
));
|
|
|
|
//
|
|
|
|
let tmp = res.remove(0).result;
|
|
|
|
assert!(matches!(
|
|
|
|
tmp.err(),
|
|
|
|
Some(e) if e.to_string() == "Found NONE for field `enabled`, with record `person:test`, but expected a bool | int"
|
|
|
|
));
|
|
|
|
//
|
|
|
|
let tmp = res.remove(0).result;
|
|
|
|
assert!(matches!(
|
|
|
|
tmp.err(),
|
|
|
|
Some(e) if e.to_string() == "Found NONE for field `name`, with record `person:test`, but expected a string"
|
|
|
|
));
|
|
|
|
//
|
|
|
|
let tmp = res.remove(0).result?;
|
|
|
|
let val = Value::parse(
|
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
age: 18,
|
|
|
|
enabled: true,
|
|
|
|
name: 'Tobie Morgan Hitchcock',
|
|
|
|
scores: [1.0, 2.0, 3.0, 4.0, 5.0],
|
|
|
|
}
|
|
|
|
]",
|
|
|
|
);
|
|
|
|
assert_eq!(tmp, val);
|
|
|
|
//
|
|
|
|
Ok(())
|
|
|
|
}
|
2023-08-27 10:22:27 +00:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn strict_typing_none_null() -> Result<(), Error> {
|
|
|
|
let sql = "
|
|
|
|
DEFINE TABLE person SCHEMAFULL;
|
|
|
|
DEFINE FIELD name ON TABLE person TYPE option<string>;
|
2024-06-12 09:15:09 +00:00
|
|
|
UPSERT person:test SET name = 'Tobie';
|
|
|
|
UPSERT person:test SET name = NULL;
|
|
|
|
UPSERT person:test SET name = NONE;
|
2023-08-27 10:22:27 +00:00
|
|
|
--
|
2024-06-07 16:24:55 +00:00
|
|
|
REMOVE TABLE person;
|
2023-08-27 10:22:27 +00:00
|
|
|
DEFINE TABLE person SCHEMAFULL;
|
|
|
|
DEFINE FIELD name ON TABLE person TYPE option<string | null>;
|
2024-06-12 09:15:09 +00:00
|
|
|
UPSERT person:test SET name = 'Tobie';
|
|
|
|
UPSERT person:test SET name = NULL;
|
|
|
|
UPSERT person:test SET name = NONE;
|
2023-08-27 10:22:27 +00:00
|
|
|
--
|
2024-06-07 16:24:55 +00:00
|
|
|
REMOVE TABLE person;
|
2023-08-27 10:22:27 +00:00
|
|
|
DEFINE TABLE person SCHEMAFULL;
|
|
|
|
DEFINE FIELD name ON TABLE person TYPE string | null;
|
2024-06-12 09:15:09 +00:00
|
|
|
UPSERT person:test SET name = 'Tobie';
|
|
|
|
UPSERT person:test SET name = NULL;
|
|
|
|
UPSERT person:test SET name = NONE;
|
2023-08-27 10:22:27 +00:00
|
|
|
";
|
2024-06-07 16:24:55 +00:00
|
|
|
let mut t = Test::new(sql).await?;
|
2023-08-27 10:22:27 +00:00
|
|
|
//
|
2024-06-07 16:24:55 +00:00
|
|
|
t.skip_ok(2)?;
|
|
|
|
t.expect_val(
|
2023-08-27 10:22:27 +00:00
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
name: 'Tobie',
|
|
|
|
}
|
|
|
|
]",
|
2024-06-07 16:24:55 +00:00
|
|
|
)?;
|
|
|
|
t.expect_error(
|
|
|
|
"Found NULL for field `name`, with record `person:test`, but expected a option<string>",
|
|
|
|
)?;
|
|
|
|
t.expect_val(
|
2023-08-27 10:22:27 +00:00
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
}
|
|
|
|
]",
|
2024-06-07 16:24:55 +00:00
|
|
|
)?;
|
2023-08-27 10:22:27 +00:00
|
|
|
//
|
2024-06-07 16:24:55 +00:00
|
|
|
t.skip_ok(3)?;
|
|
|
|
t.expect_val(
|
2023-08-27 10:22:27 +00:00
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
name: 'Tobie',
|
|
|
|
}
|
|
|
|
]",
|
2024-06-07 16:24:55 +00:00
|
|
|
)?;
|
|
|
|
t.expect_val(
|
2023-08-27 10:22:27 +00:00
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
name: NULL,
|
|
|
|
}
|
|
|
|
]",
|
2024-06-07 16:24:55 +00:00
|
|
|
)?;
|
|
|
|
t.expect_val(
|
2023-08-27 10:22:27 +00:00
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
}
|
|
|
|
]",
|
2024-06-07 16:24:55 +00:00
|
|
|
)?;
|
2023-08-27 10:22:27 +00:00
|
|
|
//
|
2024-06-07 16:24:55 +00:00
|
|
|
t.skip_ok(3)?;
|
|
|
|
t.expect_val(
|
2023-08-27 10:22:27 +00:00
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
name: 'Tobie',
|
|
|
|
}
|
|
|
|
]",
|
2024-06-07 16:24:55 +00:00
|
|
|
)?;
|
|
|
|
t.expect_val(
|
2023-08-27 10:22:27 +00:00
|
|
|
"[
|
|
|
|
{
|
|
|
|
id: person:test,
|
|
|
|
name: NULL,
|
|
|
|
}
|
|
|
|
]",
|
2024-06-07 16:24:55 +00:00
|
|
|
)?;
|
|
|
|
t.expect_error(
|
|
|
|
"Found NONE for field `name`, with record `person:test`, but expected a string | null",
|
|
|
|
)?;
|
2023-08-27 10:22:27 +00:00
|
|
|
//
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-09-09 18:04:59 +00:00
|
|
|
|
2024-09-10 12:50:31 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn literal_typing() -> Result<(), Error> {
|
|
|
|
let sql = "
|
|
|
|
DEFINE TABLE test SCHEMAFULL;
|
|
|
|
DEFINE FIELD obj ON test TYPE {
|
|
|
|
a: int,
|
|
|
|
b: option<string>
|
|
|
|
};
|
|
|
|
|
|
|
|
CREATE ONLY test:1 SET obj = { a: 1 };
|
|
|
|
CREATE ONLY test:2 SET obj = { a: 2, b: 'foo' };
|
|
|
|
CREATE ONLY test:3 SET obj = { a: 3, b: 'bar', c: 'forbidden' };
|
|
|
|
";
|
|
|
|
let mut t = Test::new(sql).await?;
|
|
|
|
//
|
|
|
|
t.skip_ok(2)?;
|
|
|
|
t.expect_val(
|
|
|
|
"{
|
|
|
|
id: test:1,
|
|
|
|
obj: { a: 1 },
|
|
|
|
}",
|
|
|
|
)?;
|
|
|
|
t.expect_val(
|
|
|
|
"{
|
|
|
|
id: test:2,
|
|
|
|
obj: { a: 2, b: 'foo' },
|
|
|
|
}",
|
|
|
|
)?;
|
|
|
|
t.expect_error(
|
|
|
|
"Found { a: 3, b: 'bar', c: 'forbidden' } for field `obj`, with record `test:3`, but expected a { a: int, b: option<string> }",
|
|
|
|
)?;
|
|
|
|
//
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-09-09 18:04:59 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn strict_typing_optional_object() -> Result<(), Error> {
|
|
|
|
let sql = "
|
|
|
|
DEFINE TABLE test SCHEMAFULL;
|
|
|
|
DEFINE FIELD obj ON test TYPE option<object>;
|
|
|
|
DEFINE FIELD obj.a ON test TYPE string;
|
|
|
|
|
|
|
|
CREATE ONLY test:1;
|
|
|
|
CREATE ONLY test:2 SET obj = {};
|
|
|
|
CREATE ONLY test:3 SET obj = { a: 'abc' };
|
|
|
|
";
|
|
|
|
let mut t = Test::new(sql).await?;
|
|
|
|
//
|
|
|
|
t.skip_ok(3)?;
|
|
|
|
//
|
|
|
|
t.expect_val(
|
|
|
|
"{
|
|
|
|
id: test:1,
|
|
|
|
}",
|
|
|
|
)?;
|
|
|
|
//
|
|
|
|
t.expect_error("Found NONE for field `obj.a`, with record `test:2`, but expected a string")?;
|
|
|
|
//
|
|
|
|
t.expect_val(
|
|
|
|
"{
|
|
|
|
id: test:3,
|
|
|
|
obj: {
|
|
|
|
a: 'abc',
|
|
|
|
},
|
|
|
|
}",
|
|
|
|
)?;
|
|
|
|
//
|
|
|
|
Ok(())
|
|
|
|
}
|