Skip to content

Commit

Permalink
fix(status): Adding consistent ordering to status output (#191)
Browse files Browse the repository at this point in the history
  • Loading branch information
IanWoodard authored Dec 31, 2024
1 parent f06c160 commit ea91d9f
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 9 deletions.
26 changes: 18 additions & 8 deletions devservices/commands/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from argparse import _SubParsersAction
from argparse import ArgumentParser
from argparse import Namespace
from collections import namedtuple

from sentry_sdk import capture_exception

Expand All @@ -30,6 +31,9 @@
LINE_LENGTH = 40


ServiceStatus = namedtuple("ServiceStatus", ["name", "formatted_output"])


def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
parser = subparsers.add_parser("status", help="View status of a service")
parser.add_argument(
Expand All @@ -41,12 +45,12 @@ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
parser.set_defaults(func=status)


def format_status_output(status_json: str) -> str:
def format_status_output(service_status_json: str) -> list[ServiceStatus]:
# Docker compose ps is line delimited json, so this constructs this into an array we can use
service_statuses = status_json.split("\n")[:-1]
output = []
output.append("-" * LINE_LENGTH)
service_statuses = service_status_json.split("\n")[:-1]
outputs = []
for service_status in service_statuses:
output = []
service = json.loads(service_status)
name = service["Service"]
state = service["State"]
Expand All @@ -71,8 +75,9 @@ def format_status_output(status_json: str) -> str:
output.append("No ports exposed")

output.append("") # Empty line for readability
outputs.append(ServiceStatus(name=name, formatted_output="\n".join(output)))

return "\n".join(output)
return outputs


def status(args: Namespace) -> None:
Expand Down Expand Up @@ -115,10 +120,15 @@ def status(args: Namespace) -> None:
console.warning(f"{service.name} is not running")
return
output = f"Service: {service.name}\n\n"
output += "=" * LINE_LENGTH + "\n"
formatted_status_outputs = []
for status_json in status_json_results:
output += format_status_output(status_json.stdout)
output += "=" * LINE_LENGTH
console.info(output + "\n")
formatted_status_outputs.extend(format_status_output(status_json.stdout))
formatted_status_outputs.sort(key=lambda x: x.name)
for formatted_status_output in formatted_status_outputs:
output += formatted_status_output[1]
output += "-" * LINE_LENGTH + "\n"
console.info(output)


def _status(
Expand Down
76 changes: 75 additions & 1 deletion tests/commands/test_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import pytest

from devservices.commands.status import status
from devservices.configs.service_config import Dependency
from devservices.configs.service_config import ServiceConfig
from devservices.exceptions import DependencyError
from devservices.exceptions import ServiceNotFoundError
Expand Down Expand Up @@ -156,15 +157,88 @@ def test_status_service_running(
assert (
"""Service: test-service
----------------------------------------
========================================
test-service
Container: test-container
Status: running
Health: healthy
Uptime: 2 days ago
Ports:
http://localhost:8080:8080 -> 8080/tcp
----------------------------------------
"""
== captured.out
)


@mock.patch("devservices.commands.status._status")
@mock.patch("devservices.commands.status.find_matching_service")
@mock.patch("devservices.commands.status.install_and_verify_dependencies")
def test_status_services_running_sorted_order(
mock_install_and_verify_dependencies: mock.Mock,
mock_find_matching_service: mock.Mock,
mock_status: mock.Mock,
capsys: pytest.CaptureFixture[str],
tmp_path: Path,
) -> None:
args = Namespace(service_name="test-service")
service = Service(
name="test-service",
repo_path=str(tmp_path),
config=ServiceConfig(
version=0.1,
service_name="test-service",
dependencies={
"test-dependency": Dependency(
description="Test dependency",
)
},
modes={"default": []},
),
)
mock_find_matching_service.return_value = service
mock_install_and_verify_dependencies.return_value = set()
mock_status.return_value = [
subprocess.CompletedProcess(
args=[],
returncode=0,
stdout='{"Service": "test-service", "State": "running", "Name": "test-container", "Health": "healthy", "RunningFor": "2 days ago", "Publishers": [{"URL": "http://localhost:8080", "PublishedPort": 8080, "TargetPort": 8080, "Protocol": "tcp"}]}\n',
),
subprocess.CompletedProcess(
args=[],
returncode=0,
stdout='{"Service": "test-dependency", "State": "running", "Name": "test-dependency-container", "Health": "healthy", "RunningFor": "2 days ago", "Publishers": [{"URL": "http://localhost:8081", "PublishedPort": 8081, "TargetPort": 8081, "Protocol": "tcp"}]}\n',
),
]

status(args)

mock_find_matching_service.assert_called_once_with("test-service")
mock_install_and_verify_dependencies.assert_called_once_with(service)
mock_status.assert_called_once_with(service, set(), [])

captured = capsys.readouterr()
assert (
"""Service: test-service
========================================
test-dependency
Container: test-dependency-container
Status: running
Health: healthy
Uptime: 2 days ago
Ports:
http://localhost:8081:8081 -> 8081/tcp
----------------------------------------
test-service
Container: test-container
Status: running
Health: healthy
Uptime: 2 days ago
Ports:
http://localhost:8080:8080 -> 8080/tcp
----------------------------------------
"""
== captured.out
Expand Down

0 comments on commit ea91d9f

Please sign in to comment.