use std::collections::hash_map::DefaultHasher;
use std::collections::{
HashMap,
HashSet,
};
use std::fmt::Debug;
use std::hash::{
Hash,
Hasher,
};
use std::time::Duration;
use bigdecimal::BigDecimal;
use chrono::{
DateTime,
NaiveDate,
NaiveTime,
Utc,
};
use num_bigint::BigInt;
use qubit_datatype::DataType;
use qubit_value::MultiValues;
use url::Url;
fn hash<T: Hash>(value: &T) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
fn assert_equal_hash<T>(left: &T, right: &T)
where
T: Debug + Eq + Hash,
{
assert_eq!(left, right);
assert_eq!(hash(left), hash(right));
}
#[test]
fn test_multi_values_float_identity_is_reflexive_and_hash_consistent() {
assert_equal_hash(
&MultiValues::Float32(vec![-0.0, f32::from_bits(0x7fc0_0001)]),
&MultiValues::Float32(vec![0.0, f32::from_bits(0x7fff_ffff)]),
);
assert_equal_hash(
&MultiValues::Float64(vec![
-0.0,
f64::from_bits(0x7ff8_0000_0000_0001),
]),
&MultiValues::Float64(vec![0.0, f64::from_bits(0x7fff_ffff_ffff_ffff)]),
);
let mut cache = HashMap::new();
cache.insert(MultiValues::Float64(vec![f64::NAN]), "first");
cache.insert(
MultiValues::Float64(vec![f64::from_bits(0x7fff_ffff_ffff_ffff)]),
"replacement",
);
assert_eq!(cache.len(), 1);
assert_eq!(cache[&MultiValues::Float64(vec![f64::NAN])], "replacement");
}
#[test]
fn test_multi_values_unset_variant_and_order_remain_part_of_identity() {
assert_ne!(
MultiValues::Unset(DataType::Int32),
MultiValues::Int32(Vec::new()),
);
assert_ne!(
MultiValues::Unset(DataType::Int32),
MultiValues::Unset(DataType::Int64),
);
assert_ne!(
MultiValues::Int32(vec![1, 2]),
MultiValues::Int32(vec![2, 1]),
);
assert_ne!(MultiValues::Int32(vec![1]), MultiValues::Int64(vec![1]),);
}
#[test]
fn test_multi_values_unordered_payloads_hash_structurally() {
let left_map = HashMap::from([
("b".to_owned(), "2".to_owned()),
("a".to_owned(), "1".to_owned()),
]);
let right_map = HashMap::from([
("a".to_owned(), "1".to_owned()),
("b".to_owned(), "2".to_owned()),
]);
assert_equal_hash(
&MultiValues::StringMap(vec![left_map]),
&MultiValues::StringMap(vec![right_map]),
);
assert_equal_hash(
&MultiValues::Json(vec![
serde_json::json!({"b": {"y": 2, "x": 1}, "a": 0}),
]),
&MultiValues::Json(vec![
serde_json::json!({"a": 0, "b": {"x": 1, "y": 2}}),
]),
);
}
#[test]
fn test_multi_values_big_decimal_identity_is_canonical() {
assert_equal_hash(
&MultiValues::BigDecimal(vec![BigDecimal::new(BigInt::from(10), 1)]),
&MultiValues::BigDecimal(vec![BigDecimal::new(BigInt::from(1), 0)]),
);
let extreme = MultiValues::BigDecimal(vec![BigDecimal::new(
BigInt::from(1),
i64::MIN,
)]);
let _ = hash(&extreme);
}
#[test]
fn test_multi_values_identity_covers_every_variant() {
let date = NaiveDate::from_ymd_opt(2026, 7, 17)
.expect("the test fixture date must be valid");
let time = NaiveTime::from_hms_nano_opt(12, 34, 56, 789)
.expect("the test fixture time must be valid");
let datetime = date.and_time(time);
let values = vec![
MultiValues::Unset(DataType::Bool),
MultiValues::Bool(vec![true]),
MultiValues::Char(vec!['x']),
MultiValues::Int8(vec![-1]),
MultiValues::Int16(vec![-2]),
MultiValues::Int32(vec![-3]),
MultiValues::Int64(vec![-4]),
MultiValues::Int128(vec![-5]),
MultiValues::UInt8(vec![1]),
MultiValues::UInt16(vec![2]),
MultiValues::UInt32(vec![3]),
MultiValues::UInt64(vec![4]),
MultiValues::UInt128(vec![5]),
MultiValues::Float32(vec![f32::NAN]),
MultiValues::Float64(vec![f64::NAN]),
MultiValues::BigInteger(vec![BigInt::from(6)]),
MultiValues::BigDecimal(vec![BigDecimal::from(7)]),
MultiValues::String(vec!["text".to_owned()]),
MultiValues::Date(vec![date]),
MultiValues::Time(vec![time]),
MultiValues::DateTime(vec![datetime]),
MultiValues::Instant(vec![DateTime::<Utc>::from_naive_utc_and_offset(
datetime, Utc,
)]),
MultiValues::Duration(vec![Duration::new(8, 9)]),
MultiValues::Url(vec![
Url::parse("https://example.com/path")
.expect("the test fixture URL must be valid"),
]),
MultiValues::StringMap(vec![HashMap::from([(
"key".to_owned(),
"value".to_owned(),
)])]),
MultiValues::Json(vec![serde_json::json!({"items": [null, true, 42]})]),
];
for value in &values {
assert_eq!(value, value);
let _ = hash(value);
}
let keys: HashSet<_> = values.into_iter().collect();
assert_eq!(keys.len(), 26);
}