f2e4c91082
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>
112 lines
2.8 KiB
Rust
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);
|
|
}
|