diff --git a/.github/workflows/test_on_pr.yaml b/.github/workflows/test_on_pr.yaml index 91e4719..c088a0b 100644 --- a/.github/workflows/test_on_pr.yaml +++ b/.github/workflows/test_on_pr.yaml @@ -24,4 +24,11 @@ jobs: - name: Run Unit Tests run: | cd PWManager.UnitTests - dotnet test --logger "console;verbosity=detailed" + rm -rf coverage + rm -rf TestResults + dotnet tool install -g dotnet-reportgenerator-globaltool + dotnet test --logger "console;verbosity=detailed" --collect:"XPlat Code Coverage" + reportgenerator "-reports:TestResults/*/coverage*" "-targetdir:coverage" "-reporttypes:TextSummary" + cat coverage/Summary.txt + rm -rf coverage + rm -rf TestResults diff --git a/PWManager.Application/Exceptions/MessageStrings.cs b/PWManager.Application/Exceptions/MessageStrings.cs index 2ba565a..e0cf453 100644 --- a/PWManager.Application/Exceptions/MessageStrings.cs +++ b/PWManager.Application/Exceptions/MessageStrings.cs @@ -39,4 +39,8 @@ public static class MessageStrings { public const string PATH_ERROR = "An unknown error occured! Could not determine execution path!"; public const string DIRECTORY_ERROR = "An unknown error occured! Execution path is not a directory!"; // ---------------------------------------- + + // PasswordBuilder + public const string MIN_LENGTH_TO_SMALL = "MinLength cannot be smaller than 0"; + public const string MAX_LENGTH_TO_SMALL = "MaxLength cannot be smaller than MinLength"; } diff --git a/PWManager.Application/Services/AccountService.cs b/PWManager.Application/Services/AccountService.cs index 3ed0cfe..b4c43d5 100644 --- a/PWManager.Application/Services/AccountService.cs +++ b/PWManager.Application/Services/AccountService.cs @@ -41,6 +41,7 @@ public void AddNewAccount(string identifier, string loginname, string password) var saved = _groupRepo.AddAccountToGroup(account, _environment.CurrentGroup); if (!saved) { + _environment.CurrentGroup.RemoveAccount(account); throw new UserFeedbackException(MessageStrings.FAILED_ADDING_ACCOUNT); } } @@ -87,6 +88,7 @@ public void DeleteAccount(string identifier) { throw new UserFeedbackException(MessageStrings.ACCOUNT_NOT_FOUND); } + _environment.CurrentGroup.RemoveAccount(acc); _groupRepo.DeleteAccountInGroup(acc, _environment.CurrentGroup); } diff --git a/PWManager.Application/Services/PasswordBuilder.cs b/PWManager.Application/Services/PasswordBuilder.cs new file mode 100644 index 0000000..b23d198 --- /dev/null +++ b/PWManager.Application/Services/PasswordBuilder.cs @@ -0,0 +1,84 @@ +using PWManager.Application.Exceptions; +using PWManager.Domain.Services.Interfaces; +using PWManager.Domain.ValueObjects; + +namespace PWManager.Application.Services; + +public class PasswordBuilder { + + private IPasswordGeneratorService _generatorService; + + private bool _includeLowerCase = false; + private bool _includeUpperCase = false; + private bool _includeSpecial = false; + private bool _includeSpaces = false; + private bool _includeNumeric = false; + private bool _includeBrackets = false; + private int _minLength = 5; + private int _maxLength = 8; + + private PasswordBuilder() : this(new PasswordGeneratorService(null)) {} + + private PasswordBuilder(IPasswordGeneratorService generatorService) { + _generatorService = generatorService; + } + + public PasswordBuilder SetMinLength(int minLength) { + if (minLength <= 0) { + throw new PasswordGenerationException(MessageStrings.MIN_LENGTH_TO_SMALL); + } + _minLength = minLength; + return this; + } + + public PasswordBuilder SetMaxLength(int maxLength) { + if (maxLength < _minLength) { + throw new PasswordGenerationException(MessageStrings.MAX_LENGTH_TO_SMALL); + } + _maxLength = maxLength; + return this; + } + + public PasswordBuilder IncludeUppercase() { + _includeUpperCase = true; + return this; + } + public PasswordBuilder IncludeLowercase() { + _includeLowerCase = true; + return this; + } + public PasswordBuilder IncludeSpecialChars() { + _includeSpecial = true; + return this; + } + public PasswordBuilder IncludeSpaces() { + _includeSpaces = true; + return this; + } + public PasswordBuilder IncludeNumeric() { + _includeNumeric = true; + return this; + } + public PasswordBuilder IncludeBrackets() { + _includeBrackets = true; + return this; + } + + public string BuildPassword() { + var criteria = BuildCriteria(); + return _generatorService.GeneratePasswordWith(criteria); + } + + internal PasswordGeneratorCriteria BuildCriteria() { + return new PasswordGeneratorCriteria(_includeLowerCase, _includeUpperCase, _includeNumeric, + _includeSpecial, _includeBrackets, _includeSpaces, _minLength, _maxLength); + } + + public static PasswordBuilder Create() { + return new PasswordBuilder(); + } + + internal static PasswordBuilder Create(IPasswordGeneratorService service) { + return new PasswordBuilder(service); + } +} \ No newline at end of file diff --git a/PWManager.Application/Services/PasswordGeneratorService.cs b/PWManager.Application/Services/PasswordGeneratorService.cs index d5f8d3c..f0f873c 100644 --- a/PWManager.Application/Services/PasswordGeneratorService.cs +++ b/PWManager.Application/Services/PasswordGeneratorService.cs @@ -8,7 +8,7 @@ namespace PWManager.Application.Services; public class PasswordGeneratorService : IPasswordGeneratorService { - private readonly PasswordGeneratorCriteria _userSettings; + private readonly ISettingsRepository _settingsRepo; private const string Lowercase = "abcdefghijklmnopqrstuvwxyz"; private const string Uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -23,7 +23,7 @@ public class PasswordGeneratorService : IPasswordGeneratorService { } public PasswordGeneratorService(ISettingsRepository settingsRepository, Random rng) { - _userSettings = settingsRepository.GetSettings().PwGenCriteria; + _settingsRepo = settingsRepository; _rng = rng; } @@ -44,7 +44,7 @@ public string GeneratePasswordWith(PasswordGeneratorCriteria criteria) { } public string GeneratePassword() { - return GeneratePasswordWith(_userSettings); + return GeneratePasswordWith(_settingsRepo.GetSettings().PwGenCriteria); } private static char[] BuildPossibleChars(PasswordGeneratorCriteria criteria) { diff --git a/PWManager.Domain/ValueObjects/PasswordGeneratorCriteria.cs b/PWManager.Domain/ValueObjects/PasswordGeneratorCriteria.cs index 4d52f53..7e2a907 100644 --- a/PWManager.Domain/ValueObjects/PasswordGeneratorCriteria.cs +++ b/PWManager.Domain/ValueObjects/PasswordGeneratorCriteria.cs @@ -12,7 +12,7 @@ public class PasswordGeneratorCriteria : ValueObject { public bool IncludeBrackets { get; } public int MinLength { get; } public int MaxLength { get; } - + public PasswordGeneratorCriteria(bool includeLowerCase, bool includeUpperCase, bool includeNumeric, bool includeSpecial, bool includeBrackets, bool includeSpaces, int minLength, int maxLength) { if (minLength <= 0) { throw new ArgumentException("MinLength cannot be less than or equal to 0"); diff --git a/PWManager.UnitTests/Application/PasswordBuilderTest.cs b/PWManager.UnitTests/Application/PasswordBuilderTest.cs new file mode 100644 index 0000000..94ee770 --- /dev/null +++ b/PWManager.UnitTests/Application/PasswordBuilderTest.cs @@ -0,0 +1,141 @@ +using NSubstitute; +using PWManager.Application.Exceptions; +using PWManager.Application.Services; +using PWManager.Domain.Services.Interfaces; +using PWManager.Domain.ValueObjects; + +namespace PWManager.UnitTests.Application; + +public class PasswordBuilderTest { + + [Fact] + public void PasswordBuilder_Should_SetMinLength() { + var criteria = PasswordBuilder.Create() + .IncludeUppercase() + .SetMinLength(8) + .BuildCriteria(); + + Assert.Equal(8, criteria.MinLength); + } + + [Fact] + public void PasswordBuilder_Should_SetMaxLength() { + var criteria = PasswordBuilder.Create() + .IncludeUppercase() + .SetMaxLength(80) + .BuildCriteria(); + + Assert.Equal(80, criteria.MaxLength); + } + + [Fact] + public void PasswordBuilder_Should_IncludeLowercase() { + var criteria = PasswordBuilder.Create() + .IncludeLowercase() + .BuildCriteria(); + + Assert.True(criteria.IncludeLowerCase); + } + + [Fact] + public void PasswordBuilder_Should_IncludeUppercase() { + var criteria = PasswordBuilder.Create() + .IncludeUppercase() + .BuildCriteria(); + + Assert.True(criteria.IncludeUpperCase); + } + + [Fact] + public void PasswordBuilder_Should_IncludeSpecialChars() { + var criteria = PasswordBuilder.Create() + .IncludeSpecialChars() + .BuildCriteria(); + + Assert.True(criteria.IncludeSpecial); + } + + [Fact] + public void PasswordBuilder_Should_IncludeSpacesAndUppercase() { + var criteria = PasswordBuilder.Create() + .IncludeUppercase() + .IncludeSpaces() + .BuildCriteria(); + + Assert.True(criteria.IncludeSpaces); + } + + [Fact] + public void PasswordBuilder_Should_IncludeBrackets() { + var criteria = PasswordBuilder.Create() + .IncludeBrackets() + .BuildCriteria(); + + Assert.True(criteria.IncludeBrackets); + } + + [Fact] + public void PasswordBuilder_Should_IncludeNumeric() { + var criteria = PasswordBuilder.Create() + .IncludeNumeric() + .BuildCriteria(); + + Assert.True(criteria.IncludeNumeric); + } + + [Fact] + public void PasswordBuilder_Should_IncludeAll() { + var criteria = PasswordBuilder.Create() + .IncludeUppercase() + .IncludeLowercase() + .IncludeSpaces() + .IncludeSpecialChars() + .IncludeBrackets() + .IncludeNumeric() + .SetMinLength(10) + .SetMaxLength(100) + .BuildCriteria(); + + Assert.True(criteria.IncludeUpperCase); + Assert.True(criteria.IncludeLowerCase); + Assert.True(criteria.IncludeSpaces); + Assert.True(criteria.IncludeSpecial); + Assert.True(criteria.IncludeBrackets); + Assert.True(criteria.IncludeNumeric); + Assert.Equal(10, criteria.MinLength); + Assert.Equal(100, criteria.MaxLength); + } + + [Fact] + public void PasswordBuilder_Should_BuildPassword() { + var generator = Substitute.For(); + generator.GeneratePasswordWith(Arg.Any()).Returns("GeneratedPassword"); + var password = PasswordBuilder.Create(generator) + .IncludeUppercase() + .BuildPassword(); + + Assert.Equal("GeneratedPassword", password); + } + + [Fact] + public void PasswordBuilder_ShouldNot_SetMinLengthWrong() { + var ex = Assert.Throws(() => { + PasswordBuilder.Create() + .SetMinLength(-23) + .BuildCriteria(); + }); + + Assert.Equal(MessageStrings.MIN_LENGTH_TO_SMALL, ex.Message); + } + + [Fact] + public void PasswordBuilder_ShouldNot_SetMaxLengthWrong() { + var ex = Assert.Throws(() => { + PasswordBuilder.Create() + .SetMaxLength(-23) + .BuildCriteria(); + }); + + Assert.Equal(MessageStrings.MAX_LENGTH_TO_SMALL, ex.Message); + } +} \ No newline at end of file diff --git a/PWManager.UnitTests/Services/AccountServiceTest.cs b/PWManager.UnitTests/Services/AccountServiceTest.cs new file mode 100644 index 0000000..5b7272e --- /dev/null +++ b/PWManager.UnitTests/Services/AccountServiceTest.cs @@ -0,0 +1,190 @@ +using NSubstitute; +using PWManager.Application.Context; +using PWManager.Application.Exceptions; +using PWManager.Application.Services; +using PWManager.Application.Services.Interfaces; +using PWManager.Domain.Entities; +using PWManager.Domain.Repositories; +using PWManager.Domain.Services.Interfaces; + +namespace PWManager.UnitTests.Services; + +public class AccountServiceTest { + + private AccountService _sut; + + [Fact] + public void AccountService_Should_GetAllNames() { + var envWithGroup = MockUserEnvironmentWithGroup(); + + _sut = new AccountService(envWithGroup, null, null, null); + + var groups = _sut.GetCurrentAccountNames(); + + Assert.Equal(2, groups.Count); + Assert.Equal("AccountId", groups[0]); + Assert.Equal("AccountId2", groups[1]); + } + + [Fact] + public void AccountService_Should_AddNewAccount() { + var env = MockUserEnvironmentWithGroup(); + var groupRepo = MockGroupRepo(); + + _sut = new AccountService(env, groupRepo, null, null); + + _sut.AddNewAccount("NewIdentifier", "NewName", "NewPassword"); + + Assert.Equal(3, env.CurrentGroup.Accounts.Count); + Assert.Equal("NewIdentifier", env.CurrentGroup.Accounts[2].Identifier); + Assert.Equal("NewName", env.CurrentGroup.Accounts[2].LoginName); + Assert.Equal("NewPassword", env.CurrentGroup.Accounts[2].Password); + + groupRepo.Received().AddAccountToGroup(Arg.Any(), Arg.Any()); + + } + + [Fact] + public void AccountService_ShouldNot_AddExistingAccount() { + var env = MockUserEnvironmentWithGroup(); + _sut = new AccountService(env, null, null, null); + + var ex = Assert.Throws(() => _sut.AddNewAccount("AccountId", "Name", "password")); + + Assert.Equal(MessageStrings.AccountAlreadyExist("AccountId"), ex.Message); + } + + [Fact] + public void AccountService_ShouldNot_AddWhenFailed() { + var env = MockUserEnvironmentWithGroup(); + var groupRepo = MockGroupRepo(); + groupRepo.AddAccountToGroup(Arg.Any(), Arg.Any()).Returns(false); + _sut = new AccountService(env, groupRepo, null, null); + + var ex = Assert.Throws(() => _sut.AddNewAccount("NewAccountId", "Name", "password")); + + Assert.Equal(MessageStrings.FAILED_ADDING_ACCOUNT, ex.Message); + Assert.Null(env.CurrentGroup.FindByIdentifier("NewAccountId")); + } + + [Fact] + public void AccountService_Should_GetAccountByIdentifier() { + var env = MockUserEnvironmentWithGroup(); + + _sut = new AccountService(env, null, null, null); + var account = _sut.GetAccountByIdentifier("AccountId"); + + Assert.Equal(env.CurrentGroup.Accounts[0], account); + } + + [Fact] + public void AccountService_Should_CopyPassword() { + var env = MockUserEnvironmentWithGroup(); + var clipboard = Substitute.For(); + + _sut = new AccountService(env, null, clipboard, null); + + _sut.CopyPasswordToClipboard("AccountId"); + + clipboard.Received().WriteClipboard(Arg.Is(e => e == "Password1")); + } + + [Fact] + public void AccountService_ShouldNot_CopyNonExistingPassword() { + var env = MockUserEnvironmentWithGroup(); + _sut = new AccountService(env, null, null, null); + + var ex = Assert.Throws(() => _sut.CopyPasswordToClipboard("AccountIdNotExists")); + + Assert.Equal(MessageStrings.ACCOUNT_NOT_FOUND, ex.Message); + } + + [Fact] + public void AccountService_ShouldNot_CopyNonExistingLoginName() { + var env = MockUserEnvironmentWithGroup(); + _sut = new AccountService(env, null, null, null); + + var ex = Assert.Throws(() => _sut.CopyLoginnameToClipboard("AccountIdNotExists")); + + Assert.Equal(MessageStrings.ACCOUNT_NOT_FOUND, ex.Message); + } + + [Fact] + public void AccountService_Should_CopyLoginName() { + var env = MockUserEnvironmentWithGroup(); + var clipboard = Substitute.For(); + + _sut = new AccountService(env, null, clipboard, null); + + _sut.CopyLoginnameToClipboard("AccountId"); + + clipboard.Received().WriteClipboard(Arg.Is(e => e == "Name1")); + } + + [Fact] + public void AccountService_Should_RegeneratePassword() { + var env = MockUserEnvironmentWithGroup(); + var newPassword = "NewPasswordGenerated"; + var passGen = MockPassGenReturns(newPassword); + var groupRepo = MockGroupRepo(); + + _sut = new AccountService(env, groupRepo, null, passGen); + + _sut.RegeneratePassword("AccountId"); + + Assert.Equal(newPassword, env.CurrentGroup.Accounts[0].Password); + groupRepo.Received().UpdateAccountInGroup(Arg.Any(), Arg.Any()); + } + + [Fact] + public void AccountService_ShouldNot_RegenerateNonExistingPassword() { + var env = MockUserEnvironmentWithGroup(); + _sut = new AccountService(env, null, null, null); + + var ex = Assert.Throws(() => _sut.RegeneratePassword("AccountIdNotExists")); + + Assert.Equal(MessageStrings.ACCOUNT_NOT_FOUND, ex.Message); + } + + [Fact] + public void AccountService_Should_DeleteAccount() { + var env = MockUserEnvironmentWithGroup(); + var groupRepo = MockGroupRepo(); + + _sut = new AccountService(env, groupRepo, null, null); + + _sut.DeleteAccount("AccountId"); + + Assert.Null(env.CurrentGroup.FindByIdentifier("AccountId")); + groupRepo.Received().DeleteAccountInGroup(Arg.Any(), Arg.Any()); + } + + [Fact] + public void AccountService_ShouldNot_DeleteNonExistingAccount() { + var env = MockUserEnvironmentWithGroup(); + _sut = new AccountService(env, null, null, null); + + var ex = Assert.Throws(() => _sut.DeleteAccount("AccountIdNotExists")); + + Assert.Equal(MessageStrings.ACCOUNT_NOT_FOUND, ex.Message); + } + + private IGroupRepository MockGroupRepo() { + var groupRepo = Substitute.For(); + groupRepo.AddAccountToGroup(Arg.Any(), Arg.Any()).Returns(true); + groupRepo.UpdateAccountInGroup(Arg.Any(), Arg.Any()).Returns(true); + groupRepo.DeleteAccountInGroup(Arg.Any(), Arg.Any()).Returns(true); + return groupRepo; + } + private IUserEnvironment MockUserEnvironmentWithGroup() { + var env = Substitute.For(); + env.CurrentGroup.Returns(new Group("GroupId", new List() {new Account("AccountId", "Name1", "Password1"), new Account("AccountId2", "Name2", "Password2")} )); + return env; + } + + private IPasswordGeneratorService MockPassGenReturns(string pw) { + var pwGen = Substitute.For(); + pwGen.GeneratePassword().Returns(pw); + return pwGen; + } +} \ No newline at end of file diff --git a/PWManager.UnitTests/Services/GroupServiceTest.cs b/PWManager.UnitTests/Services/GroupServiceTest.cs new file mode 100644 index 0000000..8c7c2d0 --- /dev/null +++ b/PWManager.UnitTests/Services/GroupServiceTest.cs @@ -0,0 +1,94 @@ +using NSubstitute; +using NSubstitute.ReceivedExtensions; +using PWManager.Application.Context; +using PWManager.Application.Exceptions; +using PWManager.Application.Services; +using PWManager.Domain.Entities; +using PWManager.Domain.Repositories; + +namespace PWManager.UnitTests.Services; + +public class GroupServiceTest { + + private GroupService _sut; + + [Fact] + public void GroupService_Should_AddGroup() { + var groupRepo = MockGroupRepo(); + + _sut = new GroupService(null, groupRepo); + + _sut.AddGroup("userIdlel", "NewIdentifier"); + + groupRepo.Received().AddGroup(Arg.Is(e => e.Identifier == "NewIdentifier" && e.UserId == "userIdlel")); + } + + [Fact] + public void GroupService_ShouldNot_AddExistingGroup() { + var groupRepo = MockGroupRepo(); + groupRepo.GetGroup(Arg.Any()).Returns(new Group("", "")); + + _sut = new GroupService(null, groupRepo); + + var ex = Assert.Throws(() => _sut.AddGroup("yes", "yes")); + + Assert.Equal(MessageStrings.GroupAlreadyExist("yes"), ex.Message); + } + + [Fact] + public void GroupService_Should_DeleteGroup() { + var repo = MockGroupRepo(); + + _sut = new GroupService(null, repo); + + _sut.DeleteGroup("identifier"); + + repo.Received().RemoveGroup(Arg.Is(e => e == "identifier")); + } + + [Fact] + public void GroupService_ShouldNot_DeleteGroupFailed() { + var repo = MockGroupRepo(); + repo.RemoveGroup(Arg.Any()).Returns(false); + + _sut = new GroupService(null, repo); + + var ex = Assert.Throws(() => _sut.DeleteGroup("identifier")); + + Assert.Equal(MessageStrings.FailedDeletingGroup("identifier"), ex.Message); + } + + [Fact] + public void GroupService_Should_GetAllNames() { + var repo = MockGroupRepo(); + + _sut = new GroupService(null, repo); + + var names = _sut.GetAllGroupNames(); + + Assert.Equal(2, names.Count); + Assert.Equal("Name1", names[0]); + Assert.Equal("Name2", names[1]); + } + + [Fact] + public void GroupService_Should_SwitchGroup() { + var env = Substitute.For(); + var repo = MockGroupRepo(); + var group = new Group("a", "asd"); + repo.GetGroup(Arg.Any()).Returns(group); + + _sut = new GroupService(env, repo); + + _sut.SwitchGroup("a"); + + env.Received().CurrentGroup = group; + } + + private IGroupRepository MockGroupRepo() { + var groupRepo = Substitute.For(); + groupRepo.RemoveGroup(Arg.Any()).Returns(true); + groupRepo.GetAllGroupNames().Returns(new List { "Name1", "Name2" }); + return groupRepo; + } +} \ No newline at end of file diff --git a/PWManager.UnitTests/Services/SettingsServiceTest.cs b/PWManager.UnitTests/Services/SettingsServiceTest.cs new file mode 100644 index 0000000..9a54085 --- /dev/null +++ b/PWManager.UnitTests/Services/SettingsServiceTest.cs @@ -0,0 +1,102 @@ +using NSubstitute; +using PWManager.Application.Context; +using PWManager.Application.Services; +using PWManager.Domain.Entities; +using PWManager.Domain.Repositories; +using PWManager.Domain.ValueObjects; + +namespace PWManager.UnitTests.Services; + +public class SettingsServiceTest { + + private SettingsService _sut; + + [Fact] + public void SettingsService_Should_ChangeClipboardTimeout() { + var env = MockEnvWithSettings(); + var repo = Substitute.For(); + _sut = new SettingsService(repo,env); + + var time = TimeSpan.FromSeconds(50); + _sut.ChangeClipboardTimeoutSetting(time); + + Assert.Equal(time, env.UserSettings.Timeout.ClipboardTimeOutDuration); + repo.Received().UpdateSettings(Arg.Any()); + } + + [Fact] + public void SettingsService_Should_ChangeAccountTimeout() { + var env = MockEnvWithSettings(); + var repo = Substitute.For(); + _sut = new SettingsService(repo,env); + + var time = TimeSpan.FromSeconds(50); + _sut.ChangeAccountTimeoutSetting(time); + + Assert.Equal(time, env.UserSettings.Timeout.AccountTimeOutDuration); + repo.Received().UpdateSettings(Arg.Any()); + } + + [Fact] + public void SettingsService_Should_ChangeMainGroup() { + var env = MockEnvWithSettings(); + var repo = Substitute.For(); + _sut = new SettingsService(repo,env); + + var main = "newMainGroup"; + _sut.ChangeMainGroupSetting(new MainGroupSetting(main)); + + Assert.Equal(main, env.UserSettings.MainGroup.MainGroupIdentifier); + repo.Received().UpdateSettings(Arg.Any()); + } + + [Fact] + public void SettingsService_Should_ChangePasswordGenerator() { + var env = MockEnvWithSettings(); + var repo = Substitute.For(); + _sut = new SettingsService(repo,env); + + var pw = new PasswordGeneratorCriteria(false, false, false, true, true, true, 2, 5); + _sut.ChangePasswordGenerationCriteria(pw); + + Assert.Equal(pw, env.UserSettings.PwGenCriteria); + repo.Received().UpdateSettings(Arg.Any()); + } + + [Fact] + public void SettingsService_Should_GetSettingsFromEnv() { + var env = MockEnvWithSettings(); + _sut = new SettingsService(null, env); + + var settings = _sut.GetSettings(); + + Assert.Equal(settings, env.UserSettings); + } + + [Fact] + public void SettingsService_Should_GetSettingsFromRepo() { + var env = MockEnvWithSettings(); + var repo = Substitute.For(); + repo.GetSettings().Returns(env.UserSettings); + + env = Substitute.For(); + + _sut = new SettingsService(repo, env); + + var settings = _sut.GetSettings(); + + Assert.Equal(settings, env.UserSettings); + Assert.Equal(settings, repo.GetSettings()); + } + + private IUserEnvironment MockEnvWithSettings() { + var env = Substitute.For(); + env.UserSettings.Returns(new Settings( + "userId", + new PasswordGeneratorCriteria(true, true, true, true, true, true, 5, 10), + new TimeoutSettings(TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(2)), + new MainGroupSetting("main") + )); + return env; + } +} \ No newline at end of file