@_nazmiforreal/flutter-ota 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dart-src/packages/cli-tools/.dart_tool/package_config.json +496 -0
  2. package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +743 -0
  3. package/dart-src/packages/cli-tools/lib/src/cli_base.dart +7 -6
  4. package/dart-src/packages/cli-tools/lib/src/commands/build.dart +46 -49
  5. package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +44 -23
  6. package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +27 -17
  7. package/dart-src/packages/cli-tools/lib/src/commands/console.dart +11 -6
  8. package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +39 -22
  9. package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +22 -13
  10. package/dart-src/packages/cli-tools/lib/src/commands/init.dart +6 -5
  11. package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +15 -9
  12. package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +14 -8
  13. package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +37 -12
  14. package/dart-src/packages/cli-tools/lib/src/operations.dart +45 -0
  15. package/dart-src/packages/cli-tools/lib/src/pack.dart +9 -0
  16. package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +234 -0
  17. package/dart-src/packages/cli-tools/pubspec.lock +638 -0
  18. package/dart-src/packages/cli-tools/test/backends_test.dart +207 -0
  19. package/dart-src/packages/cli-tools/test/build_pack_test.dart +101 -0
  20. package/dart-src/packages/cli-tools/test/cli_test.dart +442 -0
  21. package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +83 -0
  22. package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +256 -0
  23. package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +347 -0
  24. package/dart-src/packages/cli-tools/test/standalone_backend_test.dart +315 -0
  25. package/dart-src/packages/core/test/rollout_test.dart +128 -0
  26. package/dart-src/packages/core/test/semver_test.dart +52 -0
  27. package/dart-src/packages/core/test/semver_vectors.json +1 -0
  28. package/dart-src/packages/core/test/types_test.dart +129 -0
  29. package/dart-src/packages/core/test/uuid_test.dart +38 -0
  30. package/dart-src/plugins/aws/test/aws_database_test.dart +167 -0
  31. package/dart-src/plugins/aws/test/aws_lambda_edge_storage_test.dart +190 -0
  32. package/dart-src/plugins/aws/test/aws_storage_test.dart +73 -0
  33. package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +83 -0
  34. package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +122 -0
  35. package/dart-src/plugins/cloudflare/test/d1_bundle_mapper_test.dart +106 -0
  36. package/dart-src/plugins/cloudflare/test/d1_database_test.dart +190 -0
  37. package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +256 -0
  38. package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +83 -0
  39. package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +77 -0
  40. package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +166 -0
  41. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_store_test.dart +74 -0
  42. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +332 -0
  43. package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +711 -0
  44. package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +294 -0
  45. package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +192 -0
  46. package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +51 -0
  47. package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +1600 -0
  48. package/dart-src/plugins/plugin-core/test/helpers_test.dart +259 -0
  49. package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +139 -0
  50. package/dart-src/plugins/plugin-core/test/request_update_bundle_state_test.dart +86 -0
  51. package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +77 -0
  52. package/dart-src/plugins/postgres/test/mock_postgres_client.dart +297 -0
  53. package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +140 -0
  54. package/dart-src/plugins/postgres/test/postgres_database_test.dart +181 -0
  55. package/dart-src/plugins/standalone/test/standalone_test.dart +264 -0
  56. package/dart-src/plugins/supabase/test/mock_supabase_client.dart +654 -0
  57. package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +112 -0
  58. package/dart-src/plugins/supabase/test/supabase_database_test.dart +207 -0
  59. package/dart-src/plugins/supabase/test/supabase_storage_test.dart +175 -0
  60. package/package.json +1 -1
  61. package/scripts/vendor.sh +29 -0
