Capture ThinkStorm project: codebase state, workflows, and access control policies
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
"""
|
||||
ThinkStorm Trash & Empty Trash Tests
|
||||
Tests moving ideas to trash, hiding from default browsing, restoring from trash,
|
||||
permanently deleting single ideas, and emptying the entire trash bin with cascade cleanup.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from starlette.testclient import TestClient
|
||||
from thinkstorm.main import app
|
||||
from thinkstorm.database import init_db, get_db
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def setup_app():
|
||||
init_db()
|
||||
|
||||
def get_auth_token(username="admin", password="admin-thinkstorm-pass-2026"):
|
||||
client = TestClient(app)
|
||||
resp = client.post("/api/auth/login", json={"username": username, "password": password})
|
||||
assert resp.status_code == 200
|
||||
return resp.json()["token"]
|
||||
|
||||
def test_trash_and_hide_from_default_catalog():
|
||||
client = TestClient(app)
|
||||
token = get_auth_token()
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
# 1. Create an idea and set to AVAILABLE
|
||||
sub_resp = client.post("/api/ideas", json={"text": "A low-value idea that should be discarded: spam text test."})
|
||||
idea_id = sub_resp.json()["id"]
|
||||
|
||||
with get_db() as conn:
|
||||
conn.execute("UPDATE ideas SET lifecycle_state = 'AVAILABLE' WHERE id = ?", (idea_id,))
|
||||
|
||||
# Verify visible in default list
|
||||
list_resp = client.get("/api/ideas", headers=headers)
|
||||
idea_ids_default = [i["id"] for i in list_resp.json()]
|
||||
assert idea_id in idea_ids_default
|
||||
|
||||
# 2. Move idea to Trash
|
||||
trash_resp = client.post(f"/api/ideas/{idea_id}/trash", headers=headers)
|
||||
assert trash_resp.status_code == 200
|
||||
trash_data = trash_resp.json()
|
||||
assert trash_data["lifecycle_state"] == "TRASHED"
|
||||
assert trash_data["previous_lifecycle_state"] == "AVAILABLE"
|
||||
|
||||
# 3. Verify hidden from default ideas list and ALL filter
|
||||
list_resp_after = client.get("/api/ideas", headers=headers)
|
||||
idea_ids_after = [i["id"] for i in list_resp_after.json()]
|
||||
assert idea_id not in idea_ids_after
|
||||
|
||||
list_all_resp = client.get("/api/ideas?state=ALL", headers=headers)
|
||||
idea_ids_all = [i["id"] for i in list_all_resp.json()]
|
||||
assert idea_id not in idea_ids_all
|
||||
|
||||
# 4. Verify visible when explicitly filtering by TRASHED
|
||||
list_trashed_resp = client.get("/api/ideas?state=TRASHED", headers=headers)
|
||||
idea_ids_trashed = [i["id"] for i in list_trashed_resp.json()]
|
||||
assert idea_id in idea_ids_trashed
|
||||
|
||||
def test_restore_idea_from_trash():
|
||||
client = TestClient(app)
|
||||
token = get_auth_token()
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
# 1. Create idea, set to CLAIMED, then trash it
|
||||
sub_resp = client.post("/api/ideas", json={"text": "Idea that was mistakenly trashed and needs retrieval."})
|
||||
idea_id = sub_resp.json()["id"]
|
||||
|
||||
with get_db() as conn:
|
||||
conn.execute("UPDATE ideas SET lifecycle_state = 'CLAIMED', claimed_by = 'admin' WHERE id = ?", (idea_id,))
|
||||
|
||||
trash_resp = client.post(f"/api/ideas/{idea_id}/trash", headers=headers)
|
||||
assert trash_resp.status_code == 200
|
||||
|
||||
# 2. Restore idea
|
||||
restore_resp = client.post(f"/api/ideas/{idea_id}/restore", headers=headers)
|
||||
assert restore_resp.status_code == 200
|
||||
assert restore_resp.json()["lifecycle_state"] == "CLAIMED"
|
||||
|
||||
# 3. Verify idea is restored and visible
|
||||
detail_resp = client.get(f"/api/ideas/{idea_id}", headers=headers)
|
||||
assert detail_resp.status_code == 200
|
||||
assert detail_resp.json()["lifecycle_state"] == "CLAIMED"
|
||||
|
||||
def test_permanently_delete_single_idea():
|
||||
client = TestClient(app)
|
||||
token = get_auth_token()
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
# 1. Create idea and trash it
|
||||
sub_resp = client.post("/api/ideas", json={"text": "Idea to be deleted permanently right now."})
|
||||
idea_id = sub_resp.json()["id"]
|
||||
client.post(f"/api/ideas/{idea_id}/trash", headers=headers)
|
||||
|
||||
# 2. Permanently delete
|
||||
del_resp = client.delete(f"/api/ideas/{idea_id}/permanent", headers=headers)
|
||||
assert del_resp.status_code == 200
|
||||
assert "permanently deleted" in del_resp.json()["message"].lower()
|
||||
|
||||
# 3. Verify it no longer exists anywhere
|
||||
detail_resp = client.get(f"/api/ideas/{idea_id}", headers=headers)
|
||||
assert detail_resp.status_code == 404
|
||||
|
||||
def test_empty_trash_bulk():
|
||||
client = TestClient(app)
|
||||
token = get_auth_token()
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
# 1. Create 3 ideas and trash all 3
|
||||
trash_ids = []
|
||||
for i in range(3):
|
||||
resp = client.post("/api/ideas", json={"text": f"Junk idea {i+1} destined for trash empty test."})
|
||||
i_id = resp.json()["id"]
|
||||
client.post(f"/api/ideas/{i_id}/trash", headers=headers)
|
||||
trash_ids.append(i_id)
|
||||
|
||||
# Verify all 3 are currently in trash
|
||||
trashed_list = client.get("/api/ideas?state=TRASHED", headers=headers).json()
|
||||
trashed_ids_in_db = [x["id"] for x in trashed_list]
|
||||
for tid in trash_ids:
|
||||
assert tid in trashed_ids_in_db
|
||||
|
||||
# 2. Empty Trash
|
||||
empty_resp = client.post("/api/trash/empty", headers=headers)
|
||||
assert empty_resp.status_code == 200
|
||||
data = empty_resp.json()
|
||||
assert data["deleted_count"] >= 3
|
||||
|
||||
# 3. Verify trash list is now empty of those IDs
|
||||
trashed_after = client.get("/api/ideas?state=TRASHED", headers=headers).json()
|
||||
trashed_after_ids = [x["id"] for x in trashed_after]
|
||||
for tid in trash_ids:
|
||||
assert tid not in trashed_after_ids
|
||||
Reference in New Issue
Block a user