2022-12-30 08:23:19 +00:00
|
|
|
// Tests common to all protocols and storage engines
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn connect() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.health().await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn yuse() {
|
|
|
|
let db = new_db().await;
|
2023-04-19 08:46:37 +00:00
|
|
|
let item = Ulid::new().to_string();
|
2023-05-05 18:12:19 +00:00
|
|
|
match db.create(Resource::from(item.as_str())).await.unwrap_err() {
|
2023-04-19 08:46:37 +00:00
|
|
|
// Local engines return this error
|
|
|
|
Error::Db(DbError::NsEmpty) => {}
|
|
|
|
// Remote engines return this error
|
|
|
|
Error::Api(ApiError::Query(error)) if error.contains("Specify a namespace to use") => {}
|
|
|
|
error => panic!("{:?}", error),
|
|
|
|
}
|
2023-05-05 18:12:19 +00:00
|
|
|
db.use_ns(NS).await.unwrap();
|
|
|
|
match db.create(Resource::from(item.as_str())).await.unwrap_err() {
|
|
|
|
// Local engines return this error
|
|
|
|
Error::Db(DbError::DbEmpty) => {}
|
|
|
|
// Remote engines return this error
|
|
|
|
Error::Api(ApiError::Query(error)) if error.contains("Specify a database to use") => {}
|
|
|
|
error => panic!("{:?}", error),
|
|
|
|
}
|
|
|
|
db.use_db(item.as_str()).await.unwrap();
|
|
|
|
db.create(Resource::from(item)).await.unwrap();
|
2022-12-30 08:23:19 +00:00
|
|
|
}
|
|
|
|
|
2023-06-09 13:45:07 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn invalidate() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
db.invalidate().await.unwrap();
|
|
|
|
let error = db.create::<Option<RecordId>>(("user", "john")).await.unwrap_err();
|
|
|
|
assert!(error.to_string().contains("You don't have permission to perform this query type"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn signup_scope() {
|
|
|
|
let db = new_db().await;
|
|
|
|
let database = Ulid::new().to_string();
|
|
|
|
db.use_ns(NS).use_db(&database).await.unwrap();
|
|
|
|
let scope = Ulid::new().to_string();
|
|
|
|
let sql = format!(
|
|
|
|
"
|
|
|
|
DEFINE SCOPE {scope} SESSION 1s
|
|
|
|
SIGNUP ( CREATE user SET email = $email, pass = crypto::argon2::generate($pass) )
|
|
|
|
SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass) )
|
|
|
|
"
|
|
|
|
);
|
|
|
|
let response = db.query(sql).await.unwrap();
|
|
|
|
response.check().unwrap();
|
|
|
|
db.signup(Scope {
|
|
|
|
namespace: NS,
|
|
|
|
database: &database,
|
|
|
|
scope: &scope,
|
|
|
|
params: AuthParams {
|
|
|
|
email: "john.doe@example.com",
|
|
|
|
pass: "password123",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn signin_ns() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let user = Ulid::new().to_string();
|
|
|
|
let pass = "password123";
|
|
|
|
let sql = format!("DEFINE LOGIN {user} ON NAMESPACE PASSWORD '{pass}'");
|
|
|
|
let response = db.query(sql).await.unwrap();
|
|
|
|
response.check().unwrap();
|
|
|
|
db.signin(Namespace {
|
|
|
|
namespace: NS,
|
|
|
|
username: &user,
|
|
|
|
password: pass,
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn signin_db() {
|
|
|
|
let db = new_db().await;
|
|
|
|
let database = Ulid::new().to_string();
|
|
|
|
db.use_ns(NS).use_db(&database).await.unwrap();
|
|
|
|
let user = Ulid::new().to_string();
|
|
|
|
let pass = "password123";
|
|
|
|
let sql = format!("DEFINE LOGIN {user} ON DATABASE PASSWORD '{pass}'");
|
|
|
|
let response = db.query(sql).await.unwrap();
|
|
|
|
response.check().unwrap();
|
|
|
|
db.signin(Database {
|
|
|
|
namespace: NS,
|
|
|
|
database: &database,
|
|
|
|
username: &user,
|
|
|
|
password: pass,
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn signin_scope() {
|
|
|
|
let db = new_db().await;
|
|
|
|
let database = Ulid::new().to_string();
|
|
|
|
db.use_ns(NS).use_db(&database).await.unwrap();
|
|
|
|
let scope = Ulid::new().to_string();
|
|
|
|
let email = format!("{scope}@example.com");
|
|
|
|
let pass = "password123";
|
|
|
|
let sql = format!(
|
|
|
|
"
|
|
|
|
DEFINE SCOPE {scope} SESSION 1s
|
|
|
|
SIGNUP ( CREATE user SET email = $email, pass = crypto::argon2::generate($pass) )
|
|
|
|
SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass) )
|
|
|
|
"
|
|
|
|
);
|
|
|
|
let response = db.query(sql).await.unwrap();
|
|
|
|
response.check().unwrap();
|
|
|
|
db.signup(Scope {
|
|
|
|
namespace: NS,
|
|
|
|
database: &database,
|
|
|
|
scope: &scope,
|
|
|
|
params: AuthParams {
|
|
|
|
pass,
|
|
|
|
email: &email,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
db.signin(Scope {
|
|
|
|
namespace: NS,
|
|
|
|
database: &database,
|
|
|
|
scope: &scope,
|
|
|
|
params: AuthParams {
|
|
|
|
pass,
|
|
|
|
email: &email,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn authenticate() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let user = Ulid::new().to_string();
|
|
|
|
let pass = "password123";
|
|
|
|
let sql = format!("DEFINE LOGIN {user} ON NAMESPACE PASSWORD '{pass}'");
|
|
|
|
let response = db.query(sql).await.unwrap();
|
|
|
|
response.check().unwrap();
|
|
|
|
let token = db
|
|
|
|
.signin(Namespace {
|
|
|
|
namespace: NS,
|
|
|
|
username: &user,
|
|
|
|
password: pass,
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
db.authenticate(token).await.unwrap();
|
|
|
|
}
|
|
|
|
|
2022-12-30 08:23:19 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn query() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let _ = db.query("
|
|
|
|
CREATE user:john
|
|
|
|
SET name = 'John Doe'
|
|
|
|
")
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.check()
|
|
|
|
.unwrap();
|
|
|
|
let mut response = db
|
|
|
|
.query("SELECT name FROM user:john")
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.check()
|
|
|
|
.unwrap();
|
|
|
|
let Some(name): Option<String> = response.take("name").unwrap() else {
|
|
|
|
panic!("query returned no record");
|
|
|
|
};
|
|
|
|
assert_eq!(name, "John Doe");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn query_binds() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let mut response = db.query("CREATE user:john SET name = $name")
|
|
|
|
.bind(("name", "John Doe"))
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let Some(record): Option<RecordName> = response.take(0).unwrap() else {
|
|
|
|
panic!("query returned no record");
|
|
|
|
};
|
|
|
|
assert_eq!(record.name, "John Doe");
|
|
|
|
let mut response = db.query("SELECT * FROM $record_id")
|
2023-03-30 10:41:44 +00:00
|
|
|
.bind(("record_id", thing("user:john").unwrap()))
|
2022-12-30 08:23:19 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let Some(record): Option<RecordName> = response.take(0).unwrap() else {
|
|
|
|
panic!("query returned no record");
|
|
|
|
};
|
|
|
|
assert_eq!(record.name, "John Doe");
|
|
|
|
let mut response = db.query("CREATE user SET name = $name")
|
|
|
|
.bind(Record {
|
|
|
|
name: "John Doe",
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let Some(record): Option<RecordName> = response.take(0).unwrap() else {
|
|
|
|
panic!("query returned no record");
|
|
|
|
};
|
|
|
|
assert_eq!(record.name, "John Doe");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn query_chaining() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let response = db
|
|
|
|
.query(BeginStatement)
|
|
|
|
.query("CREATE account:one SET balance = 135605.16")
|
|
|
|
.query("CREATE account:two SET balance = 91031.31")
|
|
|
|
.query("UPDATE account:one SET balance += 300.00")
|
|
|
|
.query("UPDATE account:two SET balance -= 300.00")
|
|
|
|
.query(CommitStatement)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
response.check().unwrap();
|
|
|
|
}
|
|
|
|
|
2023-05-26 10:36:37 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn mixed_results_query() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let sql = "CREATE bar SET baz = rand('a'); CREATE foo;";
|
|
|
|
let mut response = db.query(sql).await.unwrap();
|
|
|
|
response.take::<Value>(0).unwrap_err();
|
|
|
|
let _: Option<RecordId> = response.take(1).unwrap();
|
|
|
|
}
|
|
|
|
|
2022-12-30 08:23:19 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn create_record_no_id() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
2023-04-28 11:20:57 +00:00
|
|
|
let _: Vec<RecordId> = db.create("user").await.unwrap();
|
|
|
|
let _: Value = db.create(Resource::from("user")).await.unwrap();
|
2022-12-30 08:23:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn create_record_with_id() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
2023-04-28 11:20:57 +00:00
|
|
|
let _: Option<RecordId> = db.create(("user", "jane")).await.unwrap();
|
|
|
|
let _: Value = db.create(Resource::from(("user", "john"))).await.unwrap();
|
|
|
|
let _: Value = db.create(Resource::from("user:doe")).await.unwrap();
|
2022-12-30 08:23:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn create_record_no_id_with_content() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
2023-04-28 11:20:57 +00:00
|
|
|
let _: Vec<RecordId> = db
|
2022-12-30 08:23:19 +00:00
|
|
|
.create("user")
|
|
|
|
.content(Record {
|
|
|
|
name: "John Doe",
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-04-28 11:20:57 +00:00
|
|
|
let _: Value = db
|
|
|
|
.create(Resource::from("user"))
|
|
|
|
.content(Record {
|
|
|
|
name: "John Doe",
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2022-12-30 08:23:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn create_record_with_id_with_content() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
2023-04-28 11:20:57 +00:00
|
|
|
let record: Option<RecordId> = db
|
2022-12-30 08:23:19 +00:00
|
|
|
.create(("user", "john"))
|
|
|
|
.content(Record {
|
|
|
|
name: "John Doe",
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-04-28 11:20:57 +00:00
|
|
|
assert_eq!(record.unwrap().id, thing("user:john").unwrap());
|
|
|
|
let value: Value = db
|
|
|
|
.create(Resource::from("user:jane"))
|
|
|
|
.content(Record {
|
|
|
|
name: "Jane Doe",
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(value.record(), thing("user:jane").ok());
|
2022-12-30 08:23:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn select_table() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let table = "user";
|
2023-04-28 11:20:57 +00:00
|
|
|
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
|
|
|
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
|
|
|
let _: Value = db.create(Resource::from(table)).await.unwrap();
|
2022-12-30 08:23:19 +00:00
|
|
|
let users: Vec<RecordId> = db.select(table).await.unwrap();
|
|
|
|
assert_eq!(users.len(), 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn select_record_id() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let record_id = ("user", "john");
|
2023-04-28 11:20:57 +00:00
|
|
|
let _: Option<RecordId> = db.create(record_id).await.unwrap();
|
2022-12-30 08:23:19 +00:00
|
|
|
let Some(record): Option<RecordId> = db.select(record_id).await.unwrap() else {
|
|
|
|
panic!("record not found");
|
|
|
|
};
|
2023-03-30 10:41:44 +00:00
|
|
|
assert_eq!(record.id, thing("user:john").unwrap());
|
2023-04-28 11:20:57 +00:00
|
|
|
let value: Value = db.select(Resource::from(record_id)).await.unwrap();
|
|
|
|
assert_eq!(value.record(), thing("user:john").ok());
|
2022-12-30 08:23:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn select_record_ranges() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let table = "user";
|
2023-04-28 11:20:57 +00:00
|
|
|
let _: Option<RecordId> = db.create((table, "amos")).await.unwrap();
|
|
|
|
let _: Option<RecordId> = db.create((table, "jane")).await.unwrap();
|
|
|
|
let _: Option<RecordId> = db.create((table, "john")).await.unwrap();
|
|
|
|
let _: Value = db.create(Resource::from((table, "zoey"))).await.unwrap();
|
2022-12-30 08:23:19 +00:00
|
|
|
let convert = |users: Vec<RecordId>| -> Vec<String> {
|
|
|
|
users
|
|
|
|
.into_iter()
|
2023-03-30 10:41:44 +00:00
|
|
|
.map(|user| user.id.id.to_string())
|
2022-12-30 08:23:19 +00:00
|
|
|
.collect()
|
|
|
|
};
|
|
|
|
let users: Vec<RecordId> = db.select(table).range(..).await.unwrap();
|
|
|
|
assert_eq!(convert(users), vec!["amos", "jane", "john", "zoey"]);
|
|
|
|
let users: Vec<RecordId> = db.select(table).range(.."john").await.unwrap();
|
|
|
|
assert_eq!(convert(users), vec!["amos", "jane"]);
|
|
|
|
let users: Vec<RecordId> = db.select(table).range(..="john").await.unwrap();
|
|
|
|
assert_eq!(convert(users), vec!["amos", "jane", "john"]);
|
|
|
|
let users: Vec<RecordId> = db.select(table).range("jane"..).await.unwrap();
|
|
|
|
assert_eq!(convert(users), vec!["jane", "john", "zoey"]);
|
|
|
|
let users: Vec<RecordId> = db.select(table).range("jane".."john").await.unwrap();
|
|
|
|
assert_eq!(convert(users), vec!["jane"]);
|
|
|
|
let users: Vec<RecordId> = db.select(table).range("jane"..="john").await.unwrap();
|
|
|
|
assert_eq!(convert(users), vec!["jane", "john"]);
|
2023-04-28 11:20:57 +00:00
|
|
|
let Value::Array(array): Value = db.select(Resource::from(table)).range("jane"..="john").await.unwrap() else {
|
|
|
|
unreachable!();
|
|
|
|
};
|
|
|
|
assert_eq!(array.len(), 2);
|
2022-12-30 08:23:19 +00:00
|
|
|
let users: Vec<RecordId> =
|
|
|
|
db.select(table).range((Bound::Excluded("jane"), Bound::Included("john"))).await.unwrap();
|
|
|
|
assert_eq!(convert(users), vec!["john"]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn update_table() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let table = "user";
|
2023-04-28 11:20:57 +00:00
|
|
|
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
|
|
|
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
|
|
|
let _: Value = db.update(Resource::from(table)).await.unwrap();
|
2022-12-30 08:23:19 +00:00
|
|
|
let users: Vec<RecordId> = db.update(table).await.unwrap();
|
|
|
|
assert_eq!(users.len(), 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn update_record_id() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let table = "user";
|
2023-04-28 11:20:57 +00:00
|
|
|
let _: Option<RecordId> = db.create((table, "john")).await.unwrap();
|
|
|
|
let _: Option<RecordId> = db.create((table, "jane")).await.unwrap();
|
2022-12-30 08:23:19 +00:00
|
|
|
let users: Vec<RecordId> = db.update(table).await.unwrap();
|
|
|
|
assert_eq!(users.len(), 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn update_table_with_content() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let sql = "
|
|
|
|
CREATE type::thing($table, 'amos') SET name = 'Amos';
|
|
|
|
CREATE type::thing($table, 'jane') SET name = 'Jane';
|
|
|
|
CREATE type::thing($table, 'john') SET name = 'John';
|
|
|
|
CREATE type::thing($table, 'zoey') SET name = 'Zoey';
|
|
|
|
";
|
|
|
|
let table = "user";
|
|
|
|
let response = db.query(sql)
|
|
|
|
.bind(("table", table))
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
response.check().unwrap();
|
|
|
|
let users: Vec<RecordBuf> = db
|
|
|
|
.update(table)
|
|
|
|
.content(Record {
|
|
|
|
name: "Doe",
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let expected = &[
|
|
|
|
RecordBuf {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: thing("user:amos").unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
name: "Doe".to_owned(),
|
|
|
|
},
|
|
|
|
RecordBuf {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: thing("user:jane").unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
name: "Doe".to_owned(),
|
|
|
|
},
|
|
|
|
RecordBuf {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: thing("user:john").unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
name: "Doe".to_owned(),
|
|
|
|
},
|
|
|
|
RecordBuf {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: thing("user:zoey").unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
name: "Doe".to_owned(),
|
|
|
|
},
|
|
|
|
];
|
|
|
|
assert_eq!(users, expected);
|
|
|
|
let users: Vec<RecordBuf> = db
|
|
|
|
.select(table)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(users, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn update_record_range_with_content() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let sql = "
|
|
|
|
CREATE type::thing($table, 'amos') SET name = 'Amos';
|
|
|
|
CREATE type::thing($table, 'jane') SET name = 'Jane';
|
|
|
|
CREATE type::thing($table, 'john') SET name = 'John';
|
|
|
|
CREATE type::thing($table, 'zoey') SET name = 'Zoey';
|
|
|
|
";
|
|
|
|
let table = "user";
|
|
|
|
let response = db.query(sql)
|
|
|
|
.bind(("table", table))
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
response.check().unwrap();
|
|
|
|
let users: Vec<RecordBuf> = db
|
|
|
|
.update(table)
|
|
|
|
.range("jane".."zoey")
|
|
|
|
.content(Record {
|
|
|
|
name: "Doe",
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(users, &[
|
|
|
|
RecordBuf {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: thing("user:jane").unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
name: "Doe".to_owned(),
|
|
|
|
},
|
|
|
|
RecordBuf {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: thing("user:john").unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
name: "Doe".to_owned(),
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
let users: Vec<RecordBuf> = db
|
|
|
|
.select(table)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(users, &[
|
|
|
|
RecordBuf {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: thing("user:amos").unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
name: "Amos".to_owned(),
|
|
|
|
},
|
|
|
|
RecordBuf {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: thing("user:jane").unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
name: "Doe".to_owned(),
|
|
|
|
},
|
|
|
|
RecordBuf {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: thing("user:john").unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
name: "Doe".to_owned(),
|
|
|
|
},
|
|
|
|
RecordBuf {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: thing("user:zoey").unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
name: "Zoey".to_owned(),
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn update_record_id_with_content() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let record_id = ("user", "john");
|
2023-04-28 11:20:57 +00:00
|
|
|
let user: Option<RecordName> = db
|
2022-12-30 08:23:19 +00:00
|
|
|
.create(record_id)
|
|
|
|
.content(Record {
|
|
|
|
name: "Jane Doe",
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-04-28 11:20:57 +00:00
|
|
|
assert_eq!(user.unwrap().name, "Jane Doe");
|
|
|
|
let user: Option<RecordName> = db
|
2022-12-30 08:23:19 +00:00
|
|
|
.update(record_id)
|
|
|
|
.content(Record {
|
|
|
|
name: "John Doe",
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-04-28 11:20:57 +00:00
|
|
|
assert_eq!(user.unwrap().name, "John Doe");
|
|
|
|
let user: Option<RecordName> = db
|
2022-12-30 08:23:19 +00:00
|
|
|
.select(record_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-04-28 11:20:57 +00:00
|
|
|
assert_eq!(user.unwrap().name, "John Doe");
|
2022-12-30 08:23:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
|
|
|
|
struct Name {
|
|
|
|
first: Cow<'static, str>,
|
|
|
|
last: Cow<'static, str>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
|
|
|
|
struct Person {
|
|
|
|
#[serde(skip_serializing)]
|
2023-03-30 10:41:44 +00:00
|
|
|
id: Option<Thing>,
|
2022-12-30 08:23:19 +00:00
|
|
|
title: Cow<'static, str>,
|
|
|
|
name: Name,
|
|
|
|
marketing: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn merge_record_id() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let record_id = ("person", "jaime");
|
2023-04-28 11:20:57 +00:00
|
|
|
let mut jaime: Option<Person> = db
|
2022-12-30 08:23:19 +00:00
|
|
|
.create(record_id)
|
|
|
|
.content(Person {
|
|
|
|
id: None,
|
|
|
|
title: "Founder & COO".into(),
|
|
|
|
name: Name {
|
|
|
|
first: "Jaime".into(),
|
|
|
|
last: "Morgan Hitchcock".into(),
|
|
|
|
},
|
|
|
|
marketing: false,
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-04-28 11:20:57 +00:00
|
|
|
assert_eq!(jaime.unwrap().id.unwrap(), thing("person:jaime").unwrap());
|
2022-12-30 08:23:19 +00:00
|
|
|
jaime = db
|
|
|
|
.update(record_id)
|
|
|
|
.merge(json!({ "marketing": true }))
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-04-28 11:20:57 +00:00
|
|
|
assert!(jaime.as_ref().unwrap().marketing);
|
2022-12-30 08:23:19 +00:00
|
|
|
jaime = db.select(record_id).await.unwrap();
|
2023-04-28 11:20:57 +00:00
|
|
|
assert_eq!(jaime.unwrap(), Person {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: Some(thing("person:jaime").unwrap()),
|
2022-12-30 08:23:19 +00:00
|
|
|
title: "Founder & COO".into(),
|
|
|
|
name: Name {
|
|
|
|
first: "Jaime".into(),
|
|
|
|
last: "Morgan Hitchcock".into(),
|
|
|
|
},
|
|
|
|
marketing: true,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn patch_record_id() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let id = "john";
|
|
|
|
let _: Option<RecordId> = db
|
|
|
|
.create(("user", id))
|
|
|
|
.content(json!({
|
|
|
|
"baz": "qux",
|
|
|
|
"foo": "bar"
|
|
|
|
}))
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let _: Option<serde_json::Value> = db
|
|
|
|
.update(("user", id))
|
|
|
|
.patch(PatchOp::replace("/baz", "boo"))
|
|
|
|
.patch(PatchOp::add("/hello", ["world"]))
|
|
|
|
.patch(PatchOp::remove("/foo"))
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let value: Option<serde_json::Value> = db.select(("user", id)).await.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
value,
|
2023-04-29 15:58:22 +00:00
|
|
|
Some(json!({
|
2023-03-30 10:41:44 +00:00
|
|
|
"id": thing(&format!("user:{id}")).unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
"baz": "boo",
|
|
|
|
"hello": ["world"]
|
2023-04-29 15:58:22 +00:00
|
|
|
}))
|
2022-12-30 08:23:19 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn delete_table() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let table = "user";
|
2023-04-28 11:20:57 +00:00
|
|
|
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
|
|
|
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
|
|
|
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
2022-12-30 08:23:19 +00:00
|
|
|
let users: Vec<RecordId> = db.select(table).await.unwrap();
|
|
|
|
assert_eq!(users.len(), 3);
|
2023-03-31 22:49:29 +00:00
|
|
|
let users: Vec<RecordId> = db.delete(table).await.unwrap();
|
|
|
|
assert_eq!(users.len(), 3);
|
2022-12-30 08:23:19 +00:00
|
|
|
let users: Vec<RecordId> = db.select(table).await.unwrap();
|
|
|
|
assert!(users.is_empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn delete_record_id() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let record_id = ("user", "john");
|
2023-04-28 11:20:57 +00:00
|
|
|
let _: Option<RecordId> = db.create(record_id).await.unwrap();
|
|
|
|
let _: Option<RecordId> = db.select(record_id).await.unwrap();
|
2023-03-31 22:49:29 +00:00
|
|
|
let john: Option<RecordId> = db.delete(record_id).await.unwrap();
|
|
|
|
assert!(john.is_some());
|
2022-12-30 08:23:19 +00:00
|
|
|
let john: Option<RecordId> = db.select(record_id).await.unwrap();
|
|
|
|
assert!(john.is_none());
|
2023-04-28 11:20:57 +00:00
|
|
|
// non-existing user
|
|
|
|
let jane: Option<RecordId> = db.delete(("user", "jane")).await.unwrap();
|
|
|
|
assert!(jane.is_none());
|
|
|
|
let value = db.delete(Resource::from(("user", "jane"))).await.unwrap();
|
|
|
|
assert_eq!(value, Value::None);
|
2022-12-30 08:23:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn delete_record_range() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let sql = "
|
|
|
|
CREATE type::thing($table, 'amos') SET name = 'Amos';
|
|
|
|
CREATE type::thing($table, 'jane') SET name = 'Jane';
|
|
|
|
CREATE type::thing($table, 'john') SET name = 'John';
|
|
|
|
CREATE type::thing($table, 'zoey') SET name = 'Zoey';
|
|
|
|
";
|
|
|
|
let table = "user";
|
|
|
|
let response = db.query(sql)
|
|
|
|
.bind(("table", table))
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
response.check().unwrap();
|
2023-03-31 22:49:29 +00:00
|
|
|
let users: Vec<RecordBuf> = db.delete(table).range("jane".."zoey").await.unwrap();
|
|
|
|
assert_eq!(users, &[
|
|
|
|
RecordBuf {
|
|
|
|
id: thing("user:jane").unwrap(),
|
|
|
|
name: "Jane".to_owned(),
|
|
|
|
},
|
|
|
|
RecordBuf {
|
|
|
|
id: thing("user:john").unwrap(),
|
|
|
|
name: "John".to_owned(),
|
|
|
|
},
|
|
|
|
]);
|
2022-12-30 08:23:19 +00:00
|
|
|
let users: Vec<RecordBuf> = db
|
|
|
|
.select(table)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(users, &[
|
|
|
|
RecordBuf {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: thing("user:amos").unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
name: "Amos".to_owned(),
|
|
|
|
},
|
|
|
|
RecordBuf {
|
2023-03-30 10:41:44 +00:00
|
|
|
id: thing("user:zoey").unwrap(),
|
2022-12-30 08:23:19 +00:00
|
|
|
name: "Zoey".to_owned(),
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn version() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.version().await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn set_unset() {
|
|
|
|
let db = new_db().await;
|
|
|
|
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
|
|
|
let (key, value) = ("name", "Doe");
|
|
|
|
let sql = "RETURN $name";
|
|
|
|
db.set(key, value).await.unwrap();
|
|
|
|
let mut response = db.query(sql).await.unwrap();
|
|
|
|
let Some(name): Option<String> = response.take(0).unwrap() else {
|
|
|
|
panic!("record not found");
|
|
|
|
};
|
|
|
|
assert_eq!(name, value);
|
|
|
|
db.unset(key).await.unwrap();
|
|
|
|
let mut response = db.query(sql).await.unwrap();
|
|
|
|
let name: Option<String> = response.take(0).unwrap();
|
|
|
|
assert!(name.is_none());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn return_bool() {
|
|
|
|
let db = new_db().await;
|
|
|
|
let mut response = db.query("RETURN true").await.unwrap();
|
|
|
|
let Some(boolean): Option<bool> = response.take(0).unwrap() else {
|
|
|
|
panic!("record not found");
|
|
|
|
};
|
|
|
|
assert!(boolean);
|
2023-04-28 11:20:57 +00:00
|
|
|
let mut response = db.query("RETURN false").await.unwrap();
|
|
|
|
let value: Value = response.take(0).unwrap();
|
2023-04-29 15:58:22 +00:00
|
|
|
assert_eq!(value, vec![Value::Bool(false)].into());
|
2022-12-30 08:23:19 +00:00
|
|
|
}
|