use spg_engine::{Engine, QueryResult};
use spg_storage::Value;
fn one_value(eng: &mut Engine, sql: &str) -> Value<'static> {
match eng.execute(sql).unwrap() {
QueryResult::Rows { rows, .. } => rows.into_iter().next().unwrap().values[0].clone(),
_ => panic!("expected Rows"),
}
}
#[test]
fn encode_hex_round_trip() {
let mut eng = Engine::new();
let v = one_value(&mut eng, "SELECT encode('hello', 'hex')");
assert_eq!(v, Value::text("68656c6c6f"));
let v2 = one_value(&mut eng, "SELECT decode('68656c6c6f', 'hex')");
assert_eq!(v2, Value::Bytes(std::borrow::Cow::Owned(b"hello".to_vec())));
}
#[test]
fn encode_base64_round_trip() {
let mut eng = Engine::new();
let v = one_value(&mut eng, "SELECT encode('Hello, World!', 'base64')");
assert_eq!(v, Value::text("SGVsbG8sIFdvcmxkIQ=="));
let v2 = one_value(&mut eng, "SELECT decode('SGVsbG8sIFdvcmxkIQ==', 'base64')");
assert_eq!(
v2,
Value::Bytes(std::borrow::Cow::Owned(b"Hello, World!".to_vec()))
);
}
#[test]
fn encode_base64url_alphabet_differs_from_std() {
let mut eng = Engine::new();
let v_std = one_value(&mut eng, "SELECT encode('hello?>', 'base64')");
let v_url = one_value(&mut eng, "SELECT encode('hello?>', 'base64url')");
match (v_std, v_url) {
(Value::Text(_s_std), Value::Text(s_url)) => {
assert!(!s_url.contains('+'), "url alphabet has no +");
assert!(!s_url.contains('/'), "url alphabet has no /");
}
_ => panic!("expected text"),
}
}
#[test]
fn encode_base32hex_round_trip() {
let mut eng = Engine::new();
let v = one_value(&mut eng, "SELECT encode('foo', 'base32hex')");
assert_eq!(v, Value::text("CPNMU==="));
let v2 = one_value(&mut eng, "SELECT decode('CPNMU===', 'base32hex')");
assert_eq!(v2, Value::Bytes(std::borrow::Cow::Owned(b"foo".to_vec())));
}
#[test]
fn encode_null_propagates() {
let mut eng = Engine::new();
let v = one_value(&mut eng, "SELECT encode(NULL, 'hex')");
assert_eq!(v, Value::Null);
let v2 = one_value(&mut eng, "SELECT decode('68', NULL)");
assert_eq!(v2, Value::Null);
}
#[test]
fn encode_unknown_format_errors() {
let mut eng = Engine::new();
let r = eng.execute("SELECT encode('x', 'rot13')");
assert!(r.is_err(), "rot13 isn't a supported encode format");
}
#[test]
fn error_on_null_returns_value_when_non_null() {
let mut eng = Engine::new();
let v = one_value(&mut eng, "SELECT error_on_null(42)");
assert!(matches!(v, Value::Int(42) | Value::BigInt(42)));
let v2 = one_value(&mut eng, "SELECT error_on_null('abc')");
assert_eq!(v2, Value::text("abc"));
}
#[test]
fn error_on_null_errors_on_null() {
let mut eng = Engine::new();
let r = eng.execute("SELECT error_on_null(NULL)");
assert!(r.is_err(), "error_on_null(NULL) must error");
let msg = format!("{:?}", r.err().unwrap());
assert!(
msg.contains("error_on_null"),
"error mentions function name"
);
}
#[test]
fn encode_base64_wraps_at_76_like_pg() {
let mut eng = Engine::new();
let wrapped = one_value(
&mut eng,
"SELECT encode(decode(repeat('61',60),'hex'),'base64')",
);
let spg_storage::Value::Text(s) = &wrapped else {
panic!("expected Text, got {wrapped:?}")
};
assert_eq!(s.len(), 81, "{s:?}");
assert!(s.contains('\n'));
assert!(!s.ends_with('\n'));
let short = one_value(&mut eng, "SELECT encode('abc'::bytea,'base64')");
let spg_storage::Value::Text(s) = &short else {
panic!()
};
assert_eq!(s.as_ref(), "YWJj");
let rt = one_value(
&mut eng,
"SELECT decode(encode(decode(repeat('61',60),'hex'),'base64'),'base64') \
= decode(repeat('61',60),'hex')",
);
assert_eq!(rt, spg_storage::Value::Bool(true));
}
#[test]
fn decode_escape_matches_pg() {
let mut eng = Engine::new();
assert_eq!(
one_value(&mut eng, "SELECT decode('a\\134b','escape')"),
spg_storage::Value::Bytes(alloc_cow(&[0x61, 0x5c, 0x62]))
);
assert_eq!(
one_value(&mut eng, "SELECT decode('a\\\\b','escape')"),
spg_storage::Value::Bytes(alloc_cow(&[0x61, 0x5c, 0x62]))
);
assert_eq!(
one_value(&mut eng, "SELECT decode('abc','escape')"),
spg_storage::Value::Bytes(alloc_cow(&[0x61, 0x62, 0x63]))
);
let rt = one_value(
&mut eng,
"SELECT decode(encode(E'\\x000141ff'::bytea,'escape'),'escape') = E'\\x000141ff'::bytea",
);
assert_eq!(rt, spg_storage::Value::Bool(true));
assert!(eng.execute("SELECT decode('a\\x','escape')").is_err());
}
fn alloc_cow(b: &[u8]) -> std::borrow::Cow<'static, [u8]> {
std::borrow::Cow::Owned(b.to_vec())
}