feat(#89): verify broker enable/disable actually applied before confirming
A firmware build can ACK a broker enable/disable as success without applying it (HV4; meshcore-firmware#179). The quick toggle and the editor save previously trusted the ACK and showed a success snackbar, so an ack-but-no-op looked like it worked. Both paths now SET, wait a short settle, re-read the slot, and report what the device ACTUALLY did: - applied -> confirm - notApplied -> warn "possible firmware issue"; the editor stays and re-seeds to the device's true state instead of popping a false success - unverified -> re-read failed (e.g. an enable that rebooted the device) Every device is treated identically: the UI reflects the re-read, never assuming a change took that the device didn't confirm. - BrokerConfig.classifyApply pure classifier + BrokerApplyOutcome (unit-tested) - ObserverConfigService.applySettleDelay - wired into mqtt_brokers_screen quick toggle + broker_editor_screen save - widget test for the ACK-but-not-applied path Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>pull/99/head
parent
3015b41f55
commit
4d7921d2f7
@ -0,0 +1,61 @@
|
||||
// Honest-apply classifier (#89). A firmware build can ACK a broker
|
||||
// enable/disable as success but not apply it (HV4 desync,
|
||||
// meshcore-firmware#179). After a toggle/save the UI re-reads the slot and
|
||||
// compares to what it asked for; this classifier turns (intended, re-read) into
|
||||
// the outcome the UI reports. It treats every device identically — it only ever
|
||||
// reflects the re-read, never assuming the change took.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:meshcore_open/models/observer_config.dart';
|
||||
|
||||
void main() {
|
||||
group('BrokerConfig.classifyApply', () {
|
||||
test('enable the device confirms -> applied', () {
|
||||
expect(
|
||||
BrokerConfig.classifyApply(intendedEnabled: true, actualEnabled: true),
|
||||
BrokerApplyOutcome.applied,
|
||||
);
|
||||
});
|
||||
|
||||
test('disable the device confirms -> applied', () {
|
||||
expect(
|
||||
BrokerConfig.classifyApply(
|
||||
intendedEnabled: false,
|
||||
actualEnabled: false,
|
||||
),
|
||||
BrokerApplyOutcome.applied,
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'disable ACKed but re-read still enabled -> notApplied (the HV4 bug)',
|
||||
() {
|
||||
expect(
|
||||
BrokerConfig.classifyApply(
|
||||
intendedEnabled: false,
|
||||
actualEnabled: true,
|
||||
),
|
||||
BrokerApplyOutcome.notApplied,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test('enable ACKed but re-read still disabled -> notApplied', () {
|
||||
expect(
|
||||
BrokerConfig.classifyApply(intendedEnabled: true, actualEnabled: false),
|
||||
BrokerApplyOutcome.notApplied,
|
||||
);
|
||||
});
|
||||
|
||||
test('re-read failed (null) -> unverified, whatever the intent', () {
|
||||
expect(
|
||||
BrokerConfig.classifyApply(intendedEnabled: true, actualEnabled: null),
|
||||
BrokerApplyOutcome.unverified,
|
||||
);
|
||||
expect(
|
||||
BrokerConfig.classifyApply(intendedEnabled: false, actualEnabled: null),
|
||||
BrokerApplyOutcome.unverified,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
Reference in new issue