FossilRepo

fossilrepo / tests / test_anonymous_access.py
Source Blame History 463 lines
c588255… ragelink 1 """Tests for anonymous (unauthenticated) access to public projects.
c588255… ragelink 2
c588255… ragelink 3 Verifies that:
c588255… ragelink 4 - Anonymous users can browse public project listings, details, and fossil views.
c588255… ragelink 5 - Anonymous users are denied access to private projects.
c588255… ragelink 6 - Anonymous users are denied write operations even on public projects.
c588255… ragelink 7 - Authenticated users retain full access as before.
c588255… ragelink 8 """
c588255… ragelink 9
c588255… ragelink 10 from unittest.mock import MagicMock, PropertyMock, patch
c588255… ragelink 11
c588255… ragelink 12 import pytest
c588255… ragelink 13 from django.contrib.auth.models import User
c588255… ragelink 14 from django.test import Client
c588255… ragelink 15
c588255… ragelink 16 from fossil.models import FossilRepository
c588255… ragelink 17 from organization.models import Team
c588255… ragelink 18 from pages.models import Page
c588255… ragelink 19 from projects.models import Project, ProjectTeam
c588255… ragelink 20
c588255… ragelink 21 # ---------------------------------------------------------------------------
c588255… ragelink 22 # Fixtures
c588255… ragelink 23 # ---------------------------------------------------------------------------
c588255… ragelink 24
c588255… ragelink 25
c588255… ragelink 26 @pytest.fixture
c588255… ragelink 27 def anon_client():
c588255… ragelink 28 """Unauthenticated client."""
c588255… ragelink 29 return Client()
c588255… ragelink 30
c588255… ragelink 31
c588255… ragelink 32 @pytest.fixture
c588255… ragelink 33 def public_project(db, org, admin_user, sample_team):
c588255… ragelink 34 """A public project visible to anonymous users."""
c588255… ragelink 35 project = Project.objects.create(
c588255… ragelink 36 name="Public Repo",
c588255… ragelink 37 organization=org,
c588255… ragelink 38 visibility="public",
c588255… ragelink 39 created_by=admin_user,
c588255… ragelink 40 )
c588255… ragelink 41 ProjectTeam.objects.create(project=project, team=sample_team, role="write", created_by=admin_user)
c588255… ragelink 42 return project
c588255… ragelink 43
c588255… ragelink 44
c588255… ragelink 45 @pytest.fixture
c588255… ragelink 46 def internal_project(db, org, admin_user, sample_team):
c588255… ragelink 47 """An internal project visible only to authenticated users."""
c588255… ragelink 48 project = Project.objects.create(
c588255… ragelink 49 name="Internal Repo",
c588255… ragelink 50 organization=org,
c588255… ragelink 51 visibility="internal",
c588255… ragelink 52 created_by=admin_user,
c588255… ragelink 53 )
c588255… ragelink 54 ProjectTeam.objects.create(project=project, team=sample_team, role="write", created_by=admin_user)
c588255… ragelink 55 return project
c588255… ragelink 56
c588255… ragelink 57
c588255… ragelink 58 @pytest.fixture
c588255… ragelink 59 def private_project(sample_project):
c588255… ragelink 60 """The default sample_project is private."""
c588255… ragelink 61 return sample_project
c588255… ragelink 62
c588255… ragelink 63
c588255… ragelink 64 @pytest.fixture
c588255… ragelink 65 def published_page(db, org, admin_user):
c588255… ragelink 66 """A published knowledge base page."""
c588255… ragelink 67 return Page.objects.create(
c588255… ragelink 68 name="Public Guide",
c588255… ragelink 69 content="# Public Guide\n\nThis is visible to everyone.",
c588255… ragelink 70 organization=org,
c588255… ragelink 71 is_published=True,
c588255… ragelink 72 created_by=admin_user,
c588255… ragelink 73 )
c588255… ragelink 74
c588255… ragelink 75
c588255… ragelink 76 @pytest.fixture
c588255… ragelink 77 def draft_page(db, org, admin_user):
c588255… ragelink 78 """An unpublished draft page."""
c588255… ragelink 79 return Page.objects.create(
c588255… ragelink 80 name="Draft Guide",
c588255… ragelink 81 content="# Draft\n\nThis is a draft.",
c588255… ragelink 82 organization=org,
c588255… ragelink 83 is_published=False,
c588255… ragelink 84 created_by=admin_user,
c588255… ragelink 85 )
c588255… ragelink 86
c588255… ragelink 87
c588255… ragelink 88 @pytest.fixture
c588255… ragelink 89 def public_fossil_repo(public_project):
c588255… ragelink 90 """Return the auto-created FossilRepository for the public project."""
c588255… ragelink 91 return FossilRepository.objects.get(project=public_project, deleted_at__isnull=True)
c588255… ragelink 92
c588255… ragelink 93
c588255… ragelink 94 @pytest.fixture
c588255… ragelink 95 def private_fossil_repo(private_project):
c588255… ragelink 96 """Return the auto-created FossilRepository for the private project."""
c588255… ragelink 97 return FossilRepository.objects.get(project=private_project, deleted_at__isnull=True)
c588255… ragelink 98
c588255… ragelink 99
c588255… ragelink 100 @pytest.fixture
c588255… ragelink 101 def writer_for_public(db, admin_user, public_project):
c588255… ragelink 102 """User with write access to the public project."""
c588255… ragelink 103 writer = User.objects.create_user(username="pub_writer", password="testpass123")
c588255… ragelink 104 team = Team.objects.create(name="Pub Writers", organization=public_project.organization, created_by=admin_user)
c588255… ragelink 105 team.members.add(writer)
c588255… ragelink 106 ProjectTeam.objects.create(project=public_project, team=team, role="write", created_by=admin_user)
c588255… ragelink 107 return writer
c588255… ragelink 108
c588255… ragelink 109
c588255… ragelink 110 @pytest.fixture
c588255… ragelink 111 def writer_client_for_public(writer_for_public):
c588255… ragelink 112 client = Client()
c588255… ragelink 113 client.login(username="pub_writer", password="testpass123")
c588255… ragelink 114 return client
c588255… ragelink 115
c588255… ragelink 116
c588255… ragelink 117 # ---------------------------------------------------------------------------
c588255… ragelink 118 # Helper: mock FossilReader for views that open the .fossil file
c588255… ragelink 119 # ---------------------------------------------------------------------------
c588255… ragelink 120
c588255… ragelink 121
c588255… ragelink 122 def _mock_fossil_reader():
c588255… ragelink 123 """Return a context-manager mock that satisfies _get_repo_and_reader."""
c588255… ragelink 124 reader = MagicMock()
c588255… ragelink 125 reader.__enter__ = MagicMock(return_value=reader)
c588255… ragelink 126 reader.__exit__ = MagicMock(return_value=False)
c588255… ragelink 127 reader.get_latest_checkin_uuid.return_value = "abc123"
c588255… ragelink 128 reader.get_files_at_checkin.return_value = []
c588255… ragelink 129 reader.get_metadata.return_value = MagicMock(
c588255… ragelink 130 checkin_count=5, project_name="Test", project_code="abc", ticket_count=0, wiki_page_count=0
c588255… ragelink 131 )
c588255… ragelink 132 reader.get_timeline.return_value = []
c588255… ragelink 133 reader.get_tickets.return_value = []
c588255… ragelink 134 reader.get_wiki_pages.return_value = []
c588255… ragelink 135 reader.get_wiki_page.return_value = None
c588255… ragelink 136 reader.get_branches.return_value = []
c588255… ragelink 137 reader.get_tags.return_value = []
c588255… ragelink 138 reader.get_technotes.return_value = []
c588255… ragelink 139 reader.get_forum_posts.return_value = []
c588255… ragelink 140 reader.get_unversioned_files.return_value = []
c588255… ragelink 141 reader.get_commit_activity.return_value = []
c588255… ragelink 142 reader.get_top_contributors.return_value = []
c588255… ragelink 143 reader.get_repo_statistics.return_value = {}
c588255… ragelink 144 reader.search.return_value = []
c588255… ragelink 145 reader.get_checkin_count.return_value = 5
c588255… ragelink 146 return reader
c588255… ragelink 147
c588255… ragelink 148
c588255… ragelink 149 def _patch_fossil_on_disk():
c588255… ragelink 150 """Patch exists_on_disk to True and FossilReader to our mock."""
c588255… ragelink 151 reader = _mock_fossil_reader()
c588255… ragelink 152 return (
c588255… ragelink 153 patch.object(FossilRepository, "exists_on_disk", new_callable=PropertyMock, return_value=True),
c588255… ragelink 154 patch("fossil.views.FossilReader", return_value=reader),
c588255… ragelink 155 reader,
c588255… ragelink 156 )
c588255… ragelink 157
c588255… ragelink 158
c588255… ragelink 159 # ===========================================================================
c588255… ragelink 160 # Project List
c588255… ragelink 161 # ===========================================================================
c588255… ragelink 162
c588255… ragelink 163
c588255… ragelink 164 @pytest.mark.django_db
c588255… ragelink 165 class TestAnonymousProjectList:
c588255… ragelink 166 def test_anonymous_sees_public_projects(self, anon_client, public_project):
c588255… ragelink 167 response = anon_client.get("/projects/")
c588255… ragelink 168 assert response.status_code == 200
c588255… ragelink 169 assert public_project.name in response.content.decode()
c588255… ragelink 170
c588255… ragelink 171 def test_anonymous_does_not_see_private_projects(self, anon_client, private_project, public_project):
c588255… ragelink 172 response = anon_client.get("/projects/")
c588255… ragelink 173 assert response.status_code == 200
c588255… ragelink 174 body = response.content.decode()
c588255… ragelink 175 assert public_project.name in body
c588255… ragelink 176 assert private_project.name not in body
c588255… ragelink 177
c588255… ragelink 178 def test_anonymous_does_not_see_internal_projects(self, anon_client, internal_project, public_project):
c588255… ragelink 179 response = anon_client.get("/projects/")
c588255… ragelink 180 assert response.status_code == 200
c588255… ragelink 181 body = response.content.decode()
c588255… ragelink 182 assert public_project.name in body
c588255… ragelink 183 assert internal_project.name not in body
c588255… ragelink 184
c588255… ragelink 185 def test_authenticated_sees_all_projects(self, admin_client, public_project, private_project, internal_project):
c588255… ragelink 186 response = admin_client.get("/projects/")
c588255… ragelink 187 assert response.status_code == 200
c588255… ragelink 188 body = response.content.decode()
c588255… ragelink 189 assert public_project.name in body
c588255… ragelink 190 assert private_project.name in body
c588255… ragelink 191 assert internal_project.name in body
c588255… ragelink 192
c588255… ragelink 193
c588255… ragelink 194 # ===========================================================================
c588255… ragelink 195 # Project Detail
c588255… ragelink 196 # ===========================================================================
c588255… ragelink 197
c588255… ragelink 198
c588255… ragelink 199 @pytest.mark.django_db
c588255… ragelink 200 class TestAnonymousProjectDetail:
c588255… ragelink 201 def test_anonymous_can_view_public_project(self, anon_client, public_project):
c588255… ragelink 202 response = anon_client.get(f"/projects/{public_project.slug}/")
c588255… ragelink 203 assert response.status_code == 200
c588255… ragelink 204 assert public_project.name in response.content.decode()
c588255… ragelink 205
c588255… ragelink 206 def test_anonymous_denied_private_project(self, anon_client, private_project):
c588255… ragelink 207 response = anon_client.get(f"/projects/{private_project.slug}/")
c588255… ragelink 208 assert response.status_code == 403
c588255… ragelink 209
c588255… ragelink 210 def test_anonymous_denied_internal_project(self, anon_client, internal_project):
c588255… ragelink 211 response = anon_client.get(f"/projects/{internal_project.slug}/")
c588255… ragelink 212 assert response.status_code == 403
c588255… ragelink 213
c588255… ragelink 214 def test_authenticated_can_view_private_project(self, admin_client, private_project):
c588255… ragelink 215 response = admin_client.get(f"/projects/{private_project.slug}/")
c588255… ragelink 216 assert response.status_code == 200
c588255… ragelink 217
c588255… ragelink 218
c588255… ragelink 219 # ===========================================================================
c588255… ragelink 220 # Code Browser (fossil view, needs .fossil file mock)
c588255… ragelink 221 # ===========================================================================
c588255… ragelink 222
c588255… ragelink 223
c588255… ragelink 224 @pytest.mark.django_db
c588255… ragelink 225 class TestAnonymousCodeBrowser:
c588255… ragelink 226 def test_anonymous_can_view_public_code_browser(self, anon_client, public_project, public_fossil_repo):
c588255… ragelink 227 disk_patch, reader_patch, reader = _patch_fossil_on_disk()
c588255… ragelink 228 with disk_patch, reader_patch:
c588255… ragelink 229 response = anon_client.get(f"/projects/{public_project.slug}/fossil/code/")
c588255… ragelink 230 assert response.status_code == 200
c588255… ragelink 231
c588255… ragelink 232 def test_anonymous_denied_private_code_browser(self, anon_client, private_project, private_fossil_repo):
c588255… ragelink 233 disk_patch, reader_patch, reader = _patch_fossil_on_disk()
c588255… ragelink 234 with disk_patch, reader_patch:
c588255… ragelink 235 response = anon_client.get(f"/projects/{private_project.slug}/fossil/code/")
c588255… ragelink 236 assert response.status_code == 403
c588255… ragelink 237
c588255… ragelink 238
c588255… ragelink 239 # ===========================================================================
c588255… ragelink 240 # Timeline
c588255… ragelink 241 # ===========================================================================
c588255… ragelink 242
c588255… ragelink 243
c588255… ragelink 244 @pytest.mark.django_db
c588255… ragelink 245 class TestAnonymousTimeline:
c588255… ragelink 246 def test_anonymous_can_view_public_timeline(self, anon_client, public_project, public_fossil_repo):
c588255… ragelink 247 disk_patch, reader_patch, reader = _patch_fossil_on_disk()
c588255… ragelink 248 with disk_patch, reader_patch:
c588255… ragelink 249 response = anon_client.get(f"/projects/{public_project.slug}/fossil/timeline/")
c588255… ragelink 250 assert response.status_code == 200
c588255… ragelink 251
c588255… ragelink 252 def test_anonymous_denied_private_timeline(self, anon_client, private_project, private_fossil_repo):
c588255… ragelink 253 disk_patch, reader_patch, reader = _patch_fossil_on_disk()
c588255… ragelink 254 with disk_patch, reader_patch:
c588255… ragelink 255 response = anon_client.get(f"/projects/{private_project.slug}/fossil/timeline/")
c588255… ragelink 256 assert response.status_code == 403
c588255… ragelink 257
c588255… ragelink 258
c588255… ragelink 259 # ===========================================================================
c588255… ragelink 260 # Tickets
c588255… ragelink 261 # ===========================================================================
c588255… ragelink 262
c588255… ragelink 263
c588255… ragelink 264 @pytest.mark.django_db
c588255… ragelink 265 class TestAnonymousTickets:
c588255… ragelink 266 def test_anonymous_can_view_public_ticket_list(self, anon_client, public_project, public_fossil_repo):
c588255… ragelink 267 disk_patch, reader_patch, reader = _patch_fossil_on_disk()
c588255… ragelink 268 with disk_patch, reader_patch:
c588255… ragelink 269 response = anon_client.get(f"/projects/{public_project.slug}/fossil/tickets/")
c588255… ragelink 270 assert response.status_code == 200
c588255… ragelink 271
c588255… ragelink 272 def test_anonymous_denied_private_ticket_list(self, anon_client, private_project, private_fossil_repo):
c588255… ragelink 273 disk_patch, reader_patch, reader = _patch_fossil_on_disk()
c588255… ragelink 274 with disk_patch, reader_patch:
c588255… ragelink 275 response = anon_client.get(f"/projects/{private_project.slug}/fossil/tickets/")
c588255… ragelink 276 assert response.status_code == 403
c588255… ragelink 277
c588255… ragelink 278
c588255… ragelink 279 # ===========================================================================
c588255… ragelink 280 # Write operations require login on public projects
c588255… ragelink 281 # ===========================================================================
c588255… ragelink 282
c588255… ragelink 283
c588255… ragelink 284 @pytest.mark.django_db
c588255… ragelink 285 class TestAnonymousWriteDenied:
c588255… ragelink 286 """Write operations must redirect anonymous users to login, even on public projects."""
c588255… ragelink 287
c588255… ragelink 288 def test_anonymous_cannot_create_ticket(self, anon_client, public_project):
c588255… ragelink 289 response = anon_client.get(f"/projects/{public_project.slug}/fossil/tickets/create/")
c588255… ragelink 290 # @login_required redirects to login
c588255… ragelink 291 assert response.status_code == 302
c588255… ragelink 292 assert "/auth/login/" in response.url
c588255… ragelink 293
c588255… ragelink 294 def test_anonymous_cannot_create_wiki(self, anon_client, public_project):
c588255… ragelink 295 response = anon_client.get(f"/projects/{public_project.slug}/fossil/wiki/create/")
c588255… ragelink 296 assert response.status_code == 302
c588255… ragelink 297 assert "/auth/login/" in response.url
c588255… ragelink 298
c588255… ragelink 299 def test_anonymous_cannot_create_forum_thread(self, anon_client, public_project):
c588255… ragelink 300 response = anon_client.get(f"/projects/{public_project.slug}/fossil/forum/create/")
c588255… ragelink 301 assert response.status_code == 302
c588255… ragelink 302 assert "/auth/login/" in response.url
c588255… ragelink 303
c588255… ragelink 304 def test_anonymous_cannot_create_release(self, anon_client, public_project):
c588255… ragelink 305 response = anon_client.get(f"/projects/{public_project.slug}/fossil/releases/create/")
c588255… ragelink 306 assert response.status_code == 302
c588255… ragelink 307 assert "/auth/login/" in response.url
c588255… ragelink 308
c588255… ragelink 309 def test_anonymous_cannot_create_project(self, anon_client):
c588255… ragelink 310 response = anon_client.get("/projects/create/")
c588255… ragelink 311 assert response.status_code == 302
c588255… ragelink 312 assert "/auth/login/" in response.url
c588255… ragelink 313
c588255… ragelink 314 def test_anonymous_cannot_access_repo_settings(self, anon_client, public_project):
c588255… ragelink 315 response = anon_client.get(f"/projects/{public_project.slug}/fossil/settings/")
c588255… ragelink 316 assert response.status_code == 302
c588255… ragelink 317 assert "/auth/login/" in response.url
c588255… ragelink 318
c588255… ragelink 319 def test_anonymous_cannot_access_sync(self, anon_client, public_project):
c588255… ragelink 320 response = anon_client.get(f"/projects/{public_project.slug}/fossil/sync/")
c588255… ragelink 321 assert response.status_code == 302
c588255… ragelink 322 assert "/auth/login/" in response.url
c588255… ragelink 323
c588255… ragelink 324 def test_anonymous_cannot_toggle_watch(self, anon_client, public_project):
c588255… ragelink 325 response = anon_client.post(f"/projects/{public_project.slug}/fossil/watch/")
c588255… ragelink 326 assert response.status_code == 302
c588255… ragelink 327 assert "/auth/login/" in response.url
c588255… ragelink 328
c588255… ragelink 329
c588255… ragelink 330 # ===========================================================================
c588255… ragelink 331 # Additional read-only fossil views on public projects
c588255… ragelink 332 # ===========================================================================
c588255… ragelink 333
c588255… ragelink 334
c588255… ragelink 335 @pytest.mark.django_db
c588255… ragelink 336 class TestAnonymousReadOnlyFossilViews:
c588255… ragelink 337 """Test that various read-only fossil views allow anonymous access on public projects."""
c588255… ragelink 338
c588255… ragelink 339 def test_branches(self, anon_client, public_project, public_fossil_repo):
c588255… ragelink 340 disk_patch, reader_patch, _ = _patch_fossil_on_disk()
c588255… ragelink 341 with disk_patch, reader_patch:
c588255… ragelink 342 response = anon_client.get(f"/projects/{public_project.slug}/fossil/branches/")
c588255… ragelink 343 assert response.status_code == 200
c588255… ragelink 344
c588255… ragelink 345 def test_tags(self, anon_client, public_project, public_fossil_repo):
c588255… ragelink 346 disk_patch, reader_patch, _ = _patch_fossil_on_disk()
c588255… ragelink 347 with disk_patch, reader_patch:
c588255… ragelink 348 response = anon_client.get(f"/projects/{public_project.slug}/fossil/tags/")
c588255… ragelink 349 assert response.status_code == 200
c588255… ragelink 350
c588255… ragelink 351 def test_stats(self, anon_client, public_project, public_fossil_repo):
c588255… ragelink 352 disk_patch, reader_patch, _ = _patch_fossil_on_disk()
c588255… ragelink 353 with disk_patch, reader_patch:
c588255… ragelink 354 response = anon_client.get(f"/projects/{public_project.slug}/fossil/stats/")
c588255… ragelink 355 assert response.status_code == 200
c588255… ragelink 356
c588255… ragelink 357 def test_search(self, anon_client, public_project, public_fossil_repo):
c588255… ragelink 358 disk_patch, reader_patch, _ = _patch_fossil_on_disk()
c588255… ragelink 359 with disk_patch, reader_patch:
c588255… ragelink 360 response = anon_client.get(f"/projects/{public_project.slug}/fossil/search/")
c588255… ragelink 361 assert response.status_code == 200
c588255… ragelink 362
c588255… ragelink 363 def test_wiki(self, anon_client, public_project, public_fossil_repo):
c588255… ragelink 364 disk_patch, reader_patch, _ = _patch_fossil_on_disk()
c588255… ragelink 365 with disk_patch, reader_patch:
c588255… ragelink 366 response = anon_client.get(f"/projects/{public_project.slug}/fossil/wiki/")
c588255… ragelink 367 assert response.status_code == 200
c588255… ragelink 368
c588255… ragelink 369 def test_releases(self, anon_client, public_project, public_fossil_repo):
c588255… ragelink 370 disk_patch, reader_patch, _ = _patch_fossil_on_disk()
c588255… ragelink 371 with disk_patch, reader_patch:
c588255… ragelink 372 response = anon_client.get(f"/projects/{public_project.slug}/fossil/releases/")
c588255… ragelink 373 assert response.status_code == 200
c588255… ragelink 374
c588255… ragelink 375 def test_technotes(self, anon_client, public_project, public_fossil_repo):
c588255… ragelink 376 disk_patch, reader_patch, _ = _patch_fossil_on_disk()
c588255… ragelink 377 with disk_patch, reader_patch:
c588255… ragelink 378 response = anon_client.get(f"/projects/{public_project.slug}/fossil/technotes/")
c588255… ragelink 379 assert response.status_code == 200
c588255… ragelink 380
c588255… ragelink 381 def test_unversioned(self, anon_client, public_project, public_fossil_repo):
c588255… ragelink 382 disk_patch, reader_patch, _ = _patch_fossil_on_disk()
c588255… ragelink 383 with disk_patch, reader_patch:
c588255… ragelink 384 response = anon_client.get(f"/projects/{public_project.slug}/fossil/files/")
c588255… ragelink 385 assert response.status_code == 200
c588255… ragelink 386
c588255… ragelink 387
c588255… ragelink 388 # ===========================================================================
c588255… ragelink 389 # Pages (knowledge base)
c588255… ragelink 390 # ===========================================================================
c588255… ragelink 391
c588255… ragelink 392
c588255… ragelink 393 @pytest.mark.django_db
c588255… ragelink 394 class TestAnonymousPages:
c588255… ragelink 395 def test_anonymous_can_view_published_page_list(self, anon_client, published_page):
c588255… ragelink 396 response = anon_client.get("/kb/")
c588255… ragelink 397 assert response.status_code == 200
c588255… ragelink 398 assert published_page.name in response.content.decode()
c588255… ragelink 399
c588255… ragelink 400 def test_anonymous_cannot_see_draft_pages_in_list(self, anon_client, published_page, draft_page):
c588255… ragelink 401 response = anon_client.get("/kb/")
c588255… ragelink 402 assert response.status_code == 200
c588255… ragelink 403 body = response.content.decode()
c588255… ragelink 404 assert published_page.name in body
c588255… ragelink 405 assert draft_page.name not in body
c588255… ragelink 406
c588255… ragelink 407 def test_anonymous_can_view_published_page_detail(self, anon_client, published_page):
c588255… ragelink 408 response = anon_client.get(f"/kb/{published_page.slug}/")
c588255… ragelink 409 assert response.status_code == 200
c588255… ragelink 410 assert published_page.name in response.content.decode()
c588255… ragelink 411
c588255… ragelink 412 def test_anonymous_denied_draft_page_detail(self, anon_client, draft_page):
c588255… ragelink 413 response = anon_client.get(f"/kb/{draft_page.slug}/")
c588255… ragelink 414 assert response.status_code == 403
c588255… ragelink 415
c588255… ragelink 416 def test_authenticated_can_view_published_page(self, admin_client, published_page):
c588255… ragelink 417 response = admin_client.get(f"/kb/{published_page.slug}/")
c588255… ragelink 418 assert response.status_code == 200
c588255… ragelink 419
c588255… ragelink 420 def test_anonymous_cannot_create_page(self, anon_client):
c588255… ragelink 421 response = anon_client.get("/kb/create/")
c588255… ragelink 422 assert response.status_code == 302
c588255… ragelink 423 assert "/auth/login/" in response.url
c588255… ragelink 424
c588255… ragelink 425
c588255… ragelink 426 # ===========================================================================
c588255… ragelink 427 # Explore page (already worked for anonymous)
c588255… ragelink 428 # ===========================================================================
c588255… ragelink 429
c588255… ragelink 430
c588255… ragelink 431 @pytest.mark.django_db
c588255… ragelink 432 class TestAnonymousExplore:
c588255… ragelink 433 def test_anonymous_can_access_explore(self, anon_client, public_project):
c588255… ragelink 434 response = anon_client.get("/explore/")
c588255… ragelink 435 assert response.status_code == 200
c588255… ragelink 436 assert public_project.name in response.content.decode()
c588255… ragelink 437
c588255… ragelink 438 def test_anonymous_explore_hides_private(self, anon_client, public_project, private_project):
c588255… ragelink 439 response = anon_client.get("/explore/")
c588255… ragelink 440 assert response.status_code == 200
c588255… ragelink 441 body = response.content.decode()
c588255… ragelink 442 assert public_project.name in body
c588255… ragelink 443 assert private_project.name not in body
c588255… ragelink 444
c588255… ragelink 445
c588255… ragelink 446 # ===========================================================================
c588255… ragelink 447 # Forum (read-only)
c588255… ragelink 448 # ===========================================================================
c588255… ragelink 449
c588255… ragelink 450
c588255… ragelink 451 @pytest.mark.django_db
c588255… ragelink 452 class TestAnonymousForum:
c588255… ragelink 453 def test_anonymous_can_view_forum_list(self, anon_client, public_project, public_fossil_repo):
c588255… ragelink 454 disk_patch, reader_patch, _ = _patch_fossil_on_disk()
c588255… ragelink 455 with disk_patch, reader_patch:
c588255… ragelink 456 response = anon_client.get(f"/projects/{public_project.slug}/fossil/forum/")
c588255… ragelink 457 assert response.status_code == 200
c588255… ragelink 458
c588255… ragelink 459 def test_anonymous_denied_private_forum(self, anon_client, private_project, private_fossil_repo):
c588255… ragelink 460 disk_patch, reader_patch, _ = _patch_fossil_on_disk()
c588255… ragelink 461 with disk_patch, reader_patch:
c588255… ragelink 462 response = anon_client.get(f"/projects/{private_project.slug}/fossil/forum/")
c588255… ragelink 463 assert response.status_code == 403

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button