raise NotImplementedError("This service intentionally raises an error, for testing")
-def bugs_list(args: argparse.Namespace) -> tuple:
+def bugs_list(args: argparse.Namespace) -> tuple[int, list]:
"""List bug reports that have been saved"""
- rows, _ = ntclient.services.bugs.list_bugs()
- n_bugs_total = len(rows)
- n_bugs_unsubmitted = len([x for x in rows if not bool(x[-1])])
-
- print(f"You have: {n_bugs_total} total bugs amassed in your journey.")
- print(f"Of these, {n_bugs_unsubmitted} require submission/reporting.")
- print()
-
- for bug in rows:
- if not args.show:
- continue
- # Skip submitted bugs by default
- if bool(bug[-1]) and not args.debug:
- continue
- # Print all bug properties (except noisy stacktrace)
- print(", ".join(str(x) for x in bug if "\n" not in str(x)))
- print()
-
- if n_bugs_unsubmitted > 0:
- print("NOTE: You have bugs awaiting submission. Please run the report command")
-
- return 0, rows
+ return ntclient.services.bugs.list_bugs(show_all=args.show)
# pylint: disable=unused-argument
-def bugs_report(args: argparse.Namespace) -> tuple:
+def bugs_report(args: argparse.Namespace) -> tuple[int, int]:
"""Report bugs"""
n_submissions = ntclient.services.bugs.submit_bugs()
return 0, n_submissions
)
if repr(exc) == dupe_bug_insertion_exc:
print("INFO: bug report already exists")
- else:
+ else: # pragma: no cover
raise
-def list_bugs() -> tuple[list, list]:
- """List all bugs, with headers."""
- rows, headers, _, _ = sql_nt("SELECT * FROM bug")
- return rows, headers
+def _list_bugs() -> list:
+ """List all bugs, with headers as dict keys."""
+ rows, _, _, _ = sql_nt("SELECT * FROM bug")
+ bugs = [dict(x) for x in rows]
+ return bugs
+
+
+def list_bugs(show_all: bool) -> tuple[int, list]:
+ """List all bugs, with headers. Returns (exit_code, bugs: list[dict])."""
+
+ bugs = _list_bugs()
+ n_bugs_total = len(bugs)
+ n_bugs_unsubmitted = len([x for x in bugs if not bool(x["submitted"])])
+
+ print(f"You have: {n_bugs_total} total bugs amassed in your journey.")
+ print(f"Of these, {n_bugs_unsubmitted} require submission/reporting.")
+ print()
+
+ for bug in bugs:
+ if not show_all:
+ continue
+ # Skip submitted bugs by default
+ if bool(bug["submitted"]) and not CLI_CONFIG.debug:
+ continue
+ # Print all bug properties (except noisy stacktrace)
+ print(", ".join(str(x) for x in bug if "\n" not in str(x)))
+ print()
+
+ if n_bugs_unsubmitted > 0:
+ print("NOTE: You have bugs awaiting submission. Please run the report command")
+ return 0, bugs
+
+
+def _list_bugs_unsubmitted() -> list:
+ """List unsubmitted bugs, with headers as dict keys."""
+ rows, _, _, _ = sql_nt("SELECT * FROM bug WHERE submitted = 0")
+ bugs = [dict(x) for x in rows]
+ return bugs
def submit_bugs() -> int:
"""Submit bug reports to developer, return n_submitted."""
- # TODO: mock sql_nt() for testing
- # Gather bugs for submission
- rows, _, _, _ = sql_nt("SELECT * FROM bug WHERE submitted = 0")
+ bugs = _list_bugs_unsubmitted()
+
+ if len(bugs) == 0:
+ print("INFO: no unsubmitted bugs found")
+ return 0
+
api_client = ntclient.services.api.ApiClient()
n_submitted = 0
- print(f"submitting {len(rows)} bug reports...")
- print("_" * len(rows))
+ print(f"submitting {len(bugs)} bug reports...")
+ print("_" * len(bugs))
- for bug in rows:
+ for bug in bugs:
_res = api_client.post_bug(bug)
- if CLI_CONFIG.debug:
+
+ if CLI_CONFIG.debug: # pragma: no cover
print(_res.json())
# Distinguish bug which are unique vs. duplicates (someone else submitted)
sql_nt("UPDATE bug SET submitted = 1 WHERE id = ?", (bug["id"],))
elif _res.status_code == 204:
sql_nt("UPDATE bug SET submitted = 2 WHERE id = ?", (bug["id"],))
- else:
+ else: # pragma: no cover
print("WARN: unknown status [{0}]".format(_res.status_code))
continue
n_submitted += 1
print("submitted: {0} bugs".format(n_submitted))
-
return n_submitted
def test_bug_list(self) -> None:
"""Tests the functions for listing bugs"""
- bugs.list_bugs()
+ exit_code, _bugs = bugs.list_bugs(show_all=True)
+
+ assert exit_code == 0
+ assert len(_bugs) >= 0
+ # assert len(rows) >= 0
+ # assert len(headers) == 11
+
+ def test_bug_list_unsubmitted(self) -> None:
+ """Tests the functions for listing unsubmitted bugs"""
+ with patch(
+ "ntclient.services.bugs._list_bugs",
+ return_value=[{"submitted": False}],
+ ):
+ exit_code, _bugs = bugs.list_bugs(show_all=False)
+
+ assert exit_code == 0
+ assert len(_bugs) == 1
+ _bug = _bugs[0]
+ assert len(_bug.values()) >= 0
+ assert len(_bug.keys()) == 1
@patch("ntclient.services.api.cache_mirrors", return_value="https://someurl.com")
@patch(
"ntclient.services.api.ApiClient.post",
return_value=MagicMock(status_code=201),
)
+ @patch("ntclient.services.bugs._list_bugs_unsubmitted", return_value=[{"id": 1}])
+ @patch("ntclient.services.bugs.sql_nt")
# pylint: disable=unused-argument
def test_bug_report(self, *args: MagicMock) -> None:
"""Tests the functions for submitting bugs"""
bugs.submit_bugs()
+
+ @patch("ntclient.services.api.cache_mirrors", return_value="https://someurl.com")
+ @patch(
+ "ntclient.services.api.ApiClient.post",
+ return_value=MagicMock(status_code=204),
+ )
+ @patch("ntclient.services.bugs._list_bugs_unsubmitted", return_value=[{"id": 1}])
+ @patch("ntclient.services.bugs.sql_nt")
+ # pylint: disable=unused-argument
+ def test_bug_report_on_204_status(self, *args: MagicMock) -> None:
+ """Tests the functions for submitting bugs"""
+ bugs.submit_bugs()
+
+ @patch("ntclient.services.api.cache_mirrors", return_value="https://someurl.com")
+ @patch(
+ "ntclient.services.api.ApiClient.post",
+ return_value=MagicMock(status_code=201),
+ )
+ @patch("ntclient.services.bugs._list_bugs_unsubmitted", return_value=[])
+ # pylint: disable=unused-argument
+ def test_bug_report_empty_list(self, *args: MagicMock) -> None:
+ """Tests the functions for submitting bugs"""
+ result = bugs.submit_bugs()
+ assert result == 0
assert code == 0
assert isinstance(result, list)
+ args = arg_parser.parse_args(args="bug report".split())
+ code, result = args.func(args)
+ assert code == 0
+ assert isinstance(result, int)
+
def test_415_invalid_path_day_throws_error(self):
"""Ensures invalid path throws exception in `day` subcommand"""
invalid_day_csv_path = os.path.join(