Skip to content

Commit 99ab606

Browse files
committed
test: add tests/ folders to 5 modules missing CI coverage (#1040)
Five OpenSPP2 modules shipped without a tests/ folder, so CI's detect-changes coverage matrix had no flags to upload against them. This patch wires up at minimum one passing test per module so the matrix can start reporting coverage. - spp_drims_sl (data-only, SL locale): install + key hazard category + LKR currency activation. - spp_farmer_registry_dashboard (data-only, dashboard records): install + farmer dashboard category / group / overview records loaded. - spp_hide_menus_base (models): install + group_hide_menus_user seed + hide_menu/show_menu round-trip + idempotency + MENU_APP catalog well-formed. - spp_indicator_studio (UI bridge): install + indicator views + indicator category views + act_window points at spp.indicator. - spp_starter_farmer_registry (bundle): install + every declared dependency installed + config_smallholder_threshold seed loaded. Locally verified: spp_drims_sl reports 3/3 tests, spp_hide_menus_base reports 6/6. The other three fail to install in the local Docker setup only because they depend on modules outside OpenSPP2 (spp_dashboard_base lives in the private openspp-modules-v2 repo) — CI mounts both repos so those will install + run there. Out of scope per the ticket: - spp_registry — handled on a separate branch. - theme_openspp_muk — theme module, no Python logic to cover.
1 parent 5900a2e commit 99ab606

10 files changed

Lines changed: 309 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_spp_farmer_registry_dashboard
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
"""Install / data-load sanity tests for spp_farmer_registry_dashboard.
3+
4+
This is a data-only module — it ships dashboard metric definitions and
5+
spreadsheet dashboards but no Python models or methods. The tests exercise
6+
the install path so CI's per-module coverage matrix records something
7+
against it, and assert that the headline dashboard data records loaded.
8+
"""
9+
10+
from odoo.tests import TransactionCase, tagged
11+
12+
13+
@tagged("post_install", "-at_install")
14+
class TestSppFarmerRegistryDashboard(TransactionCase):
15+
"""Spot-check that the seed data declared in __manifest__.py loaded."""
16+
17+
def test_module_is_installed(self):
18+
module = self.env["ir.module.module"].search([("name", "=", "spp_farmer_registry_dashboard")], limit=1)
19+
self.assertTrue(module, "spp_farmer_registry_dashboard not registered")
20+
self.assertEqual(
21+
module.state,
22+
"installed",
23+
f"spp_farmer_registry_dashboard expected 'installed', got {module.state}",
24+
)
25+
26+
def test_dashboard_metric_category_seed_loaded(self):
27+
"""data/dashboard_metrics.xml declares the farmer dashboard category."""
28+
category = self.env.ref(
29+
"spp_farmer_registry_dashboard.category_dashboard_farmer",
30+
raise_if_not_found=False,
31+
)
32+
self.assertTrue(
33+
category,
34+
"category_dashboard_farmer missing — dashboard_metrics.xml didn't load",
35+
)
36+
37+
def test_dashboard_group_seed_loaded(self):
38+
"""data/dashboards.xml declares the spreadsheet dashboard group."""
39+
group = self.env.ref(
40+
"spp_farmer_registry_dashboard.spreadsheet_dashboard_group_farmer",
41+
raise_if_not_found=False,
42+
)
43+
self.assertTrue(
44+
group,
45+
"spreadsheet_dashboard_group_farmer missing — dashboards.xml didn't load",
46+
)
47+
48+
def test_dashboard_record_seed_loaded(self):
49+
"""data/dashboards.xml declares the farmer overview dashboard."""
50+
dashboard = self.env.ref(
51+
"spp_farmer_registry_dashboard.dashboard_farmer_overview",
52+
raise_if_not_found=False,
53+
)
54+
self.assertTrue(
55+
dashboard,
56+
"dashboard_farmer_overview missing — dashboards.xml didn't load",
57+
)
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: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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+
)
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)