Files
odoo_source/addons/test_website/controllers/main.py
T
Romain Derie 3b19a53191 [IMP] test_website: add a test for 308 redirect with multi url route
This commit introduces a test in Odoo 15 about a broken behavior in Odoo
16 which was not covered by a test.
A fix will be shipped alongside this commit forward-port in Odoo 16.

The behavior involved here is about:
- Having a multi-url defined route
- One of those URL not having a model converter
- One of those URL having a model converter
- Creating a 308 redirect for the URL without model converter

Something like this route (real example from the code):
```py
@http.route([
    '''/shop''',
    '''/shop/page/<int:page>''',
    '''/shop/category/<model("product.public.category"):category>''',
    '''/shop/category/<model("product.public.category"):category>/page/<int:page>'''
], type='http', auth="public", website=True, sitemap=sitemap_shop)
```
And this redirect (real usual example from clients):
```py
self.env['website.rewrite'].create({
    'name': 'Test Multi URL 308',
    'redirect_type': '308',
    'url_from': '/shop',
    'url_to': '/magasin',
})
```

In Odoo 15, everything will work as expected:
1. Accessing /shop will redirect to /magasin
2. Accessing /shop/category/categ-1 will not redirect and user will be
   on /shop/category/categ-1 still

But in Odoo 16:
In the case described above, acessing the URL with model converter from
the route will fail and redirect to a non slugified URL in the form of
`/some-url?param=test.model%287%2C%29` which is basically the
stringified encoded version of the record `test.model(7,)`.

Note:
- It doesn't matter if the route is contained in the other, the error
  will still occur even if `'''/shop'''` was `'''/abc'''`.
- Even if all URL from the route have their own 308 redirect, the error
  will still occur.
- It won't have any issue if there is a 308 on the route with model
  converter: both the one without model converter and other url with
  model converter will work and won't be impacted.

This might be because of a concept of "merging URL" from httpocalypse,
see https://github.com/odoo/odoo/commit/347a3ccf763cc6958d5dc1df3168c9c65245736c

opw-3450054
opw-3466498
opw-3477380
opw-3475694

X-original-commit: 22ca1c48f78cdf575f9a2b0823f9487f942bc7cd
Part-of: odoo/odoo#134206
2023-09-04 19:58:33 +00:00

