import pytest from django.urls import reverse from django.contrib.auth import get_user_model from atlas.models import Case, MultiuserSession from pytest_django.asserts import assertContains, assertRedirects @pytest.mark.django_db def test_multiuser_session_model_creation(create_user): user = create_user session = MultiuserSession.objects.create( name="Test Sync Session", creator=user ) assert session.name == "Test Sync Session" assert session.creator == user assert session.active_case is None assert str(session).startswith("Session Test Sync") @pytest.mark.django_db def test_multiuser_dashboard_view_anonymous_redirect(client): response = client.get(reverse("atlas:multiuser_dashboard")) assertRedirects(response, f"{reverse('login')}?next={reverse('atlas:multiuser_dashboard')}") @pytest.mark.django_db def test_multiuser_dashboard_lists_sessions_staff(client, create_user): user = create_user user.is_staff = True user.save() session1 = MultiuserSession.objects.create(name="User Session 1", creator=user) User = get_user_model() other_user = User.objects.create_user(username="other", password="password", email="other@test.com") session2 = MultiuserSession.objects.create(name="Other Session 2", creator=other_user) client.force_login(user) response = client.get(reverse("atlas:multiuser_dashboard")) assert response.status_code == 200 assertContains(response, "User Session 1") assertContains(response, "Other Session 2") assertContains(response, "Start New Session") @pytest.mark.django_db def test_multiuser_dashboard_lists_sessions_non_staff(client, create_user): user = create_user User = get_user_model() other_user = User.objects.create_user(username="other", password="password", email="other@test.com") other_user.is_staff = True other_user.save() session2 = MultiuserSession.objects.create(name="Other Session 2", creator=other_user) client.force_login(user) response = client.get(reverse("atlas:multiuser_dashboard")) assert response.status_code == 200 assertContains(response, "Other Session 2") assert "Start New Session" not in response.content.decode("utf-8") assert "Sessions You Managed" not in response.content.decode("utf-8") @pytest.mark.django_db def test_multiuser_create_session_action(client, create_user): user = create_user user.is_staff = True user.save() client.force_login(user) response = client.post(reverse("atlas:multiuser_create_session"), data={"name": "My New Session"}) session = MultiuserSession.objects.get(name="My New Session") assertRedirects(response, reverse("atlas:multiuser_session_detail", kwargs={"session_id": session.id})) @pytest.mark.django_db def test_multiuser_create_session_action_non_staff_denied(client, create_user): user = create_user client.force_login(user) response = client.post(reverse("atlas:multiuser_create_session"), data={"name": "Denied Session"}) assert response.status_code == 302 # Redirected to admin login page because of @staff_member_required @pytest.mark.django_db def test_multiuser_session_detail_manager_vs_participant(client): User = get_user_model() manager = User.objects.create_user(username="manager", password="password", email="manager@test.com") participant = User.objects.create_user(username="participant", password="password", email="participant@test.com") session = MultiuserSession.objects.create(name="Collaboration Session", creator=manager) # Manager view client.force_login(manager) response = client.get(reverse("atlas:multiuser_session_detail", kwargs={"session_id": session.id})) assert response.status_code == 200 assertContains(response, "Host Controls:") assertContains(response, "Search cases to load...") # Participant view client.force_login(participant) response = client.get(reverse("atlas:multiuser_session_detail", kwargs={"session_id": session.id})) assert response.status_code == 200 assert "Host Controls:" not in response.content.decode("utf-8") assertContains(response, "You are viewing this session in real-time") @pytest.mark.django_db def test_multiuser_search_cases_permission(client): User = get_user_model() manager = User.objects.create_user(username="manager", password="password", email="manager@test.com") participant = User.objects.create_user(username="participant", password="password", email="participant@test.com") session = MultiuserSession.objects.create(name="Search Session", creator=manager) # Participant cannot search client.force_login(participant) response = client.get(reverse("atlas:multiuser_search_cases", kwargs={"session_id": session.id}) + "?q=test") assert response.status_code == 403 # Manager can search client.force_login(manager) response = client.get(reverse("atlas:multiuser_search_cases", kwargs={"session_id": session.id}) + "?q=test") assert response.status_code == 200 @pytest.mark.django_db def test_multiuser_load_case_action(client, make_case): User = get_user_model() manager = User.objects.create_user(username="manager", password="password", email="manager@test.com") participant = User.objects.create_user(username="participant", password="password", email="participant@test.com") session = MultiuserSession.objects.create(name="Load Case Session", creator=manager) case = make_case(title="Multiuser Dicom Case") case.author.add(manager) # Grant access to manager # Participant cannot load client.force_login(participant) response = client.post(reverse("atlas:multiuser_load_case", kwargs={"session_id": session.id}), data={"case_id": case.pk}) assert response.status_code == 403 # Manager loads case client.force_login(manager) from unittest.mock import patch, MagicMock, AsyncMock with patch("channels.layers.get_channel_layer") as mock_get_channel_layer: mock_layer = MagicMock() mock_layer.group_send = AsyncMock() mock_get_channel_layer.return_value = mock_layer response = client.post(reverse("atlas:multiuser_load_case", kwargs={"session_id": session.id}), data={"case_id": case.pk}) assert response.status_code == 200 # Verify Channels group_send was called to notify the group about the case change mock_layer.group_send.assert_called_once_with( f"multiuser_{session.id}", { "type": "change_case", "case_id": case.id } ) session.refresh_from_db() assert session.active_case == case assertContains(response, "Interactive DICOM Viewer")