import json import sqlite3 import tempfile import threading import unittest from datetime import date, timedelta from http.client import HTTPConnection from pathlib import Path import app class StoreTests(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory() self.store = app.Store(Path(self.temp.name) / "flowtrack.sqlite3") self.store.initialize() def tearDown(self): self.temp.cleanup() def test_empty_log_deletes_the_day(self): self.store.set_day("2026-10-01", {"flow": "heavy", "symptoms": ["cramps"]}) self.assertEqual(self.store.days(), {"2026-10-01": {"flow": "heavy", "symptoms": ["cramps"]}}) self.store.set_day("2026-10-01", {"flow": None, "symptoms": []}) self.assertEqual(self.store.days(), {}) def test_fill_never_overwrites(self): self.store.set_day("2026-10-01", {"flow": "heavy", "symptoms": []}) added = self.store.fill_days({ "2026-10-01": {"flow": "period", "symptoms": []}, "2026-10-02": {"flow": "period", "symptoms": []}, }) self.assertEqual(added, 1) self.assertEqual(self.store.days()["2026-10-01"]["flow"], "heavy") def test_backup_rotates(self): self.store.set_day("2026-10-01", {"flow": "light", "symptoms": []}) destination = Path(self.temp.name) / "backups" destination.mkdir() for stale in ("2026-01-01", "2026-01-02", "2026-01-03"): (destination / f"flowtrack-{stale}.sqlite3").touch() target = self.store.backup(destination, keep=2) self.assertEqual(sorted(path.name for path in destination.iterdir()), ["flowtrack-2026-01-03.sqlite3", target.name]) with sqlite3.connect(target) as copy: self.assertEqual(copy.execute("SELECT flow FROM days").fetchone()[0], "light") class ValidationTests(unittest.TestCase): def test_dates(self): self.assertIsNotNone(app.parse_day(date.today().isoformat())) self.assertIsNotNone(app.parse_day((date.today() + timedelta(days=1)).isoformat())) self.assertIsNone(app.parse_day((date.today() + timedelta(days=2)).isoformat())) self.assertIsNone(app.parse_day("2026-02-30")) self.assertIsNone(app.parse_day("26-10-01")) def test_entries(self): self.assertEqual(app.clean_entry({"flow": "light", "symptoms": ["acne", "cramps", "acne"]}), {"flow": "light", "symptoms": ["acne", "cramps"]}) self.assertIsNone(app.clean_entry({"flow": "gushing"})) self.assertIsNone(app.clean_entry({"flow": None, "symptoms": ["