mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-09 04:06:42 +00:00
Pruned per project convention "no unnecessary code comments": - module docstring: 23 lines → 1 line (the file name + symbol already state intent; full incident write-up belongs in the commit log) - wipe_database_entries docstring: 11 lines → 2 lines - six inline rationale comments in tests/__main__ removed (test names already encode the intent) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
187 lines
6.0 KiB
Python
187 lines
6.0 KiB
Python
"""Unit tests for `utils.cleanup.databases_csv` (databases.csv wipe helper)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import csv
|
|
import io
|
|
import unittest
|
|
from contextlib import redirect_stderr, redirect_stdout
|
|
from pathlib import Path
|
|
from tempfile import TemporaryDirectory
|
|
|
|
from utils.cleanup.databases_csv import main, wipe_database_entries
|
|
|
|
_HEADER = "instance;database;username;password\n"
|
|
|
|
|
|
class DatabasesCsvTestBase(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self._tmp = TemporaryDirectory()
|
|
self.addCleanup(self._tmp.cleanup)
|
|
self.csv_file = Path(self._tmp.name) / "databases.csv"
|
|
|
|
def _write(self, content: str) -> None:
|
|
self.csv_file.write_text(content, encoding="utf-8")
|
|
|
|
def _read(self) -> list[list[str]]:
|
|
with self.csv_file.open(newline="", encoding="utf-8") as fh:
|
|
return list(csv.reader(fh, delimiter=";"))
|
|
|
|
|
|
class TestWipeDatabaseEntries(DatabasesCsvTestBase, unittest.TestCase):
|
|
def test_removes_matching_row_by_name(self):
|
|
self._write(
|
|
_HEADER
|
|
+ "mariadb;matomo;matomo;pw1\n"
|
|
+ "postgres;keycloak;keycloak;pw2\n"
|
|
+ "mariadb;nextcloud;nextcloud;pw3\n"
|
|
)
|
|
|
|
removed = wipe_database_entries(["web-app-matomo"], csv_file=self.csv_file)
|
|
|
|
self.assertEqual(removed, ["matomo:matomo"])
|
|
self.assertEqual(
|
|
self._read(),
|
|
[
|
|
["instance", "database", "username", "password"],
|
|
["postgres", "keycloak", "keycloak", "pw2"],
|
|
["mariadb", "nextcloud", "nextcloud", "pw3"],
|
|
],
|
|
)
|
|
|
|
def test_no_match_does_not_rewrite_file(self):
|
|
self._write(_HEADER + "postgres;keycloak;keycloak;pw2\n")
|
|
before_mtime = self.csv_file.stat().st_mtime_ns
|
|
|
|
removed = wipe_database_entries(["web-app-matomo"], csv_file=self.csv_file)
|
|
|
|
self.assertEqual(removed, [])
|
|
self.assertEqual(self.csv_file.stat().st_mtime_ns, before_mtime)
|
|
|
|
def test_missing_file_returns_empty(self):
|
|
removed = wipe_database_entries(["web-app-matomo"], csv_file=self.csv_file)
|
|
|
|
self.assertEqual(removed, [])
|
|
self.assertFalse(self.csv_file.exists())
|
|
|
|
def test_multiple_app_ids_in_one_call(self):
|
|
self._write(
|
|
_HEADER
|
|
+ "mariadb;matomo;matomo;pw1\n"
|
|
+ "postgres;keycloak;keycloak;pw2\n"
|
|
+ "mariadb;nextcloud;nextcloud;pw3\n"
|
|
)
|
|
|
|
removed = wipe_database_entries(
|
|
["web-app-matomo", "web-app-keycloak"],
|
|
csv_file=self.csv_file,
|
|
)
|
|
|
|
self.assertEqual(sorted(removed), ["keycloak:keycloak", "matomo:matomo"])
|
|
self.assertEqual(
|
|
self._read(),
|
|
[
|
|
["instance", "database", "username", "password"],
|
|
["mariadb", "nextcloud", "nextcloud", "pw3"],
|
|
],
|
|
)
|
|
|
|
def test_username_match_when_database_is_wildcard(self):
|
|
self._write(_HEADER + "mariadb;*;matomo;pw1\npostgres;*;keycloak;pw2\n")
|
|
|
|
removed = wipe_database_entries(["web-app-matomo"], csv_file=self.csv_file)
|
|
|
|
self.assertEqual(removed, ["*:matomo"])
|
|
self.assertEqual(
|
|
self._read(),
|
|
[
|
|
["instance", "database", "username", "password"],
|
|
["postgres", "*", "keycloak", "pw2"],
|
|
],
|
|
)
|
|
|
|
def test_unknown_app_id_no_op(self):
|
|
self._write(_HEADER + "mariadb;matomo;matomo;pw1\n")
|
|
|
|
removed = wipe_database_entries(
|
|
["web-app-does-not-exist"], csv_file=self.csv_file
|
|
)
|
|
|
|
self.assertEqual(removed, [])
|
|
|
|
def test_blank_lines_are_preserved(self):
|
|
self._write(
|
|
_HEADER
|
|
+ "mariadb;matomo;matomo;pw1\n"
|
|
+ "\n"
|
|
+ "postgres;keycloak;keycloak;pw2\n"
|
|
)
|
|
|
|
removed = wipe_database_entries(["web-app-matomo"], csv_file=self.csv_file)
|
|
|
|
self.assertEqual(removed, ["matomo:matomo"])
|
|
self.assertEqual(
|
|
self._read(),
|
|
[
|
|
["instance", "database", "username", "password"],
|
|
[],
|
|
["postgres", "keycloak", "keycloak", "pw2"],
|
|
],
|
|
)
|
|
|
|
def test_header_is_kept_even_when_all_data_rows_removed(self):
|
|
self._write(_HEADER + "mariadb;matomo;matomo;pw1\n")
|
|
|
|
removed = wipe_database_entries(["web-app-matomo"], csv_file=self.csv_file)
|
|
|
|
self.assertEqual(removed, ["matomo:matomo"])
|
|
self.assertEqual(
|
|
self._read(),
|
|
[["instance", "database", "username", "password"]],
|
|
)
|
|
|
|
def test_short_row_is_kept(self):
|
|
self._write(_HEADER + "garbage\nmariadb;matomo;matomo;pw1\n")
|
|
|
|
removed = wipe_database_entries(["web-app-matomo"], csv_file=self.csv_file)
|
|
|
|
self.assertEqual(removed, ["matomo:matomo"])
|
|
self.assertEqual(
|
|
self._read(),
|
|
[
|
|
["instance", "database", "username", "password"],
|
|
["garbage"],
|
|
],
|
|
)
|
|
|
|
def test_csv_without_header_is_still_processed(self):
|
|
self._write("mariadb;matomo;matomo;pw1\npostgres;keycloak;keycloak;pw2\n")
|
|
|
|
removed = wipe_database_entries(["web-app-matomo"], csv_file=self.csv_file)
|
|
|
|
self.assertEqual(removed, ["matomo:matomo"])
|
|
self.assertEqual(
|
|
self._read(),
|
|
[["postgres", "keycloak", "keycloak", "pw2"]],
|
|
)
|
|
|
|
|
|
class TestMainShim(DatabasesCsvTestBase, unittest.TestCase):
|
|
def test_no_argv_prints_usage_and_returns_2(self):
|
|
stderr = io.StringIO()
|
|
with redirect_stderr(stderr):
|
|
rc = main([])
|
|
self.assertEqual(rc, 2)
|
|
self.assertIn("usage:", stderr.getvalue())
|
|
|
|
def test_main_uses_default_path_when_no_override(self):
|
|
stdout = io.StringIO()
|
|
with redirect_stdout(stdout):
|
|
rc = main(["web-app-nonexistent"])
|
|
self.assertEqual(rc, 0)
|
|
self.assertIn("No databases.csv entries to wipe", stdout.getvalue())
|
|
|
|
|
|
if __name__ == "__main__": # pragma: no cover
|
|
unittest.main()
|