From 7552f2241587c7d8e825864f1212ac5df0a22689 Mon Sep 17 00:00:00 2001 From: bosd <5e2fd43-d292-4c90-9d1f-74ff3436329a@anonaddy.me> Date: Fri, 17 Jul 2026 10:25:47 +0200 Subject: [PATCH 1/2] fix: create-missing-variants search on Odoo 19 + e2e registry reload Two fixes that make the nightly e2e integrity workflow green again (it was failing on Odoo 16/17/19; 18 passed). Both surfaced only through the single tests/e2e/test_variant_workflow scenario. - variant_manager: filter templates on the ``product_variant_ids`` relation instead of ``product_variant_count``. The count is a non-stored computed field and Odoo 19 rejects it in a search domain ("Cannot convert ... to SQL because it is not stored"); the one2many is a real relational field, so "no variants" is expressible across all versions. Fixes create-missing-variants on Odoo 19 for real users, not just the test. Unit-test assertion updated to match. - e2e harness: restart the Odoo service after the ``product_module`` fixture installs ``product`` mid-session. The long-running server caches target_db's (base-only) registry the first time it serves it, and a separate ``-i product --stop-after-init`` install does not reliably make it reload (Object product.template doesn't exist on Odoo 16/17). A restart clears the in-memory registry cache so the next request rebuilds it with product. Verified: full ``-m "not large"`` suite passes on fresh Odoo 16, 17, 18 and 19 (19/19 each). --- src/fluvo/lib/actions/variant_manager.py | 10 ++++++++-- tests/e2e/_runtime.py | 16 ++++++++++++++++ tests/e2e/conftest.py | 6 ++++++ tests/test_variant_manager.py | 2 +- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/fluvo/lib/actions/variant_manager.py b/src/fluvo/lib/actions/variant_manager.py index 9f0fdee2..b24692d4 100644 --- a/src/fluvo/lib/actions/variant_manager.py +++ b/src/fluvo/lib/actions/variant_manager.py @@ -88,7 +88,11 @@ def run_create_missing_variants( return False search_domain: list[Any] = list(domain) if domain else [] - search_domain.append(("product_variant_count", "=", 0)) + # Filter on the ``product_variant_ids`` relation, not ``product_variant_count``: + # the count is a non-stored computed field and Odoo 19 rejects it in a search + # domain ("Cannot convert ... to SQL because it is not stored"). The one2many is + # a real relational field, so "no variants" is expressible across all versions. + search_domain.append(("product_variant_ids", "=", False)) try: orphan_ids = template_obj.search(search_domain) @@ -153,7 +157,9 @@ def check_missing_variants_after_import( template_obj.search( [ ("id", "in", db_ids[i : i + 2000]), - ("product_variant_count", "=", 0), + # See run_create_missing_variants: search the relation, not + # the non-stored product_variant_count (unsearchable on 19). + ("product_variant_ids", "=", False), ] ) ) diff --git a/tests/e2e/_runtime.py b/tests/e2e/_runtime.py index 7ec7c80b..0321c895 100644 --- a/tests/e2e/_runtime.py +++ b/tests/e2e/_runtime.py @@ -76,6 +76,22 @@ def exec_in( return _compose("exec", "-T", service, *command, timeout=timeout) +def restart_service(service: str, timeout: int = 120) -> None: + """Restart a running service container. + + Used after a mid-session module install: the long-running Odoo server caches a + database's registry the first time it serves it, and installing a module from a + separate ``--stop-after-init`` process does not reliably make the running server + reload that cached registry (observed on Odoo 16/17). A restart clears the + in-memory registry cache so the next request rebuilds it with the new module. + + Args: + service: The compose service to restart (e.g. ``odoo``). + timeout: Seconds to allow for the restart command. + """ + _compose("restart", service, timeout=timeout) + + def create_database(db_name: str, timeout: int = 600) -> None: """Initialise a fresh Odoo database with the ``base`` module, no demo data. diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index 9c9e6870..3496ec20 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -222,4 +222,10 @@ def product_module(odoo_endpoint: dict[str, object], target_db: str) -> str: if not odoo_endpoint["managed"]: pytest.skip("variant workflow test requires the managed Odoo stack") _runtime.install_module(target_db, "product") + # The long-running server has already cached target_db's (base-only) registry + # from earlier tests; a mid-session install does not reliably make it reload + # (Object product.template doesn't exist on Odoo 16/17). Restart so the next + # request rebuilds the registry with 'product', then wait for it to come back. + _runtime.restart_service("odoo") + _runtime.wait_http_ready(str(odoo_endpoint["host"]), int(odoo_endpoint["port"])) return "product" diff --git a/tests/test_variant_manager.py b/tests/test_variant_manager.py index 5f836e90..7d6ea710 100644 --- a/tests/test_variant_manager.py +++ b/tests/test_variant_manager.py @@ -61,7 +61,7 @@ def test_extra_domain_is_combined_with_the_no_variant_filter( mock_get.return_value = conn run_create_missing_variants("conn.conf", domain=[("categ_id", "=", 5)]) template.search.assert_called_once_with( - [("categ_id", "=", 5), ("product_variant_count", "=", 0)] + [("categ_id", "=", 5), ("product_variant_ids", "=", False)] ) From 2d0252f800ddb494c5988873dc15c9d2a0aea919 Mon Sep 17 00:00:00 2001 From: bosd <5e2fd43-d292-4c90-9d1f-74ff3436329a@anonaddy.me> Date: Fri, 17 Jul 2026 11:06:03 +0200 Subject: [PATCH 2/2] chore: update 2 pydoclint baseline yield-type renderings pydoclint's rendering of the 'batch' and 'odoo_endpoint' DOC404 yield types drifted (Any -> list[Any], (str, object) -> dict[str, object]) with the current resolved toolchain, so the committed baseline no longer matched and the pydoclint pre-commit hook failed. Update just those two lines to the current rendering. No source change. --- pydoclint-baseline.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pydoclint-baseline.txt b/pydoclint-baseline.txt index 852ad27c..d53f53db 100644 --- a/pydoclint-baseline.txt +++ b/pydoclint-baseline.txt @@ -256,7 +256,7 @@ src/fluvo/lib/internal/rpc_thread.py DOC203: Method `RpcThread.spawn_thread` return type(s) in docstring not consistent with the return annotation. Return annotation has 1 type(s); docstring return section has 0 type(s). -------------------- src/fluvo/lib/internal/tools.py - DOC404: Function `batch` yield type(s) in docstring not consistent with the return annotation. The yield type (the 0th arg in Generator[...]/Iterator[...]): Any; docstring "yields" section types: + DOC404: Function `batch` yield type(s) in docstring not consistent with the return annotation. The yield type (the 0th arg in Generator[...]/Iterator[...]): list[Any]; docstring "yields" section types: DOC201: Function `to_m2o` does not have a return section in docstring DOC203: Function `to_m2o` return type(s) in docstring not consistent with the return annotation. Return annotation has 1 type(s); docstring return section has 0 type(s). DOC001: Function/method `to_m2m`: Potential formatting errors in docstring. Error message: Expected a colon in 'separated by commas.'. (Note: DOC001 could trigger other unrelated violations under this function/method too. Please fix the docstring formatting first.) @@ -377,7 +377,7 @@ tests/e2e/assertions.py tests/e2e/conftest.py DOC101: Function `odoo_endpoint`: Docstring contains fewer arguments than in function signature. DOC103: Function `odoo_endpoint`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [request: pytest.FixtureRequest]. - DOC404: Function `odoo_endpoint` yield type(s) in docstring not consistent with the return annotation. The yield type (the 0th arg in Generator[...]/Iterator[...]): (str, object); docstring "yields" section types: + DOC404: Function `odoo_endpoint` yield type(s) in docstring not consistent with the return annotation. The yield type (the 0th arg in Generator[...]/Iterator[...]): dict[str, object]; docstring "yields" section types: -------------------- tests/e2e/generators.py DOC101: Function `write_csv`: Docstring contains fewer arguments than in function signature.