Skip to content

Commit bf251a4

Browse files
Merge remote-tracking branch 'origin/19.0' into feat/dci-integration
2 parents d85aae8 + c660896 commit bf251a4

8 files changed

Lines changed: 340 additions & 0 deletions

File tree

spp_drims_sl/tests/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
from . import test_spp_drims_sl
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
"""Install / data-load sanity tests for spp_drims_sl.
3+
4+
This is a data-only module (Sri Lanka locale configuration for DRIMS) —
5+
it ships seed records but no Python models or methods. The tests below
6+
exercise the install path so CI's per-module coverage matrix has
7+
something to report against, and assert that the headline data records
8+
the rest of the module relies on are actually present after install.
9+
"""
10+
11+
from odoo.tests import TransactionCase, tagged
12+
13+
14+
@tagged("post_install", "-at_install")
15+
class TestSppDrimsSl(TransactionCase):
16+
"""Spot-check that the seed data declared in __manifest__.py loaded."""
17+
18+
def test_module_is_installed(self):
19+
module = self.env["ir.module.module"].search([("name", "=", "spp_drims_sl")], limit=1)
20+
self.assertTrue(module, "spp_drims_sl module not registered")
21+
self.assertEqual(
22+
module.state,
23+
"installed",
24+
f"spp_drims_sl expected 'installed', got {module.state}",
25+
)
26+
27+
def test_hazard_category_seed_loaded(self):
28+
"""data/hazard_categories.xml declares at least one category."""
29+
category = self.env.ref("spp_drims_sl.category_natural", raise_if_not_found=False)
30+
self.assertTrue(
31+
category,
32+
"spp_drims_sl.category_natural missing — hazard_categories.xml didn't load",
33+
)
34+
35+
def test_sl_currency_company_config(self):
36+
"""data/company_config.xml activates LKR for the locale."""
37+
currency = self.env.ref("base.LKR", raise_if_not_found=False)
38+
self.assertTrue(currency, "base.LKR currency missing")
39+
self.assertTrue(
40+
currency.active,
41+
"LKR currency expected to be active after spp_drims_sl install",
42+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
from . import test_hide_menu
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
"""Tests for spp_hide_menus_base — hide/show menu visibility logic.
3+
4+
The module patches ``ir.module.module`` to hide a curated list of stock
5+
Odoo menus (Project, Calendar, Stock, ...) from the OpenSPP user group
6+
when an install/upgrade completes. The tests exercise the ``hide_menu``
7+
and ``show_menu`` round-trip on ``spp.hide.menu`` directly so we cover
8+
the model's state transition without depending on a real "Apps install"
9+
flow.
10+
"""
11+
12+
from odoo.tests import TransactionCase, tagged
13+
14+
15+
@tagged("post_install", "-at_install")
16+
class TestSppHideMenu(TransactionCase):
17+
"""Exercise the hide / show round-trip on a sample menu."""
18+
19+
@classmethod
20+
def setUpClass(cls):
21+
super().setUpClass()
22+
# Pick any existing menu we can safely toggle in a test transaction.
23+
cls.menu = cls.env["ir.ui.menu"].search([], limit=1)
24+
if not cls.menu:
25+
raise AssertionError("No ir.ui.menu records found to test against")
26+
27+
def test_module_is_installed(self):
28+
module = self.env["ir.module.module"].search([("name", "=", "spp_hide_menus_base")], limit=1)
29+
self.assertEqual(module.state, "installed")
30+
31+
def test_group_hide_menus_user_seed(self):
32+
"""security/groups.xml must declare the hide-menus-user group."""
33+
group = self.env.ref("spp_hide_menus_base.group_hide_menus_user", raise_if_not_found=False)
34+
self.assertTrue(
35+
group,
36+
"group_hide_menus_user must exist — hide_menu() falls back on it",
37+
)
38+
39+
def test_hide_menu_transition(self):
40+
"""hide_menu() flips state show → hide and snapshots original groups."""
41+
original_groups = self.env["ir.ui.menu"].browse(self.menu.id).group_ids
42+
record = self.env["spp.hide.menu"].create({"menu_id": self.menu.id, "xml_id": "test.hide_menu_target"})
43+
self.assertEqual(record.state, "show")
44+
45+
record.hide_menu()
46+
47+
self.assertEqual(record.state, "hide")
48+
# Original groups were saved on the record so show_menu can restore them.
49+
self.assertEqual(record.default_group_ids, original_groups)
50+
51+
def test_show_menu_restores_original_groups(self):
52+
"""show_menu() restores the snapshot taken at hide time."""
53+
original_groups = self.env["ir.ui.menu"].browse(self.menu.id).group_ids
54+
record = self.env["spp.hide.menu"].create({"menu_id": self.menu.id, "xml_id": "test.hide_menu_target"})
55+
record.hide_menu()
56+
# Menu is now restricted to the hide-menus-user group only.
57+
self.assertNotEqual(self.menu.group_ids, original_groups)
58+
59+
record.show_menu()
60+
self.assertEqual(record.state, "show")
61+
self.assertEqual(self.menu.group_ids, original_groups)
62+
63+
def test_hide_menu_noop_when_already_hidden(self):
64+
"""Calling hide_menu twice doesn't change state or groups again."""
65+
record = self.env["spp.hide.menu"].create({"menu_id": self.menu.id, "xml_id": "test.hide_menu_target"})
66+
record.hide_menu()
67+
snapshot = record.default_group_ids
68+
record.hide_menu() # second call — guarded by state == "show"
69+
self.assertEqual(record.state, "hide")
70+
# Original snapshot must not be overwritten by the second call.
71+
self.assertEqual(record.default_group_ids, snapshot)
72+
73+
def test_menu_app_catalog_is_well_formed(self):
74+
"""ir.module.module.MENU_APP entries must point to a menu xml_id."""
75+
IrModuleModule = self.env["ir.module.module"]
76+
for module_name, info in IrModuleModule.MENU_APP.items():
77+
self.assertIn(
78+
"menu_xml_id",
79+
info,
80+
f"MENU_APP[{module_name!r}] missing required 'menu_xml_id'",
81+
)
82+
self.assertTrue(
83+
info["menu_xml_id"],
84+
f"MENU_APP[{module_name!r}].menu_xml_id is empty",
85+
)
86+
87+
def test_hide_menus_processes_catalog(self):
88+
"""``ir.module.module.hide_menus()`` walks MENU_APP and creates a
89+
``spp.hide.menu`` record (state=hide) for every entry whose menu
90+
xml_id resolves in the current DB.
91+
"""
92+
IrModuleModule = self.env["ir.module.module"]
93+
HideMenu = self.env["spp.hide.menu"]
94+
95+
# Figure out which catalog entries are actually resolvable here —
96+
# most stock Odoo modules in MENU_APP (mail, contacts, ...) are
97+
# present in any test DB, but a few (mass_mailing, survey, ...)
98+
# may not be installed.
99+
resolvable = []
100+
for module_name, info in IrModuleModule.MENU_APP.items():
101+
menu = self.env.ref(info["menu_xml_id"], raise_if_not_found=False)
102+
module = IrModuleModule.search([("name", "=", module_name)], limit=1)
103+
if menu and module:
104+
resolvable.append((module_name, menu.id))
105+
106+
if not resolvable:
107+
self.skipTest("No MENU_APP entries are resolvable in this test DB")
108+
109+
# Wipe any pre-existing spp.hide.menu so the test's assertions are
110+
# clearly about hide_menus()'s effect, not the install hook.
111+
HideMenu.search([]).unlink()
112+
113+
IrModuleModule.hide_menus()
114+
115+
for module_name, menu_id in resolvable:
116+
record = HideMenu.search([("menu_id", "=", menu_id)], limit=1)
117+
self.assertTrue(
118+
record,
119+
f"hide_menus() didn't create a spp.hide.menu for {module_name!r}",
120+
)
121+
self.assertEqual(
122+
record.state,
123+
"hide",
124+
f"spp.hide.menu for {module_name!r} expected state=hide, got {record.state}",
125+
)
126+
127+
def test_hide_menus_is_idempotent(self):
128+
"""Calling hide_menus() twice doesn't double-hide already-hidden menus.
129+
130+
After the first pass every resolvable entry is in state=hide. A
131+
second pass must leave them in state=hide (the inner guard
132+
``elif hidden_menus.state == "show"`` skips them).
133+
"""
134+
IrModuleModule = self.env["ir.module.module"]
135+
HideMenu = self.env["spp.hide.menu"]
136+
137+
HideMenu.search([]).unlink()
138+
IrModuleModule.hide_menus()
139+
after_first = HideMenu.search([])
140+
self.assertTrue(
141+
after_first,
142+
"hide_menus() didn't create any records — nothing to check idempotency against",
143+
)
144+
145+
IrModuleModule.hide_menus()
146+
after_second = HideMenu.search([])
147+
# No duplicates created and every record stayed in state=hide.
148+
self.assertEqual(set(after_first.ids), set(after_second.ids))
149+
for record in after_second:
150+
self.assertEqual(record.state, "hide")
151+
152+
def test_hide_menus_skips_unknown_modules(self):
153+
"""An ir.module.module record whose name isn't in MENU_APP must be
154+
ignored by hide_menus() — no spp.hide.menu record is created for it.
155+
"""
156+
IrModuleModule = self.env["ir.module.module"]
157+
HideMenu = self.env["spp.hide.menu"]
158+
159+
# ``base`` is always installed and is NOT in MENU_APP.
160+
self.assertNotIn("base", IrModuleModule.MENU_APP)
161+
162+
before = HideMenu.search([]).ids
163+
IrModuleModule.hide_menus()
164+
after = HideMenu.search([]).ids
165+
166+
# Whatever new records appeared, none should belong to the ``base`` menu.
167+
new_ids = set(after) - set(before)
168+
for record in HideMenu.browse(list(new_ids)):
169+
self.assertNotEqual(
170+
record.menu_id.id,
171+
self.env.ref("base.menu_administration").id
172+
if self.env.ref("base.menu_administration", raise_if_not_found=False)
173+
else 0,
174+
"hide_menus() shouldn't touch base.menu_administration",
175+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
from . import test_spp_indicator_studio
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
"""Install / view-load sanity tests for spp_indicator_studio.
3+
4+
This is a UI-bridge module — it ships act_window actions and form/list
5+
views for ``spp.indicator`` and ``spp.indicator.category`` but no Python
6+
models or methods of its own. The tests verify the install path and that
7+
the headline view + action records loaded.
8+
"""
9+
10+
from odoo.tests import TransactionCase, tagged
11+
12+
13+
@tagged("post_install", "-at_install")
14+
class TestSppIndicatorStudio(TransactionCase):
15+
def test_module_is_installed(self):
16+
module = self.env["ir.module.module"].search([("name", "=", "spp_indicator_studio")], limit=1)
17+
self.assertTrue(module, "spp_indicator_studio not registered")
18+
self.assertEqual(
19+
module.state,
20+
"installed",
21+
f"spp_indicator_studio expected 'installed', got {module.state}",
22+
)
23+
24+
def test_indicator_views_loaded(self):
25+
"""views/indicator_views.xml declares list/form/kanban/action records."""
26+
for xml_id in (
27+
"spp_indicator_studio.spp_statistic_view_list",
28+
"spp_indicator_studio.spp_statistic_view_form",
29+
"spp_indicator_studio.spp_statistic_view_kanban",
30+
"spp_indicator_studio.spp_statistic_action",
31+
):
32+
with self.subTest(record=xml_id):
33+
self.assertTrue(
34+
self.env.ref(xml_id, raise_if_not_found=False),
35+
f"{xml_id} missing — indicator_views.xml didn't load",
36+
)
37+
38+
def test_indicator_category_views_loaded(self):
39+
"""views/indicator_category_views.xml declares list/form/action records."""
40+
for xml_id in (
41+
"spp_indicator_studio.spp_metric_category_view_list",
42+
"spp_indicator_studio.spp_metric_category_view_form",
43+
"spp_indicator_studio.spp_metric_category_action",
44+
):
45+
with self.subTest(record=xml_id):
46+
self.assertTrue(
47+
self.env.ref(xml_id, raise_if_not_found=False),
48+
f"{xml_id} missing — indicator_category_views.xml didn't load",
49+
)
50+
51+
def test_indicator_action_targets_spp_indicator(self):
52+
"""The act_window must point at the spp.indicator model."""
53+
action = self.env.ref("spp_indicator_studio.spp_statistic_action", raise_if_not_found=False)
54+
self.assertTrue(action)
55+
self.assertEqual(action.res_model, "spp.indicator")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
from . import test_spp_starter_farmer_registry
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
"""Install / bundle-sanity tests for spp_starter_farmer_registry.
3+
4+
This is a meta-module / bundle — it only declares ``depends`` plus a
5+
single ``ir.config_parameter`` seed. The tests verify that:
6+
7+
- the bundle itself installs cleanly,
8+
- every farmer-registry dependency it bundles is reachable + installed,
9+
- the seed config parameter loaded.
10+
"""
11+
12+
from odoo.tests import TransactionCase, tagged
13+
14+
15+
@tagged("post_install", "-at_install")
16+
class TestSppStarterFarmerRegistry(TransactionCase):
17+
"""Spot-check that the bundle declared in __manifest__.py installs cleanly."""
18+
19+
BUNDLE_DEPS = (
20+
"spp_starter_social_registry",
21+
"spp_farmer_registry",
22+
"spp_farmer_registry_vocabularies",
23+
"spp_land_record",
24+
"spp_irrigation",
25+
"spp_gis",
26+
"spp_programs",
27+
)
28+
29+
def test_module_is_installed(self):
30+
module = self.env["ir.module.module"].search([("name", "=", "spp_starter_farmer_registry")], limit=1)
31+
self.assertTrue(module, "spp_starter_farmer_registry not registered")
32+
self.assertEqual(
33+
module.state,
34+
"installed",
35+
f"spp_starter_farmer_registry expected 'installed', got {module.state}",
36+
)
37+
38+
def test_bundle_dependencies_installed(self):
39+
"""Every module in ``depends`` is itself installed."""
40+
Module = self.env["ir.module.module"]
41+
for name in self.BUNDLE_DEPS:
42+
with self.subTest(dep=name):
43+
module = Module.search([("name", "=", name)], limit=1)
44+
self.assertTrue(module, f"Bundle dep {name!r} not registered")
45+
self.assertEqual(
46+
module.state,
47+
"installed",
48+
f"Bundle dep {name!r} expected 'installed', got {module.state}",
49+
)
50+
51+
def test_smallholder_threshold_param_loaded(self):
52+
"""data/config_parameters.xml declares the smallholder threshold."""
53+
param = self.env.ref(
54+
"spp_starter_farmer_registry.config_smallholder_threshold",
55+
raise_if_not_found=False,
56+
)
57+
self.assertTrue(
58+
param,
59+
"config_smallholder_threshold missing — config_parameters.xml didn't load",
60+
)

0 commit comments

Comments
 (0)