Skip to content

[ADD] stock_ux: invariant battery for the stock test suites - #1012

Open
jcadhoc wants to merge 1 commit into
ingadhoc:19.0from
adhoc-dev:19.0-t-72661-jc-tests
Open

[ADD] stock_ux: invariant battery for the stock test suites#1012
jcadhoc wants to merge 1 commit into
ingadhoc:19.0from
adhoc-dev:19.0-t-72661-jc-tests

Conversation

@jcadhoc

@jcadhoc jcadhoc commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Qué hace

Agrega una batería de invariantes para las suites de tests de stock, en stock_ux/tests/. No cambia comportamiento de producto: es infraestructura de testing.

Una invariante es una propiedad que tiene que valer después de cualquier operación, no lo que un escenario puntual fue a buscar. El caso que lo motiva: un test que verifica que un número está bien no ve lo que quedó roto al lado — una línea de más, un estado intermedio, un movimiento que nadie pidió. Los bugs que llegan como incidentes casi nunca son "el número está mal", son "el número está bien, pero al lado quedó algo roto".

Por qué en stock_ux

Es la raíz de la cadena Adhoc de stock (depende solo de core). Los módulos que lo declaran en depends heredan la batería sin copiarla. Copias por módulo se pudren: alguien relaja una para que su test pase y nadie lo ve.

Invariantes

Invariante Qué verifica
assert_sin_moves_extra El picking no gana movimientos que nadie pidió
assert_cantidad_no_supera_demanda Con el tipo de operación bloqueando adicionales, ninguna cantidad transferida supera la demanda inicial
assert_sin_disponible_negativo Ninguna ubicación interna del origen queda con disponible negativo
assert_lineas_con_descripcion Toda línea de operación tiene descripción (si no, sale en blanco en el remito)
assert_estados_consistentes Un picking validado deja todos sus moves en hecho o cancelado, y ninguno hecho en cantidad cero
assert_sin_pickings_contraflujo El documento no genera pickings en sentido contrario al suyo

La última está parametrizada (codigos_esperados): la forma se comparte, el conjunto lo declara cada módulo consumidor — una venta declara ("outgoing", "internal"); una compra, ("incoming", "internal"). Sin ese parámetro marcaría como bug toda recepción legítima en el flujo de compras.

assert_bateria corre la batería sobre un picking; assert_bateria_documento sobre el conjunto de pickings que generó un documento, que es el punto de entrada para los módulos cuyo documento genera más de un picking.

Decisiones de diseño

  • Sin interruptores para saltear invariantes. Un escenario que legítimamente viola una declara la excepción en el test, a la vista y con el motivo al lado. Una batería con condicionales adentro se vuelve el lugar donde se entierran los casos que nadie verifica.
  • Cada invariante se prueba en los dos sentidos: que no moleste a una operación sana, y que detecte la operación defectuosa que existe para atajar. Sin la segunda no hay evidencia de que la invariante mire algo.
  • Las tres categorías de datos, separadas a propósito: entorno (de la base), configuración del escenario (la crea el test — los tipos de operación con sus banderas, que es lo que decide qué se prueba) y documentos (siempre los crea el test). Ningún assert se apoya en registros que el test no haya creado.
  • La batería concentra riesgo y se asume: si tiene un error, no falla un test, quedan todas las suites en verde sin verificar nada. Por eso los tests en los dos sentidos son parte del entregable, no un extra.

Test plan

Base Odoo 19 limpia con sale_stock_ux instalado.

odoo -d <base> -u stock_ux --test-tags /stock_ux --stop-after-init
EXIT=0
odoo.tests.stats:  stock_ux: 22 tests 9.61s 3060 queries
odoo.tests.result: 0 failed, 0 error(s) of 18 tests

Sin warnings de addons (runbot los trata como error).

Rojo demostrado. Neutralizando la condición de assert_sin_pickings_contraflujo (if any(not move.origin_returned_move_id ...)if False):

EXIT=1
FAIL: TestInvariants.test_bateria_documento_recorre_el_conjunto_y_cada_picking
FAIL: TestInvariants.test_sin_pickings_contraflujo_detecta_la_contraentrega
2 failed, 0 error(s) of 16 tests

Fallan exactamente los dos tests que dicen detectarla, y ninguno de los otros. Código restaurado y verde de nuevo.

Qué sigue

Las suites que consumen esta batería van en PRs aparte, por módulo. El primero es sale_stock_ux, que además suma dos invariantes propias (las que leen campos que ese módulo define).

Task: https://www.adhoc.inc/odoo/project.task/72661

Add a battery of invariants that must hold after any stock operation, so a
test verifies not only what its scenario went looking for, but also that
nothing broke next to it.

The battery lives in stock_ux because it is the root of the Adhoc stock
dependency chain: the modules that declare it in depends inherit it without
copying it, and a shared battery cannot be quietly relaxed the way per-module
copies can.

Invariants:
- assert_sin_moves_extra: the picking gains no move nobody asked for.
- assert_cantidad_no_supera_demanda: no quantity above the initial demand when
  the operation type blocks additionals.
- assert_sin_disponible_negativo: no internal source location left with
  negative availability.
- assert_lineas_con_descripcion: every operation line carries a description.
- assert_estados_consistentes: a validated picking leaves every move done or
  cancelled, and none done at zero quantity.
- assert_sin_pickings_contraflujo: the document generates no picking in the
  opposite direction to its own. Parametrized by the codes each consuming
  module declares, so a sale declares outgoing/internal and a purchase
  incoming/internal instead of hardcoding one flow.

Each invariant is tested in both directions: that it stays quiet on a sane
operation, and that it detects the defective one it exists to catch. Without
the second direction there is no evidence the invariant looks at anything.

Change note: se agrega la batería de invariantes de stock_ux con sus tests en
los dos sentidos. No cambia comportamiento de producto: es infraestructura de
testing que heredan los módulos que dependen de stock_ux.
Copilot AI lite review requested due to automatic review settings August 26, 2026 19:16
@roboadhoc

Copy link
Copy Markdown
Collaborator

Pull request status dashboard

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Este PR agrega infraestructura de testing para stock_ux: una batería reusable de invariantes de stock (mixin) y una suite de tests que valida cada invariante en “verde” y “rojo”, para que las suites consumidoras puedan detectar estados inconsistentes luego de operaciones.

Cambios:

  • Incorpora el mixin StockUxInvariants con invariantes aplicables a pickings y a conjuntos de pickings por documento.
  • Agrega TestInvariants para probar cada invariante en ambos sentidos (no molesta / detecta).
  • Suma un StockUxCommon para construir escenario controlado (tipos de operación propios, helpers) e integra los tests en tests/__init__.py.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
stock_ux/tests/invariants.py Define el mixin con invariantes y runners (assert_bateria, assert_bateria_documento).
stock_ux/tests/test_invariants.py Suite de tests que valida las invariantes en “verde” y “rojo”.
stock_ux/tests/common.py Base común de escenario y helpers para tests (picking types propios, creación de pickings/stock).
stock_ux/tests/init.py Registra nuevos módulos de tests/helpers.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +53 to +55
real = {}
for move in vivos:
real[move.product_id] = real.get(move.product_id, 0.0) + move.product_uom_qty
Comment on lines +168 to +169
if any(not move.origin_returned_move_id for move in picking.move_ids):
contraflujo |= picking
Comment on lines +195 to +197
self.assert_sin_pickings_contraflujo(pickings, codigos_esperados)
for picking in pickings:
self.assert_bateria(picking)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants