Task 3-2: Integration Test: Binary Protocol Decode/Encode
Phase: 3 Priority: Medium Module:
ConnectedClient,TranslateMsgToWWJDepends on: Không có Reference: docs/BountyHunter-ControlServer/details/feature-iot-bridge/SPEC.md
Background
Binary protocol với magic bytes 0xFE, checksum, và length fields là core của toàn bộ communication với claw machines. Không có unit/integration tests nào cho encode/decode logic — mọi lỗi (sai checksum algorithm, sai byte order, sai command byte) chỉ được phát hiện khi test với máy thật. Cần test suite cover encode roundtrip, decode với valid/invalid data, và verify checksum validation chặn tampered packets.
Tasks
Note: Test binary protocol cần biết exact packet format (header bytes, length field position, checksum algorithm). Nên tham khảo handshake magic bytes
"aa"và các packet type constants trong source code. Test nên dùngbyte[]literals với hex notation (0xFE) cho readability. Tránh dùng magic numbers — define constants trong test class.
- [ ] Tạo test class
BinaryProtocolTestvới constants:
class BinaryProtocolTest {
// Protocol constants (phải match với source code)
private static final byte HEADER = (byte) 0xFE;
private static final byte CMD_MOVE_LEFT = 0x01; // verify với source
private static final byte CMD_MOVE_RIGHT = 0x02;
private static final byte CMD_MOVE_FORWARD = 0x03;
private static final byte CMD_MOVE_BACKWARD = 0x04;
private static final byte CMD_START_GAME = 0x10; // verify với source
}
- [ ] Test encode
startGamepacket:
@Test
void encodeStartGame_producesCorrectBytes() {
// Given
int packageId = 1;
StartGameParam param = new StartGameParam(/* game time, etc. */);
// When
byte[] packet = translateMsg.buildStartGamePacket(packageId, param);
// Then
assertThat(packet[0]).isEqualTo(HEADER); // header byte
assertThat(packet[1]).isEqualTo(CMD_START_GAME); // command byte
assertThat(calculateChecksum(packet)).isEqualTo(packet[packet.length - 1]); // checksum valid
// Verify length field matches actual data length
}
- [ ] Test encode move commands:
@ParameterizedTest
@MethodSource("provideMoveCommands")
void encodeMoveCommand_producesCorrectCommandByte(String direction, byte expectedCmd) {
byte[] packet = translateMsg.buildMovePacket(direction, 1);
assertThat(packet).contains(expectedCmd);
assertThat(calculateChecksum(packet)).isEqualTo(packet[packet.length - 1]);
}
private static Stream<Arguments> provideMoveCommands() {
return Stream.of(
Arguments.of("LEFT", CMD_MOVE_LEFT),
Arguments.of("RIGHT", CMD_MOVE_RIGHT),
Arguments.of("FORWARD", CMD_MOVE_FORWARD),
Arguments.of("BACKWARD", CMD_MOVE_BACKWARD)
);
}
- [ ] Test decode
GameStartedpacket từ machine:
@Test
void decodeGameStarted_triggersHandleGameStarted() throws Exception {
// Given: raw bytes của GameStarted packet từ machine
byte[] gameStartedPacket = buildGameStartedPacket(/* countdown seconds */);
MockConnectedClient client = new MockConnectedClient();
// When: feed bytes vào parser
HandleResult result = client.parsePacket(gameStartedPacket);
// Then
assertThat(result).isInstanceOf(GameStarted.class);
// Verify handleGameStarted() được gọi → game state updated
}
- [ ] Test decode
ErrorReportpacket:
@Test
void decodeErrorReport_withValidErrorCode_callsBackend() {
byte[] errorPacket = buildErrorReportPacket(ErrorCode.CLAW_MALFUNCTION);
// feed vào parser → verify handleErrorReport(errorCode) được gọi
}
- [ ] Test checksum validation — tampered packet bị reject:
@Test
void decodePacket_withWrongChecksum_returnsShouldBreak() {
byte[] validPacket = buildStartGamePacket(1, testParam);
byte[] tamperedPacket = Arrays.copyOf(validPacket, validPacket.length);
tamperedPacket[tamperedPacket.length - 1] ^= 0xFF; // flip all bits in checksum
HandleResult result = parser.parse(tamperedPacket);
assertThat(result).isInstanceOf(ShouldBreak.class);
}
- [ ] Test decode
AddNewIppacket → MAC address extracted:
@Test
void decodeAddNewIp_extractsMacAddress() {
String expectedMac = "AA:BB:CC:DD:EE:FF";
byte[] addNewIpPacket = buildAddNewIpPacket(expectedMac);
AddNewIp result = (AddNewIp) parser.parse(addNewIpPacket);
assertThat(result.getMacAddress()).isEqualTo(expectedMac);
}
- [ ] Test roundtrip encode → decode:
@Test
void roundtrip_encodeDecodeQueryMachineState_sameContent() {
int packageId = 42;
byte[] encoded = translateMsg.buildQueryStatePacket(packageId);
QueryMachineState decoded = (QueryMachineState) parser.parse(encoded);
assertThat(decoded.getPackageId()).isEqualTo(packageId);
}
Verification / Acceptance Criteria
- [ ]
startGameencode: header=0xFE, correct command byte, checksum passescalculateChecksum()verification - [ ] All 4 move command packets produce distinct command bytes và valid checksums
- [ ] Tampered checksum →
ShouldBreakresult (không throw exception, không silently continue) - [ ]
AddNewIpdecode → MAC address matches input string exactly, kể cả colons - [ ] Roundtrip test: encode → decode → same logical content cho tất cả packet types
- [ ] Tất cả tests pass mà không cần physical hardware (pure unit/integration với mock bytes)
Files to Modify
src/test/java/.../protocol/BinaryProtocolTest.java← file mới tạosrc/test/java/.../protocol/TestPacketBuilders.java← helper class cho test packet construction