test(#70): lock observer config model behavior (M1)
The models are the existing draft (1983ed9); Gemini flagged nothing in them.
This pins the defensive behavior they give untrusted wire data: enum fromWire
fallback, numeric tryParse fallback, secret-presence flag, SecretField
three-state, copyWith. 7 tests, all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat/64-observer-config
parent
55c2daf1f7
commit
7b450535dd
@ -0,0 +1,91 @@
|
||||
// Lock tests for the Offband config models (#64 M1).
|
||||
//
|
||||
// The models are the data structures untrusted wire fields decode into, so the
|
||||
// defensive behavior (enum fallback, numeric fallback, secret presence) is
|
||||
// pinned here. All green — M1 confirms the existing draft is contract-correct.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:meshcore_open/models/observer_config.dart';
|
||||
|
||||
void main() {
|
||||
group('enums fromWire / wire', () {
|
||||
test('BrokerTransport round-trips + rejects unknown', () {
|
||||
expect(BrokerTransport.fromWire('tcp'), BrokerTransport.tcp);
|
||||
expect(BrokerTransport.fromWire('tls'), BrokerTransport.tls);
|
||||
expect(BrokerTransport.fromWire('wss'), BrokerTransport.wss);
|
||||
expect(BrokerTransport.fromWire('bogus'), isNull);
|
||||
expect(BrokerTransport.tls.wire, 'tls');
|
||||
});
|
||||
|
||||
test('BrokerAuthType round-trips + rejects unknown', () {
|
||||
expect(BrokerAuthType.fromWire('none'), BrokerAuthType.none);
|
||||
expect(BrokerAuthType.fromWire('jwt'), BrokerAuthType.jwt);
|
||||
expect(BrokerAuthType.fromWire('nope'), isNull);
|
||||
expect(BrokerAuthType.basic.wire, 'basic');
|
||||
});
|
||||
|
||||
test('WifiStatus maps tokens, unknown -> unknown', () {
|
||||
expect(WifiStatus.fromWire('StaConnected'), WifiStatus.staConnected);
|
||||
expect(WifiStatus.fromWire('StaFailed'), WifiStatus.staFailed);
|
||||
expect(WifiStatus.fromWire('weird'), WifiStatus.unknown);
|
||||
});
|
||||
});
|
||||
|
||||
group('BrokerConfig.fromWireFields', () {
|
||||
test('maps a fully-populated slot', () {
|
||||
final b = BrokerConfig.fromWireFields(3, {
|
||||
'enabled': '1',
|
||||
'url': 'mqtt://h',
|
||||
'port': '8883',
|
||||
'transport': 'tls',
|
||||
'auth_type': 'jwt',
|
||||
'username': 'u',
|
||||
'password': '(set)',
|
||||
'topic_prefix': 'p',
|
||||
'jwt_refresh': '900',
|
||||
});
|
||||
expect(b.slot, 3);
|
||||
expect(b.enabled, isTrue);
|
||||
expect(b.url, 'mqtt://h');
|
||||
expect(b.port, 8883);
|
||||
expect(b.transport, BrokerTransport.tls);
|
||||
expect(b.authType, BrokerAuthType.jwt);
|
||||
expect(b.username, 'u');
|
||||
expect(b.passwordSet, isTrue);
|
||||
expect(b.jwtRefresh, 900);
|
||||
expect(b.isPopulated, isTrue);
|
||||
});
|
||||
|
||||
test('defaults + enum fallback on a sparse/garbage slot', () {
|
||||
final b = BrokerConfig.fromWireFields(0, {
|
||||
'transport': 'bogus',
|
||||
'auth_type': '?',
|
||||
'password': '(unset)',
|
||||
});
|
||||
expect(b.url, '');
|
||||
expect(b.port, 0);
|
||||
expect(b.transport, BrokerTransport.tcp); // unknown -> default
|
||||
expect(b.authType, BrokerAuthType.none); // unknown -> default
|
||||
expect(b.passwordSet, isFalse); // not "(set)"
|
||||
expect(b.isPopulated, isFalse); // no url
|
||||
});
|
||||
});
|
||||
|
||||
group('SecretField three-state', () {
|
||||
test('unchanged / clear / set', () {
|
||||
expect(const SecretField.unchanged(), isA<SecretUnchanged>());
|
||||
expect(const SecretField.clear(), isA<SecretClear>());
|
||||
const s = SecretField.set('x');
|
||||
expect(s, isA<SecretSet>());
|
||||
expect((s as SecretSet).value, 'x');
|
||||
});
|
||||
});
|
||||
|
||||
test('BrokerConfig.copyWith overrides selectively', () {
|
||||
const b = BrokerConfig(slot: 1, url: 'a', port: 1);
|
||||
final c = b.copyWith(url: 'b');
|
||||
expect(c.slot, 1);
|
||||
expect(c.url, 'b');
|
||||
expect(c.port, 1); // untouched
|
||||
});
|
||||
}
|
||||
Loading…
Reference in new issue