164 lines
7.6 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import json
import werkzeug
from odoo import http
from odoo.http import request
from odoo.addons.portal.controllers.web import Home
from odoo.exceptions import UserError, ValidationError, AccessError, MissingError, AccessDenied
class WebsiteTest(Home):
@http.route('/test_view', type='http', auth='public', website=True, sitemap=False)
def test_view(self, **kwargs):
return request.render('test_website.test_view')
@http.route('/ignore_args/converteronly/<string:a>', type='http', auth="public", website=True, sitemap=False)
def test_ignore_args_converter_only(self, a):
return request.make_response(json.dumps(dict(a=a, kw=None)))
@http.route('/ignore_args/none', type='http', auth="public", website=True, sitemap=False)
def test_ignore_args_none(self):
return request.make_response(json.dumps(dict(a=None, kw=None)))
@http.route('/ignore_args/a', type='http', auth="public", website=True, sitemap=False)
def test_ignore_args_a(self, a):
return request.make_response(json.dumps(dict(a=a, kw=None)))
@http.route('/ignore_args/kw', type='http', auth="public", website=True, sitemap=False)
def test_ignore_args_kw(self, a, **kw):
return request.make_response(json.dumps(dict(a=a, kw=kw)))
@http.route('/ignore_args/converter/<string:a>', type='http', auth="public", website=True, sitemap=False)
def test_ignore_args_converter(self, a, b='youhou', **kw):
return request.make_response(json.dumps(dict(a=a, b=b, kw=kw)))
@http.route('/ignore_args/converter/<string:a>/nokw', type='http', auth="public", website=True, sitemap=False)
def test_ignore_args_converter_nokw(self, a, b='youhou'):
return request.make_response(json.dumps(dict(a=a, b=b)))
@http.route('/multi_company_website', type='http', auth="public", website=True, sitemap=False)
def test_company_context(self):
return request.make_response(json.dumps(request.context.get('allowed_company_ids')))
@http.route('/test_lang_url/<model("res.country"):country>', type='http', auth='public', website=True, sitemap=False)
def test_lang_url(self, **kwargs):
return request.render('test_website.test_view')
# Test Session
@http.route('/test_get_dbname', type='json', auth='public', website=True, sitemap=False)
def test_get_dbname(self, **kwargs):
return request.env.cr.dbname
# Test Error
@http.route('/test_error_view', type='http', auth='public', website=True, sitemap=False)
def test_error_view(self, **kwargs):
return request.render('test_website.test_error_view')
@http.route('/test_user_error_http', type='http', auth='public', website=True, sitemap=False)
def test_user_error_http(self, **kwargs):
raise UserError("This is a user http test")
@http.route('/test_user_error_json', type='json', auth='public', website=True, sitemap=False)
def test_user_error_json(self, **kwargs):
raise UserError("This is a user rpc test")
@http.route('/test_validation_error_http', type='http', auth='public', website=True, sitemap=False)
def test_validation_error_http(self, **kwargs):
raise ValidationError("This is a validation http test")
@http.route('/test_validation_error_json', type='json', auth='public', website=True, sitemap=False)
def test_validation_error_json(self, **kwargs):
raise ValidationError("This is a validation rpc test")
@http.route('/test_access_error_json', type='json', auth='public', website=True, sitemap=False)
def test_access_error_json(self, **kwargs):
raise AccessError("This is an access rpc test")
@http.route('/test_access_error_http', type='http', auth='public', website=True, sitemap=False)
def test_access_error_http(self, **kwargs):
raise AccessError("This is an access http test")
@http.route('/test_missing_error_json', type='json', auth='public', website=True, sitemap=False)
def test_missing_error_json(self, **kwargs):
raise MissingError("This is a missing rpc test")
@http.route('/test_missing_error_http', type='http', auth='public', website=True, sitemap=False)
def test_missing_error_http(self, **kwargs):
raise MissingError("This is a missing http test")
@http.route('/test_internal_error_json', type='json', auth='public', website=True, sitemap=False)
def test_internal_error_json(self, **kwargs):
raise werkzeug.exceptions.InternalServerError()
@http.route('/test_internal_error_http', type='http', auth='public', website=True, sitemap=False)
def test_internal_error_http(self, **kwargs):
raise werkzeug.exceptions.InternalServerError()
@http.route('/test_access_denied_json', type='json', auth='public', website=True, sitemap=False)
def test_denied_error_json(self, **kwargs):
raise AccessDenied("This is an access denied rpc test")
@http.route('/test_access_denied_http', type='http', auth='public', website=True, sitemap=False)
def test_denied_error_http(self, **kwargs):
raise AccessDenied("This is an access denied http test")
@http.route(['/get'], type='http', auth="public", methods=['GET'], website=True, sitemap=False)
def get_method(self, **kw):
return request.make_response('get')
@http.route(['/post'], type='http', auth="public", methods=['POST'], website=True, sitemap=False)
def post_method(self, **kw):
return request.make_response('post')
@http.route(['/get_post'], type='http', auth="public", methods=['GET', 'POST'], website=True, sitemap=False)
def get_post_method(self, **kw):
return request.make_response('get_post')
@http.route(['/get_post_nomultilang'], type='http', auth="public", methods=['GET', 'POST'], website=True, multilang=False, sitemap=False)
def get_post_method_no_multilang(self, **kw):
return request.make_response('get_post_nomultilang')
# Test Perfs
@http.route(['/empty_controller_test'], type='http', auth='public', website=True, multilang=False, sitemap=False)
def empty_controller_test(self, **kw):
return 'Basic Controller Content'
# Test Redirects
@http.route(['/test_website/country/<model("res.country"):country>'], type='http', auth="public", website=True, sitemap=True)
def test_model_converter_country(self, country, **kw):
return request.render('test_website.test_redirect_view', {'country': country})
@http.route(['/test_website/200/<model("test.model"):rec>'], type='http', auth="public", website=True, sitemap=False)
def test_model_converter_seoname(self, rec, **kw):
return request.make_response('ok')
@http.route(['/test_website/test_redirect_view_qs'], type='http', auth="public", website=True, sitemap=False)
def test_redirect_view_qs(self, **kw):
return request.render('test_website.test_redirect_view_qs')
@http.route([
'/test_countries_308',
'/test_countries_308/<model("test.model"):rec>',
], type='http', auth='public', website=True, sitemap=False)
def test_countries_308(self, **kwargs):
return request.make_response('ok')
# Test Sitemap
def sitemap_test(env, rule, qs):
if not qs or qs.lower() in '/test_website_sitemap':
yield {'loc': '/test_website_sitemap'}
@http.route([
'/test_website_sitemap',
'/test_website_sitemap/something/<model("test.model"):rec>',
], type='http', auth='public', website=True, sitemap=sitemap_test)
def test_sitemap(self, rec=None, **kwargs):
return request.make_response('Sitemap Testing Page')