|
4ce269c…
|
ragelink
|
1 |
import pytest |
|
4ce269c…
|
ragelink
|
2 |
|
|
4ce269c…
|
ragelink
|
3 |
from .models import Page |
|
4ce269c…
|
ragelink
|
4 |
|
|
4ce269c…
|
ragelink
|
5 |
|
|
4ce269c…
|
ragelink
|
6 |
@pytest.mark.django_db |
|
4ce269c…
|
ragelink
|
7 |
class TestPageModel: |
|
4ce269c…
|
ragelink
|
8 |
def test_create_page(self, org, admin_user): |
|
4ce269c…
|
ragelink
|
9 |
page = Page.objects.create(name="Test Page", content="# Hello", organization=org, created_by=admin_user) |
|
4ce269c…
|
ragelink
|
10 |
assert page.slug == "test-page" |
|
4ce269c…
|
ragelink
|
11 |
assert page.guid is not None |
|
4ce269c…
|
ragelink
|
12 |
assert page.is_published is True |
|
4ce269c…
|
ragelink
|
13 |
|
|
4ce269c…
|
ragelink
|
14 |
def test_soft_delete_page(self, sample_page, admin_user): |
|
4ce269c…
|
ragelink
|
15 |
sample_page.soft_delete(user=admin_user) |
|
4ce269c…
|
ragelink
|
16 |
assert Page.objects.filter(slug=sample_page.slug).count() == 0 |
|
4ce269c…
|
ragelink
|
17 |
assert Page.all_objects.filter(slug=sample_page.slug).count() == 1 |
|
4ce269c…
|
ragelink
|
18 |
|
|
4ce269c…
|
ragelink
|
19 |
|
|
4ce269c…
|
ragelink
|
20 |
@pytest.mark.django_db |
|
4ce269c…
|
ragelink
|
21 |
class TestPageViews: |
|
4ce269c…
|
ragelink
|
22 |
def test_page_list_renders(self, admin_client, sample_page): |
|
4ce269c…
|
ragelink
|
23 |
response = admin_client.get("/kb/") |
|
4ce269c…
|
ragelink
|
24 |
assert response.status_code == 200 |
|
4ce269c…
|
ragelink
|
25 |
assert sample_page.name in response.content.decode() |
|
4ce269c…
|
ragelink
|
26 |
|
|
4ce269c…
|
ragelink
|
27 |
def test_page_list_htmx(self, admin_client, sample_page): |
|
4ce269c…
|
ragelink
|
28 |
response = admin_client.get("/kb/", HTTP_HX_REQUEST="true") |
|
4ce269c…
|
ragelink
|
29 |
assert response.status_code == 200 |
|
4ce269c…
|
ragelink
|
30 |
assert b"page-table" in response.content |
|
4ce269c…
|
ragelink
|
31 |
|
|
4ce269c…
|
ragelink
|
32 |
def test_page_list_search(self, admin_client, sample_page): |
|
4ce269c…
|
ragelink
|
33 |
response = admin_client.get("/kb/?search=Getting") |
|
4ce269c…
|
ragelink
|
34 |
assert response.status_code == 200 |
|
4ce269c…
|
ragelink
|
35 |
|
|
c588255…
|
ragelink
|
36 |
def test_page_list_accessible_to_all(self, no_perm_client, sample_page): |
|
c588255…
|
ragelink
|
37 |
# Published pages are visible to everyone, including users without PAGE_VIEW perm |
|
4ce269c…
|
ragelink
|
38 |
response = no_perm_client.get("/kb/") |
|
c588255…
|
ragelink
|
39 |
assert response.status_code == 200 |
|
c588255…
|
ragelink
|
40 |
assert sample_page.name in response.content.decode() |
|
4ce269c…
|
ragelink
|
41 |
|
|
4ce269c…
|
ragelink
|
42 |
def test_page_create(self, admin_client, org): |
|
4ce269c…
|
ragelink
|
43 |
response = admin_client.post("/kb/create/", {"name": "New Page", "content": "# New", "is_published": True}) |
|
4ce269c…
|
ragelink
|
44 |
assert response.status_code == 302 |
|
4ce269c…
|
ragelink
|
45 |
assert Page.objects.filter(slug="new-page").exists() |
|
4ce269c…
|
ragelink
|
46 |
|
|
4ce269c…
|
ragelink
|
47 |
def test_page_create_denied(self, no_perm_client, org): |
|
4ce269c…
|
ragelink
|
48 |
response = no_perm_client.post("/kb/create/", {"name": "Hack"}) |
|
4ce269c…
|
ragelink
|
49 |
assert response.status_code == 403 |
|
4ce269c…
|
ragelink
|
50 |
|
|
4ce269c…
|
ragelink
|
51 |
def test_page_detail_renders_markdown(self, admin_client, sample_page): |
|
4ce269c…
|
ragelink
|
52 |
response = admin_client.get(f"/kb/{sample_page.slug}/") |
|
4ce269c…
|
ragelink
|
53 |
assert response.status_code == 200 |
|
4ce269c…
|
ragelink
|
54 |
content = response.content.decode() |
|
4ce269c…
|
ragelink
|
55 |
assert "<h1>" in content or "Getting Started" in content |
|
4ce269c…
|
ragelink
|
56 |
|
|
c588255…
|
ragelink
|
57 |
def test_page_detail_accessible_for_published(self, no_perm_client, sample_page): |
|
c588255…
|
ragelink
|
58 |
# Published pages are viewable by anyone, even without PAGE_VIEW permission |
|
4ce269c…
|
ragelink
|
59 |
response = no_perm_client.get(f"/kb/{sample_page.slug}/") |
|
c588255…
|
ragelink
|
60 |
assert response.status_code == 200 |
|
c588255…
|
ragelink
|
61 |
|
|
c588255…
|
ragelink
|
62 |
def test_page_detail_denied_for_draft(self, no_perm_client, org, admin_user): |
|
c588255…
|
ragelink
|
63 |
# Unpublished drafts require auth + edit permission |
|
c588255…
|
ragelink
|
64 |
draft = Page.objects.create(name="Draft Only", content="Secret", organization=org, is_published=False, created_by=admin_user) |
|
c588255…
|
ragelink
|
65 |
response = no_perm_client.get(f"/kb/{draft.slug}/") |
|
4ce269c…
|
ragelink
|
66 |
assert response.status_code == 403 |
|
4ce269c…
|
ragelink
|
67 |
|
|
4ce269c…
|
ragelink
|
68 |
def test_page_update(self, admin_client, sample_page): |
|
4ce269c…
|
ragelink
|
69 |
response = admin_client.post( |
|
4ce269c…
|
ragelink
|
70 |
f"/kb/{sample_page.slug}/edit/", |
|
4ce269c…
|
ragelink
|
71 |
{"name": "Updated Page", "content": "# Updated", "is_published": True}, |
|
4ce269c…
|
ragelink
|
72 |
) |
|
4ce269c…
|
ragelink
|
73 |
assert response.status_code == 302 |
|
4ce269c…
|
ragelink
|
74 |
sample_page.refresh_from_db() |
|
4ce269c…
|
ragelink
|
75 |
assert sample_page.name == "Updated Page" |
|
4ce269c…
|
ragelink
|
76 |
|
|
4ce269c…
|
ragelink
|
77 |
def test_page_update_denied(self, no_perm_client, sample_page): |
|
4ce269c…
|
ragelink
|
78 |
response = no_perm_client.post(f"/kb/{sample_page.slug}/edit/", {"name": "Hacked"}) |
|
4ce269c…
|
ragelink
|
79 |
assert response.status_code == 403 |
|
4ce269c…
|
ragelink
|
80 |
|
|
4ce269c…
|
ragelink
|
81 |
def test_page_delete(self, admin_client, sample_page): |
|
4ce269c…
|
ragelink
|
82 |
response = admin_client.post(f"/kb/{sample_page.slug}/delete/") |
|
4ce269c…
|
ragelink
|
83 |
assert response.status_code == 302 |
|
4ce269c…
|
ragelink
|
84 |
assert Page.objects.filter(slug=sample_page.slug).count() == 0 |
|
4ce269c…
|
ragelink
|
85 |
|
|
4ce269c…
|
ragelink
|
86 |
def test_page_delete_denied(self, no_perm_client, sample_page): |
|
4ce269c…
|
ragelink
|
87 |
response = no_perm_client.post(f"/kb/{sample_page.slug}/delete/") |
|
4ce269c…
|
ragelink
|
88 |
assert response.status_code == 403 |
|
4ce269c…
|
ragelink
|
89 |
|
|
4ce269c…
|
ragelink
|
90 |
def test_draft_page_visible_to_admin(self, admin_client, org, admin_user): |
|
4ce269c…
|
ragelink
|
91 |
Page.objects.create(name="Draft Doc", content="Secret", organization=org, is_published=False, created_by=admin_user) |
|
4ce269c…
|
ragelink
|
92 |
response = admin_client.get("/kb/") |
|
4ce269c…
|
ragelink
|
93 |
assert "Draft Doc" in response.content.decode() |
|
4ce269c…
|
ragelink
|
94 |
|
|
4ce269c…
|
ragelink
|
95 |
def test_draft_page_hidden_from_viewer(self, viewer_client, org, admin_user): |
|
4ce269c…
|
ragelink
|
96 |
Page.objects.create(name="Draft Doc", content="Secret", organization=org, is_published=False, created_by=admin_user) |
|
4ce269c…
|
ragelink
|
97 |
response = viewer_client.get("/kb/") |
|
4ce269c…
|
ragelink
|
98 |
assert "Draft Doc" not in response.content.decode() |