#include #include "secsgem/hsms/header.hpp" #include "secsgem/secs2/codec.hpp" using namespace secsgem::hsms; namespace s2 = secsgem::secs2; TEST_CASE("data message header round-trip") { Header h = Header::data_message(0x1234, 1, 13, true, 0xAABBCCDD); auto bytes = h.encode(); Header back = Header::decode(bytes.data()); CHECK(back == h); CHECK(back.stype == SType::Data); CHECK(back.w_bit()); CHECK(back.stream() == 1); CHECK(back.function() == 13); CHECK(back.session_id == 0x1234); CHECK(back.system_bytes == 0xAABBCCDD); } TEST_CASE("data message header byte layout") { Header h = Header::data_message(0x0001, 1, 1, true, 0x00000001); auto b = h.encode(); std::array expected{0x00, 0x01, 0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; CHECK(b == expected); } TEST_CASE("W-bit clear when no reply expected") { Header h = Header::data_message(0, 6, 11, false, 7); CHECK_FALSE(h.w_bit()); CHECK((h.byte2 & 0x80) == 0); CHECK(h.stream() == 6); } TEST_CASE("control header layout (Select.req)") { Header h = Header::control(SType::SelectReq, 0x00000005); auto b = h.encode(); std::array expected{0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05}; CHECK(b == expected); Header back = Header::decode(b.data()); CHECK(back == h); CHECK(back.stype == SType::SelectReq); } TEST_CASE("select.rsp carries status in byte3") { Header h = Header::control(SType::SelectRsp, 5, kControlSessionId, 0, static_cast(SelectStatus::Ok)); CHECK(h.byte3 == 0); Header back = Header::decode(h.encode().data()); CHECK(back.stype == SType::SelectRsp); CHECK(back.byte3 == static_cast(SelectStatus::Ok)); } TEST_CASE("frame round-trip with body") { s2::Item body_item = s2::Item::list({s2::Item::ascii("MDLN"), s2::Item::ascii("1.0")}); std::vector body = s2::encode(body_item); Frame f(Header::data_message(0, 1, 2, false, 42), body); auto wire = f.encode(); // length prefix counts header + body uint32_t len = (wire[0] << 24) | (wire[1] << 16) | (wire[2] << 8) | wire[3]; CHECK(len == kHeaderSize + body.size()); CHECK(wire.size() == kLengthPrefixSize + len); Frame back = Frame::decode(wire.data() + kLengthPrefixSize, len); CHECK(back.header == f.header); CHECK(back.body == body); CHECK(s2::decode(back.body) == body_item); } TEST_CASE("control frame has empty body") { Frame f(Header::control(SType::LinktestReq, 99)); auto wire = f.encode(); uint32_t len = (wire[0] << 24) | (wire[1] << 16) | (wire[2] << 8) | wire[3]; CHECK(len == kHeaderSize); Frame back = Frame::decode(wire.data() + kLengthPrefixSize, len); CHECK(back.body.empty()); CHECK(back.header.stype == SType::LinktestReq); } TEST_CASE("frame decode rejects short payload") { std::vector tooShort(5, 0); CHECK_THROWS_AS(Frame::decode(tooShort.data(), tooShort.size()), FrameError); }