|
@@ -204,3 +204,90 @@ class TestExtractFilamentDataFromMqtt:
|
|
|
@pytest.mark.parametrize("data", [None, {}, {"ams": "weird-string"}])
|
|
@pytest.mark.parametrize("data", [None, {}, {"ams": "weird-string"}])
|
|
|
def test_garbage_top_level_is_empty(self, data):
|
|
def test_garbage_top_level_is_empty(self, data):
|
|
|
assert _extract_filament_data_from_mqtt(data or {}) == {}
|
|
assert _extract_filament_data_from_mqtt(data or {}) == {}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class TestOnPrintStartCallbackShape:
|
|
|
|
|
+ """Regression: the callback wrapper shape the bambu_mqtt service
|
|
|
|
|
+ actually hands to ``on_print_start`` at runtime (#1533 follow-up).
|
|
|
|
|
+
|
|
|
|
|
+ The original #1533 fix only handled the bare ``{"ams": {"ams": [...]}}``
|
|
|
|
|
+ inner shape, but the call site at ``backend/app/main.py::on_print_start``
|
|
|
|
|
+ receives the wrapper
|
|
|
|
|
+ ``{"filename", "subtask_name", "remaining_time", "raw_data": <mqtt>,
|
|
|
|
|
+ "ams_mapping"}`` from ``backend/app/services/bambu_mqtt.py:2971-2980``.
|
|
|
|
|
+ The lookup at ``data["ams"]`` therefore missed every real print and
|
|
|
|
|
+ fallback archives kept their filament fields NULL — the exact regression
|
|
|
|
|
+ the fix was supposed to close. Reproduced from JmanB52D's support
|
|
|
|
|
+ bundle whose print start log line showed
|
|
|
|
|
+ ``AMS 0: T0(type=PETG, color=FFFFFFFF, …)`` was sitting right there at
|
|
|
|
|
+ ``data["raw_data"]["ams"]["ams"][0]["tray"][0]``.
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
|
|
+ def test_callback_wrapper_payload_resolves_raw_data_path(self):
|
|
|
|
|
+ """The wrapper-shape payload must produce the same result the
|
|
|
|
|
+ inner-shape payload would."""
|
|
|
|
|
+ inner = {
|
|
|
|
|
+ "ams": {
|
|
|
|
|
+ "ams": [
|
|
|
|
|
+ _ams_unit(0, [_tray(0, "PETG", "FFFFFFFF")]),
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+ wrapper = {
|
|
|
|
|
+ "filename": "/data/Metadata/plate_1.gcode",
|
|
|
|
|
+ "subtask_name": "xyz-10mm-calibration-cube",
|
|
|
|
|
+ "remaining_time": 1200,
|
|
|
|
|
+ "raw_data": inner,
|
|
|
|
|
+ "ams_mapping": [0],
|
|
|
|
|
+ }
|
|
|
|
|
+ result = _extract_filament_data_from_mqtt(wrapper, ams_mapping=[0])
|
|
|
|
|
+ # The 6-char rgba `FFFFFF` is what the AMS reports (with `FF` alpha
|
|
|
|
|
+ # tail trimmed by the catalog) — the helper preserves whatever the
|
|
|
|
|
+ # firmware sends.
|
|
|
|
|
+ assert result == {"filament_type": "PETG", "filament_color": "FFFFFFFF"}
|
|
|
|
|
+
|
|
|
|
|
+ def test_wrapper_with_no_ams_mapping_falls_back_to_all_loaded(self):
|
|
|
|
|
+ """Wrapper shape without an ams_mapping behaves the same as the
|
|
|
|
|
+ inner-shape no-mapping path: lists every loaded slot."""
|
|
|
|
|
+ inner = {
|
|
|
|
|
+ "ams": {
|
|
|
|
|
+ "ams": [
|
|
|
|
|
+ _ams_unit(0, [_tray(0, "PLA", "FF0000"), _tray(1, "PETG", "00FF00")]),
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+ wrapper = {"raw_data": inner}
|
|
|
|
|
+ result = _extract_filament_data_from_mqtt(wrapper)
|
|
|
|
|
+ assert result == {"filament_type": "PLA,PETG", "filament_color": "FF0000,00FF00"}
|
|
|
|
|
+
|
|
|
|
|
+ def test_inner_shape_still_supported_after_wrapper_lookup(self):
|
|
|
|
|
+ """Existing callers that pass the inner shape directly (e.g. the
|
|
|
|
|
+ unit tests above) must keep working — the new lookup is additive."""
|
|
|
|
|
+ inner = {
|
|
|
|
|
+ "ams": {
|
|
|
|
|
+ "ams": [_ams_unit(0, [_tray(0, "ASA", "112233")])],
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+ assert _extract_filament_data_from_mqtt(inner) == {
|
|
|
|
|
+ "filament_type": "ASA",
|
|
|
|
|
+ "filament_color": "112233",
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ def test_wrapper_with_missing_raw_data_returns_empty(self):
|
|
|
|
|
+ """No raw_data wrapper AND no top-level ams → empty, no raise."""
|
|
|
|
|
+ wrapper = {"filename": "foo.gcode", "ams_mapping": [0]}
|
|
|
|
|
+ assert _extract_filament_data_from_mqtt(wrapper, ams_mapping=[0]) == {}
|
|
|
|
|
+
|
|
|
|
|
+ def test_wrapper_with_non_dict_raw_data_falls_through_to_inner_lookup(self):
|
|
|
|
|
+ """Defensive: a junk raw_data value (string / None) shouldn't crash
|
|
|
|
|
+ and shouldn't shadow a present inner ``ams`` either. Lets us catch
|
|
|
|
|
+ the case where MQTT decoding partially fails but the rest of the
|
|
|
|
|
+ payload is fine."""
|
|
|
|
|
+ wrapper = {
|
|
|
|
|
+ "raw_data": "garbage",
|
|
|
|
|
+ "ams": {"ams": [_ams_unit(0, [_tray(0, "PLA", "FF0000")])]},
|
|
|
|
|
+ }
|
|
|
|
|
+ assert _extract_filament_data_from_mqtt(wrapper) == {
|
|
|
|
|
+ "filament_type": "PLA",
|
|
|
|
|
+ "filament_color": "FF0000",
|
|
|
|
|
+ }
|