From b24c51265d3b88b72dcaf36a9b4da6548fd5cdb5 Mon Sep 17 00:00:00 2001 From: Julio Rios <31230801+jsilverdev@users.noreply.github.com> Date: Wed, 13 Sep 2023 13:42:23 -0500 Subject: [PATCH] Fix some issues with git urls --- CHANGELOG.md | 11 ++ lib/config/app_config.dart | 5 +- lib/exceptions/config_exceptions.dart | 12 ++ lib/exceptions/exceptions.dart | 50 +----- lib/exceptions/file_system_exceptions.dart | 18 ++ lib/exceptions/git_exceptions.dart | 25 +++ lib/exceptions/handler/handler.dart | 3 + lib/services/repo_service.dart | 68 +++++--- lib/services/shell_service.dart | 2 +- lib/utils/git_utils.dart | 24 +-- test/_data/test.test/configs/.gitkeep | 0 test/config/app_config_test.dart | 4 +- test/exceptions/handler/handler_test.dart | 13 ++ test/services/repo_service_test.dart | 193 ++++++++++++++------- test/services/shell_service_test.dart | 2 +- test/utils/git_utils_test.dart | 39 ++--- 16 files changed, 291 insertions(+), 178 deletions(-) create mode 100644 lib/exceptions/config_exceptions.dart create mode 100644 lib/exceptions/file_system_exceptions.dart create mode 100644 lib/exceptions/git_exceptions.dart create mode 100644 test/_data/test.test/configs/.gitkeep diff --git a/CHANGELOG.md b/CHANGELOG.md index baa3767..9d16ae6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,17 @@ and this project adheres to [Semantic Versioning]. - / +## [1.3.0] - 2023-09-13 + +### Added + +- Improved validation for git urls, now can accept urls without ".git" for example + +### Fixed + +- Now can catch all git exceptions and show in terminal +- If the folder doesn't exists stop the process and show a message + ## [1.2.0] - 2023-09-12 ### Changed diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 29b84a7..0f5d1d5 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -1,7 +1,7 @@ import 'package:dotenv/dotenv.dart'; import '../constants/constants.dart' as constants; -import '../exceptions/exceptions.dart'; +import '../exceptions/config_exceptions.dart'; import '../models/config_property.dart'; import '../utils/string_utils.dart'; @@ -56,5 +56,6 @@ class AppConfig { () => constants.DEFAULT_CONFIG_SECRET_PATH, ); - int get maxDurationInMin => _dotEnv[ConfigProperty.maxDurationInMin.value]?.toInt(3) ?? 3; + int get maxDurationInMin => + _dotEnv[ConfigProperty.maxDurationInMin.value]?.toInt(3) ?? 3; } diff --git a/lib/exceptions/config_exceptions.dart b/lib/exceptions/config_exceptions.dart new file mode 100644 index 0000000..40227d1 --- /dev/null +++ b/lib/exceptions/config_exceptions.dart @@ -0,0 +1,12 @@ +// coverage:ignore-file +import 'exceptions.dart'; + +abstract class ConfigException extends AppException { + const ConfigException(super.message); +} + +class ConfigPropertyMissingException extends ConfigException { + const ConfigPropertyMissingException({ + required final String property, + }) : super('"$property" property is not defined int the .env file'); +} \ No newline at end of file diff --git a/lib/exceptions/exceptions.dart b/lib/exceptions/exceptions.dart index 02c97ce..86b7053 100644 --- a/lib/exceptions/exceptions.dart +++ b/lib/exceptions/exceptions.dart @@ -6,52 +6,4 @@ abstract class AppException implements Exception { @override String toString() => _message; -} - -class ExecutableNotFoundInPathException extends AppException { - const ExecutableNotFoundInPathException( - final String executable, - ) : super("$executable is not found in the PATH. Check is it installed or in the PATH"); -} - -class ConfigPropertyMissingException extends AppException { - const ConfigPropertyMissingException({ - required final String property, - }) : super('"$property" property is not defined int the .env file'); -} - -abstract class GitException extends AppException { - const GitException(super.message); -} - -class InvalidValidGitPathException extends GitException { - const InvalidValidGitPathException({required final String path}) - : super('"$path" path is and invalid git repository'); -} - -class IncorrectTopLevelGitPathException extends GitException { - const IncorrectTopLevelGitPathException({ - required final String path, - }) : super('"$path" path is not in the top level of the git repository'); -} - -class InvalidGitLocalBranchException extends GitException { - InvalidGitLocalBranchException({ - required final String branch, - }) : super( - 'The selected branch "$branch" is not valid for the git repository'); -} - -class InvalidGitRemoteBranchException extends GitException { - InvalidGitRemoteBranchException({ - required final String branch, - }) : super( - 'The selected branch "$branch" is not in the remote git repository'); -} - -class GitRemoteToManyTimeException extends GitException { - GitRemoteToManyTimeException({ - required final int minutes, - }) : super( - "Attempting to connect to remote repository took longer than expected ($minutes min)"); -} +} \ No newline at end of file diff --git a/lib/exceptions/file_system_exceptions.dart b/lib/exceptions/file_system_exceptions.dart new file mode 100644 index 0000000..bd534fa --- /dev/null +++ b/lib/exceptions/file_system_exceptions.dart @@ -0,0 +1,18 @@ +// coverage:ignore-file +import 'exceptions.dart'; + +abstract class FileSystemException extends AppException { + const FileSystemException(super.message); +} + +class ExecutableNotFoundInPathException extends FileSystemException { + const ExecutableNotFoundInPathException( + final String executable, + ) : super("$executable is not found in the PATH. Check is it installed or in the PATH"); +} + +class FolderNotFoundException extends FileSystemException { + FolderNotFoundException({ + required String path, + }) : super('The current path "$path" not exists'); +} \ No newline at end of file diff --git a/lib/exceptions/git_exceptions.dart b/lib/exceptions/git_exceptions.dart new file mode 100644 index 0000000..15e4e15 --- /dev/null +++ b/lib/exceptions/git_exceptions.dart @@ -0,0 +1,25 @@ +// coverage:ignore-file +import 'package:process_run/process_run.dart'; + +import 'exceptions.dart'; + +abstract class GitException extends AppException { + const GitException(super.message); +} + +class IncorrectTopLevelGitPathException extends GitException { + const IncorrectTopLevelGitPathException({ + required final String path, + }) : super('"$path" path is not in the top level of the repository'); +} + +class GitShellException extends GitException { + GitShellException(ShellException e) : super(e.result?.errText ?? e.message); +} + +class GitRemoteToManyTimeException extends GitException { + GitRemoteToManyTimeException({ + required final int minutes, + }) : super( + "Attempting to connect to remote repository took longer than expected ($minutes min)"); +} diff --git a/lib/exceptions/handler/handler.dart b/lib/exceptions/handler/handler.dart index 5854570..012a929 100644 --- a/lib/exceptions/handler/handler.dart +++ b/lib/exceptions/handler/handler.dart @@ -2,10 +2,13 @@ import 'dart:async'; import '../../config/logger.dart'; import '../exceptions.dart'; +import '../git_exceptions.dart'; Future handle(FutureOr Function() function) async { try { await function(); + } on GitException catch (e) { + log.e("Problems related to git found\n$e"); } on AppException catch (e) { log.e(e); } catch (e, s) { diff --git a/lib/services/repo_service.dart b/lib/services/repo_service.dart index faa16b7..f136cfb 100644 --- a/lib/services/repo_service.dart +++ b/lib/services/repo_service.dart @@ -1,10 +1,13 @@ +import 'dart:io'; + import 'package:path/path.dart' as path; import 'package:process_run/process_run.dart'; import '../config/app_config.dart'; import '../config/logger.dart'; import '../constants/constants.dart' as constants; -import '../exceptions/exceptions.dart'; +import '../exceptions/file_system_exceptions.dart'; +import '../exceptions/git_exceptions.dart'; import '../utils/git_utils.dart'; import '../utils/string_utils.dart'; import 'shell_service.dart'; @@ -15,8 +18,13 @@ class RepoService { String _gitPath = ""; bool _isCloned = false; + final String _defaultReposFolder; - RepoService(this._appConfig, this._shellService); + RepoService( + this._appConfig, + this._shellService, { + String? defaultParentFolder, + }) : _defaultReposFolder = defaultParentFolder ?? constants.REPOS_FOLDER; Future _overrideConfigGitBranchWithCurrentOnEmpty() async { if (_appConfig.gitBranch != "") return; @@ -36,8 +44,8 @@ class RepoService { await _shellService.runScript( constants.GIT_CHECKOUT.format([safeBranch]), ); - } on ShellException { - throw InvalidGitLocalBranchException(branch: branch); + } on ShellException catch (e) { + throw GitShellException(e); } } @@ -46,19 +54,22 @@ class RepoService { log.i('Getting latests changes for "$branch" branch'); final duration = Duration(minutes: _appConfig.maxDurationInMin); try { - await _shellService.runScript( + await _shellService + .runScript( constants.GIT_REFRESH_BRANCH.format([ branch, _getRemoteConfig(), ]), - ).timeout(duration, onTimeout: () { - _shellService.dispose(); - throw GitRemoteToManyTimeException( - minutes: duration.inMinutes - ); - }); - } on ShellException { - throw InvalidGitRemoteBranchException(branch: branch); + ) + .timeout( + duration, + onTimeout: () { + _shellService.dispose(); + throw GitRemoteToManyTimeException(minutes: duration.inMinutes); + }, + ); + } on ShellException catch (e) { + throw GitShellException(e); } } @@ -79,17 +90,17 @@ class RepoService { path: _gitPath, ); } - } on ShellException { - throw InvalidValidGitPathException( - path: _gitPath, - ); + } on ShellException catch (e) { + throw GitShellException(e); } } - Future _tryCloning({ + Future _tryCloneInPath({ required String gitUrl, required String gitPath, }) async { + if ((Directory(_gitPath).existsSync())) return false; + try { await _shellService.runScript(constants.GIT_CLONE.format([ gitUrl, @@ -98,14 +109,12 @@ class RepoService { ])); log.i("Successfully cloned at: $gitPath"); return true; - } on ShellException { - log.w('The path "$gitPath" exists and is not empty'); - return false; + } on ShellException catch (e) { + throw GitShellException(e); } } Future _validateGitUrlAndClone() async { - final bool isGitUrl = isValidGitUrl(_appConfig.gitRepoPath); if (!isGitUrl) { @@ -115,16 +124,23 @@ class RepoService { } _gitPath = path.absolute( - constants.REPOS_FOLDER, + _defaultReposFolder, extractGitPath(_appConfig.gitRepoPath), ); - _isCloned = await _tryCloning( + _isCloned = await _tryCloneInPath( gitUrl: _appConfig.gitRepoPath, gitPath: _gitPath, ); } + void _validateGitPathAndMoveShell() { + if (!Directory(_gitPath).existsSync()) { + throw FolderNotFoundException(path: _gitPath); + } + _shellService.moveShellTo(_gitPath); + } + Future setup() async { _shellService.checkExecutable(constants.GIT); @@ -132,7 +148,7 @@ class RepoService { await _validateGitUrlAndClone(); - _shellService.moveShellTo(_gitPath); + _validateGitPathAndMoveShell(); await _validateIfPathIsGitRepo(); await _overrideConfigGitBranchWithCurrentOnEmpty(); @@ -151,7 +167,7 @@ class RepoService { String _getRemoteConfig() { final config = "{}".format([ - !_appConfig.gitSSLEnabled ? constants.GIT_SSL_VERIFY_FALSE : "" + !_appConfig.gitSSLEnabled ? constants.GIT_SSL_VERIFY_FALSE : "", ]); if (config.isEmpty) return ""; return "-c $config"; diff --git a/lib/services/shell_service.dart b/lib/services/shell_service.dart index b9194c0..b055eba 100644 --- a/lib/services/shell_service.dart +++ b/lib/services/shell_service.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:path/path.dart' as path; import 'package:process_run/process_run.dart'; -import '../exceptions/exceptions.dart'; +import '../exceptions/file_system_exceptions.dart'; class ShellService { Shell _shell; diff --git a/lib/utils/git_utils.dart b/lib/utils/git_utils.dart index baf20fe..7e6191b 100644 --- a/lib/utils/git_utils.dart +++ b/lib/utils/git_utils.dart @@ -1,20 +1,22 @@ +final _validUrlRegex = RegExp( + r"^(([A-Za-z0-9]+@|http(|s)\:\/\/)|(http(|s)\:\/\/[A-Za-z0-9]+@))([A-Za-z0-9.]+(:\d+)?)(?::|\/)([\d\/\w-]+?)(\.git){0,1}(/?)$", + caseSensitive: false, + multiLine: false, +); + bool isValidGitUrl(String url) { - final regExp = RegExp( - "((http|git|ssh|http(s)|file|\\/?)|(git@[\\w\\.]+))(:(\\/\\/)?)([\\w\\.@\\:/\\-~]+)(\\.git)(\\/)?", - caseSensitive: false, - multiLine: false, - ); - return regExp.hasMatch(url); + return _validUrlRegex.hasMatch(url); } String extractGitPath(String url) { if (url.endsWith(".git")) { url = url.substring(0, url.length - 4); } - RegExp httpRegex = RegExp(r"https?://[^/]+/"); - url = url.replaceFirst(httpRegex, ""); - RegExp sshRegex = RegExp(r"^([^@]+@[^:/]+:)"); - url = url.replaceFirst(sshRegex, ""); - return url; + // Remove "http://" or "https://" + url = url.replaceFirst(RegExp(r"https?:\/\/"), ""); + // Remove any@ + url = url.replaceFirst(RegExp(r"^[^@]+@"), ""); + // Remove invalid windows caracteres + return url.replaceAll(RegExp(r'[\\/:*?"<>|]'), "/"); } diff --git a/test/_data/test.test/configs/.gitkeep b/test/_data/test.test/configs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/config/app_config_test.dart b/test/config/app_config_test.dart index 91df322..d8703ab 100644 --- a/test/config/app_config_test.dart +++ b/test/config/app_config_test.dart @@ -1,10 +1,10 @@ import 'package:config_props_extractor/config/app_config.dart'; -import 'package:config_props_extractor/exceptions/exceptions.dart'; +import 'package:config_props_extractor/constants/constants.dart' as constants; +import 'package:config_props_extractor/exceptions/config_exceptions.dart'; import 'package:config_props_extractor/models/config_property.dart'; import 'package:config_props_extractor/utils/string_utils.dart'; import 'package:dotenv/dotenv.dart'; import 'package:test/test.dart'; -import 'package:config_props_extractor/constants/constants.dart' as constants; void main() { late DotEnv dotEnv; diff --git a/test/exceptions/handler/handler_test.dart b/test/exceptions/handler/handler_test.dart index 3896f44..1f571e5 100644 --- a/test/exceptions/handler/handler_test.dart +++ b/test/exceptions/handler/handler_test.dart @@ -1,10 +1,13 @@ import 'package:config_props_extractor/exceptions/exceptions.dart'; +import 'package:config_props_extractor/exceptions/git_exceptions.dart'; import 'package:config_props_extractor/exceptions/handler/handler.dart'; import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; class MockAppException extends Mock implements AppException {} +class MockGitException extends Mock implements GitException {} + void main() { test('Run Correctly', () async { expect( @@ -13,6 +16,16 @@ void main() { ); }); + + test('Catch GitException', () async { + expect( + () => handle(() { + throw MockGitException(); + }), + returnsNormally, + ); + }); + test('Catch AppException', () async { expect( () => handle(() { diff --git a/test/services/repo_service_test.dart b/test/services/repo_service_test.dart index 45c66c4..b2b6d33 100644 --- a/test/services/repo_service_test.dart +++ b/test/services/repo_service_test.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'dart:io'; import 'package:config_props_extractor/constants/constants.dart'; -import 'package:config_props_extractor/exceptions/exceptions.dart'; +import 'package:config_props_extractor/exceptions/config_exceptions.dart'; +import 'package:config_props_extractor/exceptions/file_system_exceptions.dart'; +import 'package:config_props_extractor/exceptions/git_exceptions.dart'; import 'package:config_props_extractor/services/repo_service.dart'; import 'package:config_props_extractor/services/shell_service.dart'; import 'package:config_props_extractor/utils/string_utils.dart'; @@ -26,10 +28,8 @@ void main() { mockAppConfig = MockAppConfig(); mockShellService = MockShellService(); - repoService = RepoService( - mockAppConfig, - mockShellService, - ); + repoService = RepoService(mockAppConfig, mockShellService, + defaultParentFolder: "test/_data"); when(() => mockAppConfig.maxDurationInMin).thenReturn(3); }); @@ -39,16 +39,35 @@ void main() { }); const branch = "selected_branch"; - const gitDirPath = "/path/to/folder"; - const gitUrlPath = "https://testurl.dev/path/to/folder.git"; - final gitUrlAbsoluteDirPath = absolute(REPOS_FOLDER, 'path/to/folder'); + const gitDirPath = "test/_data/test.test/configs/config"; + const gitUrlPath = "https://test.test/configs/config.git"; + final gitAbsoluteDirPath = + absolute("test/_data", 'test.test/configs/config'); + + void createFolder(path) { + final dir = Directory(path); + if (!dir.existsSync()) { + dir.createSync(recursive: true); + } + } + + void destroyFolder(path) { + final dir = Directory(path); + if (dir.existsSync()) { + dir.deleteSync(recursive: true); + } + } + + tearDown(() { + destroyFolder(gitAbsoluteDirPath); + }); Future testSetup({ required bool isBranchDefined, required bool gitForceRemote, String gitConfig = "", String path = gitDirPath, - Map scriptsInOrder = const {}, + Map Function()> scriptsInOrder = const {}, }) async { // arrange bool firstExec = true; @@ -67,12 +86,9 @@ void main() { when(() => mockAppConfig.gitForceRemote).thenReturn(gitForceRemote); scriptsInOrder.forEach((script, results) { - when(() => mockShellService.runScript(script)).thenAnswer((_) async { - if (results is Exception) { - throw results; - } - return results as List; - }); + when(() => mockShellService.runScript(script)).thenAnswer( + (_) async => results(), + ); }); // act @@ -82,7 +98,7 @@ void main() { verify( () => mockShellService.moveShellTo( - path == gitDirPath ? path : gitUrlAbsoluteDirPath, + path == gitDirPath ? path : gitAbsoluteDirPath, ), ); verify(() => mockShellService.checkExecutable(GIT)); @@ -97,65 +113,72 @@ void main() { test( 'Should run success when repo is folder when branch is defined and force remote is enabled', - () async => testSetup( - isBranchDefined: true, - gitForceRemote: true, - scriptsInOrder: { - GIT_TOP_LEVEL_PATH: [processResult(stdout: gitDirPath)], - GIT_REFRESH_BRANCH.format([branch, ""]): List.empty(), - GIT_CHECKOUT.format([branch]): List.empty(), - GIT_OVERRIDE_WITH_REMOTE.format([branch]): List.empty() - }, - ), + () async { + createFolder(gitAbsoluteDirPath); + await testSetup( + isBranchDefined: true, + gitForceRemote: true, + scriptsInOrder: { + GIT_TOP_LEVEL_PATH: () => [processResult(stdout: gitDirPath)], + GIT_REFRESH_BRANCH.format([branch, ""]): () => [], + GIT_CHECKOUT.format([branch]): () => [], + GIT_OVERRIDE_WITH_REMOTE.format([branch]): () => [] + }, + ); + }, ); test( 'Should run success when repo is folder and git SLL is disabled', () async { + createFolder(gitAbsoluteDirPath); when(() => mockAppConfig.gitSSLEnabled).thenReturn(false); await testSetup( isBranchDefined: true, gitForceRemote: true, gitConfig: "-c $GIT_SSL_VERIFY_FALSE", scriptsInOrder: { - GIT_TOP_LEVEL_PATH: [processResult(stdout: gitDirPath)], - GIT_REFRESH_BRANCH.format([branch, "-c $GIT_SSL_VERIFY_FALSE"]): - List.empty(), - GIT_CHECKOUT.format([branch]): List.empty(), - GIT_OVERRIDE_WITH_REMOTE.format([branch]): - List.empty() + GIT_TOP_LEVEL_PATH: () => [processResult(stdout: gitDirPath)], + GIT_REFRESH_BRANCH.format([ + branch, + "-c $GIT_SSL_VERIFY_FALSE", + ]): () => [], + GIT_CHECKOUT.format([branch]): () => [], + GIT_OVERRIDE_WITH_REMOTE.format([branch]): () => [] }, ); }, ); - test( - 'Should run success when repo is folder and force remote is false', - () async => testSetup( + test('Should run success when repo is folder and force remote is false', + () async { + createFolder(gitAbsoluteDirPath); + await testSetup( isBranchDefined: false, gitForceRemote: false, scriptsInOrder: { - GIT_TOP_LEVEL_PATH: [processResult(stdout: gitDirPath)], - GIT_BRANCH_SHOW_CURRENT: [processResult(stdout: branch)], - GIT_CHECKOUT.format([branch]): List.empty(), + GIT_TOP_LEVEL_PATH: () => [processResult(stdout: gitAbsoluteDirPath)], + GIT_BRANCH_SHOW_CURRENT: () => [processResult(stdout: branch)], + GIT_CHECKOUT.format([branch]): () => [], }, - ), - ); + ); + }); test( - 'Should run success when gitRepoPath is Url', + 'Should run success when gitRepoPath is Url and gitPath not exits', () async { testSetup( isBranchDefined: true, gitForceRemote: false, path: gitUrlPath, scriptsInOrder: { - GIT_CLONE.format([gitUrlPath, gitUrlAbsoluteDirPath, ""]): - List.empty(), - GIT_TOP_LEVEL_PATH: [ - processResult(stdout: gitUrlAbsoluteDirPath) - ], - GIT_CHECKOUT.format([branch]): List.empty(), + GIT_CLONE.format([gitUrlPath, gitAbsoluteDirPath, ""]): () { + createFolder(gitAbsoluteDirPath); + return []; + }, + GIT_TOP_LEVEL_PATH: () => + [processResult(stdout: gitAbsoluteDirPath)], + GIT_CHECKOUT.format([branch]): () => [], }); }, ); @@ -163,24 +186,44 @@ void main() { test( 'Should run success when gitRepoPath is Url and gitPath exists', () async { + createFolder(gitAbsoluteDirPath); testSetup( isBranchDefined: true, gitForceRemote: false, path: gitUrlPath, scriptsInOrder: { - GIT_CLONE.format([gitUrlPath, gitUrlAbsoluteDirPath, ""]): - ShellException("message", null), - GIT_TOP_LEVEL_PATH: [ - processResult(stdout: gitUrlAbsoluteDirPath) - ], - GIT_CHECKOUT.format([branch]): List.empty(), + GIT_TOP_LEVEL_PATH: () => + [processResult(stdout: gitAbsoluteDirPath)], + GIT_CHECKOUT.format([branch]): () => [], }); }, ); + test( + 'Should fail when git clone fail', + () async { + // arrange + when(() => mockAppConfig.gitRepoPath).thenReturn(gitUrlPath); + when(() => mockAppConfig.gitForceRemote).thenReturn(false); + when(() => mockShellService.runScript(GIT_CLONE.format([ + gitUrlPath, + gitAbsoluteDirPath, + "", + ]))).thenAnswer((_) async => throw ShellException("message", null)); + expect( + // act + () => repoService.setup(), + // assert + throwsA(isA()), + ); + + }, + ); + test( 'Should fail if Local Branch not exists', () async { + createFolder(gitAbsoluteDirPath); // arrange when(() => mockAppConfig.gitRepoPath).thenReturn(gitDirPath); when(() => mockAppConfig.gitForceRemote).thenReturn(false); @@ -188,14 +231,14 @@ void main() { (_) async => [processResult(stdout: gitDirPath)], ); when(() => mockAppConfig.gitBranch).thenReturn("fail_branch"); - when(() => mockShellService.runScript(GIT_CHECKOUT.format(["fail_branch"]))) - .thenAnswer((_) async => throw ShellException("exception", null)); - // act + when(() => mockShellService + .runScript(GIT_CHECKOUT.format(["fail_branch"]))) + .thenAnswer((_) async => throw ShellException("exception", null)); expect( // act () => repoService.setup(), // assert - throwsA(isA()), + throwsA(isA()), ); }, ); @@ -203,6 +246,7 @@ void main() { test( 'Should fail if Remote Branch not exists', () async { + createFolder(gitAbsoluteDirPath); // arrange when(() => mockAppConfig.gitRepoPath).thenReturn(gitDirPath); when(() => mockAppConfig.gitForceRemote).thenReturn(true); @@ -210,14 +254,15 @@ void main() { (_) async => [processResult(stdout: gitDirPath)], ); when(() => mockAppConfig.gitBranch).thenReturn("fail_branch"); - when(() => mockShellService.runScript(GIT_REFRESH_BRANCH.format(["fail_branch", ""]))) - .thenAnswer((_) async => throw ShellException("exception", null)); + when(() => mockShellService + .runScript(GIT_REFRESH_BRANCH.format(["fail_branch", ""]))) + .thenAnswer((_) async => throw ShellException("exception", null)); // act expect( // act () => repoService.setup(), // assert - throwsA(isA()), + throwsA(isA()), ); }, ); @@ -225,6 +270,7 @@ void main() { test( 'Should fail if git refresh branch is taking too much', () async { + createFolder(gitAbsoluteDirPath); // arrange when(() => mockAppConfig.gitRepoPath).thenReturn(gitDirPath); when(() => mockAppConfig.gitForceRemote).thenReturn(true); @@ -233,8 +279,9 @@ void main() { (_) async => [processResult(stdout: gitDirPath)], ); when(() => mockAppConfig.gitBranch).thenReturn("fail_branch"); - when(() => mockShellService.runScript(GIT_REFRESH_BRANCH.format(["fail_branch", ""]))) - .thenAnswer((_) async { + when(() => mockShellService + .runScript(GIT_REFRESH_BRANCH.format(["fail_branch", ""]))) + .thenAnswer((_) async { await Completer().future.timeout(Duration(minutes: 2)); return []; }); @@ -288,9 +335,10 @@ void main() { test("The path is invalid git repository", () { // arrange - when(() => mockAppConfig.gitRepoPath).thenReturn("/path/to/sub/folder"); + createFolder(gitAbsoluteDirPath); + when(() => mockAppConfig.gitRepoPath).thenReturn(gitDirPath); when(() => mockShellService.runScript(GIT_TOP_LEVEL_PATH)).thenAnswer( - (_) async => [ProcessResult(pid, exitCode, "/path/to/sub", stderr)], + (_) async => [ProcessResult(pid, exitCode, "/another/path", stderr)], ); expect( @@ -301,9 +349,22 @@ void main() { ); }); + test("The folder not exists", () { + // arrange + when(() => mockAppConfig.gitRepoPath).thenReturn(gitDirPath); + + expect( + // act + () => repoService.setup(), + // assert + throwsA(isA()), + ); + }); + test("The path is not in the top-level of the git repository", () { // arrange - when(() => mockAppConfig.gitRepoPath).thenReturn("/path/to/folder"); + createFolder(gitAbsoluteDirPath); + when(() => mockAppConfig.gitRepoPath).thenReturn(gitDirPath); when(() => mockShellService.runScript(GIT_TOP_LEVEL_PATH)) .thenAnswer((_) async => throw ShellException("exception", null)); @@ -311,7 +372,7 @@ void main() { // act () => repoService.setup(), // assert - throwsA(isA()), + throwsA(isA()), ); }); }); diff --git a/test/services/shell_service_test.dart b/test/services/shell_service_test.dart index 235907e..256de76 100644 --- a/test/services/shell_service_test.dart +++ b/test/services/shell_service_test.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:config_props_extractor/exceptions/exceptions.dart'; +import 'package:config_props_extractor/exceptions/file_system_exceptions.dart'; import 'package:config_props_extractor/services/shell_service.dart'; import 'package:mocktail/mocktail.dart'; import 'package:process_run/process_run.dart'; diff --git a/test/utils/git_utils_test.dart b/test/utils/git_utils_test.dart index 17edfce..cbeb995 100644 --- a/test/utils/git_utils_test.dart +++ b/test/utils/git_utils_test.dart @@ -2,20 +2,24 @@ import 'package:config_props_extractor/utils/git_utils.dart'; import 'package:test/test.dart'; void main() { + const validUrls = [ + "https://fake.test/test/url/repo.git", + "http://fake.test/test/url/repo.git", + "http://fake.test/test/url/repo", + "git@fake.test:test/url/repo.git", + "git@fake.test:test/url/repo.git", + "test@fake.test:test/url/repo.git", + "test@fake.test:test/url/repo", + ]; + test( 'Should check if current url is git url', () async { // arrange - final validUrls = [ - "https://fake.git/test/url/repo.git", - "http://fake.git/test/url/repo.git", - "git@fake.git:test/url/repo.git", - "git@gitlab.com:test/url/repo.git", - "test@fake.git:test/url/repo.git", - ]; - final invalidUrls = [ - "path/to/location", + "http://fake.test/test/url/repo.no_git", + "test@fake.test:test/url/repo.no_git" + "path/to/location", "", ]; @@ -34,20 +38,15 @@ void main() { 'Should extract gitPath', () async { // arrange - final urls = [ - "https://fake.git/test/url/repo.git", - "http://fake.git/test/url/repo.git", - "git@fake.git:test/url/repo.git", - "git@gitlab.com:test/url/repo.git", - "test@fake.git:test/url/repo.git", - ]; - // act - for (var url in urls) { + for (var url in validUrls) { // assert - expect(extractGitPath(url), "test/url/repo"); + expect( + extractGitPath(url), + "fake.test/test/url/repo", + reason: "For url $url", + ); } - }, ); }