#![cfg(all(feature = "wal2json", feature = "pg-walstream", feature = "maxwell"))]
extern crate alloc;
use alloc::string::String;
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, WireType,
};
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",
wire_type: WireType::Uuid,
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",
wire_type: WireType::Uuid,
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",
wire_type: WireType::Uuid,
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",
wire_type: WireType::Uuid,
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",
wire_type: WireType::Uuid,
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",
wire_type: WireType::Uuid,
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",
wire_type: WireType::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",
wire_type: WireType::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",
wire_type: WireType::Uuid,
value: &s,
}
.decoded_by(&UuidBlob16Decoder)
.unwrap();
assert_eq!(got, Value::Blob(UUID_BYTES.to_vec()));
}
#[test]
fn uuid_shape_chosen_at_registration() {
let mut blob_map: TypeMap<Wal2Json, String, Vec<u8>> = TypeMap::new();
blob_map.register(WireType::Uuid, UuidBlob16Decoder);
let mut text_map: TypeMap<Wal2Json, String, Vec<u8>> = TypeMap::new();
text_map.register(WireType::Uuid, UuidText36Decoder);
let s = serde_json::Value::String(UUID_HYPHENATED.into());
let as_blob = blob_map
.decode(Wal2JsonColumn {
column_name: "a",
wire_type: WireType::Uuid,
value: &s,
})
.unwrap();
let as_text = text_map
.decode(Wal2JsonColumn {
column_name: "b",
wire_type: WireType::Uuid,
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_route_uuid_to_text36() {
let pg: TypeMap<PgWalstream, String, Vec<u8>> = TypeMap::defaults();
let cv = ColumnValue::text(UUID_HYPHENATED);
let got = pg
.decode(PgWalstreamColumn {
column_name: "id",
wire_type: WireType::Uuid,
data: &cv,
})
.unwrap();
assert_eq!(got, Value::Text(String::from(UUID_HYPHENATED)));
}