"""Integration tests for Wiki API routes""" import pytest from app import db from app.models import Epic, Wiki, wiki_entity_links @pytest.mark.integration class TestWikiRoutes: """Test Wiki API routes""" # ========== GET /boards//wikis ========== def test_get_board_wikis_success( self, client, db_session, regular_user, auth_headers, test_board ): """Test getting all wikis for a board""" # Create wikis for board wiki1 = Wiki( name="Getting Started", board_id=test_board.id, slug="getting-started", content=[ {"type": "paragraph", "children": [{"text": "Getting started content"}]} ], created_by=regular_user.id, ) wiki2 = Wiki( name="API Documentation", board_id=test_board.id, slug="api-documentation", content=[{"type": "paragraph", "children": [{"text": "API docs content"}]}], created_by=regular_user.id, ) db_session.add(wiki1) db_session.add(wiki2) db_session.commit() response = client.get( f"/api/boards/{test_board.id}/wikis", headers=auth_headers ) assert response.status_code == 200 data = response.get_json() assert len(data) == 2 assert data[0]["name"] == "Getting Started" assert data[1]["name"] == "API Documentation" def test_get_board_wikis_empty(self, client, db_session, auth_headers, test_board): """Test getting wikis when none exist for board""" response = client.get( f"/api/boards/{test_board.id}/wikis", headers=auth_headers ) assert response.status_code == 200 data = response.get_json() assert len(data) == 0 def test_get_board_wikis_board_not_found(self, client, db_session, auth_headers): """Test getting wikis for non-existent board""" response = client.get("/api/boards/99999/wikis", headers=auth_headers) assert response.status_code == 404 data = response.get_json() assert "not found" in data["error"].lower() def test_get_board_wikis_unauthorized(self, client, db_session, test_board): """Test getting wikis without authentication""" response = client.get(f"/api/boards/{test_board.id}/wikis") assert response.status_code == 401 # ========== POST /boards//wikis ========== def test_create_wiki_success( self, client, db_session, regular_user, auth_headers, test_board ): """Test creating a new wiki successfully""" wiki_data = { "name": "User Guide", "slug": "user-guide", "content": [{"type": "paragraph", "children": [{"text": "Welcome..."}]}], "summary": "A comprehensive user guide", "category": "Documentation", "tags": ["getting-started", "tutorial"], } response = client.post( f"/api/boards/{test_board.id}/wikis", headers=auth_headers, json=wiki_data, ) assert response.status_code == 201 data = response.get_json() assert data["name"] == "User Guide" assert data["slug"] == "user-guide" assert data["summary"] == "A comprehensive user guide" assert data["category"] == "Documentation" assert data["tags"] == ["getting-started", "tutorial"] assert data["created_by"] == regular_user.id assert data["updated_by"] == regular_user.id assert "id" in data def test_create_wiki_minimal_data( self, client, db_session, auth_headers, test_board ): """Test creating wiki with only required fields""" wiki_data = { "name": "Simple Wiki", "content": [{"type": "paragraph", "children": [{"text": "Content"}]}], } response = client.post( f"/api/boards/{test_board.id}/wikis", headers=auth_headers, json=wiki_data, ) assert response.status_code == 201 data = response.get_json() assert data["name"] == "Simple Wiki" assert data["slug"] == "simple-wiki" # Auto-generated assert data["summary"] is None assert data["category"] is None assert data["tags"] is None def test_create_wiki_auto_generate_slug( self, client, db_session, auth_headers, test_board ): """Test creating wiki with auto-generated slug""" wiki_data = { "name": "API Reference Guide", "content": [{"type": "paragraph", "children": [{"text": "Content"}]}], } response = client.post( f"/api/boards/{test_board.id}/wikis", headers=auth_headers, json=wiki_data, ) assert response.status_code == 201 data = response.get_json() assert data["slug"] == "api-reference-guide" def test_create_wiki_slug_collision( self, client, db_session, auth_headers, test_board, regular_user ): """Test creating wiki with colliding slug""" # Create first wiki wiki1 = Wiki( name="Test Wiki", board_id=test_board.id, slug="test-wiki", content=[{"type": "paragraph", "children": [{"text": "Content"}]}], created_by=regular_user.id, ) db_session.add(wiki1) db_session.commit() # Create second wiki with same name (should auto-increment slug) wiki_data = { "name": "Test Wiki", "content": [{"type": "paragraph", "children": [{"text": "Content"}]}], } response = client.post( f"/api/boards/{test_board.id}/wikis", headers=auth_headers, json=wiki_data, ) assert response.status_code == 201 data = response.get_json() assert data["slug"] == "test-wiki-1" def test_create_wiki_board_not_found(self, client, db_session, auth_headers): """Test creating wiki for non-existent board""" wiki_data = { "name": "Test Wiki", "content": [{"type": "paragraph", "children": [{"text": "Content"}]}], } response = client.post( "/api/boards/99999/wikis", headers=auth_headers, json=wiki_data, ) assert response.status_code == 404 data = response.get_json() assert "not found" in data["error"].lower() def test_create_wiki_unauthorized(self, client, db_session, test_board): """Test creating wiki without authentication""" wiki_data = { "name": "Test Wiki", "content": [{"type": "paragraph", "children": [{"text": "Content"}]}], } response = client.post( f"/api/boards/{test_board.id}/wikis", json=wiki_data, ) assert response.status_code == 401 def test_create_wiki_missing_name( self, client, db_session, auth_headers, test_board ): """Test creating wiki without name""" wiki_data = {"summary": "No name provided"} response = client.post( f"/api/boards/{test_board.id}/wikis", headers=auth_headers, json=wiki_data, ) assert response.status_code == 400 def test_create_wiki_name_too_long( self, client, db_session, auth_headers, test_board ): """Test creating wiki with name exceeding max length""" wiki_data = { "name": "A" * 201, "content": [{"type": "paragraph", "children": [{"text": "Content"}]}], } response = client.post( f"/api/boards/{test_board.id}/wikis", headers=auth_headers, json=wiki_data, ) assert response.status_code == 400 def test_create_wiki_invalid_slug( self, client, db_session, auth_headers, test_board ): """Test creating wiki with invalid slug format""" wiki_data = { "name": "Test Wiki", "slug": "invalid slug!", "content": [{"type": "paragraph", "children": [{"text": "Content"}]}], } response = client.post( f"/api/boards/{test_board.id}/wikis", headers=auth_headers, json=wiki_data, ) assert response.status_code == 400 # ========== GET /wikis/ ========== def test_get_wiki_success( self, client, db_session, auth_headers, test_board, regular_user ): """Test getting a specific wiki with details""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, slug="test-wiki", content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.get(f"/api/wikis/{wiki.id}", headers=auth_headers) assert response.status_code == 200 data = response.get_json() assert data["id"] == wiki.id assert data["name"] == "Test Wiki" assert data["slug"] == "test-wiki" assert "linked_cards" in data assert "linked_epics" in data def test_get_wiki_with_linked_entities( self, client, db_session, auth_headers, test_board, regular_user, test_card ): """Test getting wiki with linked cards and epics""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, slug="test-wiki", content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() # Link card to wiki db.session.execute( wiki_entity_links.insert().values( wiki_id=wiki.id, entity_type="card", entity_id=test_card.id ) ) db_session.commit() response = client.get(f"/api/wikis/{wiki.id}", headers=auth_headers) assert response.status_code == 200 data = response.get_json() assert "linked_cards" in data assert "linked_epics" in data assert len(data["linked_cards"]) == 1 assert data["linked_cards"][0]["id"] == test_card.id def test_get_wiki_not_found(self, client, db_session, auth_headers): """Test getting non-existent wiki""" response = client.get("/api/wikis/99999", headers=auth_headers) assert response.status_code == 404 data = response.get_json() assert "not found" in data["error"].lower() def test_get_wiki_unauthorized(self, client, db_session, test_board, regular_user): """Test getting wiki without authentication""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, slug="test-wiki", content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.get(f"/api/wikis/{wiki.id}") assert response.status_code == 401 # ========== PUT /wikis/ ========== def test_update_wiki_success( self, client, db_session, auth_headers, test_board, regular_user ): """Test updating a wiki successfully""" wiki = Wiki( name="Original Name", board_id=test_board.id, slug="original-slug", content=[{"type": "paragraph", "children": [{"text": "Original content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() update_data = { "name": "Updated Name", "slug": "updated-slug", "summary": "Updated summary", "category": "Documentation", "tags": ["updated", "tag"], } response = client.put( f"/api/wikis/{wiki.id}", headers=auth_headers, json=update_data, ) assert response.status_code == 200 data = response.get_json() assert data["name"] == "Updated Name" assert data["slug"] == "updated-slug" assert data["summary"] == "Updated summary" assert data["category"] == "Documentation" assert data["tags"] == ["updated", "tag"] def test_update_wiki_partial( self, client, db_session, auth_headers, test_board, regular_user ): """Test updating wiki with partial data""" wiki = Wiki( name="Original Name", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Original content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() update_data = {"name": "Updated Name"} response = client.put( f"/api/wikis/{wiki.id}", headers=auth_headers, json=update_data, ) assert response.status_code == 200 data = response.get_json() assert data["name"] == "Updated Name" # Other fields should remain unchanged def test_update_wiki_content( self, client, db_session, auth_headers, test_board, regular_user ): """Test updating wiki content""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Original content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() new_content = [{"type": "heading", "children": [{"text": "Updated Content"}]}] response = client.put( f"/api/wikis/{wiki.id}", headers=auth_headers, json={"content": new_content}, ) assert response.status_code == 200 data = response.get_json() assert data["content"] == new_content def test_update_wiki_not_found(self, client, db_session, auth_headers): """Test updating non-existent wiki""" response = client.put( "/api/wikis/99999", headers=auth_headers, json={"name": "Updated"}, ) assert response.status_code == 404 def test_update_wiki_unauthorized( self, client, db_session, test_board, regular_user ): """Test updating wiki without authentication""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.put(f"/api/wikis/{wiki.id}", json={"name": "Updated"}) assert response.status_code == 401 def test_update_wiki_invalid_name( self, client, db_session, auth_headers, test_board, regular_user ): """Test updating wiki with invalid name""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.put( f"/api/wikis/{wiki.id}", headers=auth_headers, json={"name": ""}, ) assert response.status_code == 400 def test_update_wiki_invalid_slug( self, client, db_session, auth_headers, test_board, regular_user ): """Test updating wiki with invalid slug""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.put( f"/api/wikis/{wiki.id}", headers=auth_headers, json={"slug": "invalid slug!"}, ) assert response.status_code == 400 # ========== DELETE /wikis/ ========== def test_delete_wiki_success( self, client, db_session, auth_headers, test_board, regular_user ): """Test deleting a wiki successfully""" wiki = Wiki( name="To Delete", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() wiki_id = wiki.id response = client.delete(f"/api/wikis/{wiki.id}", headers=auth_headers) assert response.status_code == 200 data = response.get_json() assert "message" in data # Verify wiki is deleted deleted_wiki = db.session.get(Wiki, wiki_id) assert deleted_wiki is None def test_delete_wiki_with_links( self, client, db_session, auth_headers, test_board, regular_user, test_card ): """Test deleting wiki removes entity links""" wiki = Wiki( name="Wiki with Links", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() # Link card to wiki db.session.execute( wiki_entity_links.insert().values( wiki_id=wiki.id, entity_type="card", entity_id=test_card.id ) ) db_session.commit() response = client.delete(f"/api/wikis/{wiki.id}", headers=auth_headers) assert response.status_code == 200 # Verify links are deleted (cascade) from sqlalchemy import select links = db.session.scalars( select(wiki_entity_links).where(wiki_entity_links.c.wiki_id == wiki.id) ).all() assert len(links) == 0 def test_delete_wiki_not_found(self, client, db_session, auth_headers): """Test deleting non-existent wiki""" response = client.delete("/api/wikis/99999", headers=auth_headers) assert response.status_code == 404 def test_delete_wiki_unauthorized( self, client, db_session, test_board, regular_user ): """Test deleting wiki without authentication""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.delete(f"/api/wikis/{wiki.id}") assert response.status_code == 401 # ========== POST /wikis//links ========== def test_create_wiki_link_card( self, client, db_session, auth_headers, test_board, regular_user, test_card ): """Test linking wiki to card successfully""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.post( f"/api/wikis/{wiki.id}/links", headers=auth_headers, json={"entity_type": "card", "entity_id": test_card.id}, ) assert response.status_code == 201 data = response.get_json() assert "message" in data # Verify link is created from sqlalchemy import select link = db.session.scalars( select(wiki_entity_links).where( wiki_entity_links.c.wiki_id == wiki.id, wiki_entity_links.c.entity_type == "card", wiki_entity_links.c.entity_id == test_card.id, ) ).first() assert link is not None def test_create_wiki_link_epic( self, client, db_session, auth_headers, test_board, regular_user, test_list, ): """Test linking wiki to epic successfully""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) epic = Epic(name="Test Epic", board_id=test_board.id) db_session.add(wiki) db_session.add(epic) db_session.commit() response = client.post( f"/api/wikis/{wiki.id}/links", headers=auth_headers, json={"entity_type": "epic", "entity_id": epic.id}, ) assert response.status_code == 201 # Verify link is created from sqlalchemy import select link = db.session.scalars( select(wiki_entity_links).where( wiki_entity_links.c.wiki_id == wiki.id, wiki_entity_links.c.entity_type == "epic", wiki_entity_links.c.entity_id == epic.id, ) ).first() assert link is not None def test_create_wiki_link_list( self, client, db_session, auth_headers, test_board, regular_user, test_list, ): """Test linking wiki to list successfully""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.post( f"/api/wikis/{wiki.id}/links", headers=auth_headers, json={"entity_type": "list", "entity_id": test_list.id}, ) assert response.status_code == 201 def test_create_wiki_link_board( self, client, db_session, auth_headers, test_board, regular_user ): """Test linking wiki to board successfully""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.post( f"/api/wikis/{wiki.id}/links", headers=auth_headers, json={"entity_type": "board", "entity_id": test_board.id}, ) assert response.status_code == 201 def test_create_wiki_link_already_exists( self, client, db_session, auth_headers, test_board, regular_user, test_card ): """Test creating duplicate link""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() # Create first link db.session.execute( wiki_entity_links.insert().values( wiki_id=wiki.id, entity_type="card", entity_id=test_card.id ) ) db_session.commit() # Try to create duplicate link response = client.post( f"/api/wikis/{wiki.id}/links", headers=auth_headers, json={"entity_type": "card", "entity_id": test_card.id}, ) assert response.status_code == 400 data = response.get_json() assert "already exists" in data["error"].lower() def test_create_wiki_link_wiki_not_found(self, client, db_session, auth_headers): """Test linking non-existent wiki""" response = client.post( "/api/wikis/99999/links", headers=auth_headers, json={"entity_type": "card", "entity_id": 1}, ) assert response.status_code == 404 def test_create_wiki_link_entity_not_found( self, client, db_session, auth_headers, test_board, regular_user ): """Test linking to non-existent entity""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.post( f"/api/wikis/{wiki.id}/links", headers=auth_headers, json={"entity_type": "card", "entity_id": 99999}, ) assert response.status_code == 404 def test_create_wiki_link_unauthorized( self, client, db_session, test_board, regular_user ): """Test creating link without authentication""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.post( f"/api/wikis/{wiki.id}/links", json={"entity_type": "card", "entity_id": 1}, ) assert response.status_code == 401 # ========== DELETE /wikis//links ========== def test_delete_wiki_link_success( self, client, db_session, auth_headers, test_board, regular_user, test_card ): """Test deleting a wiki link successfully""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() # Create link db.session.execute( wiki_entity_links.insert().values( wiki_id=wiki.id, entity_type="card", entity_id=test_card.id ) ) db_session.commit() response = client.delete( f"/api/wikis/{wiki.id}/links", headers=auth_headers, json={"entity_type": "card", "entity_id": test_card.id}, ) assert response.status_code == 200 data = response.get_json() assert "message" in data # Verify link is deleted from sqlalchemy import select link = db.session.scalars( select(wiki_entity_links).where( wiki_entity_links.c.wiki_id == wiki.id, wiki_entity_links.c.entity_type == "card", wiki_entity_links.c.entity_id == test_card.id, ) ).first() assert link is None def test_delete_wiki_link_wiki_not_found(self, client, db_session, auth_headers): """Test deleting link for non-existent wiki""" response = client.delete( "/api/wikis/99999/links", headers=auth_headers, json={"entity_type": "card", "entity_id": 1}, ) assert response.status_code == 404 def test_delete_wiki_link_not_found( self, client, db_session, auth_headers, test_board, regular_user ): """Test deleting non-existent link""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.delete( f"/api/wikis/{wiki.id}/links", headers=auth_headers, json={"entity_type": "card", "entity_id": 99999}, ) assert response.status_code == 404 def test_delete_wiki_link_unauthorized( self, client, db_session, test_board, regular_user ): """Test deleting link without authentication""" wiki = Wiki( name="Test Wiki", board_id=test_board.id, content=[{"type": "paragraph", "children": [{"text": "Test content"}]}], created_by=regular_user.id, ) db_session.add(wiki) db_session.commit() response = client.delete( f"/api/wikis/{wiki.id}/links", json={"entity_type": "card", "entity_id": 1}, ) assert response.status_code == 401