CLI-STORY-002 — Report Final Positions

Story

As an operator, I want to receive the final position and heading of every rover after all commands have run, so that I know where each rover ended up.

Architecture Reference: 05-building-blocks.mdOutputFormatter adapter; 01-introduction.md — FR-5; 03-context.md — System → Operator interface (stdout)


Scenarios

SCENARIO 1: Single rover output is formatted correctly

Scenario ID: CLI-STORY-002-S1

GIVEN

  • A rover ends at x=1, y=3, heading=N

WHEN

  • OutputFormatter.format(rover) is called

THEN

  • The returned string is 1 3 N


SCENARIO 2: Multiple rovers produce one line each in order

Scenario ID: CLI-STORY-002-S2

GIVEN

  • Two rovers complete their missions in deployment order

WHEN

  • Output is produced

THEN

  • Two lines are printed to stdout, one per rover, in deployment order


SCENARIO 3: Output goes to stdout, not stderr

Scenario ID: CLI-STORY-002-S3

GIVEN

  • A valid mission completes

WHEN

  • The CLI writes output

THEN

  • All rover position lines appear on stdout

  • stderr is empty


Backend Sub-Story

Story ID: CLI-BE-002.1

As a developer I want a thin OutputFormatter adapter so that rover state serialisation is isolated from domain logic and easily testable.

Architecture Reference: 05-building-blocks.mdOutputFormatter; 04-solution-strategy.md — Hexagonal architecture (adapter layer)

Scenarios:

SCENARIO 1: Formatter returns space-separated string

Scenario ID: CLI-BE-002.1-S1

GIVEN

  • A Rover(5, 1, Heading.E) exists

WHEN

  • OutputFormatter().format(rover) is called

THEN

  • Returns "5 1 E"


Adapter — mars_rover/adapters/output_formatter.py

from mars_rover.domain.rover import Rover


class OutputFormatter:
    def format(self, rover: Rover) -> str:
        return f"{rover.x} {rover.y} {rover.heading.value}"

Design notes:

  • Thin adapter — only responsibility is serialising Rover state to the wire format

  • Returns a str rather than printing directly, keeping it testable with plain strings (no stdout capture needed)

  • heading.value extracts the string representation from the Heading enum ("N", "E", etc.)

Unit tests — tests/adapters/test_output_formatter.py

from mars_rover.adapters.output_formatter import OutputFormatter
from mars_rover.domain.heading import Heading
from mars_rover.domain.rover import Rover


def test_format_kata_example_1():
    rover = Rover(1, 3, Heading.N)
    assert OutputFormatter().format(rover) == "1 3 N"


def test_format_kata_example_2():
    rover = Rover(5, 1, Heading.E)
    assert OutputFormatter().format(rover) == "5 1 E"


def test_format_origin_facing_south():
    rover = Rover(0, 0, Heading.S)
    assert OutputFormatter().format(rover) == "0 0 S"


def test_format_all_headings():
    formatter = OutputFormatter()
    cases = [
        (Heading.N, "0 0 N"),
        (Heading.E, "0 0 E"),
        (Heading.S, "0 0 S"),
        (Heading.W, "0 0 W"),
    ]
    for heading, expected in cases:
        assert formatter.format(Rover(0, 0, heading)) == expected

Frontend Sub-Story

Story ID: CLI-FE-002.1

As an operator I want to read rover positions from stdout so that I can capture or redirect the output in shell scripts.

Architecture Reference: 03-context.md — System → Operator interface; 07-deployment.md — single-process CLI

Scenarios:

SCENARIO 1: Output is readable from terminal or shell redirection

Scenario ID: CLI-FE-002.1-S1

GIVEN

  • A mission completes successfully

WHEN

  • The operator reads stdout

THEN

  • Each line contains x y HEADING for one rover

  • The output can be captured with > output.txt or piped to another command

The output is plain text on stdout — one line per rover. No UI component required.


Infrastructure Sub-Story

Story ID: CLI-INFRA-002.1

As a developer I want the output formatting functionality to be containerized and testable so that rover position reporting works consistently across environments.

Architecture Reference: 07-deployment.md — deployment topology; 01-introduction.md — FR-5


SCENARIO 1: Container formats and outputs rover positions correctly

Scenario ID: CLI-INFRA-002.1-S1

GIVEN

  • The Docker container includes output formatting logic

  • Multiple rovers have completed their missions with final positions

WHEN

  • The container processes the mission results

THEN

  • Each rover position is formatted as “x y HEADING”

  • Rovers are output in deployment order

  • Output is written to stdout with proper line endings


SCENARIO 2: Container handles missing or invalid rover data gracefully

Scenario ID: CLI-INFRA-002.1-S2

GIVEN

  • The Docker container includes error handling for output formatting

  • Some rover data is missing or corrupted

WHEN

  • The container attempts to format the output

THEN

  • Error is logged to stderr with descriptive message

  • Container exits with non-zero exit code

  • No partial or invalid output is produced


SCENARIO 3: Dockerfile builds with output formatting dependencies

Scenario ID: CLI-INFRA-002.1-S3

GIVEN

  • The mars_rover/adapters/output_formatter.py file exists

  • The Dockerfile includes adapter layer code

WHEN

  • docker build -t mars-rover . is executed

THEN

  • The build includes output formatting code at /app/mars_rover/adapters/output_formatter.py

  • All formatting-related dependencies are available

  • The container can import and use the OutputFormatter class

  • Build completes without errors


SCENARIO 4: Test suite validates output formatting inside container

Scenario ID: CLI-INFRA-002.1-S4

GIVEN

  • Test files exist for output formatting functionality

  • The Docker container includes pytest

WHEN

  • docker run --rm mars-rover pytest tests/adapters/test_output_formatter.py -v is executed

THEN

  • All output formatting tests run inside the container

  • Tests validate correct position formatting and error handling

  • pytest discovers and executes all formatter-related tests

  • Container exits with code 0 on test success


Definition of Done

  • OutputFormatter implemented in mars_rover/adapters/output_formatter.py

  • All formatter unit tests pass

  • Container formats and outputs rover positions correctly

  • Container handles missing or invalid rover data gracefully

  • Dockerfile builds successfully with output formatting dependencies

  • Test suite runs inside container and validates output formatting

  • ruff, black, and isort pass with no warnings