# Copyright 2026 Criptomart # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) import json from odoo import http from odoo.tests import tagged from odoo.tests.common import HttpCase REDIRECT_URL_PARAM = "website_sale_disable_cart.redirect_url" @tagged("post_install", "-at_install") class TestDisableCart(HttpCase): """Check the standard cart endpoints are neutralized.""" def _set_redirect_url(self, url): self.env["ir.config_parameter"].sudo().set_param(REDIRECT_URL_PARAM, url) def _location_of(self, path): """Return the redirect target of a GET on ``path``.""" response = self.url_open(path, allow_redirects=False) self.assertIn(response.status_code, (301, 302, 303, 307, 308)) return response.headers.get("Location", "") def test_cart_redirects_to_default_url(self): """Without configuration, /shop/cart falls back to /shop.""" self.env["ir.config_parameter"].sudo().search( [("key", "=", REDIRECT_URL_PARAM)] ).unlink() self.assertTrue(self._location_of("/shop/cart").endswith("/shop")) def test_cart_redirects_to_configured_url(self): """The configured internal path is honored.""" self._set_redirect_url("/eskaera") self.assertTrue(self._location_of("/shop/cart").endswith("/eskaera")) def test_external_redirect_url_is_rejected(self): """An external URL must not be used (no open redirect).""" self._set_redirect_url("https://example.com/phishing") self.assertTrue(self._location_of("/shop/cart").endswith("/shop")) def test_protocol_relative_redirect_url_is_rejected(self): """A protocol relative URL is external too.""" self._set_redirect_url("//example.com/phishing") self.assertTrue(self._location_of("/shop/cart").endswith("/shop")) def test_cart_redirect_url_cannot_loop(self): """Pointing the redirect back to the cart must not loop.""" self._set_redirect_url("/shop/cart") self.assertTrue(self._location_of("/shop/cart").endswith("/shop")) def _json_rpc(self, path, params=None): """Call a JSON route and return its result.""" response = self.url_open( path, data=json.dumps( {"jsonrpc": "2.0", "method": "call", "params": params or {}} ), headers={"Content-Type": "application/json"}, ) payload = response.json() self.assertNotIn("error", payload, payload.get("error")) return payload["result"] def test_cart_update_redirects_without_creating_an_order(self): """POSTing to /shop/cart/update redirects and leaves no order behind.""" self._set_redirect_url("/shop") product = self.env["product.product"].create( {"name": "Disabled Cart Product", "list_price": 10.0, "is_published": True} ) orders_before = self.env["sale.order"].search_count([]) # A public session is needed to build a valid CSRF token, otherwise the # request is rejected before reaching the controller. self.authenticate(None, None) response = self.url_open( "/shop/cart/update", data={ "product_id": product.id, "add_qty": 1, "csrf_token": http.Request.csrf_token(self), }, allow_redirects=False, ) self.assertIn(response.status_code, (302, 303)) self.assertTrue(response.headers.get("Location", "").endswith("/shop")) self.assertEqual(self.env["sale.order"].search_count([]), orders_before) def test_cart_update_json_is_a_noop(self): """The JSON update endpoint answers an empty payload and writes nothing.""" product = self.env["product.product"].create( {"name": "Disabled Cart Product", "list_price": 10.0, "is_published": True} ) orders_before = self.env["sale.order"].search_count([]) result = self._json_rpc( "/shop/cart/update_json", {"product_id": product.id, "add_qty": 1} ) self.assertEqual(result, {}) self.assertEqual(self.env["sale.order"].search_count([]), orders_before) def test_cart_quantity_is_zero(self): """The JSON quantity endpoint always answers 0.""" self.assertEqual(self._json_rpc("/shop/cart/quantity"), 0)