# Copyright 2025 Criptomart # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) """Smoke tests for the Eskaera pages as seen by a plain portal user. Covers that the main pages answer 200 and that reading a product's UoM for display does not raise an AccessError for a portal user. `/eskaera/labels` and `/eskaera/i18n` are `type="json"` routes: a bare GET is answered with 400 by design, so they are exercised through a JSON-RPC call. """ from datetime import datetime from datetime import timedelta from odoo.tests import tagged from odoo.tests.common import HttpCase class PortalRoutesCommon: """Build a portal user that belongs to an open group order.""" def setUp(self): super().setUp() self.group = self.env["res.partner"].create( { "name": "Portal Routes Group", "is_company": True, "is_group": True, "email": "routes-group@test.com", } ) # The shop guard reads `partner_id.group_ids`, so the membership has # to be set from the member side to be visible right away. self.member_partner = self.env["res.partner"].create( { "name": "Routes Member", "email": "routes-member@test.com", "group_ids": [(6, 0, [self.group.id])], } ) # HttpCase.authenticate() wants the password, so reuse the login. self.portal_login = "portal.routes@test.com" self.portal_user = self.env["res.users"].create( { "name": "Portal Routes User", "login": self.portal_login, "password": self.portal_login, "partner_id": self.member_partner.id, "groups_id": [(4, self.env.ref("base.group_portal").id)], } ) start_date = datetime.now().date() self.group_order = self.env["group.order"].create( { "name": "Routes Test Order", "group_ids": [(6, 0, [self.group.id])], "type": "regular", "start_date": start_date, "end_date": start_date + timedelta(days=7), "period": "weekly", "pickup_day": "3", "cutoff_day": "0", } ) self.group_order.action_open() def _login_portal(self): self.authenticate(self.portal_login, self.portal_login) @tagged("post_install", "-at_install") class TestPortalGetRoutes(PortalRoutesCommon, HttpCase): """The main GET pages answer 200 for a portal user.""" def test_portal_get_routes_return_200(self): """Every public Eskaera page renders for a portal user.""" self._login_portal() routes = [ "/eskaera", f"/eskaera/{self.group_order.id}", f"/eskaera/{self.group_order.id}/checkout", f"/eskaera/{self.group_order.id}/load-page?page=1", ] for route in routes: response = self.url_open(route, allow_redirects=True) self.assertEqual( response.status_code, 200, msg=f"Route {route} returned an error" ) def test_shop_page_is_not_bounced_to_the_list(self): """A member reaches the shop itself, not the "/eskaera" fallback. The access guard redirects non-members to the list page, which also answers 200 -- so a plain status check would pass even when the member never got in. """ self._login_portal() response = self.url_open( f"/eskaera/{self.group_order.id}", allow_redirects=True ) self.assertEqual(response.status_code, 200) self.assertTrue( response.url.endswith(f"/eskaera/{self.group_order.slug}"), msg=f"Bounced to {response.url} instead of the shop page", ) def test_slug_urls_answer_for_portal_user(self): """The canonical slug URLs answer too, not just the numeric ones.""" self._login_portal() for suffix in ("", "/checkout"): route = f"/eskaera/{self.group_order.slug}{suffix}" response = self.url_open(route, allow_redirects=True) self.assertEqual( response.status_code, 200, msg=f"Route {route} returned an error" ) @tagged("post_install", "-at_install") class TestPortalLabelsEndpoint(PortalRoutesCommon, HttpCase): """`/eskaera/labels` is a JSON-RPC endpoint, not a plain GET page.""" def test_labels_endpoint_returns_translations(self): """A JSON-RPC call returns the label dictionary.""" self._login_portal() labels = self.make_jsonrpc_request("/eskaera/labels") self.assertIsInstance(labels, dict) # A few keys the checkout summary relies on. for key in ("product", "quantity", "price", "subtotal", "total"): self.assertIn(key, labels) def test_i18n_alias_returns_the_same_payload(self): """`/eskaera/i18n` is an alias of `/eskaera/labels`.""" self._login_portal() labels = self.make_jsonrpc_request("/eskaera/labels") alias = self.make_jsonrpc_request("/eskaera/i18n") self.assertEqual(labels, alias) def test_labels_endpoint_is_public(self): """The endpoint answers without logging in (auth="public").""" labels = self.make_jsonrpc_request("/eskaera/labels") self.assertIsInstance(labels, dict) self.assertTrue(labels) def test_plain_get_is_rejected(self): """A bare GET is not a valid call for a JSON route. Guards the mistake this test file used to make: asserting 200 on a plain GET against `type="json"`, which Odoo answers with 400. """ self._login_portal() response = self.url_open("/eskaera/labels", allow_redirects=True) self.assertEqual(response.status_code, 400) @tagged("post_install", "-at_install") class TestPortalProductUoMAccess(PortalRoutesCommon, HttpCase): """Rendering the shop must not need UoM read rights beyond the portal's.""" def setUp(self): super().setUp() uom_category = self.env["uom.category"].create({"name": "Test UoM Cat"}) self.uom = self.env["uom.uom"].create( { "name": "Test UoM", "uom_type": "reference", "factor": 1.0, "category_id": uom_category.id, } ) self.product = self.env["product.product"].create( { "name": "Portal UoM Product", "type": "consu", "list_price": 10.0, "is_published": True, "sale_ok": True, "uom_id": self.uom.id, "uom_po_id": self.uom.id, } ) self.group_order.product_ids = [(4, self.product.id)] def test_portal_user_can_view_shop_with_uom(self): """The shop page renders for a portal user with a custom UoM.""" self._login_portal() response = self.url_open( f"/eskaera/{self.group_order.id}", allow_redirects=True ) self.assertEqual(response.status_code, 200) self.assertIn("Portal UoM Product", response.text)