{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cell-23-23-00",
   "metadata": {},
   "source": [
    "# 23-23 · Testy argumentów wiersza poleceń\n",
    "\n",
    "Praktyka do sekcji [„Checking the CLI” `projects/python/safesort/tests/test_cli.py` w kategoriach parsowania argumentów — całkowicie przez argparse, bez odwoływania się do dysku."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-23-01",
   "metadata": {},
   "source": [
    "## Cel\n",
    "\n",
    "Zapisz i wykonaj testy parsera argumentów: wszystkie pięć podpoleceń, domyślną wartość root dla undo oraz kod uzupełniający niezerowy, jeśli podinstrukcja jest niewystarczająca lub zawodząca."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-23-02",
   "metadata": {},
   "source": [
    "## Example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23-23-03",
   "metadata": {},
   "outputs": [],
   "source": [
    "import argparse\n",
    "from pathlib import Path\n",
    "\n",
    "\n",
    "def build_parser():\n",
    "    parser = argparse.ArgumentParser(prog=\"safesort\")\n",
    "    subparsers = parser.add_subparsers(dest=\"command\", required=True)\n",
    "    for name in (\"scan\", \"plan\", \"apply\", \"duplicates\"):\n",
    "        sub = subparsers.add_parser(name)\n",
    "        sub.add_argument(\"root\", type=Path)\n",
    "    undo_parser = subparsers.add_parser(\"undo\")\n",
    "    undo_parser.add_argument(\"root\", type=Path, nargs=\"?\", default=Path(\".\"))\n",
    "    return parser\n",
    "\n",
    "\n",
    "def test_parser_accepts_all_subcommands():\n",
    "    parser = build_parser()\n",
    "    for name in (\"scan\", \"plan\", \"apply\", \"duplicates\"):\n",
    "        args = parser.parse_args([name, \"/tmp/x\"])\n",
    "        assert args.command == name\n",
    "        assert args.root == Path(\"/tmp/x\")\n",
    "\n",
    "\n",
    "def test_undo_defaults_to_current_directory():\n",
    "    parser = build_parser()\n",
    "    args = parser.parse_args([\"undo\"])\n",
    "    assert args.command == \"undo\"\n",
    "    assert args.root == Path(\".\")\n",
    "\n",
    "\n",
    "def test_missing_subcommand_exits_nonzero():\n",
    "    import contextlib\n",
    "    import io\n",
    "\n",
    "    parser = build_parser()\n",
    "    buffer = io.StringIO()\n",
    "    try:\n",
    "        with contextlib.redirect_stderr(buffer):\n",
    "            parser.parse_args([])\n",
    "        podnyalos_iskluchenie = False\n",
    "        kod = None\n",
    "    except SystemExit as exc:\n",
    "        podnyalos_iskluchenie = True\n",
    "        kod = exc.code\n",
    "\n",
    "    assert podnyalos_iskluchenie is True\n",
    "    assert kod != 0\n",
    "\n",
    "\n",
    "test_parser_accepts_all_subcommands()\n",
    "test_undo_defaults_to_current_directory()\n",
    "test_missing_subcommand_exits_nonzero()\n",
    "print(\"OK: все три теста прошли.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-23-04",
   "metadata": {},
   "source": [
    "## Sprawdzenie wyniku"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23-23-05",
   "metadata": {},
   "outputs": [],
   "source": [
    "parser_dlya_proverki = build_parser()\n",
    "args_proverki = parser_dlya_proverki.parse_args([\"apply\", \"/home/anna/Downloads\"])\n",
    "\n",
    "assert args_proverki.command == \"apply\"\n",
    "assert args_proverki.root == Path(\"/home/anna/Downloads\")\n",
    "print(\"Верно: apply разобран с правильным путём.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-23-06",
   "metadata": {},
   "source": [
    "## Starter\n",
    "\n",
    "Wypełnij zaznaczone miejsce. Niezmieniony starter nie przechodzi tests."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "task-23-23",
   "metadata": {
    "tags": [
     "exercise",
     "starter"
    ]
   },
   "outputs": [],
   "source": [
    "def kod_razbora(argv: list[str]) -> int:\n",
    "    # TODO: call build_parser().parse_args(argv), catch SystemExit.\n",
    "    raise NotImplementedError\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-23-08",
   "metadata": {},
   "source": [
    "## Task\n",
    "\n",
    "Write `kod_razbora(argv)`: Zwróć 0 dla pomyślnego parsowania lub SystemExit kod dla help/error."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-23-09",
   "metadata": {},
   "source": [
    "## Tests\n",
    "\n",
    "Run After task cell: jest podstawowy przykład i przynajmniej jeden skrajny przypadek."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "tests-23-23",
   "metadata": {
    "tags": [
     "exercise-tests"
    ]
   },
   "outputs": [],
   "source": [
    "assert kod_razbora([\"apply\", \"/tmp/x\"]) == 0\n",
    "assert kod_razbora([\"--help\"]) == 0\n",
    "assert kod_razbora([\"zip\", \"/tmp/x\"]) != 0\n",
    "assert kod_razbora([]) != 0\n",
    "print(\"Tests passed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-23-11",
   "metadata": {},
   "source": [
    "## Hint\n",
    "\n",
    "Normalne `parse_args` po prostu wraca Namespace. `--help` i błąd należą do `except SystemExit as exc`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-23-12",
   "metadata": {},
   "source": [
    "## Solution\n",
    "\n",
    "Pokaż rozwiązanie po własnej próbie</summary>\n",
    "\n",
    "```python\n",
    "def kod_razbora(argv: list[str]) -> int:\n",
    "    try:\n",
    "        build_parser().parse_args(argv)\n",
    "    except SystemExit as exc:\n",
    "        return int(exc.code)\n",
    "    return 0\n",
    "```\n",
    "\n",
    "</details>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Cartesian Python 3.14",
   "language": "python",
   "name": "cartesian-python314"
  },
  "language_info": {
   "name": "python",
   "version": "3.14.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
