Files
bambuddy/backend/tests/unit/test_run_filament_helper.py
T
maziggy fc32b388de fix(stats): align Filament Used / By Time / Success Rate with Total Consumed and Total Prints (#1390 follow-up)
Three independent root causes behind the divergences the reporter
  flagged after the archived-spool fix shipped — fixed together.

  (1) Filament Used vs Total Consumed.
  _compute_run_filament_grams returned the slicer estimate for completed
  prints even when inventory had measured the actual AMS weight delta.
  That made Stats and Inventory two different sources of truth: Stats
  showed slicer-estimate grams, Inventory showed AMS-tracked grams, and
  the two never agreed. Reordered the helper so the tracked spool delta
  (same source that drives weight_used behind Total Consumed) takes
  priority for every status. Slicer estimate stays as the fallback when
  no inventory was tracked; partial-progress scale stays as the fallback
  for failed/cancelled with no tracker. The _run_cost block right next
  to it was already tracker-first; only filament_used_grams was
  inconsistent.

  (2) Printer Stats By Time vs Quick Stats Print Time.
  /archives/slim only set actual_time_seconds when status == "completed".
  For failed/cancelled rows the frontend fell back to print_time_seconds
  (the slicer's full-print estimate — wrong number for a print that
  failed at 15%). Quick Stats already summed elapsed duration across
  all statuses, so the two halves of the page disagreed by the
  (estimate - actual-elapsed) gap on every non-completed event. Dropped
  the completed-only gate; failed/cancelled now report measured elapsed.

  (3) Success Rate %.
  Was successful / (successful + failed), excluding cancelled / stopped
  from the denominator. With "Total Prints: N" displayed right above
  the gauge that produced confusing numbers — 4 successful, 0 failed,
  48 cancelled showed 100% out of an apparent 52 prints. Switched to
  successful / total_prints — matches the count the user reads from
  the widget header.
2026-05-19 10:53:36 +02:00

72 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Unit tests for the per-run filament helper (#1378, #1390).
The helper computes what value to write into PrintLogEntry.filament_used_grams
for a given print event — partial-aware so failed / cancelled / stopped prints
don't inflate stats with the full slicer estimate, and tracker-aware so
completed prints agree with the per-spool counter on the Inventory page.
"""
from backend.app.main import _compute_run_filament_grams
class TestComputeRunFilamentGrams:
def test_completed_no_tracker_returns_archive_estimate(self):
# Completed print without inventory tracking: the slicer estimate is
# the canonical "this print used X" value.
assert _compute_run_filament_grams("completed", 100.0, 100, []) == 100.0
def test_completed_prefers_tracked_over_estimate(self):
# #1390: when inventory tracked the AMS weight delta, Stats should
# reflect that — same source that drives "Total Consumed" on the
# Inventory page. Two halves of the app must show the same number.
assert _compute_run_filament_grams("completed", 100.0, 100, [{"weight_used": 96.5}]) == 96.5
def test_failed_uses_tracked_spool_delta(self):
# Failed reprint at 10g actual: inventory tracked the spool delta.
# The estimate was 100g; we want 10g recorded for stats.
assert _compute_run_filament_grams("failed", 100.0, 10, [{"weight_used": 10.0}]) == 10.0
def test_cancelled_uses_tracked_spool_delta(self):
# Same logic for cancelled.
assert _compute_run_filament_grams("cancelled", 100.0, 12, [{"weight_used": 8.5}]) == 8.5
def test_stopped_uses_tracked_spool_delta(self):
assert _compute_run_filament_grams("stopped", 100.0, 15, [{"weight_used": 12.0}]) == 12.0
def test_failed_with_no_tracked_falls_back_to_progress_scale(self):
# No inventory tracking: scale estimate by progress% (10% of 100g = 10g).
assert _compute_run_filament_grams("failed", 100.0, 10, []) == 10.0
def test_failed_with_no_tracked_and_no_progress_returns_none(self):
# Nothing to infer from — return None rather than guess the estimate.
assert _compute_run_filament_grams("failed", 100.0, 0, []) is None
def test_failed_with_partial_progress_rounds_correctly(self):
# 100g × 33% = 33.0g (rounded to 1 decimal)
assert _compute_run_filament_grams("failed", 100.0, 33, []) == 33.0
def test_failed_with_no_estimate_returns_none(self):
# No estimate, no tracked usage → can't compute anything.
assert _compute_run_filament_grams("failed", None, 50, []) is None
def test_failed_with_no_estimate_but_tracked_uses_tracked(self):
# Tracked spool delta is authoritative even without an estimate.
assert _compute_run_filament_grams("failed", None, 50, [{"weight_used": 5.0}]) == 5.0
def test_tracked_overrides_progress_scale_when_both_available(self):
# If inventory says 8g but progress says 15g, trust inventory (it's measured).
assert _compute_run_filament_grams("failed", 100.0, 15, [{"weight_used": 8.0}]) == 8.0
def test_progress_above_100_clamps_to_full_estimate(self):
# Defensive: progress overshoot doesn't multiply past the estimate.
assert _compute_run_filament_grams("failed", 100.0, 150, []) == 100.0
def test_multiple_tracked_slots_summed(self):
# Multi-filament print, two slots tracked.
usage = [{"weight_used": 5.0}, {"weight_used": 3.5}, {"weight_used": 1.0}]
assert _compute_run_filament_grams("failed", 100.0, 20, usage) == 9.5
def test_completed_with_none_estimate_returns_none(self):
# Archive somehow has no estimate (rare; archive_print parsed nothing).
assert _compute_run_filament_grams("completed", None, 100, []) is None