Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

crates/monetize-proto/tests/terminate_wire.rs

//! ★ **A32-27: the terminate wire carries no backup** ("ta ingen backup alls", owner 2026-10-22).
//! `EstateOrderTerminateRequest.backup` (4) and `EstateOrderTerminateResponse.backup_offered`
//! (2) / `backups` (4) are deleted and their numbers and names RESERVED, so no later field reuses
//! them. Wire compatibility both ways: an older page's request that still says `backup: skip`
//! decodes in today's message (the field is skipped), and today's answer decodes in an older
//! client (its `backup_offered` reads false, its `backups` empty).

use monetize_proto as pb;
use prost::Message;

/// The request as a client from before A32-27 encoded it.
#[derive(Clone, PartialEq, prost::Message)]
struct OldTerminateRequest {
    #[prost(string, tag = "1")]
    company: String,
    #[prost(bytes = "vec", tag = "2")]
    actor_ticket: Vec<u8>,
    #[prost(string, tag = "3")]
    confirm: String,
    #[prost(string, tag = "4")]
    backup: String,
}

/// The answer as a client from before A32-27 decodes it.
#[derive(Clone, PartialEq, prost::Message)]
struct OldTerminateResponse {
    #[prost(string, tag = "1")]
    words: String,
    #[prost(bool, tag = "2")]
    backup_offered: bool,
    #[prost(bool, tag = "3")]
    done: bool,
    #[prost(string, repeated, tag = "4")]
    backups: Vec<String>,
}

fn message_block<'a>(proto: &'a str, name: &str) -> &'a str {
    let head = format!("message {name} {{");
    let from = proto.find(&head).unwrap_or_else(|| panic!("{name} is declared"));
    let rest = &proto[from..];
    &rest[..rest.find("\n}").expect("the message ends")]
}

#[test]
fn the_terminate_messages_reserve_the_backup_fields_and_carry_none() {
    let proto = include_str!("../proto/monetize/v1/estate_order.proto");
    let req = message_block(proto, "EstateOrderTerminateRequest");
    assert!(!req.contains("string backup"), "the request carries no backup: {req}");
    assert!(req.contains("reserved 4;") && req.contains("reserved \"backup\";"), "its number and name are reserved: {req}");
    let resp = message_block(proto, "EstateOrderTerminateResponse");
    assert!(!resp.contains("backup_offered =") && !resp.contains("backups ="), "the answer carries no backup: {resp}");
    assert!(resp.contains("reserved 2, 4;") && resp.contains("reserved \"backup_offered\", \"backups\";"), "its numbers and names are reserved: {resp}");
}

#[test]
fn an_older_page_s_request_and_an_older_client_s_answer_still_decode() {
    let old = OldTerminateRequest { company: "acme".into(), actor_ticket: vec![1, 2, 3], confirm: "acme".into(), backup: "skip".into() };
    let now = pb::EstateOrderTerminateRequest::decode(old.encode_to_vec().as_slice()).expect("an older request decodes");
    assert_eq!((now.company.as_str(), now.actor_ticket.as_slice(), now.confirm.as_str()), ("acme", &[1u8, 2, 3][..], "acme"));
    assert!(now.encode_to_vec().len() < old.encode_to_vec().len(), "the backup word is not carried on");

    let answer = pb::EstateOrderTerminateResponse { words: "acme is being ended now".into(), done: true };
    let read = OldTerminateResponse::decode(answer.encode_to_vec().as_slice()).expect("an older client reads the answer");
    assert_eq!(read, OldTerminateResponse { words: "acme is being ended now".into(), backup_offered: false, done: true, backups: Vec::new() });
}