use std::any::TypeId;
use std::any::type_name;
use qubit_codec::JSON_BYTES_VALUE_CODEC_ID;
use qubit_codec::JSON_STRING_VALUE_CODEC_ID;
use qubit_codec::JsonBytesValueCodec;
use qubit_codec::JsonStringValueCodec;
use qubit_codec::ValueBytesCodecDescriptor;
use qubit_codec::ValueBytesCodecRegistry;
use qubit_codec::ValueCodecExecutionError;
use qubit_codec::ValueCodecId;
use qubit_codec::ValueStringCodecDescriptor;
use qubit_codec::ValueStringCodecRegistry;
use qubit_codec::register_value_bytes_codec;
use qubit_codec::register_value_string_codec;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
use serde_json::json;
static STRING_DESCRIPTOR: ValueStringCodecDescriptor =
ValueStringCodecDescriptor::of::<JsonStringValueCodec<Value>, Value>();
static BYTES_DESCRIPTOR: ValueBytesCodecDescriptor =
ValueBytesCodecDescriptor::of::<JsonBytesValueCodec<Value>, Value>();
#[derive(Deserialize, Serialize)]
struct RequiredDto {
text: String,
}
static REQUIRED_BYTES: ValueBytesCodecDescriptor =
ValueBytesCodecDescriptor::of::<JsonBytesValueCodec<RequiredDto>, RequiredDto>();
static REQUIRED_STRING: ValueStringCodecDescriptor =
ValueStringCodecDescriptor::of::<JsonStringValueCodec<RequiredDto>, RequiredDto>();
register_value_bytes_codec!(
id = "review.required.bytes",
codec = JsonBytesValueCodec<RequiredDto>,
value = RequiredDto,
);
register_value_string_codec!(
id = "review.required.string",
codec = JsonStringValueCodec<RequiredDto>,
value = RequiredDto,
);
#[test]
fn test_json_value_codec_ids_match_stable_constants() {
assert_eq!(
ValueCodecId::new(JSON_STRING_VALUE_CODEC_ID).as_str(),
"qubit.codec.json.string"
);
assert_eq!(
ValueCodecId::new(JSON_BYTES_VALUE_CODEC_ID).as_str(),
"qubit.codec.json.bytes"
);
}
#[test]
fn test_json_string_value_codec_descriptor_metadata() {
assert_eq!(
STRING_DESCRIPTOR.codec_type_id(),
TypeId::of::<JsonStringValueCodec<Value>>()
);
assert_eq!(
STRING_DESCRIPTOR.codec_type_name(),
type_name::<JsonStringValueCodec<Value>>()
);
assert_eq!(STRING_DESCRIPTOR.value_type_id(), TypeId::of::<Value>());
assert_eq!(STRING_DESCRIPTOR.value_type_name(), type_name::<Value>());
assert!(format!("{STRING_DESCRIPTOR:?}").contains("JsonStringValueCodec"));
}
#[test]
fn test_json_bytes_value_codec_descriptor_metadata() {
assert_eq!(
BYTES_DESCRIPTOR.codec_type_id(),
TypeId::of::<JsonBytesValueCodec<Value>>()
);
assert_eq!(BYTES_DESCRIPTOR.value_type_id(), TypeId::of::<Value>());
assert!(format!("{BYTES_DESCRIPTOR:?}").contains("JsonBytesValueCodec"));
}
#[test]
fn test_json_string_value_codec_descriptor_executes_both_directions() {
let encoded = STRING_DESCRIPTOR
.encode(&json!({"ok": true}))
.expect("encode should succeed");
assert_eq!(encoded, r#"{"ok":true}"#);
let decoded = STRING_DESCRIPTOR.decode(&encoded).expect("decode should succeed");
assert_eq!(decoded.downcast_ref::<Value>(), Some(&json!({"ok": true})));
}
#[test]
fn test_json_bytes_value_codec_descriptor_executes_both_directions() {
let encoded = BYTES_DESCRIPTOR
.encode(&json!([1, 2, 3]))
.expect("encode should succeed");
assert_eq!(encoded, br#"[1,2,3]"#);
let decoded = BYTES_DESCRIPTOR.decode(&encoded).expect("decode should succeed");
assert_eq!(decoded.downcast_ref::<Value>(), Some(&json!([1, 2, 3])));
}
#[test]
fn test_json_string_value_codec_descriptor_reports_type_and_domain_errors() {
let mismatch = STRING_DESCRIPTOR.encode(&42_u32).expect_err("wrong value type");
assert!(matches!(mismatch, ValueCodecExecutionError::TypeMismatch { .. }));
assert!(mismatch.to_string().contains(type_name::<Value>()));
let decode = STRING_DESCRIPTOR.decode("{").expect_err("malformed JSON");
assert!(matches!(decode, ValueCodecExecutionError::DecodeFailed { .. }));
}
#[test]
fn test_json_bytes_value_codec_descriptor_reports_type_and_domain_errors() {
let mismatch = BYTES_DESCRIPTOR
.encode(&"not-json-value")
.expect_err("wrong value type");
assert!(matches!(mismatch, ValueCodecExecutionError::TypeMismatch { .. }));
let decode = BYTES_DESCRIPTOR.decode(b"[").expect_err("malformed JSON");
assert!(matches!(decode, ValueCodecExecutionError::DecodeFailed { .. }));
}
#[test]
fn test_global_registries_collect_json_value_codec_registrations() {
let string_registry = ValueStringCodecRegistry::try_global().expect("valid string registry");
let string_registration = string_registry
.get(JSON_STRING_VALUE_CODEC_ID)
.expect("string JSON codec should be registered");
assert_eq!(string_registration.id(), ValueCodecId::new(JSON_STRING_VALUE_CODEC_ID));
assert_eq!(
string_registration.descriptor().codec_type_id(),
TypeId::of::<JsonStringValueCodec<Value>>()
);
assert_eq!(string_registration.source().crate_name(), env!("CARGO_PKG_NAME"));
let bytes_registry = ValueBytesCodecRegistry::try_global().expect("valid bytes registry");
let bytes_registration = bytes_registry
.get(JSON_BYTES_VALUE_CODEC_ID)
.expect("bytes JSON codec should be registered");
assert_eq!(bytes_registration.id(), ValueCodecId::new(JSON_BYTES_VALUE_CODEC_ID));
assert_eq!(
bytes_registration.descriptor().codec_type_id(),
TypeId::of::<JsonBytesValueCodec<Value>>()
);
assert_eq!(bytes_registration.source().crate_name(), env!("CARGO_PKG_NAME"));
}
#[test]
fn test_json_string_and_bytes_registries_have_distinct_codec_ids() {
let string = ValueStringCodecRegistry::global()
.get(JSON_STRING_VALUE_CODEC_ID)
.expect("string registration");
let bytes = ValueBytesCodecRegistry::global()
.get(JSON_BYTES_VALUE_CODEC_ID)
.expect("bytes registration");
assert_ne!(string.id(), bytes.id());
assert_ne!(string.descriptor().codec_type_id(), bytes.descriptor().codec_type_id());
}
#[test]
fn test_json_codecs_register_required_dto_without_value_traits() {
let bytes = REQUIRED_BYTES
.encode(&RequiredDto {
text: "bytes".to_owned(),
})
.expect("bytes descriptor should encode the required DTO");
assert_eq!(bytes, br#"{"text":"bytes"}"#);
let decoded_bytes = REQUIRED_BYTES.decode(&bytes).expect("bytes descriptor should decode");
assert_eq!(
decoded_bytes.downcast_ref::<RequiredDto>().map(|dto| dto.text.as_str()),
Some("bytes")
);
let string = REQUIRED_STRING
.encode(&RequiredDto {
text: "string".to_owned(),
})
.expect("string descriptor should encode the required DTO");
assert_eq!(string, r#"{"text":"string"}"#);
let decoded_string = REQUIRED_STRING
.decode(&string)
.expect("string descriptor should decode");
assert_eq!(
decoded_string
.downcast_ref::<RequiredDto>()
.map(|dto| dto.text.as_str()),
Some("string")
);
}