mod support;
use cratefield_core::Module;
use cratefield_module_device_auth::{DeviceAuth, DeviceClient};
use support::{CountingIssuer, SeqRandom};
const SQL: &str = include_str!("../migrations/sqlite/0001_init.sql");
fn columns(sql: &str) -> Vec<String> {
let start = sql
.find("CREATE TABLE")
.expect("the migration creates a table");
let open = start + sql[start..].find('(').expect("the column list opens");
let close = open + sql[open..].find(')').expect("the column list closes");
sql[open + 1..close]
.split(',')
.map(str::trim)
.filter(|line| !line.is_empty())
.map(|line| {
line.split_whitespace()
.next()
.expect("a column definition names a column")
.to_ascii_lowercase()
})
.collect()
}
fn module() -> DeviceAuth {
DeviceAuth::builder()
.client(DeviceClient::new("sealb-cli").scopes(["read", "write"]))
.issuer(CountingIssuer::new())
.random(SeqRandom::new())
.build()
}
#[test]
fn the_migration_creates_the_declared_table() {
let module = module();
assert_eq!(module.tables(), ["device_auth_codes"]);
assert!(
SQL.contains("CREATE TABLE IF NOT EXISTS device_auth_codes"),
"the migration and the declaration name the same table"
);
}
#[test]
fn no_clear_text_code_or_secret_column_exists() {
let columns = columns(SQL);
assert!(!columns.is_empty(), "the column scan found nothing");
for column in &columns {
for stem in ["code", "token", "secret"] {
if column.contains(stem) {
assert!(
column.ends_with("_hash"),
"column `{column}` carries the stem `{stem}` but is not a hash: \
it would store a replayable value"
);
}
}
for bare in [
"code",
"token",
"secret",
"device_code",
"user_code",
"access_token",
"refresh_token",
] {
assert_ne!(
column, bare,
"column `{column}` stores a value in the clear"
);
}
}
for hash in ["device_code_hash", "user_code_hash"] {
assert!(
columns.contains(&hash.to_owned()),
"missing {hash}: {columns:?}"
);
}
}
#[test]
fn the_code_hashes_are_the_keys() {
assert!(
SQL.contains("device_code_hash TEXT PRIMARY KEY"),
"the device code hash is the primary key"
);
assert!(
SQL.contains("user_code_hash TEXT NOT NULL UNIQUE"),
"the user code hash is present and unique"
);
}
#[test]
fn the_columns_the_queries_filter_on_are_indexed() {
assert!(SQL.contains("device_auth_codes_expires_idx ON device_auth_codes (expires_at)"));
assert!(SQL.contains("device_auth_codes_status_idx ON device_auth_codes (status)"));
}