From bf19d3d6b8602316a74355895a4e705bd32e5239 Mon Sep 17 00:00:00 2001 From: Kegan Maher <kegan@compiler.la> Date: Wed, 30 Oct 2024 16:42:13 +0000 Subject: [PATCH] feat(cli): define the agency list command benefits agency list -h --- benefits/cli/agency/__init__.py | 0 benefits/cli/agency/list.py | 73 +++++++++++++++++++ benefits/cli/management/commands/agency.py | 6 +- tests/pytest/cli/agency/__init__.py | 0 tests/pytest/cli/agency/test_list.py | 68 +++++++++++++++++ tests/pytest/cli/conftest.py | 12 +++ .../cli/management/commands/test_agency.py | 7 +- 7 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 benefits/cli/agency/__init__.py create mode 100644 benefits/cli/agency/list.py create mode 100644 tests/pytest/cli/agency/__init__.py create mode 100644 tests/pytest/cli/agency/test_list.py create mode 100644 tests/pytest/cli/conftest.py diff --git a/benefits/cli/agency/__init__.py b/benefits/cli/agency/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/benefits/cli/agency/list.py b/benefits/cli/agency/list.py new file mode 100644 index 0000000000..974b84c2e6 --- /dev/null +++ b/benefits/cli/agency/list.py @@ -0,0 +1,73 @@ +from dataclasses import dataclass + +from django.db.models import Q + +from benefits.cli.commands import BaseOptions, BenefitsCommand +from benefits.core.models import TransitAgency + + +@dataclass +class Options(BaseOptions): + all: bool = False + name: str = None + slug: str = None + + +class List(BenefitsCommand): + """List transit agencies.""" + + help = __doc__ + name = "list" + options_cls = Options + + def add_arguments(self, parser): + parser.add_argument( + "-a", + "--all", + action="store_true", + default=False, + help="Show both active and inactive agencies. By default show only active agencies.", + ) + parser.add_argument( + "-n", + "--name", + type=str, + help="Filter for agencies with matching (partial) short_name or long_name.", + ) + parser.add_argument( + "-s", + "--slug", + type=str, + help="Filter for agencies with matching (partial) slug.", + ) + + def handle(self, *args, **options): + opts = self.parse_opts(**options) + agencies = TransitAgency.objects.all() + + if not opts.all: + agencies = agencies.filter(active=True) + + if opts.name: + q = Q(short_name__contains=opts.name) | Q(long_name__contains=opts.name) + agencies = agencies.filter(q) + + if opts.slug: + agencies = agencies.filter(slug__contains=opts.slug) + + if len(agencies) > 0: + if len(agencies) > 1: + msg = f"{len(agencies)} agencies:" + else: + msg = "1 agency:" + self.stdout.write(self.style.SUCCESS(msg)) + + active = filter(lambda a: a.active, agencies) + inactive = filter(lambda a: not a.active, agencies) + + for agency in active: + self.stdout.write(self.style.HTTP_NOT_MODIFIED(f"{agency}")) + for agency in inactive: + self.stdout.write(self.style.WARNING(f"[inactive] {agency}")) + else: + self.stdout.write(self.style.HTTP_NOT_FOUND("No matching agencies")) diff --git a/benefits/cli/management/commands/agency.py b/benefits/cli/management/commands/agency.py index 9c24a9f18e..25196c5401 100644 --- a/benefits/cli/management/commands/agency.py +++ b/benefits/cli/management/commands/agency.py @@ -1,3 +1,4 @@ +from benefits.cli.agency.list import List from benefits.cli.commands import BenefitsCommand @@ -6,7 +7,8 @@ class Command(BenefitsCommand): help = __doc__ name = "agency" - subcommands = [] + subcommands = [List] def __init__(self, stdout=None, stderr=None, no_color=False, force_color=False): - super().__init__(stdout, stderr, no_color, force_color) + # make List the default_subcmd + super().__init__(stdout, stderr, no_color, force_color, List) diff --git a/tests/pytest/cli/agency/__init__.py b/tests/pytest/cli/agency/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/pytest/cli/agency/test_list.py b/tests/pytest/cli/agency/test_list.py new file mode 100644 index 0000000000..34d3ddab0f --- /dev/null +++ b/tests/pytest/cli/agency/test_list.py @@ -0,0 +1,68 @@ +import pytest + +from benefits.cli.agency.list import List + + +@pytest.fixture +def cmd(cmd): + def call(*args, **kwargs): + return cmd(List, *args, **kwargs) + + return call + + +@pytest.mark.django_db +def test_list(cmd): + out, err = cmd() + + assert err == "" + assert "No matching agencies" in out + + +@pytest.mark.django_db +def test_list_agency(cmd, model_TransitAgency): + out, err = cmd() + + assert err == "" + assert "1 agency" in out + assert str(model_TransitAgency) in out + + +@pytest.mark.django_db +def test_list_agencies(cmd, model_TransitAgency): + orig_agency = str(model_TransitAgency) + + model_TransitAgency.pk = None + model_TransitAgency.long_name = "Another agency" + model_TransitAgency.save() + + out, err = cmd() + + assert err == "" + assert "2 agencies" in out + assert orig_agency in out + assert str(model_TransitAgency) in out + + +@pytest.mark.django_db +def test_list_agencies_active(cmd, model_TransitAgency): + orig_agency = str(model_TransitAgency) + + model_TransitAgency.pk = None + model_TransitAgency.long_name = "Another agency" + model_TransitAgency.active = False + model_TransitAgency.save() + + out, err = cmd() + + assert err == "" + assert "1 agency" in out + assert orig_agency in out + assert str(model_TransitAgency) not in out + + out, err = cmd("--all") + + assert err == "" + assert "2 agencies" in out + assert orig_agency in out + assert f"[inactive] {model_TransitAgency}" in out diff --git a/tests/pytest/cli/conftest.py b/tests/pytest/cli/conftest.py new file mode 100644 index 0000000000..1f7e3b5c4f --- /dev/null +++ b/tests/pytest/cli/conftest.py @@ -0,0 +1,12 @@ +import pytest + +from django.core.management import call_command + + +@pytest.fixture +def cmd(capfd): + def call(cls, *args, **kwargs): + call_command(cls(), *args, **kwargs) + return capfd.readouterr() + + return call diff --git a/tests/pytest/cli/management/commands/test_agency.py b/tests/pytest/cli/management/commands/test_agency.py index ba795df6f6..085d7dd5dd 100644 --- a/tests/pytest/cli/management/commands/test_agency.py +++ b/tests/pytest/cli/management/commands/test_agency.py @@ -1,5 +1,6 @@ import pytest +from benefits.cli.agency.list import List from benefits.cli.management.commands.agency import Command @@ -7,7 +8,7 @@ def test_class(): assert Command.help == Command.__doc__ assert Command.name == "agency" - assert Command.subcommands == [] + assert Command.subcommands == [List] @pytest.mark.django_db @@ -16,3 +17,7 @@ def test_init(): assert "agency" in cmd.subparsers assert cmd.subparser == cmd.subparsers["agency"] + + list_cmd = getattr(cmd, "list") + assert isinstance(list_cmd, List) + assert cmd.default_handler == list_cmd.handle