#![allow(clippy::unwrap_used, clippy::panic)]
use batpak::coordinate::DagPosition;
use batpak::event::{DecodeSource, DecodeTyped, Event, EventHeader, EventKind, TypedDecodeError};
use batpak::EventPayload;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, EventPayload)]
#[batpak(category = 1, type_id = 1)]
struct Alpha {
value: u64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, EventPayload)]
#[batpak(category = 1, type_id = 2)]
struct Beta {
label: String,
}
fn make_header(event_id: u128, kind: EventKind, payload_size: u32) -> EventHeader {
EventHeader::new(
event_id,
0,
None,
0,
DagPosition::root(),
payload_size,
kind,
)
}
fn json_event(kind: EventKind, payload: serde_json::Value) -> Event<serde_json::Value> {
Event::new(make_header(1, kind, 0), payload)
}
fn msgpack_event(kind: EventKind, bytes: Vec<u8>) -> Event<Vec<u8>> {
let size = u32::try_from(bytes.len()).unwrap_or(u32::MAX);
Event::new(make_header(2, kind, size), bytes)
}
fn assert_decode_typed_lane<T: DecodeTyped + ?Sized>(_event: &T) {}
mod json_lane {
use super::*;
#[test]
fn route_typed_match_decode_ok() {
let event = json_event(Alpha::KIND, serde_json::json!({ "value": 42 }));
assert_decode_typed_lane(&event);
let routed: Option<Alpha> = event.route_typed().expect("route_typed");
assert_eq!(routed, Some(Alpha { value: 42 }));
}
#[test]
fn route_typed_kind_mismatch_returns_none() {
let event = json_event(Beta::KIND, serde_json::json!({ "label": "x" }));
let routed: Option<Alpha> = event.route_typed().expect("route_typed");
assert!(
routed.is_none(),
"PROPERTY: route_typed must return Ok(None) on kind mismatch, not an error"
);
}
#[test]
fn route_typed_decode_failure_propagates_err() {
let event = json_event(Alpha::KIND, serde_json::json!({ "label": "not a number" }));
let result: Result<Option<Alpha>, TypedDecodeError> = event.route_typed();
let err = result.expect_err("kind matched but decode should fail");
match err {
TypedDecodeError::DecodeFailure { kind, source } => {
assert_eq!(kind, Alpha::KIND);
assert!(matches!(source, DecodeSource::Json(_)));
}
other => panic!("expected DecodeFailure, got {other:?}"),
}
}
#[test]
fn decode_typed_kind_mismatch_returns_kind_mismatch_err() {
let event = json_event(Beta::KIND, serde_json::json!({ "label": "x" }));
let result: Result<Alpha, TypedDecodeError> = event.decode_typed();
match result {
Err(TypedDecodeError::KindMismatch { expected, got }) => {
assert_eq!(expected, Alpha::KIND);
assert_eq!(got, Beta::KIND);
}
other => panic!("expected KindMismatch, got {other:?}"),
}
}
#[test]
fn decode_typed_decode_failure_returns_decode_failure_err() {
let event = json_event(Alpha::KIND, serde_json::json!({ "wrong": true }));
let result: Result<Alpha, TypedDecodeError> = event.decode_typed();
match result {
Err(TypedDecodeError::DecodeFailure { kind, source }) => {
assert_eq!(kind, Alpha::KIND);
assert!(matches!(source, DecodeSource::Json(_)));
}
other => panic!("expected DecodeFailure, got {other:?}"),
}
}
}
mod msgpack_lane {
use super::*;
#[test]
fn route_typed_match_decode_ok() {
let bytes = rmp_serde::to_vec_named(&Alpha { value: 77 }).expect("encode");
let event = msgpack_event(Alpha::KIND, bytes);
let routed: Option<Alpha> = event.route_typed().expect("route_typed");
assert_eq!(routed, Some(Alpha { value: 77 }));
}
#[test]
fn route_typed_kind_mismatch_returns_none() {
let bytes = rmp_serde::to_vec_named(&Beta { label: "x".into() }).expect("encode");
let event = msgpack_event(Beta::KIND, bytes);
let routed: Option<Alpha> = event.route_typed().expect("route_typed");
assert!(
routed.is_none(),
"PROPERTY: route_typed must return Ok(None) on kind mismatch, not an error"
);
}
#[test]
fn route_typed_decode_failure_propagates_err() {
let bytes = rmp_serde::to_vec_named(&Beta { label: "x".into() }).expect("encode");
let event = msgpack_event(Alpha::KIND, bytes);
let result: Result<Option<Alpha>, TypedDecodeError> = event.route_typed();
let err = result.expect_err("kind matched but decode should fail");
match err {
TypedDecodeError::DecodeFailure { kind, source } => {
assert_eq!(kind, Alpha::KIND);
assert!(matches!(source, DecodeSource::Msgpack(_)));
}
other => panic!("expected DecodeFailure, got {other:?}"),
}
}
#[test]
fn decode_typed_kind_mismatch_returns_kind_mismatch_err() {
let bytes = rmp_serde::to_vec_named(&Beta { label: "x".into() }).expect("encode");
let event = msgpack_event(Beta::KIND, bytes);
let result: Result<Alpha, TypedDecodeError> = event.decode_typed();
match result {
Err(TypedDecodeError::KindMismatch { expected, got }) => {
assert_eq!(expected, Alpha::KIND);
assert_eq!(got, Beta::KIND);
}
other => panic!("expected KindMismatch, got {other:?}"),
}
}
#[test]
fn decode_typed_decode_failure_returns_decode_failure_err() {
let event = msgpack_event(Alpha::KIND, b"not-valid-msgpack".to_vec());
let result: Result<Alpha, TypedDecodeError> = event.decode_typed();
match result {
Err(TypedDecodeError::DecodeFailure { kind, source }) => {
assert_eq!(kind, Alpha::KIND);
assert!(matches!(source, DecodeSource::Msgpack(_)));
}
other => panic!("expected DecodeFailure, got {other:?}"),
}
}
}