Skip to content

Task 3-2: Integration Test: Binary Protocol Decode/Encode

Phase: 3 Priority: Medium Module: ConnectedClient, TranslateMsgToWWJ Depends 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ùng byte[] literals với hex notation (0xFE) cho readability. Tránh dùng magic numbers — define constants trong test class.

  • [ ] Tạo test class BinaryProtocolTest vớ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 startGame packet:
@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 GameStarted packet 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 ErrorReport packet:
@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 AddNewIp packet → 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

  • [ ] startGame encode: header=0xFE, correct command byte, checksum passes calculateChecksum() verification
  • [ ] All 4 move command packets produce distinct command bytes và valid checksums
  • [ ] Tampered checksum → ShouldBreak result (không throw exception, không silently continue)
  • [ ] AddNewIp decode → 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ạo
  • src/test/java/.../protocol/TestPacketBuilders.java ← helper class cho test packet construction