diff --git a/web/tests/test_content.py b/web/tests/test_content.py index ecbe09f..4ea2de4 100644 --- a/web/tests/test_content.py +++ b/web/tests/test_content.py @@ -68,16 +68,17 @@ async def _create_published_scenario(slug="test-scenario", city="TestCity", coun async def _create_article(slug="test-article", url_path="/test-article", - status="published", published_at=None): + status="published", published_at=None, + article_type="editorial"): """Insert an article row, return its id.""" pub = published_at or utcnow_iso() return await execute( """INSERT INTO articles (url_path, slug, title, meta_description, country, region, - status, published_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)""", + status, published_at, article_type) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", (url_path, slug, f"Title {slug}", f"Desc {slug}", "US", "North America", - status, pub), + status, pub, article_type), ) @@ -1228,6 +1229,96 @@ class TestAdminArticles: assert resp.status_code == 302 assert await fetch_one("SELECT 1 FROM articles WHERE id = ?", (article_id,)) is None + async def test_delete_never_removes_md_source(self, admin_client, db, tmp_path, monkeypatch): + """Regression: deleting an article must NOT touch source .md files.""" + import padelnomics.content.routes as content_routes_mod + + build_dir = tmp_path / "build" + build_dir.mkdir() + monkeypatch.setattr(content_routes_mod, "BUILD_DIR", build_dir) + + (build_dir / "del-safe.html").write_text("

built

") + md_file = tmp_path / "del-safe.md" + md_file.write_text("# Source") + + article_id = await _create_article(slug="del-safe", url_path="/del-safe") + + async with admin_client.session_transaction() as sess: + sess["csrf_token"] = "test" + + resp = await admin_client.post(f"/admin/articles/{article_id}/delete", form={ + "csrf_token": "test", + }) + assert resp.status_code == 302 + assert await fetch_one("SELECT 1 FROM articles WHERE id = ?", (article_id,)) is None + assert not (build_dir / "del-safe.html").exists(), "build file should be removed" + assert md_file.exists(), "source .md must NOT be deleted" + + async def test_bulk_delete_by_ids_never_removes_md(self, admin_client, db, tmp_path, monkeypatch): + """Regression: bulk delete by explicit IDs must NOT touch source .md files.""" + import padelnomics.content.routes as content_routes_mod + + build_dir = tmp_path / "build" + build_dir.mkdir() + monkeypatch.setattr(content_routes_mod, "BUILD_DIR", build_dir) + + (build_dir / "bulk-del-1.html").write_text("

1

") + (build_dir / "bulk-del-2.html").write_text("

2

") + md1 = tmp_path / "bulk-del-1.md" + md2 = tmp_path / "bulk-del-2.md" + md1.write_text("# One") + md2.write_text("# Two") + + id1 = await _create_article(slug="bulk-del-1", url_path="/bulk-del-1", article_type="generated") + id2 = await _create_article(slug="bulk-del-2", url_path="/bulk-del-2", article_type="cornerstone") + + async with admin_client.session_transaction() as sess: + sess["csrf_token"] = "test" + + resp = await admin_client.post("/admin/articles/bulk", form={ + "csrf_token": "test", + "action": "delete", + "article_ids": f"{id1},{id2}", + "apply_to_all": "false", + "article_type": "generated", + }) + assert resp.status_code == 200 + assert await fetch_one("SELECT 1 FROM articles WHERE id = ?", (id1,)) is None + assert await fetch_one("SELECT 1 FROM articles WHERE id = ?", (id2,)) is None + assert not (build_dir / "bulk-del-1.html").exists() + assert not (build_dir / "bulk-del-2.html").exists() + assert md1.exists(), "generated article .md must NOT be deleted" + assert md2.exists(), "cornerstone article .md must NOT be deleted" + + async def test_bulk_delete_apply_to_all_never_removes_md(self, admin_client, db, tmp_path, monkeypatch): + """Regression: bulk delete apply_to_all must NOT touch source .md files.""" + import padelnomics.content.routes as content_routes_mod + + build_dir = tmp_path / "build" + build_dir.mkdir() + monkeypatch.setattr(content_routes_mod, "BUILD_DIR", build_dir) + + (build_dir / "ata-del.html").write_text("

x

") + md_file = tmp_path / "ata-del.md" + md_file.write_text("# Source") + + await _create_article(slug="ata-del", url_path="/ata-del", article_type="generated") + + async with admin_client.session_transaction() as sess: + sess["csrf_token"] = "test" + + resp = await admin_client.post("/admin/articles/bulk", form={ + "csrf_token": "test", + "action": "delete", + "apply_to_all": "true", + "article_type": "generated", + "search": "ata-del", + }) + assert resp.status_code == 200 + assert await fetch_one("SELECT 1 FROM articles WHERE slug = 'ata-del'") is None + assert not (build_dir / "ata-del.html").exists() + assert md_file.exists(), "source .md must NOT be deleted" +