Files
Steven Wroblewski f2e4c91082 Regel-Kennung und Aufzeichnung Version 3
Die eingefrorenen Skripte ziehen von den festgeschriebenen Tests in den
Kern. Aus ihren Endzuständen leitet sich die Regel-Kennung ab, die sich
mit jeder Regel ändert, die ein Skript berührt. Aufzeichnungen tragen
sie und werden mit anderen Regeln abgelehnt, statt eine scheinbare
Abweichung zu melden. Fremde Aufzeichnungen dürfen höchstens 30 Tage
lang sein. Ein Test hält fest, dass zu lange Namen beim Dekodieren
abgelehnt werden.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-10-01 18:00:00 +02:00

112 lines
2.8 KiB
Rust

use grovewright_core::codec::{DecodeError, Reader, Writer};
use grovewright_core::command::Command;
use grovewright_core::map::Pos;
use grovewright_core::player::{PlayerId, MAX_NAME_BYTES};
fn round_trip(command: &Command) -> Command {
let mut w = Writer::new();
command.encode(&mut w);
let bytes = w.into_bytes();
let mut r = Reader::new(&bytes);
let decoded = Command::decode(&mut r).expect("command decodes");
r.finish().expect("no bytes are left over");
decoded
}
#[test]
fn every_command_survives_encoding() {
let commands = [
Command::Join {
key: [7; 16],
name: "Ada".to_owned(),
},
Command::Leave {
player: PlayerId(3),
},
Command::Dig {
player: PlayerId(1),
pos: Pos::new(-4, 1_000),
},
];
for command in &commands {
assert_eq!(&round_trip(command), command);
}
}
#[test]
fn an_overlong_name_is_shortened_when_encoded() {
let command = Command::Join {
key: [1; 16],
name: "x".repeat(MAX_NAME_BYTES + 10),
};
assert_eq!(
round_trip(&command),
Command::Join {
key: [1; 16],
name: "x".repeat(MAX_NAME_BYTES),
}
);
}
#[test]
fn a_name_longer_than_allowed_is_rejected_when_decoded() {
// A join written by hand: the encoder would shorten the name. The tag of
// a join is 1; the recording format pins the wire format of commands.
let mut w = Writer::new();
w.u8(1);
w.raw(&[1; 16]);
w.str(&"x".repeat(MAX_NAME_BYTES + 1));
let bytes = w.into_bytes();
assert_eq!(
Command::decode(&mut Reader::new(&bytes)),
Err(DecodeError::InvalidValue("player name"))
);
}
#[test]
fn an_unknown_command_is_rejected() {
assert_eq!(
Command::decode(&mut Reader::new(&[0xEE])),
Err(DecodeError::InvalidValue("command tag"))
);
}
#[test]
fn a_cut_off_command_is_rejected() {
let mut w = Writer::new();
Command::Dig {
player: PlayerId(0),
pos: Pos::new(1, 2),
}
.encode(&mut w);
let bytes = w.into_bytes();
assert_eq!(
Command::decode(&mut Reader::new(&bytes[..bytes.len() - 1])),
Err(DecodeError::UnexpectedEnd)
);
}
#[test]
fn several_commands_can_follow_each_other_in_one_buffer() {
let commands = vec![
Command::Leave {
player: PlayerId(2),
},
Command::Dig {
player: PlayerId(0),
pos: Pos::new(5, 6),
},
];
let mut w = Writer::new();
for command in &commands {
command.encode(&mut w);
}
let bytes = w.into_bytes();
let mut r = Reader::new(&bytes);
let mut decoded = Vec::new();
while r.remaining() > 0 {
decoded.push(Command::decode(&mut r).unwrap());
}
assert_eq!(decoded, commands);
}