server-tools/jsonifier/tests/test_get_parser.py

342 lines
12 KiB
Python

# Copyright 2017 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from unittest import mock
from odoo import fields, tools
from odoo.exceptions import UserError
from odoo.tests.common import TransactionCase
from ..models.utils import convert_simple_to_full_parser
def jsonify_custom(self, field_name):
return "yeah!"
class TestParser(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
# disable tracking test suite wise
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.env.user.tz = "Europe/Brussels"
cls.partner = cls.env["res.partner"].create(
{
"name": "Akretion",
"country_id": cls.env.ref("base.fr").id,
"lang": "en_US", # default
"category_id": [(0, 0, {"name": "Inovator"})],
"child_ids": [
(
0,
0,
{
"name": "Sebatien Beau",
"country_id": cls.env.ref("base.fr").id,
},
)
],
"date": fields.Date.from_string("2019-10-31"),
}
)
Langs = cls.env["res.lang"].with_context(active_test=False)
cls.lang = Langs.search([("code", "=", "fr_FR")])
cls.lang.active = True
category = cls.env["res.partner.category"].create({"name": "name"})
cls.translated_target = "name_{}".format(cls.lang.code)
category.with_context(lang=cls.lang.code).write({"name": cls.translated_target})
cls.global_resolver = cls.env["ir.exports.resolver"].create(
{"python_code": "value['X'] = 'X'; result = value", "type": "global"}
)
cls.resolver = cls.env["ir.exports.resolver"].create(
{"python_code": "result = value + '_pidgin'", "type": "field"}
)
cls.category_export = cls.env["ir.exports"].create(
{
"global_resolver_id": cls.global_resolver.id,
"language_agnostic": True,
"export_fields": [
(0, 0, {"name": "name"}),
(
0,
0,
{
"name": "name",
"target": "name:{}".format(cls.translated_target),
"lang_id": cls.lang.id,
},
),
(
0,
0,
{
"name": "name",
"target": "name:name_resolved",
"resolver_id": cls.resolver.id,
},
),
],
}
)
cls.category = category.with_context(lang=None)
cls.category_lang = category.with_context(lang=cls.lang.code)
def test_getting_parser(self):
expected_parser = [
"name",
"active",
"partner_latitude",
"color",
("category_id", ["name"]),
("country_id", ["name", "code"]),
(
"child_ids",
[
"name",
"id",
"email",
("country_id", ["name", "code"]),
("child_ids", ["name"]),
],
),
"lang",
"comment",
]
exporter = self.env.ref("jsonifier.ir_exp_partner")
parser = exporter.get_json_parser()
expected_full_parser = convert_simple_to_full_parser(expected_parser)
self.assertEqual(parser, expected_full_parser)
# modify an ir.exports_line to put a target for a field
self.env.ref("jsonifier.category_id_name").write(
{"target": "category_id:category/name"}
)
expected_parser[4] = ("category_id:category", ["name"])
parser = exporter.get_json_parser()
expected_full_parser = convert_simple_to_full_parser(expected_parser)
self.assertEqual(parser, expected_full_parser)
def test_json_export(self):
# Enforces TZ to validate the serialization result of a Datetime
parser = [
"lang",
"comment",
"partner_latitude",
"name",
"color",
(
"child_ids:children",
[
("child_ids:children", ["name"]),
"email",
("country_id:country", ["code", "name"]),
"name",
"id",
],
),
("country_id:country", ["code", "name"]),
"active",
("category_id", ["name"]),
"create_date",
"date",
]
# put our own create date to ease tests
self.env.cr.execute(
"update res_partner set create_date=%s where id=%s",
("2019-10-31 14:39:49", self.partner.id),
)
expected_json = {
"lang": "en_US",
"comment": None,
"partner_latitude": 0.0,
"name": "Akretion",
"color": 0,
"country": {"code": "FR", "name": "France"},
"active": True,
"category_id": [{"name": "Inovator"}],
"children": [
{
"id": self.partner.child_ids.id,
"country": {"code": "FR", "name": "France"},
"children": [],
"name": "Sebatien Beau",
"email": None,
}
],
"create_date": "2019-10-31T14:39:49",
"date": "2019-10-31",
}
json_partner = self.partner.jsonify(parser)
self.assertDictEqual(json_partner[0], expected_json)
# Check that only boolean fields have boolean values into json
# By default if a field is not set into Odoo, the value is always False
# This value is not the expected one into the json
self.partner.write({"child_ids": [(6, 0, [])], "active": False, "lang": False})
json_partner = self.partner.jsonify(parser)
expected_json["active"] = False
expected_json["lang"] = None
expected_json["children"] = []
self.assertDictEqual(json_partner[0], expected_json)
def test_one(self):
parser = [
"name",
]
expected_json = {
"name": "Akretion",
}
json_partner = self.partner.jsonify(parser, one=True)
self.assertDictEqual(json_partner, expected_json)
# cannot call on multiple records
with self.assertRaises(ValueError) as err:
self.env["res.partner"].search([]).jsonify(parser, one=True)
self.assertIn("Expected singleton", str(err.exception))
def test_json_export_callable_parser(self):
self.partner.__class__.jsonify_custom = jsonify_custom
parser = [
# callable subparser
("name", lambda rec, fname: rec[fname] + " rocks!"),
("name:custom", "jsonify_custom"),
]
expected_json = {
"name": "Akretion rocks!",
"custom": "yeah!",
}
json_partner = self.partner.jsonify(parser)
self.assertDictEqual(json_partner[0], expected_json)
del self.partner.__class__.jsonify_custom
def test_full_parser(self):
parser = self.category_export.get_json_parser()
json = self.category.jsonify(parser)[0]
json_fr = self.category_lang.jsonify(parser)[0]
self.assertEqual(
json, json_fr
) # starting from different languages should not change anything
self.assertEqual(json[self.translated_target], self.translated_target)
self.assertEqual(json["name_resolved"], "name_pidgin") # field resolver
self.assertEqual(json["X"], "X") # added by global resolver
def test_simple_parser_translations(self):
"""The simple parser result should depend on the context language."""
parser = ["name"]
json = self.category.jsonify(parser)[0]
json_fr = self.category_lang.jsonify(parser)[0]
self.assertEqual(json["name"], "name")
self.assertEqual(json_fr["name"], self.translated_target)
def test_simple_star_target_and_field_resolver(self):
"""The simple parser result should depend on the context language."""
code = (
"is_number = field_type in ('integer', 'float');"
"ftype = 'NUMBER' if is_number else 'TEXT';"
"value = value if is_number else str(value);"
"result = {'Key': name, 'Value': value, 'Type': ftype, 'IsPublic': True}"
)
resolver = self.env["ir.exports.resolver"].create({"python_code": code})
lang_parser = [
{"target": "customTags=list", "name": "name", "resolver": resolver},
{"target": "customTags=list", "name": "id", "resolver": resolver},
]
parser = {"language_agnostic": True, "langs": {False: lang_parser}}
expected_json = {
"customTags": [
{"Value": "name", "Key": "name", "Type": "TEXT", "IsPublic": True},
{
"Value": self.category.id,
"Key": "id",
"Type": "NUMBER",
"IsPublic": True,
},
]
}
json = self.category.jsonify(parser)[0]
self.assertEqual(json, expected_json)
def test_simple_export_with_function(self):
self.category.__class__.jsonify_custom = jsonify_custom
export = self.env["ir.exports"].create(
{
"export_fields": [
(0, 0, {"name": "name", "instance_method_name": "jsonify_custom"}),
],
}
)
json = self.category.jsonify(export.get_json_parser())[0]
self.assertEqual(json, {"name": "yeah!"})
def test_export_relational_display_names(self):
"""If we export a relational, we get its display_name in the json."""
parser = [
"state_id",
"country_id",
"category_id",
"user_ids",
]
expected_json = {
"state_id": None,
"country_id": "France",
"category_id": ["Inovator"],
"user_ids": [],
}
json_partner = self.partner.jsonify(parser, one=True)
self.assertDictEqual(json_partner, expected_json)
def test_export_reference_display_names(self):
"""Reference work the same as relational"""
menu = self.env.ref("base.menu_action_res_users")
json_menu = menu.jsonify(["action"], one=True)
self.assertDictEqual(json_menu, {"action": "Users"})
def test_bad_parsers_strict(self):
rec = self.category.with_context(jsonify_record_strict=True)
bad_field_name = ["Name"]
with self.assertRaises(KeyError):
rec.jsonify(bad_field_name, one=True)
bad_function_name = {"fields": [{"name": "name", "function": "notafunction"}]}
with self.assertRaises(UserError):
rec.jsonify(bad_function_name, one=True)
bad_subparser = {"fields": [({"name": "name"}, [{"name": "subparser_name"}])]}
with self.assertRaises(UserError):
rec.jsonify(bad_subparser, one=True)
def test_bad_parsers_fail_gracefully(self):
rec = self.category
logger_patch_path = "odoo.addons.jsonifier.models.models._logger.error"
# logging is disabled when testing as it's useless and makes build fail.
tools.config["test_enable"] = False
bad_field_name = ["Name"]
with mock.patch(logger_patch_path) as mocked_logger:
rec.jsonify(bad_field_name, one=True)
mocked_logger.assert_called()
bad_function_name = {"fields": [{"name": "name", "function": "notafunction"}]}
with mock.patch(logger_patch_path) as mocked_logger:
rec.jsonify(bad_function_name, one=True)
mocked_logger.assert_called()
bad_subparser = {"fields": [({"name": "name"}, [{"name": "subparser_name"}])]}
with mock.patch(logger_patch_path) as mocked_logger:
rec.jsonify(bad_subparser, one=True)
mocked_logger.assert_called()
tools.config["test_enable"] = True