import json from unittest.mock import patch from tinyweb.db import get_db, return_db from tinyweb.handlers import ( handle_export, handle_import_form, handle_import_submit, handle_reindex_form, handle_reindex_submit, ) def _all_urls(): db = get_db() try: return {r["url"] for r in db.execute("SELECT url FROM pages").fetchall()} finally: return_db(db) def test_export_returns_json(seeded_db, csrf_session): resp = handle_export({}) assert resp["status"] == 200 assert resp["content_type"] == "application/json" data = json.loads(resp["body"]) assert len(data) == 4 assert data[0]["url"] == "https://example.com/rust-intro" assert data[0]["title"] == "Rust Intro" def test_export_paginates(seeded_db, csrf_session): from tinyweb.handlers.data import MAX_EXPORT resp = handle_export({"batch": ["0"]}) data = json.loads(resp["body"]) assert len(data) <= MAX_EXPORT def test_export_respects_batch_param(seeded_db, csrf_session): resp = handle_export({"batch": ["99"]}) data = json.loads(resp["body"]) assert data == [] def test_export_bad_batch_defaults_to_zero(seeded_db, csrf_session): resp = handle_export({"batch": ["abc"]}) assert resp["status"] == 200 def test_export_has_content_disposition(seeded_db, csrf_session): resp = handle_export({}) assert any( k.lower() == "content-disposition" and "tinyweb-export.json" in v for k, v in resp.get("headers", {}).items() ) def test_import_form_renders(temp_db, csrf_session): resp = handle_import_form() assert resp["status"] == 200 assert "import" in resp["body"].lower() assert "textarea" in resp["body"] def test_import_submit_empty_rejected(temp_db, csrf_session): resp = handle_import_submit({"data": [""]}) assert resp["status"] == 200 assert "paste json" in resp["body"].lower() def test_import_submit_invalid_json(temp_db, csrf_session): resp = handle_import_submit({"data": ["not json"]}) assert resp["status"] == 200 assert "invalid json" in resp["body"].lower() def test_import_submit_non_array_rejected(temp_db, csrf_session): resp = handle_import_submit({"data": ['"string"']}) assert resp["status"] == 200 assert "expected a json array" in resp["body"].lower() def test_import_submit_too_many_rejected(temp_db, csrf_session): big = [{"url": "https://x.com/y"}] * 101 resp = handle_import_submit({"data": [json.dumps(big)]}) assert resp["status"] == 200 assert "too many" in resp["body"].lower() @patch("tinyweb.handlers.data.index_url") def test_import_submit_calls_index_url(mock_index, temp_db, csrf_session): payload = [ {"url": "https://example.com/a", "note": "note a"}, {"url": "https://example.com/b", "note": "note b"}, ] resp = handle_import_submit({"data": [json.dumps(payload)]}) assert resp["status"] == 200 assert "imported 2" in resp["body"].lower() assert mock_index.call_count == 2 mock_index.assert_any_call("https://example.com/a", "note a") mock_index.assert_any_call("https://example.com/b", "note b") @patch("tinyweb.handlers.data.index_url") def test_import_submit_skips_empty_url(mock_index, temp_db, csrf_session): payload = [ {"url": "", "note": ""}, {"url": "https://example.com/real", "note": ""}, ] resp = handle_import_submit({"data": [json.dumps(payload)]}) assert "imported 1" in resp["body"].lower() assert mock_index.call_count == 1 @patch("tinyweb.handlers.data.index_url") def test_import_submit_counts_errors(mock_index, temp_db, csrf_session): mock_index.side_effect = [None, ValueError("boom")] payload = [ {"url": "https://example.com/a", "note": ""}, {"url": "https://example.com/b", "note": ""}, ] resp = handle_import_submit({"data": [json.dumps(payload)]}) assert "imported 1" in resp["body"].lower() assert "1 error" in resp["body"].lower() def test_reindex_form_shows_disabled_message(temp_db, csrf_session): resp = handle_reindex_form() assert resp["status"] == 200 assert "disabled" in resp["body"].lower() assert "settings" in resp["body"] def test_reindex_form_shows_progress_when_running(temp_db, csrf_session): from tinyweb.db import set_setting set_setting("semantic_search", "1") set_setting("reindex_progress", "5/10") resp = handle_reindex_form() assert "reindex in progress" in resp["body"].lower() assert "5/10" in resp["body"] def test_reindex_submit_redirects(temp_db, csrf_session): resp = handle_reindex_submit({}) assert resp["status"] in (302, 303)