feat: add trainer scan protocol models

This commit is contained in:
2026-04-28 21:28:40 +02:00
parent 76b7195e5e
commit 7628947623
2 changed files with 284 additions and 0 deletions

View File

@ -108,6 +108,126 @@ void main() {
});
});
group('TrainerScanEvent.fromBytes', () {
test('parses scan lifecycle events', () {
expect(
TrainerScanEvent.fromBytes(const [1, 0, 7]).kind,
TrainerScanEventKind.scanStarted,
);
expect(
TrainerScanEvent.fromBytes(const [1, 2, 8]).kind,
TrainerScanEventKind.scanFinished,
);
expect(
TrainerScanEvent.fromBytes(const [1, 3, 9]).kind,
TrainerScanEventKind.scanCancelled,
);
});
test('parses device event with signed RSSI and flags', () {
final event = TrainerScanEvent.fromBytes([
1,
1,
42,
0xc1,
1,
2,
3,
4,
5,
6,
0xd6,
trainerScanDeviceFlagFtmsDetected |
trainerScanDeviceFlagNameComplete |
trainerScanDeviceFlagConnectable,
5,
...'Kickr'.codeUnits,
]);
expect(event.kind, TrainerScanEventKind.device);
expect(event.sequence, 42);
expect(event.result, isNotNull);
expect(event.result!.address.flags, 0xc1);
expect(event.result!.address.bytes, [1, 2, 3, 4, 5, 6]);
expect(event.result!.rssi, -42);
expect(event.result!.name, 'Kickr');
expect(event.result!.ftmsDetected, isTrue);
expect(event.result!.nameComplete, isTrue);
expect(event.result!.scanResponseSeen, isFalse);
expect(event.result!.connectable, isTrue);
});
test('rejects invalid scan payloads', () {
expect(
() => TrainerScanEvent.fromBytes(const []),
throwsFormatException,
);
expect(
() => TrainerScanEvent.fromBytes(const [2, 0, 1]),
throwsFormatException,
);
expect(
() => TrainerScanEvent.fromBytes(const [1, 9, 1]),
throwsFormatException,
);
expect(
() => TrainerScanEvent.fromBytes(const [1, 1, 1]),
throwsFormatException,
);
expect(
() => TrainerScanEvent.fromBytes(const [
1,
1,
1,
0,
1,
2,
3,
4,
5,
6,
0,
0,
4,
65,
]),
throwsFormatException,
);
});
});
group('encodeTrainerAddress', () {
test('encodes flags and address bytes', () {
expect(
encodeTrainerAddress(
const TrainerAddress(flags: 0xc1, bytes: [1, 2, 3, 4, 5, 6]),
),
[0xc1, 1, 2, 3, 4, 5, 6],
);
});
test('rejects invalid address values', () {
expect(
() => encodeTrainerAddress(
const TrainerAddress(flags: 0, bytes: [1, 2, 3, 4, 5]),
),
throwsFormatException,
);
expect(
() => encodeTrainerAddress(
const TrainerAddress(flags: 256, bytes: [1, 2, 3, 4, 5, 6]),
),
throwsFormatException,
);
expect(
() => encodeTrainerAddress(
const TrainerAddress(flags: 0, bytes: [1, 2, 3, 4, 5, 256]),
),
throwsFormatException,
);
});
});
group('standard GATT telemetry parsing', () {
test('decodes battery level percentage', () {
expect(parseBatteryLevelPercent([0]), 0);