From 8e8d5389e86f08a0a73af6f037e87d50de614adb Mon Sep 17 00:00:00 2001 From: Hampus Lavin Date: Tue, 10 Dec 2024 08:35:06 +0100 Subject: [PATCH] fix: don't return ReadException for failed json parsing (#10) * fix: dont return ReadException for failed json parsing * test: add missing test case and reformulate cases --- .../local_storage_manager.dart | 6 +- .../local_storage_manager_test.dart | 154 ++++++++++++++++++ 2 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 test/local_storage_manager/local_storage_manager_test.dart diff --git a/lib/src/local_storage_manager/local_storage_manager.dart b/lib/src/local_storage_manager/local_storage_manager.dart index 81b0f59..ba50494 100644 --- a/lib/src/local_storage_manager/local_storage_manager.dart +++ b/lib/src/local_storage_manager/local_storage_manager.dart @@ -101,15 +101,15 @@ abstract base class LocalStorageManager { if (!file.existsSync()) return null; - dynamic json; + String fileContent; try { - json = jsonDecode(file.readAsStringSync()); + fileContent = file.readAsStringSync(); } catch (e, stackTrace) { throw ReadException(file, e, stackTrace); } try { - return fromJson(json); + return fromJson(jsonDecode(fileContent)); } catch (e, stackTrace) { throw DeserializationException(file, e, stackTrace); } diff --git a/test/local_storage_manager/local_storage_manager_test.dart b/test/local_storage_manager/local_storage_manager_test.dart new file mode 100644 index 0000000..005cdd1 --- /dev/null +++ b/test/local_storage_manager/local_storage_manager_test.dart @@ -0,0 +1,154 @@ +import 'dart:io'; +import 'package:test/test.dart'; +import 'package:path/path.dart' as p; +import 'package:cli_tools/src/local_storage_manager/local_storage_manager_exceptions.dart'; +import 'package:cli_tools/src/local_storage_manager/local_storage_manager.dart'; + +void main() { + var tempDir = Directory.systemTemp.createTempSync(); + var localStoragePath = tempDir.path; + + tearDown(() { + tempDir.listSync().forEach((file) => file.deleteSync()); + }); + + test( + 'Given an existing file ' + 'when calling removeFile ' + 'then the file is deleted successfully', () async { + var fileName = 'test.json'; + var file = File(p.join(localStoragePath, fileName)); + file.createSync(); + + await LocalStorageManager.removeFile( + fileName: fileName, + localStoragePath: localStoragePath, + ); + + expect(file.existsSync(), isFalse); + }); + + test( + 'Given a non-existing file ' + 'when calling removeFile ' + 'then no exception is thrown', () async { + var fileName = 'nonexistent.json'; + + expect( + () => LocalStorageManager.removeFile( + fileName: fileName, + localStoragePath: localStoragePath, + ), + returnsNormally, + ); + }); + + test( + 'Given a valid json object ' + 'when calling storeJsonFile ' + 'then the file is created with correct content', () async { + var fileName = 'test.json'; + var json = {'key': 'value'}; + + await LocalStorageManager.storeJsonFile( + fileName: fileName, + json: json, + localStoragePath: localStoragePath, + ); + + var file = File(p.join(localStoragePath, fileName)); + expect(file.existsSync(), isTrue); + expect(file.readAsStringSync(), '{\n "key": "value"\n}'); + }); + + test( + 'Given a json object with non compatible values ' + 'when calling storeJsonFile ' + 'then it throws SerializationException', () async { + var fileName = 'test.json'; + var invalidJson = {'key': Object()}; // Non-serializable value. + + expect( + () => LocalStorageManager.storeJsonFile( + fileName: fileName, + json: invalidJson, + localStoragePath: localStoragePath, + ), + throwsA(isA()), + ); + }); + + test( + 'Given an existing json file ' + 'when calling tryFetchAndDeserializeJsonFile ' + 'then it returns the deserialized object', () async { + var fileName = 'test.json'; + var fileContent = '{"key": "value"}'; + var file = File(p.join(localStoragePath, fileName)); + file.writeAsStringSync(fileContent); + + var result = await LocalStorageManager.tryFetchAndDeserializeJsonFile< + Map>( + fileName: fileName, + localStoragePath: localStoragePath, + fromJson: (json) => json, + ); + + expect(result, {'key': 'value'}); + }); + + test( + 'Given a missing json file ' + 'when calling tryFetchAndDeserializeJsonFile ' + 'then it returns null', () async { + var fileName = 'nonexistent.json'; + + var result = await LocalStorageManager.tryFetchAndDeserializeJsonFile< + Map>( + fileName: fileName, + localStoragePath: localStoragePath, + fromJson: (json) => json, + ); + + expect(result, isNull); + }); + + test( + 'Given a malformed json file ' + 'when calling tryFetchAndDeserializeJsonFile ' + 'then it throws DeserializationException', () async { + var fileName = 'invalid.json'; + var malformedJson = '{"key": "value"'; + var file = File(p.join(localStoragePath, fileName)); + file.writeAsStringSync(malformedJson); + + expect( + () => LocalStorageManager.tryFetchAndDeserializeJsonFile< + Map>( + fileName: fileName, + localStoragePath: localStoragePath, + fromJson: (json) => json, + ), + throwsA(isA()), + ); + }); + + test( + 'Given a corrupt file ' + 'when calling tryFetchAndDeserializeJsonFile ' + 'then it throws ReadException', () async { + var fileName = 'invalid.json'; + var file = File(p.join(localStoragePath, fileName)); + file.writeAsBytesSync([0xC3, 0x28]); + + expect( + () => LocalStorageManager.tryFetchAndDeserializeJsonFile< + Map>( + fileName: fileName, + localStoragePath: localStoragePath, + fromJson: (json) => json, + ), + throwsA(isA()), + ); + }); +}