From 586ee8ee392be334aed98da45bdb00753e3d0b11 Mon Sep 17 00:00:00 2001 From: Indraneel Rajeevan <105813454+indraneel12@users.noreply.github.com> Date: Fri, 17 Oct 2025 19:13:44 +0530 Subject: [PATCH 1/9] fix(ConfigurationParser): File Extension validation logic No longer case-sensitive, as the corresponding upstream packages do support case-insensitivity. --- packages/config/lib/src/config/configuration_parser.dart | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/config/lib/src/config/configuration_parser.dart b/packages/config/lib/src/config/configuration_parser.dart index e6e478d..f6f9870 100644 --- a/packages/config/lib/src/config/configuration_parser.dart +++ b/packages/config/lib/src/config/configuration_parser.dart @@ -29,15 +29,14 @@ abstract final class ConfigurationParser { } /// Parses a configuration from a file. - static ConfigurationSource fromFile( - final String filePath, - ) { - if (filePath.endsWith('.json')) { + static ConfigurationSource fromFile(final String filePath) { + final check = filePath.toLowerCase(); + if (check.endsWith('.json')) { return fromString( _loadFile(filePath), format: ConfigEncoding.json, ); - } else if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) { + } else if (check.endsWith('.yaml') || check.endsWith('.yml')) { return fromString( _loadFile(filePath), format: ConfigEncoding.yaml, From a3c4c2568aedc504411fddb6f15a71fc5531e8d3 Mon Sep 17 00:00:00 2001 From: Indraneel Rajeevan <105813454+indraneel12@users.noreply.github.com> Date: Fri, 17 Oct 2025 20:02:28 +0530 Subject: [PATCH 2/9] test: Configuration Loading from external files Added new Test Cases to check all combinations of File Content and case-insensitive File Extension permutations. --- .../test/config/config_file_load_test.dart | 277 ++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 packages/config/test/config/config_file_load_test.dart diff --git a/packages/config/test/config/config_file_load_test.dart b/packages/config/test/config/config_file_load_test.dart new file mode 100644 index 0000000..6acde31 --- /dev/null +++ b/packages/config/test/config/config_file_load_test.dart @@ -0,0 +1,277 @@ +import 'dart:io' show File; +import 'dart:math' show pow; + +import 'package:config/config.dart' show ConfigurationParser; +import 'package:test/test.dart'; + +void main() => _runTests(); + +enum _Fact { correct, wrong } + +enum _AllowedFile { + json, + yaml; + + static const extensions = { + json: {'json'}, + yaml: {'yaml', 'yml'} + }; +} + +const _mockContent = { + _AllowedFile.json: { + _Fact.correct: '{"mock-key": "mock val"}', + _Fact.wrong: '{"mock-key": mock val}', + }, + _AllowedFile.yaml: { + _Fact.correct: 'mock-key: "mock val"', + _Fact.wrong: 'mock-key: mock val key2: value2', + }, +}; + +const _mockKey = '/mock-key'; +const _mockVal = 'mock val'; + +const _mockFilenames = { + _AllowedFile.json: { + _Fact.correct: 'mock_correct_json_file', + _Fact.wrong: 'mock_wrong_json_file', + }, + _AllowedFile.yaml: { + _Fact.correct: 'mock_correct_yaml_file', + _Fact.wrong: 'mock_wrong_yaml_file', + }, +}; + +const _anUnsupportedExtension = 'txt'; + +final _mockExtensions = { + _AllowedFile.json: [ + for (final fileExtension in _AllowedFile.extensions[_AllowedFile.json]!) + ..._generateCaseInsensitive(fileExtension) + ], + _AllowedFile.yaml: [ + for (final fileExtension in _AllowedFile.extensions[_AllowedFile.yaml]!) + ..._generateCaseInsensitive(fileExtension) + ], +}; + +final _mockDir = './test_tmp_${DateTime.now().millisecondsSinceEpoch}'; +final _aNonExistentFilename = 'xyz_${DateTime.now().millisecondsSinceEpoch}'; + +void _verifyMockFileExtensions() { + final jsonExtensionSamplesCount = _mockExtensions[_AllowedFile.json]!.length; + final yamlExtensionSamplesCount = _mockExtensions[_AllowedFile.yaml]!.length; + assert( + jsonExtensionSamplesCount == + Set.from(_mockExtensions[_AllowedFile.json]!).length, + 'JSON extension samples must be unique.', + ); + assert( + jsonExtensionSamplesCount == + _AllowedFile.extensions[_AllowedFile.json]!.fold( + 0, (final prev, final curr) => prev + pow(2, curr.length)), + 'JSON extesion samples must be mathematically sound.', + ); + assert( + yamlExtensionSamplesCount == + Set.from(_mockExtensions[_AllowedFile.yaml]!).length, + 'YAML extension samples must be unique.', + ); + assert( + yamlExtensionSamplesCount == + _AllowedFile.extensions[_AllowedFile.yaml]!.fold( + 0, (final prev, final curr) => prev + pow(2, curr.length)), + 'YAML extesion samples must be mathematically sound.', + ); + print('Unique JSON extensions for testing: $jsonExtensionSamplesCount'); + print('Unique YAML extensions for testing: $yamlExtensionSamplesCount'); +} + +String _buildFilepath( + final _AllowedFile format, + final _Fact fact, + final String extension, +) => + '$_mockDir/${_mockFilenames[format]![fact]!}.$extension'; + +void _prepareMockFiles() { + _mockFilenames.forEach((final fileFormat, final namesMap) { + for (final fact in _Fact.values) { + for (final fileExtension in _mockExtensions[fileFormat]!) { + File(_buildFilepath(fileFormat, fact, fileExtension)) + ..createSync(recursive: true, exclusive: true) + ..writeAsStringSync(_mockContent[fileFormat]![fact]!); + } + File(_buildFilepath(fileFormat, fact, _anUnsupportedExtension)) + ..createSync(recursive: true, exclusive: true) + ..writeAsStringSync(_mockContent[fileFormat]![fact]!); + } + }); +} + +void _cleanupMockFiles() => File(_mockDir).deleteSync(recursive: true); + +String? _toggleCase(final String ch) { + if (ch.length != 1) { + throw ArgumentError; + } + final uppercaseCh = ch.toUpperCase(); + final lowercaseCh = ch.toLowerCase(); + if (ch == uppercaseCh && ch == lowercaseCh) { + return null; + } + return ch == uppercaseCh ? lowercaseCh : uppercaseCh; +} + +Iterable _generateCaseInsensitive(final String input) sync* { + if (input.isEmpty) { + yield ''; + return; + } + final chOriginalCase = input[0]; + final chToggledCase = _toggleCase(chOriginalCase); + if (input.length == 1) { + yield* [chOriginalCase, if (chToggledCase != null) chToggledCase]; + return; + } + for (final recursiveSample in _generateCaseInsensitive(input.substring(1))) { + yield* [ + '$chOriginalCase$recursiveSample', + if (chToggledCase != null) '$chToggledCase$recursiveSample' + ]; + } +} + +void _runTests() { + setUpAll(() { + _verifyMockFileExtensions(); + _prepareMockFiles(); + }); + tearDownAll(() { + _cleanupMockFiles(); + }); + + group('JSON files', () { + test('with correct file content and correct extension', () { + for (final fileExtension in _mockExtensions[_AllowedFile.json]!) { + expect( + ConfigurationParser.fromFile(_buildFilepath( + _AllowedFile.json, + _Fact.correct, + fileExtension, + )).valueOrNull(_mockKey), + equals(_mockVal), + ); + } + }); + test('with correct file content but wrong extension', () { + expect( + () => ConfigurationParser.fromFile(_buildFilepath( + _AllowedFile.json, + _Fact.correct, + _anUnsupportedExtension, + )), + throwsUnsupportedError, + ); + }); + test('with wrong file content but correct extension', () { + for (final fileExtension in _mockExtensions[_AllowedFile.json]!) { + expect( + () => ConfigurationParser.fromFile(_buildFilepath( + _AllowedFile.json, + _Fact.wrong, + fileExtension, + )), + throwsFormatException, + ); + } + }); + test('with wrong file content and wrong extension', () { + expect( + () => ConfigurationParser.fromFile(_buildFilepath( + _AllowedFile.json, + _Fact.wrong, + _anUnsupportedExtension, + )), + throwsUnsupportedError, + ); + }); + }); + + group('YAML files', () { + test('with correct file content and correct extension', () { + for (final fileExtension in _mockExtensions[_AllowedFile.yaml]!) { + expect( + ConfigurationParser.fromFile(_buildFilepath( + _AllowedFile.yaml, + _Fact.correct, + fileExtension, + )).valueOrNull(_mockKey), + equals(_mockVal), + ); + } + }); + test('with correct file content but wrong extension', () { + expect( + () => ConfigurationParser.fromFile(_buildFilepath( + _AllowedFile.yaml, + _Fact.correct, + _anUnsupportedExtension, + )), + throwsUnsupportedError, + ); + }); + test('with wrong file content but correct extension', () { + for (final fileExtension in _mockExtensions[_AllowedFile.yaml]!) { + expect( + () => ConfigurationParser.fromFile(_buildFilepath( + _AllowedFile.yaml, + _Fact.wrong, + fileExtension, + )), + throwsFormatException, + ); + } + }); + test('with wrong file content and wrong extension', () { + expect( + () => ConfigurationParser.fromFile(_buildFilepath( + _AllowedFile.yaml, + _Fact.wrong, + _anUnsupportedExtension, + )), + throwsUnsupportedError, + ); + }); + }); + + group('Non-existent file', () { + test('of ".json" type', () { + expect( + () => ConfigurationParser.fromFile('$_aNonExistentFilename.json'), + throwsArgumentError, + ); + }); + test('of ".yaml" type', () { + expect( + () => ConfigurationParser.fromFile('$_aNonExistentFilename.yaml'), + throwsArgumentError, + ); + }); + test('of ".yml" type', () { + expect( + () => ConfigurationParser.fromFile('$_aNonExistentFilename.yml'), + throwsArgumentError, + ); + }); + test('of unsupported type', () { + expect( + () => ConfigurationParser.fromFile( + '$_aNonExistentFilename.$_anUnsupportedExtension', + ), + throwsUnsupportedError, + ); + }); + }); +} From ad33d064d7d80a13802908f4a703e67f760a0f2c Mon Sep 17 00:00:00 2001 From: Indraneel Rajeevan <105813454+indraneel12@users.noreply.github.com> Date: Fri, 17 Oct 2025 20:05:54 +0530 Subject: [PATCH 3/9] docs: Comprehensive documentation for `ConfigurationParser.fromFile` Improved the existing documentation by adding: - ArgumentError knowledge - UnsupportedError knowledge - FormatException knowledge - explicit mention of case-insensitive file extensions --- packages/config/lib/src/config/configuration_parser.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/config/lib/src/config/configuration_parser.dart b/packages/config/lib/src/config/configuration_parser.dart index f6f9870..a69fa9f 100644 --- a/packages/config/lib/src/config/configuration_parser.dart +++ b/packages/config/lib/src/config/configuration_parser.dart @@ -29,6 +29,14 @@ abstract final class ConfigurationParser { } /// Parses a configuration from a file. + /// + /// It is expected that the caller ensures [filePath]: + /// - exists in the File System, + /// otherwise throws [ArgumentError] + /// - extension is in {".json", ".yaml", ".yml"} (case-insensitive), + /// otherwise throws [UnsupportedError] + /// + /// Throws a [FormatException] if the file content is invalid. static ConfigurationSource fromFile(final String filePath) { final check = filePath.toLowerCase(); if (check.endsWith('.json')) { From b276d4afa99e17fd5442cbc50b56576523ba39c6 Mon Sep 17 00:00:00 2001 From: Indraneel Rajeevan <105813454+indraneel12@users.noreply.github.com> Date: Fri, 17 Oct 2025 20:46:32 +0530 Subject: [PATCH 4/9] fix: Minor typo in an `assert` message --- packages/config/test/config/config_file_load_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/config/test/config/config_file_load_test.dart b/packages/config/test/config/config_file_load_test.dart index 6acde31..8ecfeb3 100644 --- a/packages/config/test/config/config_file_load_test.dart +++ b/packages/config/test/config/config_file_load_test.dart @@ -71,7 +71,7 @@ void _verifyMockFileExtensions() { jsonExtensionSamplesCount == _AllowedFile.extensions[_AllowedFile.json]!.fold( 0, (final prev, final curr) => prev + pow(2, curr.length)), - 'JSON extesion samples must be mathematically sound.', + 'JSON extension samples must be mathematically sound.', ); assert( yamlExtensionSamplesCount == @@ -82,7 +82,7 @@ void _verifyMockFileExtensions() { yamlExtensionSamplesCount == _AllowedFile.extensions[_AllowedFile.yaml]!.fold( 0, (final prev, final curr) => prev + pow(2, curr.length)), - 'YAML extesion samples must be mathematically sound.', + 'YAML extension samples must be mathematically sound.', ); print('Unique JSON extensions for testing: $jsonExtensionSamplesCount'); print('Unique YAML extensions for testing: $yamlExtensionSamplesCount'); From a2e7722730a600845cab4ba65b99e840941b359e Mon Sep 17 00:00:00 2001 From: Indraneel Rajeevan <105813454+indraneel12@users.noreply.github.com> Date: Fri, 17 Oct 2025 20:48:30 +0530 Subject: [PATCH 5/9] style: Potential noise removed --- packages/config/test/config/config_file_load_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/config/test/config/config_file_load_test.dart b/packages/config/test/config/config_file_load_test.dart index 8ecfeb3..48fc60e 100644 --- a/packages/config/test/config/config_file_load_test.dart +++ b/packages/config/test/config/config_file_load_test.dart @@ -84,8 +84,8 @@ void _verifyMockFileExtensions() { 0, (final prev, final curr) => prev + pow(2, curr.length)), 'YAML extension samples must be mathematically sound.', ); - print('Unique JSON extensions for testing: $jsonExtensionSamplesCount'); - print('Unique YAML extensions for testing: $yamlExtensionSamplesCount'); + // print('Unique JSON extensions for testing: $jsonExtensionSamplesCount'); + // print('Unique YAML extensions for testing: $yamlExtensionSamplesCount'); } String _buildFilepath( From 0a24eec282c5ecb8f66824edf3882945263263ed Mon Sep 17 00:00:00 2001 From: Indraneel Rajeevan <105813454+indraneel12@users.noreply.github.com> Date: Fri, 17 Oct 2025 20:51:36 +0530 Subject: [PATCH 6/9] fix: Test Cases with Cross-platform compatibility For certain case-insensitive File Systems. --- packages/config/test/config/config_file_load_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/config/test/config/config_file_load_test.dart b/packages/config/test/config/config_file_load_test.dart index 48fc60e..4775130 100644 --- a/packages/config/test/config/config_file_load_test.dart +++ b/packages/config/test/config/config_file_load_test.dart @@ -100,11 +100,11 @@ void _prepareMockFiles() { for (final fact in _Fact.values) { for (final fileExtension in _mockExtensions[fileFormat]!) { File(_buildFilepath(fileFormat, fact, fileExtension)) - ..createSync(recursive: true, exclusive: true) + ..createSync(recursive: true, exclusive: false) ..writeAsStringSync(_mockContent[fileFormat]![fact]!); } File(_buildFilepath(fileFormat, fact, _anUnsupportedExtension)) - ..createSync(recursive: true, exclusive: true) + ..createSync(recursive: true, exclusive: false) ..writeAsStringSync(_mockContent[fileFormat]![fact]!); } }); From c817aab285ca290bf35a67644ac92eb820ec7b9b Mon Sep 17 00:00:00 2001 From: Indraneel Rajeevan <105813454+indraneel12@users.noreply.github.com> Date: Tue, 21 Oct 2025 06:26:44 +0530 Subject: [PATCH 7/9] refactor(ConfigurationParser): Better readability - improved a variable name - made a branch structure slightly more readable --- packages/config/lib/src/config/configuration_parser.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/config/lib/src/config/configuration_parser.dart b/packages/config/lib/src/config/configuration_parser.dart index a69fa9f..a0f4477 100644 --- a/packages/config/lib/src/config/configuration_parser.dart +++ b/packages/config/lib/src/config/configuration_parser.dart @@ -38,13 +38,15 @@ abstract final class ConfigurationParser { /// /// Throws a [FormatException] if the file content is invalid. static ConfigurationSource fromFile(final String filePath) { - final check = filePath.toLowerCase(); - if (check.endsWith('.json')) { + final lowercaseFilePath = filePath.toLowerCase(); + if (lowercaseFilePath.endsWith('.json')) { return fromString( _loadFile(filePath), format: ConfigEncoding.json, ); - } else if (check.endsWith('.yaml') || check.endsWith('.yml')) { + } + if (lowercaseFilePath.endsWith('.yaml') || + lowercaseFilePath.endsWith('.yml')) { return fromString( _loadFile(filePath), format: ConfigEncoding.yaml, From 42cec480dda6c964ae39e15c12af3147b5317f87 Mon Sep 17 00:00:00 2001 From: Indraneel Rajeevan <105813454+indraneel12@users.noreply.github.com> Date: Tue, 21 Oct 2025 06:32:25 +0530 Subject: [PATCH 8/9] refactor(config_file_load_test): Better readability Hardcoded the small output of generator-function. --- .../test/config/config_file_load_test.dart | 130 +++++++----------- 1 file changed, 46 insertions(+), 84 deletions(-) diff --git a/packages/config/test/config/config_file_load_test.dart b/packages/config/test/config/config_file_load_test.dart index 4775130..2b989d5 100644 --- a/packages/config/test/config/config_file_load_test.dart +++ b/packages/config/test/config/config_file_load_test.dart @@ -1,5 +1,4 @@ import 'dart:io' show File; -import 'dart:math' show pow; import 'package:config/config.dart' show ConfigurationParser; import 'package:test/test.dart'; @@ -8,15 +7,7 @@ void main() => _runTests(); enum _Fact { correct, wrong } -enum _AllowedFile { - json, - yaml; - - static const extensions = { - json: {'json'}, - yaml: {'yaml', 'yml'} - }; -} +enum _AllowedFile { json, yaml } const _mockContent = { _AllowedFile.json: { @@ -43,51 +34,58 @@ const _mockFilenames = { }, }; -const _anUnsupportedExtension = 'txt'; - -final _mockExtensions = { +const _mockExtensions = { _AllowedFile.json: [ - for (final fileExtension in _AllowedFile.extensions[_AllowedFile.json]!) - ..._generateCaseInsensitive(fileExtension) + 'json', + 'jsoN', + 'jsOn', + 'jsON', + 'jSon', + 'jSoN', + 'jSOn', + 'jSON', + 'Json', + 'JsoN', + 'JsOn', + 'JsON', + 'JSon', + 'JSoN', + 'JSOn', + 'JSON', ], _AllowedFile.yaml: [ - for (final fileExtension in _AllowedFile.extensions[_AllowedFile.yaml]!) - ..._generateCaseInsensitive(fileExtension) + 'yaml', + 'yamL', + 'yaMl', + 'yaML', + 'yAml', + 'yAmL', + 'yAMl', + 'yAML', + 'Yaml', + 'YamL', + 'YaMl', + 'YaML', + 'YAml', + 'YAmL', + 'YAMl', + 'YAML', + 'yml', + 'ymL', + 'yMl', + 'yML', + 'Yml', + 'YmL', + 'YMl', + 'YML', ], }; +const _anUnsupportedExtension = 'txt'; + final _mockDir = './test_tmp_${DateTime.now().millisecondsSinceEpoch}'; final _aNonExistentFilename = 'xyz_${DateTime.now().millisecondsSinceEpoch}'; -void _verifyMockFileExtensions() { - final jsonExtensionSamplesCount = _mockExtensions[_AllowedFile.json]!.length; - final yamlExtensionSamplesCount = _mockExtensions[_AllowedFile.yaml]!.length; - assert( - jsonExtensionSamplesCount == - Set.from(_mockExtensions[_AllowedFile.json]!).length, - 'JSON extension samples must be unique.', - ); - assert( - jsonExtensionSamplesCount == - _AllowedFile.extensions[_AllowedFile.json]!.fold( - 0, (final prev, final curr) => prev + pow(2, curr.length)), - 'JSON extension samples must be mathematically sound.', - ); - assert( - yamlExtensionSamplesCount == - Set.from(_mockExtensions[_AllowedFile.yaml]!).length, - 'YAML extension samples must be unique.', - ); - assert( - yamlExtensionSamplesCount == - _AllowedFile.extensions[_AllowedFile.yaml]!.fold( - 0, (final prev, final curr) => prev + pow(2, curr.length)), - 'YAML extension samples must be mathematically sound.', - ); - // print('Unique JSON extensions for testing: $jsonExtensionSamplesCount'); - // print('Unique YAML extensions for testing: $yamlExtensionSamplesCount'); -} - String _buildFilepath( final _AllowedFile format, final _Fact fact, @@ -112,45 +110,9 @@ void _prepareMockFiles() { void _cleanupMockFiles() => File(_mockDir).deleteSync(recursive: true); -String? _toggleCase(final String ch) { - if (ch.length != 1) { - throw ArgumentError; - } - final uppercaseCh = ch.toUpperCase(); - final lowercaseCh = ch.toLowerCase(); - if (ch == uppercaseCh && ch == lowercaseCh) { - return null; - } - return ch == uppercaseCh ? lowercaseCh : uppercaseCh; -} - -Iterable _generateCaseInsensitive(final String input) sync* { - if (input.isEmpty) { - yield ''; - return; - } - final chOriginalCase = input[0]; - final chToggledCase = _toggleCase(chOriginalCase); - if (input.length == 1) { - yield* [chOriginalCase, if (chToggledCase != null) chToggledCase]; - return; - } - for (final recursiveSample in _generateCaseInsensitive(input.substring(1))) { - yield* [ - '$chOriginalCase$recursiveSample', - if (chToggledCase != null) '$chToggledCase$recursiveSample' - ]; - } -} - void _runTests() { - setUpAll(() { - _verifyMockFileExtensions(); - _prepareMockFiles(); - }); - tearDownAll(() { - _cleanupMockFiles(); - }); + setUpAll(() => _prepareMockFiles()); + tearDownAll(() => _cleanupMockFiles()); group('JSON files', () { test('with correct file content and correct extension', () { From 1c86d6e3a789fc71db51ad5df98a18add7fe2ffc Mon Sep 17 00:00:00 2001 From: Indraneel Rajeevan <105813454+indraneel12@users.noreply.github.com> Date: Tue, 21 Oct 2025 06:53:34 +0530 Subject: [PATCH 9/9] refactor(config_file_load_test): Given-When-Then format - better description and coverage - readability improvements --- .../test/config/config_file_load_test.dart | 280 ++++++++++-------- 1 file changed, 158 insertions(+), 122 deletions(-) diff --git a/packages/config/test/config/config_file_load_test.dart b/packages/config/test/config/config_file_load_test.dart index 2b989d5..afcc874 100644 --- a/packages/config/test/config/config_file_load_test.dart +++ b/packages/config/test/config/config_file_load_test.dart @@ -54,6 +54,14 @@ const _mockExtensions = { 'JSON', ], _AllowedFile.yaml: [ + 'yml', + 'ymL', + 'yMl', + 'yML', + 'Yml', + 'YmL', + 'YMl', + 'YML', 'yaml', 'yamL', 'yaMl', @@ -70,14 +78,6 @@ const _mockExtensions = { 'YAmL', 'YAMl', 'YAML', - 'yml', - 'ymL', - 'yMl', - 'yML', - 'Yml', - 'YmL', - 'YMl', - 'YML', ], }; @@ -87,8 +87,8 @@ final _mockDir = './test_tmp_${DateTime.now().millisecondsSinceEpoch}'; final _aNonExistentFilename = 'xyz_${DateTime.now().millisecondsSinceEpoch}'; String _buildFilepath( - final _AllowedFile format, final _Fact fact, + final _AllowedFile format, final String extension, ) => '$_mockDir/${_mockFilenames[format]![fact]!}.$extension'; @@ -97,11 +97,11 @@ void _prepareMockFiles() { _mockFilenames.forEach((final fileFormat, final namesMap) { for (final fact in _Fact.values) { for (final fileExtension in _mockExtensions[fileFormat]!) { - File(_buildFilepath(fileFormat, fact, fileExtension)) + File(_buildFilepath(fact, fileFormat, fileExtension)) ..createSync(recursive: true, exclusive: false) ..writeAsStringSync(_mockContent[fileFormat]![fact]!); } - File(_buildFilepath(fileFormat, fact, _anUnsupportedExtension)) + File(_buildFilepath(fact, fileFormat, _anUnsupportedExtension)) ..createSync(recursive: true, exclusive: false) ..writeAsStringSync(_mockContent[fileFormat]![fact]!); } @@ -114,126 +114,162 @@ void _runTests() { setUpAll(() => _prepareMockFiles()); tearDownAll(() => _cleanupMockFiles()); - group('JSON files', () { - test('with correct file content and correct extension', () { - for (final fileExtension in _mockExtensions[_AllowedFile.json]!) { - expect( - ConfigurationParser.fromFile(_buildFilepath( - _AllowedFile.json, - _Fact.correct, - fileExtension, - )).valueOrNull(_mockKey), - equals(_mockVal), - ); - } - }); - test('with correct file content but wrong extension', () { - expect( - () => ConfigurationParser.fromFile(_buildFilepath( - _AllowedFile.json, - _Fact.correct, - _anUnsupportedExtension, - )), - throwsUnsupportedError, - ); - }); - test('with wrong file content but correct extension', () { - for (final fileExtension in _mockExtensions[_AllowedFile.json]!) { - expect( - () => ConfigurationParser.fromFile(_buildFilepath( - _AllowedFile.json, - _Fact.wrong, - fileExtension, - )), - throwsFormatException, - ); - } + group('Given correct file content', () { + group('with a supported file extension', () { + group('when loading a JSON file', () { + test('then it is parsed successfully', () { + for (final jsonExtension in _mockExtensions[_AllowedFile.json]!) { + expect( + ConfigurationParser.fromFile(_buildFilepath( + _Fact.correct, + _AllowedFile.json, + jsonExtension, + )).valueOrNull(_mockKey), + equals(_mockVal), + ); + } + }); + }); + group('when loading a YAML file', () { + test('then it is parsed successfully', () { + for (final yamlExtension in _mockExtensions[_AllowedFile.yaml]!) { + expect( + ConfigurationParser.fromFile(_buildFilepath( + _Fact.correct, + _AllowedFile.yaml, + yamlExtension, + )).valueOrNull(_mockKey), + equals(_mockVal), + ); + } + }); + }); }); - test('with wrong file content and wrong extension', () { - expect( - () => ConfigurationParser.fromFile(_buildFilepath( - _AllowedFile.json, - _Fact.wrong, - _anUnsupportedExtension, - )), - throwsUnsupportedError, - ); + group('with an unsupported file extension', () { + group('when loading a JSON file', () { + test('then it reports an Unsupported Error', () { + expect( + () => ConfigurationParser.fromFile(_buildFilepath( + _Fact.correct, + _AllowedFile.json, + _anUnsupportedExtension, + )), + throwsUnsupportedError, + ); + }); + }); + group('when loading a YAML file', () { + test('then it reports an Unsupported Error', () { + expect( + () => ConfigurationParser.fromFile(_buildFilepath( + _Fact.correct, + _AllowedFile.yaml, + _anUnsupportedExtension, + )), + throwsUnsupportedError, + ); + }); + }); }); }); - group('YAML files', () { - test('with correct file content and correct extension', () { - for (final fileExtension in _mockExtensions[_AllowedFile.yaml]!) { - expect( - ConfigurationParser.fromFile(_buildFilepath( - _AllowedFile.yaml, - _Fact.correct, - fileExtension, - )).valueOrNull(_mockKey), - equals(_mockVal), - ); - } + group('Given wrong file content', () { + group('with a supported file extension', () { + group('when loading a JSON file', () { + test('then it reports a Format Exception', () { + for (final jsonExtension in _mockExtensions[_AllowedFile.json]!) { + expect( + () => ConfigurationParser.fromFile(_buildFilepath( + _Fact.wrong, + _AllowedFile.json, + jsonExtension, + )), + throwsFormatException, + ); + } + }); + }); + group('when loading a YAML file', () { + test('then it reports a Format Exception', () { + for (final yamlExtension in _mockExtensions[_AllowedFile.yaml]!) { + expect( + () => ConfigurationParser.fromFile(_buildFilepath( + _Fact.wrong, + _AllowedFile.yaml, + yamlExtension, + )), + throwsFormatException, + ); + } + }); + }); }); - test('with correct file content but wrong extension', () { - expect( - () => ConfigurationParser.fromFile(_buildFilepath( - _AllowedFile.yaml, - _Fact.correct, - _anUnsupportedExtension, - )), - throwsUnsupportedError, - ); - }); - test('with wrong file content but correct extension', () { - for (final fileExtension in _mockExtensions[_AllowedFile.yaml]!) { - expect( - () => ConfigurationParser.fromFile(_buildFilepath( - _AllowedFile.yaml, - _Fact.wrong, - fileExtension, - )), - throwsFormatException, - ); - } - }); - test('with wrong file content and wrong extension', () { - expect( - () => ConfigurationParser.fromFile(_buildFilepath( - _AllowedFile.yaml, - _Fact.wrong, - _anUnsupportedExtension, - )), - throwsUnsupportedError, - ); + group('with an unsupported file extension', () { + group('when loading a JSON file', () { + test('then it reports an Unsupported Error', () { + expect( + () => ConfigurationParser.fromFile(_buildFilepath( + _Fact.wrong, + _AllowedFile.json, + _anUnsupportedExtension, + )), + throwsUnsupportedError, + ); + }); + }); + group('when loading a YAML file', () { + test('then it reports an Unsupported Error', () { + expect( + () => ConfigurationParser.fromFile(_buildFilepath( + _Fact.wrong, + _AllowedFile.yaml, + _anUnsupportedExtension, + )), + throwsUnsupportedError, + ); + }); + }); }); }); - group('Non-existent file', () { - test('of ".json" type', () { - expect( - () => ConfigurationParser.fromFile('$_aNonExistentFilename.json'), - throwsArgumentError, - ); - }); - test('of ".yaml" type', () { - expect( - () => ConfigurationParser.fromFile('$_aNonExistentFilename.yaml'), - throwsArgumentError, - ); - }); - test('of ".yml" type', () { - expect( - () => ConfigurationParser.fromFile('$_aNonExistentFilename.yml'), - throwsArgumentError, - ); + group('Given a non-existent file', () { + group('with a supported file extension', () { + group('when attempting to load a JSON file', () { + test('then it reports an Argument Error', () { + for (final jsonExtension in _mockExtensions[_AllowedFile.json]!) { + expect( + () => ConfigurationParser.fromFile( + '$_aNonExistentFilename.$jsonExtension', + ), + throwsArgumentError, + ); + } + }); + }); + group('when attempting to load a YAML file', () { + test('then it reports an Argument Error', () { + for (final yamlExtension in _mockExtensions[_AllowedFile.yaml]!) { + expect( + () => ConfigurationParser.fromFile( + '$_aNonExistentFilename.$yamlExtension', + ), + throwsArgumentError, + ); + } + }); + }); }); - test('of unsupported type', () { - expect( - () => ConfigurationParser.fromFile( - '$_aNonExistentFilename.$_anUnsupportedExtension', - ), - throwsUnsupportedError, - ); + group('with an unsupported file extension', () { + group('when attempting to load any file', () { + test('then it reports an Unsupported Error', () { + expect( + () => ConfigurationParser.fromFile( + '$_aNonExistentFilename.$_anUnsupportedExtension', + ), + throwsUnsupportedError, + ); + }); + }); }); }); }