Skip to content

CI broken on main: ~67 tests fail since the linopy <0.7 → <0.8 bump #685

Description

@FBumann

Summary

main's Tests workflow has been failing since at least 2026-04-20 (run 24668209730). The breakage was masked because dependabot PRs don't trigger the workflow — the regression presumably entered via PR #681, which widened the linopy pin from <0.7 to <0.8. The four merged PRs since then (#681#683) are all dependabot bumps that bypass CI.

Found while validating PR #684; reproduced by stashing #684's changes and running the same tests on bare main with the lockfile-resolved linopy 0.5.8.

Failures by category

1. tests/deprecated/test_{component,flow,storage}.py — ~52 tests

Snapshot tests in tests/deprecated/conftest.py::assert_conequal compare a flixopt-built constraint against a hand-built one. Under newer linopy these now disagree on dim order of the LHS data:

L   coeffs    (period, scenario, time, _term) float64 ...
R   coeffs    (time,   period, scenario, _term) float64 ...

Same values, different dim order — linopy.testing.assert_linequal treats this as inequality. Likely fallout of a linopy 0.6/0.7 internal change to default dim ordering of LinearExpression.data. Fix is either (a) assert_linequal should be order-insensitive, (b) the test fixtures need to be rebuilt in the new order, or (c) the deprecated tests should be retired.

2. tests/test_clustering/test_cluster_reduce_expand.py — 12 tests

TestInterclusterStorageLinking::test_* + TestStorageClusterModes::test_storage_cluster_mode_intercluster_cyclic. main's last successful Tests run (2026-04-20) already showed 30 failures here, so this regressed earlier and was never fixed.

3. tests/test_math/test_clustering.py::TestClusteringExact::test_storage_cyclic_charge_discharge_pattern — 3 tests

Cyclic intercluster SOC test. Actual values are exactly expected + 50 (constant offset, cyclic constraint still binds). Looks like a clustering-mode initial-SOC reference shift rather than a solver issue.

4. tests/test_math/test_flow_invest.py::TestFlowInvest::test_invest_* — 12 tests

test_invest_optional_not_built, test_invest_not_mandatory_skips_when_uneconomical, test_invest_retirement_triggers_when_not_investing — all three assert a binary invested flag is 0 (build is uneconomical); CI gets 1.

These pass locally on PR #684 (9/9 passed in 6.97s with pytest -v --numprocesses=auto + lockfile linopy 0.5.8) but fail on all four Python versions in CI. Likely solver indeterminism in degenerate MIP cases where building has identical cost to not-building — the optimizer is free to pick either, and mip_gap=0 doesn't pin the choice.

Suggested fixes: either widen the assertions to accept ties, add a small effects_per_flow_hour to make the not-built solution strictly cheaper, or use assert_allclose on the objective rather than on the binary itself.

Reproduction

# Match CI: lockfile linopy + xdist parallelism
uv sync --all-extras --dev
pytest -v --numprocesses=auto

Suggested next steps

  • Enable CI on dependabot PRs (or at least on linopy bumps) so this doesn't happen silently again.
  • Decide the fate of tests/deprecated/: rebuild snapshots, make assertions order-insensitive, or remove.
  • Triage the intercluster-storage suite (categories 2 & 3) — likely one root cause, possibly linopy ≥ 0.6 coord changes.
  • Harden the three test_invest_* tests against MIP tie-breaking.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions