from unittest.mock import MagicMock, patch import pytest from servala.core.models.service import ( CloudProvider, ControlPlane, ControlPlaneCRD, ServiceInstance, ServiceOffering, ) @pytest.mark.parametrize( "url,redirect", ( ("/", "/accounts/login/?next=/"), ("/accounts/profile/", "/accounts/login/?next=/accounts/profile/"), ), ) def test_root_view_redirects_valid_urls(client, url, redirect): response = client.get(url) assert response.status_code == 302 assert response.url == redirect def test_root_view_invalid_urls_404(client): response = client.get("/foobarbaz/") assert response.status_code == 404 @pytest.mark.django_db def test_owner_can_access_dashboard(client, org_owner, organization): client.force_login(org_owner) response = client.get(organization.urls.base) assert organization.namespace == f"org-{organization.pk}" assert response.status_code == 200 assert organization.name in response.content.decode() @pytest.mark.django_db def test_user_cannot_see_other_organization(client, org_owner, other_organization): client.force_login(org_owner) response = client.get(other_organization.urls.base) assert response.status_code == 403 assert other_organization.name not in response.content.decode() @pytest.mark.django_db def test_organization_linked_in_sidebar( client, org_owner, organization, other_organization ): client.force_login(org_owner) response = client.get("/", follow=True) assert response.status_code == 200 assert organization.name in response.content.decode() assert other_organization.name not in response.content.decode() @pytest.mark.django_db def test_service_detail_redirects_with_single_offering( client, org_owner, organization, service, service_offering ): client.force_login(org_owner) url = f"/org/{organization.slug}/services/{service.slug}/" response = client.get(url) assert response.status_code == 302 expected_url = f"/org/{organization.slug}/services/{service.slug}/offering/{service_offering.pk}/" assert response.url == expected_url @pytest.mark.django_db def test_service_detail_shows_multiple_offerings( client, org_owner, organization, service, service_offering ): second_provider = CloudProvider.objects.create( name="AWS", description="Amazon Web Services" ) second_offering = ServiceOffering.objects.create( service=service, provider=second_provider, description="Redis on AWS", osb_plan_id="test-plan-456", ) client.force_login(org_owner) url = f"/org/{organization.slug}/services/{service.slug}/" response = client.get(url) assert response.status_code == 200 content = response.content.decode() assert service_offering.provider.name in content assert second_offering.provider.name in content assert "Create Instance" in content @pytest.mark.django_db def test_service_detail_respects_cloud_provider_restrictions( client, org_owner, organization, service, service_offering ): second_provider = CloudProvider.objects.create( name="AWS", description="Amazon Web Services" ) ServiceOffering.objects.create( service=service, provider=second_provider, description="Redis on AWS", osb_plan_id="test-plan-456", ) organization.origin.limit_cloudproviders.add(service_offering.provider) client.force_login(org_owner) url = f"/org/{organization.slug}/services/{service.slug}/" response = client.get(url) assert response.status_code == 302 expected_url = f"/org/{organization.slug}/services/{service.slug}/offering/{service_offering.pk}/" assert response.url == expected_url @pytest.mark.django_db def test_service_detail_no_redirect_with_restricted_multiple_offerings( client, org_owner, organization, service, service_offering ): second_provider = CloudProvider.objects.create( name="AWS", description="Amazon Web Services" ) second_offering = ServiceOffering.objects.create( service=service, provider=second_provider, description="Redis on AWS", osb_plan_id="test-plan-456", ) third_provider = CloudProvider.objects.create( name="Azure", description="Microsoft Azure" ) third_offering = ServiceOffering.objects.create( service=service, provider=third_provider, description="Redis on Azure", osb_plan_id="test-plan-789", ) organization.origin.limit_cloudproviders.add( service_offering.provider, second_provider ) client.force_login(org_owner) url = f"/org/{organization.slug}/services/{service.slug}/" response = client.get(url) assert response.status_code == 200 content = response.content.decode() assert service_offering.provider.name in content assert second_offering.provider.name in content assert third_offering.provider.name not in content @pytest.mark.django_db def test_service_instance_update_spec_pushes_display_name_annotation( organization, control_plane_crd, org_owner ): instance = ServiceInstance.objects.create( name="test-instance", display_name="Original Name", organization=organization, context=control_plane_crd, ) mock_api = MagicMock() with ( patch.object(ControlPlane, "custom_objects_api", mock_api), patch.object(ControlPlaneCRD, "kind_plural", "testkinds"), ): instance.display_name = "Updated Name" instance.update_spec(spec_data={}, updated_by=org_owner) mock_api.patch_namespaced_custom_object.assert_called_once() call_kwargs = mock_api.patch_namespaced_custom_object.call_args[1] assert "metadata" in call_kwargs["body"] annotations = call_kwargs["body"]["metadata"]["annotations"] assert annotations["servala.com/displayName"] == "Updated Name" instance.refresh_from_db() assert instance.display_name == "Updated Name" @pytest.mark.django_db def test_build_billing_annotations_includes_display_name(): annotations = ServiceInstance._build_billing_annotations( compute_plan_assignment=None, control_plane=MagicMock( storage_plan_odoo_product_id=None, storage_plan_odoo_unit_id=None, ), instance_name="test-instance", display_name="My Display Name", organization=None, service=None, ) assert annotations["servala.com/displayName"] == "My Display Name" @pytest.mark.django_db def test_build_billing_annotations_omits_display_name_when_none(): annotations = ServiceInstance._build_billing_annotations( compute_plan_assignment=None, control_plane=MagicMock( storage_plan_odoo_product_id=None, storage_plan_odoo_unit_id=None, ), instance_name="test-instance", display_name=None, organization=None, service=None, ) assert "servala.com/displayName" not in annotations