odoo_17.0.1/core/web/tests/test_image.py

155 lines
6.2 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import io
import base64
from PIL import Image
from werkzeug.urls import url_unquote_plus
from odoo.tests.common import HttpCase, tagged
@tagged('-at_install', 'post_install')
class TestImage(HttpCase):
def test_01_content_image_resize_placeholder(self):
"""The goal of this test is to make sure the placeholder image is
resized appropriately depending on the given URL parameters."""
# CASE: resize placeholder, given size but original ratio is always kept
response = self.url_open('/web/image/0/200x150')
response.raise_for_status()
image = Image.open(io.BytesIO(response.content))
self.assertEqual(image.size, (150, 150))
# CASE: resize placeholder to 128
response = self.url_open('/web/image/fake/0/image_128')
response.raise_for_status()
image = Image.open(io.BytesIO(response.content))
self.assertEqual(image.size, (128, 128))
# CASE: resize placeholder to 256
response = self.url_open('/web/image/fake/0/image_256')
response.raise_for_status()
image = Image.open(io.BytesIO(response.content))
self.assertEqual(image.size, (256, 256))
# CASE: resize placeholder to 1024 (but placeholder image is too small)
response = self.url_open('/web/image/fake/0/image_1024')
response.raise_for_status()
image = Image.open(io.BytesIO(response.content))
self.assertEqual(image.size, (256, 256))
# CASE: no size found, use placeholder original size
response = self.url_open('/web/image/fake/0/image_no_size')
response.raise_for_status()
image = Image.open(io.BytesIO(response.content))
self.assertEqual(image.size, (256, 256))
def test_02_content_image_Etag_304(self):
"""This test makes sure that the 304 response is properly returned if the ETag is properly set"""
attachment = self.env['ir.attachment'].create({
'datas': b"R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=",
'name': 'testEtag.gif',
'public': True,
'mimetype': 'image/gif',
})
response = self.url_open('/web/image/%s' % attachment.id, timeout=None)
response.raise_for_status()
self.assertEqual(response.status_code, 200)
self.assertEqual(base64.b64encode(response.content), attachment.datas)
etag = response.headers.get('ETag')
response2 = self.url_open('/web/image/%s' % attachment.id, headers={"If-None-Match": etag})
response2.raise_for_status()
self.assertEqual(response2.status_code, 304)
self.assertEqual(len(response2.content), 0)
def test_03_web_content_filename(self):
"""This test makes sure the Content-Disposition header matches the given filename"""
att = self.env['ir.attachment'].create({
'datas': b'R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=',
'name': 'testFilename.gif',
'public': True,
'mimetype': 'image/gif'
})
# CASE: no filename given
res = self.url_open('/web/image/%s/0x0/?download=true' % att.id)
res.raise_for_status()
self.assertEqual(res.headers['Content-Disposition'], 'attachment; filename=testFilename.gif')
# CASE: given filename without extension
res = self.url_open('/web/image/%s/0x0/custom?download=true' % att.id)
res.raise_for_status()
self.assertEqual(res.headers['Content-Disposition'], 'attachment; filename=custom.gif')
# CASE: given filename and extention
res = self.url_open('/web/image/%s/0x0/custom.png?download=true' % att.id)
res.raise_for_status()
self.assertEqual(res.headers['Content-Disposition'], 'attachment; filename=custom.png')
def test_04_web_content_filename_secure(self):
"""This test makes sure the Content-Disposition header matches the given filename"""
att = self.env['ir.attachment'].create({
'datas': b'R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=',
'name': """fô☺o-l'éb \n a"!r".gif""",
'public': True,
'mimetype': 'image/gif',
})
def assert_filenames(
url,
expected_filename,
expected_filename_star='',
message=r"File that will be saved on disc should have the original filename without \n and \r",
):
res = self.url_open(url)
res.raise_for_status()
if expected_filename_star:
inline, filename, filename_star = res.headers['Content-Disposition'].split('; ')
else:
inline, filename = res.headers['Content-Disposition'].split('; ')
filename_star = ''
filename = filename.removeprefix("filename=").strip('"')
filename_star = url_unquote_plus(filename_star.removeprefix("filename*=UTF-8''").strip('"'))
self.assertEqual(inline, 'inline')
self.assertEqual(filename, expected_filename, message)
self.assertEqual(filename_star, expected_filename_star, message)
assert_filenames(f'/web/image/{att.id}',
r"""foo-l'eb _ a\"!r\".gif""",
r"""fô☺o-l'éb _ a"!r".gif""",
)
assert_filenames(f'/web/image/{att.id}/custom_invalid_name\nis-ok.gif',
r"""custom_invalid_name_is-ok.gif""",
)
assert_filenames(f'/web/image/{att.id}/\r\n',
r"""__.gif""",
)
assert_filenames(f'/web/image/{att.id}/你好',
r""".gif""",
r"""你好.gif""",
)
assert_filenames(f'/web/image/{att.id}/%E9%9D%A2%E5%9B%BE.gif',
r""".gif""",
r"""面图.gif""",
)
assert_filenames(f'/web/image/{att.id}/hindi_नमस्ते.gif',
r"""hindi_.gif""",
r"""hindi_नमस्ते.gif""",
)
assert_filenames(f'/web/image/{att.id}/arabic_مرحبا',
r"""arabic_.gif""",
r"""arabic_مرحبا.gif""",
)
assert_filenames(f'/web/image/{att.id}/4wzb_!!63148-0-t1.jpg_360x1Q75.jpg_.webp',
r"""4wzb_!!63148-0-t1.jpg_360x1Q75.jpg_.webp""",
)