#![cfg(all(feature = "wal2json", feature = "pg-walstream", feature = "maxwell"))]
extern crate alloc;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use sqlite_diff_rs::maxwell::MaxwellColumn;
use sqlite_diff_rs::pg_walstream::{ColumnValue, PgWalstream, PgWalstreamColumn};
use sqlite_diff_rs::wal2json::{Wal2Json, Wal2JsonColumn};
use sqlite_diff_rs::{
DecodeError, TypeMap, UuidBlob16Decoder, UuidText36Decoder, Value, WireAdapter,
};
const UUID_HYPHENATED: &str = "550e8400-e29b-41d4-a716-446655440000";
const UUID_BRACED: &str = "{550e8400-e29b-41d4-a716-446655440000}";
const UUID_UPPERCASE: &str = "550E8400-E29B-41D4-A716-446655440000";
const UUID_BYTES: [u8; 16] = [
0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44, 0x00, 0x00,
];
#[test]
fn uuid_blob16_pg_walstream_hyphenated() {
let cv = ColumnValue::text(UUID_HYPHENATED);
let got: Value<String, Vec<u8>> = PgWalstreamColumn {
column_name: "id",
oid: 2950,
type_modifier: -1,
data: &cv,
}
.decoded_by(&UuidBlob16Decoder)
.unwrap();
assert_eq!(got, Value::Blob(UUID_BYTES.to_vec()));
}
#[test]
fn uuid_blob16_pg_walstream_braced_and_uppercase() {
for form in [UUID_BRACED, UUID_UPPERCASE] {
let cv = ColumnValue::text(form);
let got: Value<String, Vec<u8>> = PgWalstreamColumn {
column_name: "id",
oid: 2950,
type_modifier: -1,
data: &cv,
}
.decoded_by(&UuidBlob16Decoder)
.unwrap();
assert_eq!(got, Value::Blob(UUID_BYTES.to_vec()), "form {form}");
}
}
#[test]
fn uuid_blob16_pg_walstream_null() {
let cv = ColumnValue::Null;
let got: Value<String, Vec<u8>> = PgWalstreamColumn {
column_name: "id",
oid: 2950,
type_modifier: -1,
data: &cv,
}
.decoded_by(&UuidBlob16Decoder)
.unwrap();
assert_eq!(got, Value::Null);
}
#[test]
fn uuid_blob16_pg_walstream_rejects_bad_input() {
let cv = ColumnValue::text("not a uuid");
let result: Result<Value<String, Vec<u8>>, _> = PgWalstreamColumn {
column_name: "id",
oid: 2950,
type_modifier: -1,
data: &cv,
}
.decoded_by(&UuidBlob16Decoder);
assert!(matches!(
result.unwrap_err(),
DecodeError::InvalidUuid { .. }
));
}
#[test]
fn uuid_text36_pg_walstream_hyphenated_verbatim() {
let cv = ColumnValue::text(UUID_HYPHENATED);
let got: Value<String, Vec<u8>> = PgWalstreamColumn {
column_name: "id",
oid: 2950,
type_modifier: -1,
data: &cv,
}
.decoded_by(&UuidText36Decoder)
.unwrap();
assert_eq!(got, Value::Text(String::from(UUID_HYPHENATED)));
}
#[test]
fn uuid_text36_pg_walstream_braced_normalized_to_36() {
let cv = ColumnValue::text(UUID_BRACED);
let got: Value<String, Vec<u8>> = PgWalstreamColumn {
column_name: "id",
oid: 2950,
type_modifier: -1,
data: &cv,
}
.decoded_by(&UuidText36Decoder)
.unwrap();
match got {
Value::Text(s) => {
assert_eq!(s.len(), 36, "expected 36-char UUID, got {}", s.len());
}
other => panic!("expected Text, got {other:?}"),
}
}
#[test]
fn uuid_blob16_wal2json_string() {
let s = serde_json::Value::String(UUID_HYPHENATED.into());
let got: Value<String, Vec<u8>> = Wal2JsonColumn {
column_name: "id",
pg_type_name: "uuid",
value: &s,
}
.decoded_by(&UuidBlob16Decoder)
.unwrap();
assert_eq!(got, Value::Blob(UUID_BYTES.to_vec()));
}
#[test]
fn uuid_text36_wal2json_string() {
let s = serde_json::Value::String(UUID_HYPHENATED.into());
let got: Value<String, Vec<u8>> = Wal2JsonColumn {
column_name: "id",
pg_type_name: "uuid",
value: &s,
}
.decoded_by(&UuidText36Decoder)
.unwrap();
assert_eq!(got, Value::Text(String::from(UUID_HYPHENATED)));
}
#[test]
fn uuid_blob16_maxwell_string() {
let s = serde_json::Value::String(UUID_HYPHENATED.into());
let got: Value<String, Vec<u8>> = MaxwellColumn {
column_name: "id",
mysql_type: Some("varchar"),
value: &s,
}
.decoded_by(&UuidBlob16Decoder)
.unwrap();
assert_eq!(got, Value::Blob(UUID_BYTES.to_vec()));
}
#[test]
fn uuid_dispatch_discriminator_per_key() {
let mut types: TypeMap<Wal2Json, String, Vec<u8>> = TypeMap::new();
types.register(Arc::from("uuid"), UuidBlob16Decoder);
types.register(Arc::from("uuid_text"), UuidText36Decoder);
let s = serde_json::Value::String(UUID_HYPHENATED.into());
let as_blob = types
.decode(Wal2JsonColumn {
column_name: "a",
pg_type_name: "uuid",
value: &s,
})
.unwrap();
let as_text = types
.decode(Wal2JsonColumn {
column_name: "b",
pg_type_name: "uuid_text",
value: &s,
})
.unwrap();
assert_eq!(as_blob, Value::Blob(UUID_BYTES.to_vec()));
assert_eq!(as_text, Value::Text(String::from(UUID_HYPHENATED)));
assert_ne!(as_blob, as_text);
}
#[test]
fn defaults_do_not_carry_uuid_by_default() {
let pg: TypeMap<PgWalstream, String, Vec<u8>> = TypeMap::defaults();
let cv = ColumnValue::text(UUID_HYPHENATED);
let err = pg
.decode(PgWalstreamColumn {
column_name: "id",
oid: 2950, type_modifier: -1,
data: &cv,
})
.unwrap_err();
assert!(matches!(err, DecodeError::NoDecoderForType { .. }));
}