@_nazmiforreal/flutter-ota 0.1.2 → 0.1.4
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.
- package/dart-src/packages/cli-tools/.dart_tool/package_config.json +496 -0
- package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +743 -0
- package/dart-src/packages/cli-tools/lib/src/cli_base.dart +7 -6
- package/dart-src/packages/cli-tools/lib/src/commands/build.dart +46 -49
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +44 -23
- package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +27 -17
- package/dart-src/packages/cli-tools/lib/src/commands/console.dart +11 -6
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +39 -22
- package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +22 -13
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +117 -36
- package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +15 -9
- package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +14 -8
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +37 -12
- package/dart-src/packages/cli-tools/lib/src/config.dart +13 -3
- package/dart-src/packages/cli-tools/lib/src/operations.dart +45 -0
- package/dart-src/packages/cli-tools/lib/src/pack.dart +9 -0
- package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +234 -0
- package/dart-src/packages/cli-tools/pubspec.lock +638 -0
- package/dart-src/packages/cli-tools/test/backends_test.dart +207 -0
- package/dart-src/packages/cli-tools/test/build_pack_test.dart +101 -0
- package/dart-src/packages/cli-tools/test/cli_test.dart +442 -0
- package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +83 -0
- package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +256 -0
- package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +347 -0
- package/dart-src/packages/cli-tools/test/standalone_backend_test.dart +315 -0
- package/dart-src/packages/core/test/rollout_test.dart +128 -0
- package/dart-src/packages/core/test/semver_test.dart +52 -0
- package/dart-src/packages/core/test/semver_vectors.json +1 -0
- package/dart-src/packages/core/test/types_test.dart +129 -0
- package/dart-src/packages/core/test/uuid_test.dart +38 -0
- package/dart-src/plugins/aws/test/aws_database_test.dart +167 -0
- package/dart-src/plugins/aws/test/aws_lambda_edge_storage_test.dart +190 -0
- package/dart-src/plugins/aws/test/aws_storage_test.dart +73 -0
- package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +83 -0
- package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +122 -0
- package/dart-src/plugins/cloudflare/test/d1_bundle_mapper_test.dart +106 -0
- package/dart-src/plugins/cloudflare/test/d1_database_test.dart +190 -0
- package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +256 -0
- package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +83 -0
- package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +77 -0
- package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +166 -0
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_store_test.dart +74 -0
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +332 -0
- package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +711 -0
- package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +294 -0
- package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +192 -0
- package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +51 -0
- package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +1600 -0
- package/dart-src/plugins/plugin-core/test/helpers_test.dart +259 -0
- package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +139 -0
- package/dart-src/plugins/plugin-core/test/request_update_bundle_state_test.dart +86 -0
- package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +77 -0
- package/dart-src/plugins/postgres/test/mock_postgres_client.dart +297 -0
- package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +140 -0
- package/dart-src/plugins/postgres/test/postgres_database_test.dart +181 -0
- package/dart-src/plugins/standalone/test/standalone_test.dart +264 -0
- package/dart-src/plugins/supabase/test/mock_supabase_client.dart +654 -0
- package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +112 -0
- package/dart-src/plugins/supabase/test/supabase_database_test.dart +207 -0
- package/dart-src/plugins/supabase/test/supabase_storage_test.dart +175 -0
- package/package.json +1 -1
- package/scripts/vendor.sh +29 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import 'dart:convert' show utf8;
|
|
2
|
+
|
|
3
|
+
import 'package:flutter_patcher_plugin_core/flutter_patcher_plugin_core.dart'
|
|
4
|
+
show StorageObject;
|
|
5
|
+
|
|
6
|
+
import 'package:flutter_patcher_cloudflare/flutter_patcher_cloudflare.dart'
|
|
7
|
+
show R2S3StorageConfig, R2S3ClientLike;
|
|
8
|
+
|
|
9
|
+
/// In-memory store backing [MockR2Client].
|
|
10
|
+
class Store {
|
|
11
|
+
final Map<String, List<int>> objects = {};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/// Mock R2 S3 client that stores objects in memory.
|
|
15
|
+
class MockR2Client implements R2S3ClientLike {
|
|
16
|
+
MockR2Client(this.store, {this.bucketName = 'bucket'});
|
|
17
|
+
|
|
18
|
+
final Store store;
|
|
19
|
+
final String bucketName;
|
|
20
|
+
|
|
21
|
+
@override
|
|
22
|
+
Future<void> deleteObject(String key) async => store.objects.remove(key);
|
|
23
|
+
|
|
24
|
+
@override
|
|
25
|
+
Future<void> deleteObjects(List<String> keys) async {
|
|
26
|
+
for (final key in keys) {
|
|
27
|
+
store.objects.remove(key);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@override
|
|
32
|
+
Future<bool> headObject(String key) async =>
|
|
33
|
+
store.objects.containsKey(key);
|
|
34
|
+
|
|
35
|
+
@override
|
|
36
|
+
Future<List<int>> getObjectAsBytes(String key) async {
|
|
37
|
+
final bytes = store.objects[key];
|
|
38
|
+
if (bytes == null) throw Exception('NoSuchKey: $key');
|
|
39
|
+
return bytes;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@override
|
|
43
|
+
Future<String> getObjectAsString(String key) async =>
|
|
44
|
+
utf8.decode(await getObjectAsBytes(key));
|
|
45
|
+
|
|
46
|
+
@override
|
|
47
|
+
Future<String> getSignedUrl(String key, {int expiresIn = 3600}) async =>
|
|
48
|
+
'https://$bucketName.r2.cloudflarestorage.com/$bucketName/$key'
|
|
49
|
+
'?X-Amz-Signature=mock&X-Amz-Expires=$expiresIn';
|
|
50
|
+
|
|
51
|
+
@override
|
|
52
|
+
Future<List<StorageObject>> listObjects([String? prefix]) async {
|
|
53
|
+
final p = prefix ?? '';
|
|
54
|
+
return store.objects.keys
|
|
55
|
+
.where((k) => k.startsWith(p))
|
|
56
|
+
.map(
|
|
57
|
+
(k) => StorageObject(
|
|
58
|
+
key: k,
|
|
59
|
+
storageUri: 'r2://$bucketName/$k',
|
|
60
|
+
size: store.objects[k]!.length,
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
.toList();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@override
|
|
67
|
+
Future<void> putObject(
|
|
68
|
+
String key,
|
|
69
|
+
List<int> body,
|
|
70
|
+
String contentType,
|
|
71
|
+
) async {
|
|
72
|
+
store.objects[key] = body;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// Build an [R2S3StorageConfig] wired to an in-memory [Store].
|
|
77
|
+
R2S3StorageConfig mockR2Config(Store store) => R2S3StorageConfig(
|
|
78
|
+
accountId: 'test-account',
|
|
79
|
+
bucketName: 'test-bucket',
|
|
80
|
+
accessKeyId: 'test-access-key',
|
|
81
|
+
secretAccessKey: 'test-secret',
|
|
82
|
+
clientFactory: (_) => MockR2Client(store),
|
|
83
|
+
);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import 'dart:io' show Directory, File;
|
|
2
|
+
|
|
3
|
+
import 'package:flutter_patcher_cloudflare/flutter_patcher_cloudflare.dart'
|
|
4
|
+
show r2Storage;
|
|
5
|
+
import 'package:flutter_patcher_plugin_core/flutter_patcher_plugin_core.dart'
|
|
6
|
+
show StoragePlugin;
|
|
7
|
+
import 'package:path/path.dart' show basename;
|
|
8
|
+
import 'package:test/test.dart';
|
|
9
|
+
|
|
10
|
+
import 'mock_r2_client.dart' show Store, mockR2Config;
|
|
11
|
+
|
|
12
|
+
void main() {
|
|
13
|
+
group('cloudflare r2Storage', () {
|
|
14
|
+
late Store store;
|
|
15
|
+
late StoragePlugin plugin;
|
|
16
|
+
late String tmpFile;
|
|
17
|
+
late String outFile;
|
|
18
|
+
|
|
19
|
+
setUp(() {
|
|
20
|
+
store = Store();
|
|
21
|
+
plugin = r2Storage(mockR2Config(store));
|
|
22
|
+
final dir = Directory.systemTemp.createTempSync('fp_r2_').path;
|
|
23
|
+
tmpFile = '$dir/bundle.zip';
|
|
24
|
+
File(tmpFile).writeAsBytesSync([1, 2, 3, 4]);
|
|
25
|
+
outFile = '$dir/out.zip';
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('plugin metadata', () {
|
|
29
|
+
expect(plugin.name, 'r2Storage');
|
|
30
|
+
expect(plugin.supportedProtocol, 'r2');
|
|
31
|
+
expect(plugin.profiles.hasNode, isTrue);
|
|
32
|
+
expect(plugin.profiles.hasRuntime, isTrue);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('node upload + exists + downloadFile + delete', () async {
|
|
36
|
+
final node = plugin.profiles.node!;
|
|
37
|
+
final res = await node.upload('v1', tmpFile);
|
|
38
|
+
expect(
|
|
39
|
+
res['storageUri'],
|
|
40
|
+
'r2://test-bucket/v1/${basename(tmpFile)}',
|
|
41
|
+
);
|
|
42
|
+
expect(await node.exists(res['storageUri']!), isTrue);
|
|
43
|
+
|
|
44
|
+
await node.downloadFile(res['storageUri']!, outFile);
|
|
45
|
+
expect(File(outFile).readAsBytesSync(), [1, 2, 3, 4]);
|
|
46
|
+
|
|
47
|
+
await node.delete(res['storageUri']!);
|
|
48
|
+
expect(await node.exists(res['storageUri']!), isFalse);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('runtime getDownloadUrl + readText (and missing → null)', () async {
|
|
52
|
+
final node = plugin.profiles.node!;
|
|
53
|
+
final runtime = plugin.profiles.runtime!;
|
|
54
|
+
|
|
55
|
+
final manifest = '${Directory.systemTemp.path}/fp_manifest.json';
|
|
56
|
+
File(manifest).writeAsStringSync('{"hello":1}');
|
|
57
|
+
final uploaded = await node.upload('manifests', manifest);
|
|
58
|
+
|
|
59
|
+
final url = await runtime.getDownloadUrl(uploaded['storageUri']!);
|
|
60
|
+
expect(url['fileUrl'], contains('manifests/'));
|
|
61
|
+
|
|
62
|
+
final text = await runtime.readText(uploaded['storageUri']!);
|
|
63
|
+
expect(text, '{"hello":1}');
|
|
64
|
+
|
|
65
|
+
expect(await runtime.readText('r2://test-bucket/missing'), isNull);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('listObjects filters by prefix', () async {
|
|
69
|
+
final node = plugin.profiles.node!;
|
|
70
|
+
await node.upload('a', tmpFile);
|
|
71
|
+
await node.upload('b', tmpFile);
|
|
72
|
+
|
|
73
|
+
expect(await node.listObjects(), hasLength(2));
|
|
74
|
+
expect(await node.listObjects('a/'), hasLength(1));
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import 'package:flutter_patcher_plugin_core/src/asset_storage_layout.dart';
|
|
2
|
+
import 'package:flutter_patcher_plugin_core/src/bundle_storage_layout.dart';
|
|
3
|
+
import 'package:flutter_patcher_plugin_core/src/legacy_asset_storage_layout.dart';
|
|
4
|
+
import 'package:test/test.dart';
|
|
5
|
+
|
|
6
|
+
void main() {
|
|
7
|
+
group('assetStorageLayout', () {
|
|
8
|
+
group('getAssetStorageLayout', () {
|
|
9
|
+
test('classifies /assets roots as content-addressed storage', () {
|
|
10
|
+
expect(
|
|
11
|
+
getAssetStorageLayout('s3://bucket/assets'),
|
|
12
|
+
'content-addressed',
|
|
13
|
+
);
|
|
14
|
+
expect(
|
|
15
|
+
getAssetStorageLayout('s3://bucket/releases/assets/'),
|
|
16
|
+
'content-addressed',
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('classifies non-/assets roots as legacy per-bundle file storage',
|
|
21
|
+
() {
|
|
22
|
+
expect(
|
|
23
|
+
getAssetStorageLayout('s3://bucket/releases/bundle-id/files'),
|
|
24
|
+
'legacy-files',
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
group('getManifestAssetStoragePath', () {
|
|
30
|
+
test('resolves content-addressed manifest assets by file hash', () {
|
|
31
|
+
expect(
|
|
32
|
+
getManifestAssetStoragePath(
|
|
33
|
+
assetBaseStorageUri: 's3://bucket/assets',
|
|
34
|
+
assetPath: 'index.ios.bundle.br',
|
|
35
|
+
fileHash: 'abcdef',
|
|
36
|
+
),
|
|
37
|
+
'sha256/ab/abcdef.br',
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('resolves legacy manifest assets by manifest-relative path', () {
|
|
42
|
+
expect(
|
|
43
|
+
getManifestAssetStoragePath(
|
|
44
|
+
assetBaseStorageUri: 's3://bucket/releases/bundle-id/files',
|
|
45
|
+
assetPath: 'assets/logo.png',
|
|
46
|
+
fileHash: 'abcdef',
|
|
47
|
+
),
|
|
48
|
+
'assets/logo.png',
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
group('createStorageUriWithRelativePath', () {
|
|
54
|
+
test('creates escaped child storage uris', () {
|
|
55
|
+
expect(
|
|
56
|
+
createStorageUriWithRelativePath(
|
|
57
|
+
baseStorageUri: 's3://bucket/releases/assets/',
|
|
58
|
+
relativePath: 'assets/icon one.png',
|
|
59
|
+
),
|
|
60
|
+
's3://bucket/releases/assets/assets/icon%20one.png',
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
group('resolveManifestAssetStorageUri', () {
|
|
66
|
+
test('resolves manifest asset storage uris through the layout', () {
|
|
67
|
+
expect(
|
|
68
|
+
resolveManifestAssetStorageUri(
|
|
69
|
+
assetBaseStorageUri: 's3://bucket/assets',
|
|
70
|
+
assetPath: 'assets/logo.png',
|
|
71
|
+
fileHash: 'abcdef',
|
|
72
|
+
),
|
|
73
|
+
's3://bucket/assets/sha256/ab/abcdef.png',
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
group('isBrotliManifestAssetPath', () {
|
|
79
|
+
test('matches index.*.bundle paths', () {
|
|
80
|
+
expect(isBrotliManifestAssetPath('index.ios.bundle'), isTrue);
|
|
81
|
+
expect(isBrotliManifestAssetPath('nested/index.android.bundle'),
|
|
82
|
+
isTrue);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('rejects non-index bundle paths', () {
|
|
86
|
+
expect(isBrotliManifestAssetPath('main.ios.bundle'), isFalse);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
group('getManifestAssetDownloadPath', () {
|
|
91
|
+
test('appends .br to index bundle paths', () {
|
|
92
|
+
expect(
|
|
93
|
+
getManifestAssetDownloadPath('index.ios.bundle'),
|
|
94
|
+
'index.ios.bundle.br',
|
|
95
|
+
);
|
|
96
|
+
expect(
|
|
97
|
+
getManifestAssetDownloadPath('nested/index.android.bundle'),
|
|
98
|
+
'nested/index.android.bundle.br',
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('passes through non-index bundle paths', () {
|
|
103
|
+
expect(
|
|
104
|
+
getManifestAssetDownloadPath('main.ios.bundle'),
|
|
105
|
+
'main.ios.bundle',
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
group('bundleStorageLayout', () {
|
|
112
|
+
group('createBundleStorageKey', () {
|
|
113
|
+
test('stores new bundle artifacts below the bundles namespace', () {
|
|
114
|
+
expect(createBundleStorageKey('bundle-id'), 'bundles/bundle-id');
|
|
115
|
+
expect(
|
|
116
|
+
createBundleStorageKey('bundle-id', ['patches', 'base-id']),
|
|
117
|
+
'bundles/bundle-id/patches/base-id',
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
group('createStorageRootUriWithPath', () {
|
|
123
|
+
test('derives the shared storage root from bundle URIs', () {
|
|
124
|
+
expect(
|
|
125
|
+
createStorageRootUriWithPath(
|
|
126
|
+
's3://bucket/releases/bundles/bundle-id/manifest.json',
|
|
127
|
+
'bundle-id',
|
|
128
|
+
'assets',
|
|
129
|
+
),
|
|
130
|
+
's3://bucket/releases/assets',
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('rejects storage URIs that do not contain the bundle id', () {
|
|
135
|
+
expect(
|
|
136
|
+
() => createStorageRootUriWithPath(
|
|
137
|
+
'https://uploads.example.com/object',
|
|
138
|
+
'bundle-id',
|
|
139
|
+
'assets',
|
|
140
|
+
),
|
|
141
|
+
throwsA(isA<StateError>()),
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('preserves encoded storage root segments', () {
|
|
146
|
+
expect(
|
|
147
|
+
createStorageRootUriWithPath(
|
|
148
|
+
's3://bucket/release%20files/bundles/bundle-id/manifest.json',
|
|
149
|
+
'bundle-id',
|
|
150
|
+
'assets/sha256',
|
|
151
|
+
),
|
|
152
|
+
's3://bucket/release%20files/assets/sha256',
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
group('legacyAssetStorageLayout', () {
|
|
159
|
+
test('getLegacyManifestAssetStoragePath returns assetPath unchanged', () {
|
|
160
|
+
expect(
|
|
161
|
+
getLegacyManifestAssetStoragePath(assetPath: 'assets/logo.png'),
|
|
162
|
+
'assets/logo.png',
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import 'package:flutter_patcher_plugin_core/src/bundle_unit_of_work.dart';
|
|
2
|
+
import 'package:flutter_patcher_plugin_core/src/bundle_unit_of_work_store.dart';
|
|
3
|
+
import 'package:test/test.dart';
|
|
4
|
+
|
|
5
|
+
void main() {
|
|
6
|
+
setUp(() {
|
|
7
|
+
clearUnitOfWorkStore();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
group('isUnitOfWorkContext', () {
|
|
11
|
+
test('returns true for Map<String, Object?>', () {
|
|
12
|
+
expect(isUnitOfWorkContext(<String, Object?>{}), isTrue);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('returns false for null', () {
|
|
16
|
+
expect(isUnitOfWorkContext(null), isFalse);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('returns false for non-map', () {
|
|
20
|
+
expect(isUnitOfWorkContext('string'), isFalse);
|
|
21
|
+
expect(isUnitOfWorkContext(42), isFalse);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('returns false for wrong map type', () {
|
|
25
|
+
// Dart generics are covariant, so Map<String, int> IS Map<String, Object?>
|
|
26
|
+
// Only key-type mismatches fail the check
|
|
27
|
+
expect(isUnitOfWorkContext(<int, Object?>{}), isFalse);
|
|
28
|
+
expect(isUnitOfWorkContext(<int, String>{}), isFalse);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
group('getRequestBundleUnitOfWork', () {
|
|
33
|
+
test('returns null for null context', () {
|
|
34
|
+
expect(getRequestBundleUnitOfWork(null), isNull);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('returns null for invalid context', () {
|
|
38
|
+
expect(getRequestBundleUnitOfWork('not a map'), isNull);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('creates new UoW for valid context', () {
|
|
42
|
+
final context = <String, Object?>{};
|
|
43
|
+
final uow1 = getRequestBundleUnitOfWork(context);
|
|
44
|
+
expect(uow1, isA<BundleUnitOfWork>());
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('returns same UoW for same context (identity)', () {
|
|
48
|
+
final context = <String, Object?>{};
|
|
49
|
+
final uow1 = getRequestBundleUnitOfWork(context);
|
|
50
|
+
final uow2 = getRequestBundleUnitOfWork(context);
|
|
51
|
+
expect(identical(uow1, uow2), isTrue);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('returns different UoW for different contexts', () {
|
|
55
|
+
final ctx1 = <String, Object?>{};
|
|
56
|
+
final ctx2 = <String, Object?>{};
|
|
57
|
+
final uow1 = getRequestBundleUnitOfWork(ctx1);
|
|
58
|
+
final uow2 = getRequestBundleUnitOfWork(ctx2);
|
|
59
|
+
expect(identical(uow1, uow2), isFalse);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
group('clearUnitOfWorkStore', () {
|
|
64
|
+
test('removes all entries', () {
|
|
65
|
+
final ctx = <String, Object?>{};
|
|
66
|
+
final uow = getRequestBundleUnitOfWork(ctx);
|
|
67
|
+
expect(uow, isNotNull);
|
|
68
|
+
clearUnitOfWorkStore();
|
|
69
|
+
// After clearing, a new UoW should be created for the same context
|
|
70
|
+
final uow2 = getRequestBundleUnitOfWork(ctx);
|
|
71
|
+
expect(identical(uow, uow2), isFalse);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|