"""The public buyer sign-up screen. Two things matter: the page is reachable without signing in, and the account it creates is a plain buyer — the same kind of account everything else uses, with no platform privileges. Keycloak is patched here; the live Admin API path is exercised by the smoke test, not by the suite. """ from unittest.mock import patch from urllib.parse import urlencode from django.contrib.auth import get_user_model from django.test import TestCase from django.urls import reverse from apps.users.keycloak import DuplicateEmailError, KeycloakUnavailableError User = get_user_model() SIGNUP_URL = "/portal/signup/" VALID = { "full_name": "Ada Greenfield", "company_name": "Greenfield Foods Ltd", "email": "signup-smoke-01@example.com", "password": "a-long-enough-passphrase", "confirm_password": "a-long-enough-passphrase", } class SignupViewTests(TestCase): def test_the_page_is_public(self): response = self.client.get(SIGNUP_URL) self.assertEqual(response.status_code, 200) self.assertContains(response, "Create your buyer account") @patch("apps.buyer.views.create_keycloak_user", return_value="kc-sub-0001") def test_successful_signup_creates_both_records(self, create): response = self.client.post(SIGNUP_URL, VALID) expected = reverse("oidc_authentication_init") + "?" + urlencode({"next": "/portal/?welcome=1"}) self.assertRedirects(response, expected, fetch_redirect_response=False) create.assert_called_once_with( email=VALID["email"], full_name=VALID["full_name"], password=VALID["password"] ) user = User.objects.get(email=VALID["email"]) self.assertEqual(user.role, "buyer") self.assertFalse(user.is_staff) self.assertTrue(user.is_active) self.assertEqual(user.company_name, "Greenfield Foods Ltd") self.assertEqual(user.keycloak_id, "kc-sub-0001") def test_a_duplicate_email_is_refused_without_calling_keycloak(self): User.objects.create_user(email=VALID["email"], role="buyer") with patch("apps.buyer.views.create_keycloak_user") as create: response = self.client.post(SIGNUP_URL, VALID) create.assert_not_called() self.assertEqual(response.status_code, 200) self.assertContains(response, "An account with this email already exists") @patch("apps.buyer.views.create_keycloak_user", side_effect=DuplicateEmailError("x")) def test_a_keycloak_duplicate_is_refused_the_same_way(self, create): response = self.client.post(SIGNUP_URL, VALID) self.assertEqual(response.status_code, 200) self.assertContains(response, "An account with this email already exists") self.assertFalse(User.objects.filter(email=VALID["email"]).exists()) @patch("apps.buyer.views.create_keycloak_user", side_effect=KeycloakUnavailableError("down")) def test_an_unreachable_keycloak_is_reported_not_a_500(self, create): response = self.client.post(SIGNUP_URL, VALID) self.assertEqual(response.status_code, 200) self.assertContains(response, "could not create your account") self.assertFalse(User.objects.filter(email=VALID["email"]).exists()) @patch("apps.buyer.views.create_keycloak_user", return_value="kc-sub-0002") def test_mismatched_confirmation_is_refused(self, create): response = self.client.post(SIGNUP_URL, {**VALID, "confirm_password": "something-else-entirely"}) create.assert_not_called() self.assertEqual(response.status_code, 200) self.assertContains(response, "The two passwords do not match") self.assertFalse(User.objects.filter(email=VALID["email"]).exists()) @patch("apps.buyer.views.create_keycloak_user", return_value="kc-sub-0003") def test_a_short_password_is_refused(self, create): response = self.client.post(SIGNUP_URL, {**VALID, "password": "short", "confirm_password": "short"}) create.assert_not_called() self.assertEqual(response.status_code, 200) self.assertContains(response, "at least 12 characters") @patch("apps.buyer.views.create_keycloak_user", return_value="kc-sub-0004") def test_a_password_equal_to_the_email_is_refused(self, create): password = VALID["email"] response = self.client.post( SIGNUP_URL, {**VALID, "password": password, "confirm_password": password} ) create.assert_not_called() self.assertEqual(response.status_code, 200) self.assertContains(response, "not your email address or company name") def test_a_signed_in_user_is_sent_to_their_portal(self): User.objects.create_user(email="already-in@example.com", role="buyer", is_staff=False) self.client.force_login(User.objects.get(email="already-in@example.com")) response = self.client.get(SIGNUP_URL) self.assertRedirects(response, "/", fetch_redirect_response=False) class DashboardWelcomeTests(TestCase): def test_the_welcome_message_shows_after_signup(self): User.objects.create_user( email="welcome-buyer@example.com", role="buyer", is_staff=False ) self.client.force_login(User.objects.get(email="welcome-buyer@example.com")) response = self.client.get("/portal/?welcome=1") self.assertContains(response, "Your account is ready") def test_no_welcome_message_on_a_normal_visit(self): User.objects.create_user( email="quiet-buyer@example.com", role="buyer", is_staff=False ) self.client.force_login(User.objects.get(email="quiet-buyer@example.com")) response = self.client.get("/portal/") self.assertNotContains(response, "Your account is ready")