125 lines
4.8 KiB
Python
125 lines
4.8 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo.tests import TransactionCase, tagged
|
|
import functools
|
|
from unittest.mock import patch
|
|
|
|
|
|
@tagged('-at_install', 'post_install')
|
|
class TestWebsiteSitemap(TransactionCase):
|
|
def test_sitemap_page_lastmod(self):
|
|
website = self.env['website'].search([], limit=1)
|
|
page_url = '/test-page'
|
|
Page = self.env['website.page']
|
|
page = Page.create({
|
|
'name': 'Test Page',
|
|
'website_id': website.id,
|
|
'url': page_url,
|
|
'type': 'qweb',
|
|
'arch': '<t t-call="website.layout"/>',
|
|
'is_published': True,
|
|
})
|
|
View = self.env['ir.ui.view']
|
|
|
|
def set_write_dates(page_date, view_date):
|
|
self.env.cr.execute(
|
|
"UPDATE website_page SET write_date = %s WHERE id = %s",
|
|
(page_date, page.id)
|
|
)
|
|
self.env.cr.execute(
|
|
"UPDATE ir_ui_view SET write_date = %s WHERE id = %s",
|
|
(view_date, page.view_id.id)
|
|
)
|
|
Page.invalidate_model(['write_date'])
|
|
View.invalidate_model(['write_date'])
|
|
self.assertEqual(str(page.write_date), page_date)
|
|
self.assertEqual(str(page.view_id.write_date), view_date)
|
|
|
|
def get_sitemap_lastmod():
|
|
pages = website._enumerate_pages()
|
|
return next(p['lastmod'] for p in pages if p['loc'] == page_url)
|
|
|
|
old_date = "2002-05-06 12:00:00"
|
|
|
|
new_date = "2014-05-15 12:00:00"
|
|
set_write_dates(new_date, old_date)
|
|
self.assertEqual(str(get_sitemap_lastmod()), new_date[:10])
|
|
|
|
new_date2 = "2015-10-01 12:00:00"
|
|
set_write_dates(old_date, new_date2)
|
|
self.assertEqual(str(get_sitemap_lastmod()), new_date2[:10])
|
|
|
|
def test_sitemap_dedup_overridden_controllers(self):
|
|
website = self.env['website'].search([], limit=1)
|
|
|
|
# Fake router and rule to simulate two sitemap entries with and without trailing slash
|
|
def fake_sitemap_callable(env, rule, qs):
|
|
yield {'loc': '/dupe'}
|
|
yield {'loc': '/dupe/'}
|
|
|
|
class FakeEndpoint:
|
|
routing = {'sitemap': fake_sitemap_callable}
|
|
|
|
class FakeRule:
|
|
endpoint = FakeEndpoint()
|
|
|
|
class FakeRouter:
|
|
def iter_rules(self):
|
|
return [FakeRule()]
|
|
|
|
# Patch routing_map to return our fake router so only our fake rules are considered
|
|
with patch('odoo.addons.website.models.ir_http.Http.routing_map', autospec=True, return_value=FakeRouter()):
|
|
locs = list(website.with_user(website.user_id)._enumerate_pages())
|
|
|
|
dupes = [l['loc'] for l in locs if l['loc'].startswith('/dupe')]
|
|
# Only one entry should remain, normalized to '/dupe'
|
|
self.assertEqual(dupes, ['/dupe'])
|
|
|
|
def test_sitemap_callable_dedup_with_partial_and_bound(self):
|
|
# Some routes are duplicated at runtime (e.g., when a redirect
|
|
# is configured). The framework may clone an existing endpoint for the
|
|
# extra rule, and 3rd-party modules sometimes wrap callables using
|
|
# `functools.partial` to adapt them.
|
|
# As a result, the very same sitemap generator can be referenced in two
|
|
# different ways: once as a classic bound method (self.sitemap) and once
|
|
# as a `functools.partial(self.sitemap)` wrapper.
|
|
# If we were deduplicating based on the callable object identity only,
|
|
# those two references would look different and the sitemap code could
|
|
# run twice.
|
|
website = self.env['website'].search([], limit=1)
|
|
|
|
call_count = {'n': 0} # mutable object to be used in CallableHolder.
|
|
|
|
class CallableHolder:
|
|
def sitemap(self, env, rule, qs):
|
|
call_count['n'] += 1
|
|
yield {'loc': '/once'}
|
|
|
|
holder = CallableHolder()
|
|
|
|
# First rule uses the bound method directly
|
|
class EndpointBound:
|
|
routing = {'sitemap': holder.sitemap}
|
|
|
|
class RuleBound:
|
|
endpoint = EndpointBound()
|
|
|
|
# Second rule uses a partial wrapping the same bound method
|
|
class EndpointPartial:
|
|
routing = {'sitemap': functools.partial(holder.sitemap)}
|
|
|
|
class RulePartial:
|
|
endpoint = EndpointPartial()
|
|
|
|
class FakeRouter:
|
|
def iter_rules(self):
|
|
return [RuleBound(), RulePartial()]
|
|
|
|
with patch('odoo.addons.website.models.ir_http.Http.routing_map', autospec=True, return_value=FakeRouter()):
|
|
locs = list(website.with_user(website.user_id)._enumerate_pages())
|
|
|
|
# The sitemap callable should have been executed only once
|
|
self.assertEqual(call_count['n'], 1)
|
|
# And the returned loc should be present (normalized already)
|
|
self.assertIn({'loc': '/once'}, locs)
|