Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(status): Adding consistent ordering to status output #191

Merged
merged 1 commit into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading