"""Threads, comments, and who may touch them.""" from __future__ import annotations import pytest @pytest.fixture def thread(db, make_member): author = make_member("autora") cursor = db.execute( "INSERT INTO threads (author_id, title, body_md) VALUES (?, 'Original', 'Cuerpo')", (author,), ) return {"id": cursor.lastrowid, "author": author} def test_a_member_can_post_and_read_it_back(client, post, make_member, sign_in): sign_in(make_member("maria")) post("/comunidad/nuevo", {"title": "Reunión del jueves", "body": "A las 19h en el café."}) body = client.get("/comunidad/").get_data(as_text=True) assert "Reunión del jueves" in body def test_markdown_is_rendered_but_html_is_not(client, post, make_member, sign_in): sign_in(make_member("maria")) post("/comunidad/nuevo", { "title": "Prueba", "body": "**fuerte** y ", }) body = client.get("/comunidad/tema/1").get_data(as_text=True) assert "fuerte" in body assert "" not in body assert "<script>" in body def test_nobody_edits_someone_elses_words(client, post, thread, make_member, sign_in): """Not even an admin. Removing a post is visible to its author; quietly rewriting it is not, which is why moderation here means deletion.""" sign_in(make_member("admina", role="admin")) response = post(f"/comunidad/tema/{thread['id']}/editar", {"title": "Reescrito", "body": "Otra cosa"}) assert response.status_code == 403 def test_a_user_cannot_delete_someone_elses_thread(client, post, thread, make_member, sign_in): sign_in(make_member("ajena")) assert post(f"/comunidad/tema/{thread['id']}/eliminar").status_code == 403 def test_an_admin_can_delete_any_thread(client, db, post, thread, make_member, sign_in): sign_in(make_member("admina", role="admin")) post(f"/comunidad/tema/{thread['id']}/eliminar") row = db.execute("SELECT deleted_at FROM threads WHERE id = ?", (thread["id"],)).fetchone() assert row["deleted_at"] is not None def test_the_author_can_edit_their_own(client, db, post, thread, sign_in): sign_in(thread["author"]) post(f"/comunidad/tema/{thread['id']}/editar", {"title": "Corregido", "body": "Mejor"}) row = db.execute("SELECT title, edited_at FROM threads WHERE id = ?", (thread["id"],)).fetchone() assert row["title"] == "Corregido" assert row["edited_at"] is not None def test_a_deleted_thread_disappears_from_the_list_and_the_page( client, db, post, thread, sign_in): sign_in(thread["author"]) post(f"/comunidad/tema/{thread['id']}/eliminar") assert "Original" not in client.get("/comunidad/").get_data(as_text=True) assert client.get(f"/comunidad/tema/{thread['id']}").status_code == 404 def test_a_deleted_thread_is_left_out_of_the_export(client, db, post, thread, sign_in): sign_in(thread["author"]) post(f"/comunidad/tema/{thread['id']}/eliminar") assert "Original" not in client.get("/comunidad/mis-datos").get_data(as_text=True) def test_a_closed_thread_refuses_replies(client, db, post, thread, make_member, sign_in): db.execute("UPDATE threads SET locked = 1 WHERE id = ?", (thread["id"],)) sign_in(make_member("maria")) post(f"/comunidad/tema/{thread['id']}/comentar", {"body": "¿Hola?"}) count = db.execute("SELECT COUNT(*) AS n FROM comments WHERE thread_id = ?", (thread["id"],)).fetchone()["n"] assert count == 0 def test_only_admins_pin(client, post, thread, make_member, sign_in): sign_in(make_member("maria")) assert post(f"/comunidad/tema/{thread['id']}/estado", {"field": "pinned", "value": "1"}).status_code == 403 def test_the_state_field_is_not_a_way_into_the_query(client, post, thread, make_member, sign_in): """`field` is interpolated into the UPDATE, so it is checked against a fixed pair first. This asserts the check, not the interpolation.""" sign_in(make_member("admina", role="admin")) assert post(f"/comunidad/tema/{thread['id']}/estado", {"field": "role", "value": "1"}).status_code == 400 def test_the_cooldown_stops_a_double_submit(app, client, post, make_member, sign_in, db): app.config["COOLDOWN_SECONDS"] = 60 sign_in(make_member("rapida")) post("/comunidad/nuevo", {"title": "Primero", "body": "Uno"}) post("/comunidad/nuevo", {"title": "Segundo", "body": "Dos"}) count = db.execute("SELECT COUNT(*) AS n FROM threads").fetchone()["n"] assert count == 1