1import 'package:blood_pressure_app/model/export_import/column.dart';
  2import 'package:blood_pressure_app/model/export_import/import_field_type.dart';
  3import 'package:blood_pressure_app/model/export_import/record_formatter.dart';
  4import 'package:flutter/material.dart';
  5import 'package:flutter_test/flutter_test.dart';
  6import 'package:health_data_store/health_data_store.dart';
  7
  8import '../../features/measurement_list/measurement_list_entry_test.dart';
  9import '../../util.dart';
 10import 'record_formatter_test.dart';
 11
 12void main() {
 13  group('NativeColumn', () {
 14    test('should contain fields in allColumns', () {
 15      expect(NativeColumn.allColumns, containsAll([
 16        NativeColumn.timestampUnixMs,
 17        NativeColumn.systolic,
 18        NativeColumn.diastolic,
 19        NativeColumn.pulse,
 20        NativeColumn.notes,
 21        NativeColumn.color,
 22        NativeColumn.needlePin,
 23      ]),);
 24    });
 25    test('should have internalIdentifier prefixed with "native."', () {
 26      for (final c in NativeColumn.allColumns) {
 27        expect(c.internalIdentifier, startsWith('native.'));
 28      }
 29    });
 30    test('should encode into non-empty string', () {
 31      // Use BuildInColumn for utility columns
 32      for (final c in NativeColumn.allColumns) {
 33        final r = _filledRecord(true);
 34        expect(c.encode(r.$1, r.$2, r.$3, Weight.kg(123)), isNotEmpty, reason: '${c.internalIdentifier} is NativeColumn');
 35      }
 36    });
 37    test('should only contain restoreable types', () {
 38      // Use BuildInColumn for utility columns
 39      for (final c in NativeColumn.allColumns) {
 40        expect(c.restoreAbleType, isNotNull);
 41      }
 42    });
 43    test('should decode correctly', () {
 44      final r = _filledRecord(true);
 45      for (final c in NativeColumn.allColumns) {
 46        final txt = c.encode(r.$1, r.$2, r.$3, Weight.kg(123));
 47        final decoded = c.decode(txt);
 48        expect(decoded, isNotNull, reason: 'a real value was encoded: ${c.internalIdentifier}: ${r.debugToString()} > $txt');
 49        switch (decoded!.$1) {
 50          case RowDataFieldType.timestamp:
 51            expect(decoded.$2, isA<DateTime>().having(
 52              (p0) => p0.millisecondsSinceEpoch, 'milliseconds', r.$1.time.millisecondsSinceEpoch,),);
 53            break;
 54        case RowDataFieldType.sys:
 55          expect(decoded.$2, isA<int>()
 56            .having((p0) => p0, 'systolic', r.$1.sys?.mmHg));
 57        case RowDataFieldType.dia:
 58          expect(decoded.$2, isA<int>()
 59            .having((p0) => p0, 'diastolic', r.$1.dia?.mmHg));
 60        case RowDataFieldType.pul:
 61          expect(decoded.$2, isA<int>()
 62            .having((p0) => p0, 'pulse', r.$1.pul));
 63        case RowDataFieldType.notes:
 64          expect(decoded.$2, isA<String>()
 65            .having((p0) => p0, 'note', r.$2.note));
 66        case RowDataFieldType.color:
 67          expect(decoded.$2, isA<int>()
 68            .having((p0) => p0, 'pin', r.$2.color));
 69        case RowDataFieldType.intakes:
 70          expect(decoded.$2, isA<List>()
 71            .having((p0) => p0.length, 'length', 1,)
 72            .having((p0) => p0[0].$1, 'designation', 'mockMed',)
 73            .having((p0) => p0[0].$2, 'dosis', 123.4,));
 74        case RowDataFieldType.weightKg:
 75          expect(decoded.$2, isA<double>()
 76            .having((p0) => p0, 'weight', 123.0));
 77        }
 78      }
 79    });
 80  });
 81
 82  group('BuildInColumn', () {
 83    test('should contain fields in allColumns', () {
 84      expect(BuildInColumn.allColumns, containsAll([
 85        BuildInColumn.pulsePressure,
 86        BuildInColumn.mhDate,
 87        BuildInColumn.mhSys,
 88        BuildInColumn.mhDia,
 89        BuildInColumn.mhPul,
 90        BuildInColumn.mhDesc,
 91        BuildInColumn.mhTags,
 92        BuildInColumn.mhWeight,
 93        BuildInColumn.mhOxygen,
 94      ]),);
 95    });
 96    test('should have internalIdentifier prefixed with "buildin."', () {
 97      for (final c in BuildInColumn.allColumns) {
 98        expect(c.internalIdentifier, startsWith('buildin.'));
 99      }
100    });
101    test('should encode without problems', () {
102      for (final c in BuildInColumn.allColumns) {
103        final r = _filledRecord();
104        expect(c.encode(r.$1, r.$2, r.$3, null), isNotNull);
105      }
106    });
107    test('should decode correctly', () {
108      final r = _filledRecord(true);
109      for (final c in BuildInColumn.allColumns) {
110        final txt = c.encode(r.$1, r.$2, r.$3, Weight.kg(123.45));
111        final decoded = c.decode(txt);
112        switch (decoded?.$1) {
113          case RowDataFieldType.timestamp:
114            if (c is TimeColumn) {
115              // This ensures no columns with useless conversions get introduced.
116              expect(decoded?.$2, isA<DateTime>().having(
117                  (p0) => p0.difference(r.$1.time).inDays,
118                  'inaccuracy',
119                  lessThan(1),),);
120            } else {
121              expect(decoded?.$2, isA<DateTime>().having(
122                  (p0) => p0.millisecondsSinceEpoch, 'milliseconds', r.$1.time.millisecondsSinceEpoch,),);
123            }
124          case RowDataFieldType.sys:
125            expect(decoded?.$2, isA<int>()
126              .having((p0) => p0, 'systolic', r.$1.sys?.mmHg));
127          case RowDataFieldType.dia:
128            expect(decoded?.$2, isA<int>()
129              .having((p0) => p0, 'diastolic', r.$1.dia?.mmHg));
130          case RowDataFieldType.pul:
131            expect(decoded?.$2, isA<int>()
132              .having((p0) => p0, 'pulse', r.$1.pul));
133          case RowDataFieldType.notes:
134            expect(decoded?.$2, isA<String>()
135              .having((p0) => p0, 'note', r.$2.note));
136          case RowDataFieldType.color:
137            expect(decoded?.$2, isA<int>()
138              .having((p0) => p0, 'pin', r.$2.color));
139          case RowDataFieldType.intakes:
140            expect(decoded?.$2, isA<List<(String, double)>>()
141              .having((p0) => p0.length, 'length', 1,)
142              .having((p0) => p0[0].$1, 'designation', 'mockMed',)
143              .having((p0) => p0[0].$2, 'dosis', 123.4,));
144          case RowDataFieldType.weightKg:
145            expect(decoded?.$2, isA<double>()
146              .having((p0) => p0, 'weight', 123.45));
147          case null:
148          // no-op
149        }
150      }
151    });
152  });
153
154  group('UserColumn', () {
155    test('should have internalIdentifier prefixed with "userColumn."', () {
156      final column = UserColumn('test', 'csvTitle', 'pattern');
157      expect(column.internalIdentifier, startsWith('userColumn.'));
158    });
159    test('should encode like ScriptedFormatter', () {
160      final r = _filledRecord();
161      expect(
162        UserColumn('','', 'TEST').encode(r.$1, r.$2, r.$3, null),
163        ScriptedFormatter('TEST').encode(r.$1, r.$2, r.$3, null),
164      );
165      expect(
166        UserColumn('','', r'$SYS').encode(r.$1, r.$2, r.$3, null),
167        ScriptedFormatter(r'$SYS').encode(r.$1, r.$2, r.$3, null),
168      );
169      expect(
170        UserColumn('','', r'$SYS-$DIA').encode(r.$1, r.$2, r.$3, null),
171        ScriptedFormatter(r'$SYS-$DIA').encode(r.$1, r.$2, r.$3, null),
172      );
173      expect(
174        UserColumn('','', r'$TIMESTAMP').encode(r.$1, r.$2, r.$3, null),
175        ScriptedFormatter(r'$TIMESTAMP').encode(r.$1, r.$2, r.$3, null),
176      );
177      expect(
178        UserColumn('','', '').encode(r.$1, r.$2, r.$3, null),
179        ScriptedFormatter('').encode(r.$1, r.$2, r.$3, null),
180      );
181    });
182    test('should decode like ScriptedFormatter', () {
183      final r = _filledRecord();
184      final testPatterns = ['TEST', r'$SYS', r'{{$SYS-$DIA}}', r'$TIMESTAMP', ''];
185
186      for (final pattern in testPatterns) {
187        final column = UserColumn('','', pattern);
188        final formatter = ScriptedFormatter(pattern);
189        expect(
190          column.decode(column.encode(r.$1, r.$2, r.$3, null)),
191          formatter.decode(formatter.encode(r.$1, r.$2, r.$3, null)),
192        );
193      }
194    });
195  });
196
197  group('TimeColumn', () {
198    test('should have internalIdentifier prefixed with "timeFormatter."', () {
199      final column = TimeColumn('csvTitle', 'formatPattern');
200      expect(column.internalIdentifier, startsWith('timeFormatter.'));
201    });
202  });
203}
204
205FullEntry _filledRecord([bool addIntakes = false]) => mockEntry(
206  sys: 123,
207  dia: 456,
208  pul: 789,
209  note: 'test',
210  pin: Colors.pink,
211  intake: addIntakes
212    ? mockIntake(mockMedicine(designation: 'mockMed'), dosis: 123.4,)
213    : null,
214);