@@ -0,0 +1,332 @@
1
+ import 'package:flutter_patcher_plugin_core/src/bundle_unit_of_work.dart';
2
+ import 'package:flutter_patcher_plugin_core/src/types.dart';
3
+ import 'package:test/test.dart';
4
+
5
+ Bundle _bundle(String id, {
6
+ String channel = 'production',
7
+ Platform platform = Platform.android,
8
+ bool enabled = true,
9
+ String? targetAppVersion,
10
+ String? fingerprintHash,
11
+ }) => Bundle(
12
+ id: id,
13
+ platform: platform,
14
+ shouldForceUpdate: false,
15
+ enabled: enabled,
16
+ fileHash: 'hash_$id',
17
+ storageUri: 's3://bucket/$id.zip',
18
+ channel: channel,
19
+ targetAppVersion: targetAppVersion,
20
+ fingerprintHash: fingerprintHash,
21
+ );
22
+
23
+ void main() {
24
+ group('BundleUnitOfWork', () {
25
+ late BundleUnitOfWork uow;
26
+
27
+ setUp(() {
28
+ uow = BundleUnitOfWork();
29
+ });
30
+
31
+ group('seed', () {
32
+ test('adds bundles to entries', () {
33
+ uow.seed([_bundle('a'), _bundle('b')]);
34
+ expect(uow.peek('a'), isNotNull);
35
+ expect(uow.peek('b'), isNotNull);
36
+ });
37
+
38
+ test('ignores null entries', () {
39
+ uow.seed([_bundle('a'), null]);
40
+ expect(uow.peek('a'), isNotNull);
41
+ });
42
+
43
+ test('hasSeeds reports correctly', () {
44
+ expect(uow.hasSeeds(), isFalse);
45
+ uow.seed([_bundle('a')]);
46
+ expect(uow.hasSeeds(), isTrue);
47
+ });
48
+
49
+ test('seededBundles returns present bundles', () {
50
+ uow.seed([_bundle('a'), _bundle('b')]);
51
+ final seeded = uow.seededBundles();
52
+ expect(seeded.length, 2);
53
+ expect(seeded.map((b) => b.id).toSet(), {'a', 'b'});
54
+ });
55
+
56
+ test('seed does not override tracked changes', () {
57
+ final b1 = _bundle('a');
58
+ uow.markInsert(b1);
59
+ // Seed with different data — insert should stay
60
+ uow.seed([Bundle(
61
+ id: 'a', platform: Platform.android, shouldForceUpdate: false,
62
+ enabled: false, fileHash: 'other', storageUri: 'other',
63
+ channel: 'production',
64
+ )]);
65
+ final changed = uow.changedSets();
66
+ expect(changed.length, 1);
67
+ expect(changed.first.data.fileHash, 'hash_a'); // original insert
68
+ });
69
+ });
70
+
71
+ group('peek', () {
72
+ test('returns null for missing', () {
73
+ expect(uow.peek('missing'), isNull);
74
+ });
75
+
76
+ test('returns inserted bundle', () {
77
+ final b = _bundle('x');
78
+ uow.markInsert(b);
79
+ expect(uow.peek('x'), equals(b));
80
+ });
81
+
82
+ test('returns null after delete', () {
83
+ final b = _bundle('x');
84
+ uow.markInsert(b);
85
+ uow.markDelete(b);
86
+ expect(uow.peek('x'), isNull);
87
+ });
88
+ });
89
+
90
+ group('peekChanged', () {
91
+ test('returns not found for unchanged', () {
92
+ expect(uow.peekChanged('x'), isA<TrackedBundleNotFound>());
93
+ });
94
+
95
+ test('returns found for insert', () {
96
+ final b = _bundle('x');
97
+ uow.markInsert(b);
98
+ final result = uow.peekChanged('x');
99
+ expect(result, isA<TrackedBundleFound>());
100
+ expect((result as TrackedBundleFound).value, equals(b));
101
+ });
102
+
103
+ test('returns found with null value for delete', () {
104
+ final b = _bundle('x');
105
+ uow.markInsert(b);
106
+ uow.markDelete(b);
107
+ final result = uow.peekChanged('x');
108
+ expect(result, isA<TrackedBundleFound>());
109
+ expect((result as TrackedBundleFound).value, isNull);
110
+ });
111
+ });
112
+
113
+ group('getById (deduped async load)', () {
114
+ test('returns cached entry on second call', () {
115
+ var loadCount = 0;
116
+ uow.seed([_bundle('a')]);
117
+ return uow.getById('a', () async {
118
+ loadCount++;
119
+ return _bundle('a');
120
+ }).then((b) {
121
+ expect(b, isNotNull);
122
+ expect(loadCount, 0); // never called — seed provided it
123
+ });
124
+ });
125
+
126
+ test('loads and caches missing entry', () async {
127
+ final b = _bundle('x');
128
+ final result = await uow.getById('x', () async => b);
129
+ expect(result, equals(b));
130
+ expect(uow.peek('x'), equals(b)); // cached
131
+ });
132
+
133
+ test('caches null result as missing', () async {
134
+ final result = await uow.getById('missing', () async => null);
135
+ expect(result, isNull);
136
+ // Second call should not invoke loader
137
+ var called = false;
138
+ final result2 = await uow.getById('missing', () async {
139
+ called = true;
140
+ return null;
141
+ });
142
+ expect(result2, isNull);
143
+ expect(called, isFalse);
144
+ });
145
+
146
+ test('coalesces concurrent loads', () async {
147
+ var loadCount = 0;
148
+ final results = await Future.wait([
149
+ uow.getById('x', () async {
150
+ loadCount++;
151
+ return _bundle('x');
152
+ }),
153
+ uow.getById('x', () async {
154
+ loadCount++;
155
+ return _bundle('x');
156
+ }),
157
+ ]);
158
+ expect(results[0], isNotNull);
159
+ expect(results[1], isNotNull);
160
+ expect(loadCount, 1);
161
+ });
162
+ });
163
+
164
+ group('markInsert / markUpdate / markDelete', () {
165
+ test('markInsert tracks as insert', () {
166
+ uow.markInsert(_bundle('a'));
167
+ final sets = uow.changedSets();
168
+ expect(sets.length, 1);
169
+ expect(sets.first.operation, BundleChangeOperation.insert);
170
+ });
171
+
172
+ test('markUpdate on existing insert keeps insert', () {
173
+ final b1 = _bundle('a');
174
+ final b2 = Bundle(
175
+ id: 'a', platform: Platform.android, shouldForceUpdate: false,
176
+ enabled: false, fileHash: 'hash2', storageUri: 'uri2',
177
+ channel: 'production',
178
+ );
179
+ uow.markInsert(b1);
180
+ uow.markUpdate(b2);
181
+ final sets = uow.changedSets();
182
+ expect(sets.length, 1);
183
+ expect(sets.first.operation, BundleChangeOperation.insert);
184
+ expect(sets.first.data.fileHash, 'hash2');
185
+ });
186
+
187
+ test('markUpdate on seeded bundle tracks as update with before', () {
188
+ uow.seed([_bundle('a')]);
189
+ uow.markUpdate(_bundle('a'));
190
+ final sets = uow.changedSets();
191
+ expect(sets.length, 1);
192
+ expect(sets.first.operation, BundleChangeOperation.update);
193
+ });
194
+
195
+ test('markDelete tracks as delete', () {
196
+ uow.seed([_bundle('a')]);
197
+ uow.markDelete(_bundle('a'));
198
+ final sets = uow.changedSets();
199
+ expect(sets.length, 1);
200
+ expect(sets.first.operation, BundleChangeOperation.delete);
201
+ });
202
+ });
203
+
204
+ group('hasChanges / listFetchExtraCount', () {
205
+ test('hasChanges is false initially', () {
206
+ expect(uow.hasChanges(), isFalse);
207
+ });
208
+
209
+ test('hasChanges is true after markInsert', () {
210
+ uow.markInsert(_bundle('a'));
211
+ expect(uow.hasChanges(), isTrue);
212
+ });
213
+
214
+ test('listFetchExtraCount counts updates and deletes', () {
215
+ uow.seed([_bundle('a'), _bundle('b'), _bundle('c')]);
216
+ uow.markUpdate(_bundle('a'));
217
+ uow.markDelete(_bundle('b'));
218
+ expect(uow.listFetchExtraCount(), 2); // 1 update + 1 delete
219
+ });
220
+
221
+ test('listFetchExtraCount ignores inserts', () {
222
+ uow.markInsert(_bundle('a'));
223
+ expect(uow.listFetchExtraCount(), 0);
224
+ });
225
+ });
226
+
227
+ group('totalDelta', () {
228
+ test('returns 0 for no changes', () {
229
+ expect(uow.totalDelta(null), 0);
230
+ });
231
+
232
+ test('returns -1 for delete matching where', () {
233
+ uow.seed([_bundle('a', channel: 'beta')]);
234
+ uow.markDelete(_bundle('a'));
235
+ final delta = uow.totalDelta(
236
+ const DatabaseBundleQueryWhere(channel: 'beta'),
237
+ );
238
+ expect(delta, -1);
239
+ });
240
+
241
+ test('returns +1 for update that newly matches where', () {
242
+ uow.seed([_bundle('a', channel: 'production')]);
243
+ uow.markUpdate(_bundle('a', channel: 'beta'));
244
+ final delta = uow.totalDelta(
245
+ const DatabaseBundleQueryWhere(channel: 'beta'),
246
+ );
247
+ expect(delta, 1);
248
+ });
249
+
250
+ test('returns 0 when update stays in same where', () {
251
+ uow.seed([_bundle('a', channel: 'beta')]);
252
+ uow.markUpdate(_bundle('a', channel: 'beta'));
253
+ final delta = uow.totalDelta(
254
+ const DatabaseBundleQueryWhere(channel: 'beta'),
255
+ );
256
+ expect(delta, 0);
257
+ });
258
+ });
259
+
260
+ group('overlayList', () {
261
+ test('applies in-memory updates over DB results', () {
262
+ uow.seed([_bundle('db1'), _bundle('db2')]);
263
+ uow.markUpdate(_bundle('db1'));
264
+
265
+ final dbResults = [_bundle('db1'), _bundle('db2')];
266
+ final result = uow.overlayList(
267
+ dbResults,
268
+ limit: 20,
269
+ where: null,
270
+ orderBy: null,
271
+ );
272
+ final ids = result.map((b) => b.id).toList();
273
+ expect(ids, containsAll(['db1', 'db2']));
274
+ });
275
+
276
+ test('excludes inserts from overlay (visible only after commit)', () {
277
+ uow.seed([_bundle('db1')]);
278
+ uow.markInsert(_bundle('mem1'));
279
+
280
+ final dbResults = [_bundle('db1')];
281
+ final result = uow.overlayList(
282
+ dbResults,
283
+ limit: 20,
284
+ where: null,
285
+ orderBy: null,
286
+ );
287
+ final ids = result.map((b) => b.id).toList();
288
+ expect(ids, contains('db1'));
289
+ expect(ids, isNot(contains('mem1')));
290
+ });
291
+
292
+ test('respects limit', () {
293
+ for (var i = 0; i < 10; i++) {
294
+ uow.seed([_bundle('b$i')]);
295
+ }
296
+ final dbResults = List.generate(10, (i) => _bundle('b$i'));
297
+ final result = uow.overlayList(
298
+ dbResults,
299
+ limit: 3,
300
+ where: null,
301
+ orderBy: null,
302
+ );
303
+ expect(result.length, 3);
304
+ });
305
+
306
+ test('deletes are excluded', () {
307
+ uow.seed([_bundle('a'), _bundle('b')]);
308
+ uow.markDelete(_bundle('a'));
309
+ final dbResults = [_bundle('a'), _bundle('b')];
310
+ final result = uow.overlayList(
311
+ dbResults,
312
+ limit: 20,
313
+ where: null,
314
+ orderBy: null,
315
+ );
316
+ expect(result.map((b) => b.id).toList(), ['b']);
317
+ });
318
+ });
319
+
320
+ group('clear', () {
321
+ test('resets all state', () {
322
+ uow.seed([_bundle('a')]);
323
+ uow.markInsert(_bundle('b'));
324
+ uow.clear();
325
+ expect(uow.hasChanges(), isFalse);
326
+ expect(uow.hasSeeds(), isFalse);
327
+ expect(uow.peek('a'), isNull);
328
+ expect(uow.peek('b'), isNull);
329
+ });
330
+ });
331
+ });
332
+ }