import tempfile from pathlib import Path from flask_fatima import create_app, load_yaml_config, log_database_summary URL_PREFIX = "/fatima" def url(path="/"): return f"{URL_PREFIX}{path}" def client(url_prefix=URL_PREFIX): database = tempfile.NamedTemporaryFile(suffix=".sqlite") app = create_app({ "TESTING": True, "SECRET_KEY": "test-secret", "APP_USERNAME": "user", "APP_PASSWORD": "pass", "DATABASE": database.name, "URL_PREFIX": url_prefix, }) test_client = app.test_client() test_client._database_file = database return test_client def test_calendar_requires_login(): response = client().get(url()) assert response.status_code == 302 assert response.headers["Location"].endswith(url("/login")) def test_database_summary_is_appended_to_log(capsys): test_client = client() with tempfile.TemporaryDirectory() as directory: log_path = Path(directory) / "fatima.log" log_database_summary(test_client.application.config["DATABASE"], log_path) logged = log_path.read_text(encoding="utf-8") assert "Fatima database:" in logged assert f"path={test_client.application.config['DATABASE']}" in logged assert "amounts=0 | statuses=0 | recorded_dates=0" in logged assert "Fatima database:" in capsys.readouterr().out def test_database_path_is_resolved_relative_to_config_file(): with tempfile.TemporaryDirectory() as directory: config_path = Path(directory) / "production.yaml" config_path.write_text( """ flask: secret_key: test-secret database: data/calendar.sqlite auth: username: user password: pass """, encoding="utf-8", ) config = load_yaml_config(config_path) assert config["DATABASE"] == str( (Path(directory) / "data" / "calendar.sqlite").resolve() ) def test_prefix_applies_to_generated_links_and_unprefixed_routes_are_rejected(): test_client = client() test_client.post(url("/login"), data={"username": "user", "password": "pass"}) page = test_client.get(url()) assert b'href="/fatima/static/styles.css"' in page.data assert b'src="/fatima/static/calendar.js"' in page.data assert b'data-save-url="/fatima/api/choices"' in page.data assert b'href="/fatima/report"' in page.data assert page.data.index(b'class="report-button"') < page.data.index(b'class="today-button"') assert test_client.get("/").status_code == 404 def test_url_prefix_is_configurable_and_can_be_disabled(): custom_client = client("/calendar") response = custom_client.get("/calendar/") assert response.headers["Location"].endswith("/calendar/login") assert custom_client.get(url()).status_code == 404 root_client = client("") assert root_client.get("/").headers["Location"].endswith("/login") def test_login_and_logout(): test_client = client() response = test_client.post(url("/login"), data={"username": "user", "password": "pass"}) assert response.status_code == 302 assert response.headers["Location"].endswith(url()) assert test_client.get(url()).status_code == 200 assert test_client.post(url("/logout")).headers["Location"].endswith(url("/login")) def test_bad_login_shows_error(): response = client().post(url("/login"), data={"username": "user", "password": "wrong"}) assert response.status_code == 200 assert b"not correct" in response.data def test_login_uses_wwwsuynl_logo(): response = client().get(url("/login")) assert b'src="/fatima/static/wwwsuynl.png"' in response.data assert b'href="/fatima/static/wwwsuynl.png"' in response.data assert b'alt="www.suy.nl"' in response.data def test_choice_is_saved_and_rendered(): test_client = client() test_client.post(url("/login"), data={"username": "user", "password": "pass"}) response = test_client.post(url("/api/choices"), json={"date": "2026-07-15", "amount": "27.50"}) assert response.status_code == 200 assert response.json["amount_cents"] == 2750 assert b'"2026-07-15": 2750' in test_client.get(url()).data def test_choice_rejects_invalid_amount(): test_client = client() test_client.post(url("/login"), data={"username": "user", "password": "pass"}) response = test_client.post(url("/api/choices"), json={"date": "2026-07-15", "amount": "-1"}) assert response.status_code == 400 def test_day_status_is_saved_and_rendered(): test_client = client() test_client.post(url("/login"), data={"username": "user", "password": "pass"}) response = test_client.post( url("/api/statuses"), json={"date": "2026-07-15", "status": "vacation"}, ) assert response.status_code == 200 assert response.json["status"] == "vacation" assert b'"2026-07-15": "vacation"' in test_client.get(url()).data def test_day_status_rejects_unknown_value(): test_client = client() test_client.post(url("/login"), data={"username": "user", "password": "pass"}) response = test_client.post( url("/api/statuses"), json={"date": "2026-07-15", "status": "elsewhere"}, ) assert response.status_code == 400 def test_delete_removes_amount_and_status(): test_client = client() test_client.post(url("/login"), data={"username": "user", "password": "pass"}) test_client.post(url("/api/choices"), json={"date": "2026-07-15", "amount": "55"}) test_client.post(url("/api/statuses"), json={"date": "2026-07-15", "status": "sick"}) response = test_client.post(url("/api/delete-date"), json={"date": "2026-07-15"}) assert response.status_code == 200 assert response.json["deleted"] is True page = test_client.get(url()).data assert b'"2026-07-15": 5500' not in page assert b'"2026-07-15": "sick"' not in page def test_report_groups_money_and_days_by_status(): from datetime import date test_client = client() test_client.post(url("/login"), data={"username": "user", "password": "pass"}) year = date.today().year worked_date = f"{year}-01-10" vacation_date = f"{year}-02-10" test_client.post(url("/api/choices"), json={"date": worked_date, "amount": "55"}) test_client.post(url("/api/choices"), json={"date": vacation_date, "amount": "27.50"}) test_client.post(url("/api/statuses"), json={"date": vacation_date, "status": "vacation"}) response = test_client.get(url("/report")) assert response.status_code == 200 assert b"\xe2\x82\xac55.00" in response.data assert b"\xe2\x82\xac27.50" in response.data assert b"2 recorded days" in response.data def test_report_can_navigate_to_another_year(): test_client = client() test_client.post(url("/login"), data={"username": "user", "password": "pass"}) response = test_client.get(url("/report?year=2025")) assert response.status_code == 200 assert b"2025 report" in response.data assert b"year=2024" in response.data assert b"year=2026" in response.data