use bumpalo::Bump;
use reifydb_core::fingerprint::{RequestFingerprint, StatementFingerprint};
use super::{request::fingerprint_request, statement::fingerprint_statement};
use crate::ast::parse_str;
fn fp(query: &str) -> StatementFingerprint {
let bump = Bump::new();
let stmts = parse_str(&bump, query).unwrap();
assert_eq!(stmts.len(), 1, "expected single statement for: {query}");
fingerprint_statement(&stmts[0])
}
fn fp_request(query: &str) -> (Vec<StatementFingerprint>, RequestFingerprint) {
let bump = Bump::new();
let stmts = parse_str(&bump, query).unwrap();
let fps: Vec<_> = stmts.iter().map(|s| fingerprint_statement(s)).collect();
let req = fingerprint_request(&fps);
(fps, req)
}
#[test]
fn same_pattern_different_numbers() {
assert_eq!(fp("FROM users FILTER {id == 42}"), fp("FROM users FILTER {id == 99}"),);
}
#[test]
fn same_pattern_different_strings() {
assert_eq!(fp("FROM users FILTER {name == 'alice'}"), fp("FROM users FILTER {name == 'bob'}"),);
}
#[test]
fn same_pattern_different_in_list_values() {
assert_eq!(fp("FROM users FILTER {id IN [1, 2, 3]}"), fp("FROM users FILTER {id IN [4, 5, 6]}"),);
}
#[test]
fn same_insert_pattern() {
assert_eq!(fp("INSERT users [{id: 1, name: 'alice'}]"), fp("INSERT users [{id: 2, name: 'bob'}]"),);
}
#[test]
fn different_tables() {
assert_ne!(fp("FROM users FILTER {id == 1}"), fp("FROM orders FILTER {id == 1}"),);
}
#[test]
fn different_operators() {
assert_ne!(fp("FROM users FILTER {id == 1}"), fp("FROM users FILTER {id != 1}"),);
}
#[test]
fn different_columns() {
assert_ne!(fp("FROM users FILTER {id == 1}"), fp("FROM users FILTER {name == 1}"),);
}
#[test]
fn different_statement_types() {
assert_ne!(fp("FROM users FILTER {id == 1}"), fp("DELETE users FILTER {id == 1}"),);
}
#[test]
fn different_literal_types() {
assert_ne!(fp("FROM users FILTER {x == 1}"), fp("FROM users FILTER {x == 'hello'}"),);
}
#[test]
fn request_fingerprint_single_statement() {
let (fps, _req) = fp_request("FROM users FILTER {id == 1}");
assert_eq!(fps.len(), 1);
}
#[test]
fn request_fingerprint_same_pattern() {
let (_, req1) = fp_request("FROM users FILTER {id == 1}; FROM orders FILTER {id == 1}");
let (_, req2) = fp_request("FROM users FILTER {id == 99}; FROM orders FILTER {id == 99}");
assert_eq!(req1, req2);
}
#[test]
fn request_fingerprint_different_order() {
let (_, req1) = fp_request("FROM users FILTER {id == 1}; FROM orders FILTER {id == 1}");
let (_, req2) = fp_request("FROM orders FILTER {id == 1}; FROM users FILTER {id == 1}");
assert_ne!(req1, req2);
}
#[test]
fn fingerprint_is_deterministic() {
let a = fp("FROM users FILTER {id == 42}");
let b = fp("FROM users FILTER {id == 42}");
assert_eq!(a, b);
}
#[test]
fn statement_fingerprint_bytes_roundtrip() {
let fp1 = fp("FROM users FILTER {id == 1}");
let bytes = fp1.to_le_bytes();
let fp2 = StatementFingerprint::from_le_bytes(bytes);
assert_eq!(fp1, fp2);
}
#[test]
fn request_fingerprint_bytes_roundtrip() {
let (_, req1) = fp_request("FROM users FILTER {id == 1}");
let bytes = req1.to_le_bytes();
let req2 = RequestFingerprint::from_le_bytes(bytes);
assert_eq!(req1, req2);
}
#[test]
fn create_queue_does_not_collide_with_other_primitives() {
let queue = fp("CREATE QUEUE ns::jobs { id: int4 } WITH { fifo: {} }");
assert_ne!(queue, fp("CREATE TABLE ns::jobs { id: int4 }"));
assert_ne!(queue, fp("CREATE RINGBUFFER ns::jobs { id: int4 } WITH { capacity: 10 }"));
assert_ne!(queue, fp("CREATE SERIES ns::jobs { id: int4 } WITH { key: id }"));
}
#[test]
fn drop_queue_does_not_collide_with_other_primitives() {
let queue = fp("DROP QUEUE ns::jobs");
assert_ne!(queue, fp("DROP TABLE ns::jobs"));
assert_ne!(queue, fp("DROP RINGBUFFER ns::jobs"));
assert_ne!(queue, fp("DROP SERIES ns::jobs"));
}
#[test]
fn create_queue_identity_covers_name_namespace_and_columns() {
let base = fp("CREATE QUEUE ns::jobs { id: int4 } WITH { fifo: {} }");
assert_ne!(base, fp("CREATE QUEUE ns::other { id: int4 } WITH { fifo: {} }"));
assert_ne!(base, fp("CREATE QUEUE other::jobs { id: int4 } WITH { fifo: {} }"));
assert_ne!(base, fp("CREATE QUEUE ns::jobs { id: int4, extra: utf8 } WITH { fifo: {} }"));
assert_ne!(base, fp("CREATE QUEUE ns::jobs { id: utf8 } WITH { fifo: {} }"));
}
#[test]
fn create_queue_is_stable_across_identical_statements() {
assert_eq!(
fp("CREATE QUEUE ns::jobs { id: int4 } WITH { fifo: { partitions: 8 } }"),
fp("CREATE QUEUE ns::jobs { id: int4 } WITH { fifo: { partitions: 8 } }")
);
}
#[test]
fn create_user_and_create_service_differ() {
assert_ne!(fp("CREATE USER alice"), fp("CREATE SERVICE alice"));
}
#[test]
fn create_service_fingerprint_is_stable() {
assert_eq!(fp("CREATE SERVICE alice"), fp("CREATE SERVICE alice"));
}