The mjai-manue-go test suite is designed to ensure correctness across multiple layers of the system, from low-level tile mathematics to high-level AI decision-making. The suite combines traditional unit tests for domain logic with golden-file integration tests that verify the agent's behavior against the MJAI protocol.
The testing strategy is divided into three primary categories:
round.State machine, shanten calculations, and legal action generation.ManueAgent makes expected decisions (e.g., calling Ron on a Yakuman hand).| Concept | Code Entity | Role |
|---|---|---|
| State Visualization | round.BoardRenderer | Generates a human-readable text representation of the board internal/domain/game/round/board_renderer.go14-16 |
| Protocol Verification | TestGoldenStdout | Runs the MJAI loop and compares output against fixtures internal/adapter/mjai/runtime/golden_test.go16-119 |
| Legal Action Logic | s.LegalActions | Calculates and caches valid moves for a given state internal/domain/game/round/legal_actions.go12-26 |
| Agent Mocking | newTsumogiriAgentForTest | Provides a deterministic agent for application-layer tests internal/application/test_utils_test.go21-23 |
| Hand Fixtures | newValidHands | Generates standard starting hands for round state initialization internal/application/test_utils_test.go40-61 |
| Event Application | s.Apply | Dispatches events to the internal state machine internal/application/bot.go63-65 |
Sources: internal/domain/game/round/board_renderer.go14-16 internal/domain/game/round/legal_actions.go12-26 internal/application/test_utils_test.go21-61 internal/application/bot.go63-65
The mjairuntime package uses "golden files" to perform integration testing. These tests ensure that the Driver correctly handles the MJAI protocol sequence and that the AI responds with the expected JSON messages. Fixtures are stored as .input.mjson and .stdio.golden files.
The TestGoldenStdout function orchestrates the flow from input MJSON to validated output.
runJSONLines: The core loop that reads MJAI messages from an io.Reader, dispatches them to the Driver, and writes responses to an io.Writer internal/adapter/mjai/runtime/json_lines.go22-50jsonMessages: A helper that splits the stdout string into individual JSON objects for comparison, ignoring field order and CRLF differences internal/adapter/mjai/runtime/golden_test.go151-177stripMessageLogs: Removes the log field from outbound messages before comparison, as AI internal trace logs are non-deterministic or too verbose for golden matching internal/adapter/mjai/runtime/golden_test.go179-183newManueAgentForGoldenTest: Initializes a full ManueAgent with embedded GameStats and DangerTree to test real AI logic internal/adapter/mjai/runtime/golden_test.go121-139Sources: internal/adapter/mjai/runtime/json_lines.go22-50 internal/adapter/mjai/runtime/testdata/manue/temporary_furiten_continues_after_pon_skip.stdio.golden1-5
The round package contains extensive unit tests for the game's state machine and the logic used to determine legal moves.
The LegalActions method is tested for various scenarios, including:
LegalActions for an "invisible" player (one whose hand is unknown) returns an error internal/domain/game/round/legal_actions_test.go23-31Tests verify that the state machine correctly processes transitions via s.Apply internal/application/bot.go63-65
TestState_Apply_Pon: Validates that after a Pon, the target's river is cleared of the taken tile, the actor's melds are updated, and the actor gains the ability to discard internal/domain/game/round/event_applier_pon_test.go13-47 It also tests failure cases such as when the actor and target are the same internal/domain/game/round/event_applier_pon_test.go49-71TestState_Apply_Draw: Confirms the wall count (NumLeftTiles) decreases and the DrawnTile is correctly assigned to the actor internal/domain/game/round/event_applier_draw_test.go14-40TestState_Apply_Discard: Ensures tiles are moved from the player's hand to the River and DiscardedTiles collection internal/domain/game/round/event_applier_discard_test.go12-41TestState_Apply_Discard_ReturnsErrorForSwapCallTileAfterPon ensures that "Kuikae" (discarding the same tile just called) is rejected by the state machine internal/domain/game/round/event_applier_discard_test.go65-100Sources: internal/domain/game/round/event_applier_pon_test.go13-71 internal/domain/game/round/event_applier_draw_test.go14-40 internal/domain/game/round/event_applier_discard_test.go12-100 internal/application/bot.go63-65
To facilitate debugging complex game states, the system includes a BoardRenderer that produces a text-based representation of the round.State.
The RenderBoard function internal/domain/game/round/board_renderer.go19-49 outputs a multi-line string containing:
pipai) and Dora indicators internal/domain/game/round/board_renderer.go23-24tehai), including the drawn tile internal/domain/game/round/board_renderer.go41-42ho), with Riichi indicators (=) internal/domain/game/round/board_renderer.go43[1s(0)/1s 1s]) internal/domain/game/round/board_renderer.go42Tests apply events to a state and verify the visual output to ensure state transitions (like Pon or Discard) are reflected correctly.
internal/domain/game/round/board_renderer_test.go77-109
Sources: internal/domain/game/round/board_renderer.go19-49 internal/domain/game/round/board_renderer_test.go77-109
The suite provides utilities in application_test and round to reduce boilerplate when setting up game scenarios.
mustNewRoundStateForTest: Quickly creates a round.State with a standard starting configuration internal/domain/game/round/event_applier_test.go14-37tile.MustTileFromCode: Converts shorthand strings like "6m" or "E" into tile.Tile objects, panicking on failure for use in test setup internal/application/test_utils_test.go43unknownHand: Generates a hand filled with "?" tiles to simulate opponent hands in a hidden-information context internal/application/test_utils_test.go63-69mustNewBotForTest: Instantiates a Bot with a TsumogiriAgent for testing application-layer message routing internal/application/test_utils_test.go15-19newValidHands: Provides a template hand for seat 0 and unknown hands for others internal/application/test_utils_test.go40-61Sources: internal/application/test_utils_test.go15-69 internal/application/bot_test.go17-57 internal/domain/game/round/event_applier_test.go14-37 internal/domain/game/event/start_round.go10-41
Refresh this wiki