Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Domain navigation #17

Merged
merged 4 commits into from
Oct 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 205 additions & 0 deletions PWManager.Domain/Abstractions/Result.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
namespace PWManager.Domain.Abstractions;

public enum ResultState : byte {
Faulted,
Success
}

public readonly struct Result<A> : IEquatable<Result<A>>, IComparable<Result<A>> {

internal readonly ResultState State;
internal readonly A Value;
readonly Exception? exception;

public Result(A value) {
State = ResultState.Success;
this.Value = value;
exception = null;
}

public Result(Exception ex) {

Check warning on line 20 in PWManager.Domain/Abstractions/Result.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field 'Value' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 20 in PWManager.Domain/Abstractions/Result.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field 'Value' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 20 in PWManager.Domain/Abstractions/Result.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field 'Value' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
State = ResultState.Faulted;
this.exception = ex;
Value = default;

Check warning on line 23 in PWManager.Domain/Abstractions/Result.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference assignment.

Check warning on line 23 in PWManager.Domain/Abstractions/Result.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference assignment.

Check warning on line 23 in PWManager.Domain/Abstractions/Result.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference assignment.
}

public static implicit operator Result<A>(A value) => new(value);

public static implicit operator Result<A>(Exception ex) => new(ex);

public static implicit operator Exception(Result<A> result) => ExceptionOrDefault(result.exception);

public static implicit operator Result(Result<A> res) =>
res.IsFaulted ? new(ExceptionOrDefault(res.exception)) : new();


public bool IsFaulted => State == ResultState.Faulted;

public bool IsSuccess => State == ResultState.Success;

public override string ToString() =>
IsFaulted ?
exception?.ToString() ?? "Undefined Exception!" :
Value?.ToString() ?? "(null)";

public bool Equals(Result<A> b) {
if (IsFaulted && !b.IsFaulted || !IsFaulted && b.IsFaulted) return false;
if (IsFaulted && b.IsFaulted) {
if (exception == null && b.exception == null)
return true;
return exception?.Equals(b.exception) ?? false;
}
if (Value == null && b.Value == null)
return true;
return Value?.Equals(b.Value) ?? false;
}

public static bool operator ==(Result<A> a, Result<A> b) =>
Equals(a, b);

public static bool operator !=(Result<A> a, Result<A> b) =>
!(a == b);

public A IfFail(A defaultValue) =>
IsFaulted ? defaultValue : Value;

public A IfFail(Func<Exception, A> f) =>
IsFaulted ? f(ExceptionOrDefault(exception)) : Value;

public void IfSucc(Action<A> f) {
if (IsSuccess)
f(Value);
}

public R Match<R>(Func<A, R> Succ, Func<Exception, R> Fail) =>
IsFaulted ? Fail(ExceptionOrDefault(exception)) : Succ(Value);

public Result<B> Map<B>(Func<A, B> map) =>
IsFaulted ? new Result<B>(ExceptionOrDefault(exception)) : new Result<B>(map(Value));

public async Task<Result<B>> MapAsync<B>(Func<A, Task<B>> map) =>
IsFaulted ? new Result<B>(ExceptionOrDefault(exception)) : new Result<B>(await map(Value));

public int CompareTo(Result<A> b) {
if (IsFaulted && b.IsFaulted) return 0;
if (IsFaulted && !b.IsFaulted) return -1;
if (!IsFaulted && b.IsFaulted) return 1;
return Comparer<A>.Default.Compare(Value, b.Value);
}

public static bool operator <(Result<A> a, Result<A> b) =>
a.CompareTo(b) < 0;
public static bool operator <=(Result<A> a, Result<A> b) =>
a.CompareTo(b) <= 0;
public static bool operator >(Result<A> a, Result<A> b) =>
a.CompareTo(b) > 0;
public static bool operator >=(Result<A> a, Result<A> b) =>
a.CompareTo(b) >= 0;

public override bool Equals(object? obj) {
return obj != null && obj is Result<A> && Equals((Result<A>)obj);
}

public override int GetHashCode() {
return HashCode.Combine(IsSuccess, Value, exception);
}

private static Exception ExceptionOrDefault(Exception? ex) =>
ex ?? new Exception("Undefined Exception!");
}

public readonly struct Result : IEquatable<Result>, IComparable<Result> {

internal readonly ResultState State;
readonly Exception? exception;

public Result() {
State = ResultState.Success;
exception = null;
}

public Result(Exception ex) {
State = ResultState.Faulted;
this.exception = ex;
}

public static implicit operator Result(bool value) => value ? new() : new(new Exception("Undefined Exception!"));

public static implicit operator Result(Exception ex) => new(ex);

public static implicit operator Exception(Result result) => ExceptionOrDefault(result.exception);


public static implicit operator Result<bool>(Result res) =>
res.IsFaulted ? new(ExceptionOrDefault(res.exception)) : new(true);


public bool IsFaulted => State == ResultState.Faulted;

public bool IsSuccess => State == ResultState.Success;

public override string ToString() =>
IsFaulted ?
exception?.ToString() ?? "Undefined Exception!" :
"Successful";

public bool Equals(Result b) {
if (IsFaulted && !b.IsFaulted || !IsFaulted && b.IsFaulted) return false;
if (IsFaulted && b.IsFaulted) {
if (exception == null && b.exception == null)
return true;
return exception?.Equals(b.exception) ?? false;
}
return true;
}

public static bool operator ==(Result a, Result b) =>
Equals(a, b);

public static bool operator !=(Result a, Result b) =>
!(a == b);

public void IfFail(Action<Exception> f) {
if (IsFaulted)
f(ExceptionOrDefault(exception));
}
public void IfFail(Action f) {
if (IsFaulted)
f();
}

public void IfSucc(Action f) {
if (IsSuccess)
f();
}

public R Match<R>(Func<R> Succ, Func<Exception, R> Fail) =>
IsFaulted ? Fail(ExceptionOrDefault(exception)) : Succ();

public int CompareTo(Result b) {
if (IsFaulted && b.IsFaulted) return 0;
if (IsFaulted && !b.IsFaulted) return -1;
if (!IsFaulted && b.IsFaulted) return 1;
return 0;
}

public static bool operator <(Result a, Result b) =>
a.CompareTo(b) < 0;
public static bool operator <=(Result a, Result b) =>
a.CompareTo(b) <= 0;
public static bool operator >(Result a, Result b) =>
a.CompareTo(b) > 0;
public static bool operator >=(Result a, Result b) =>
a.CompareTo(b) >= 0;

private static Exception ExceptionOrDefault(Exception? ex) =>
ex ?? new Exception("Undefined Exception!");

public override bool Equals(object? obj) {
return obj != null && obj is Result && Equals((Result)obj);
}

public override int GetHashCode() {
return HashCode.Combine(IsSuccess, exception);
}
}
32 changes: 25 additions & 7 deletions PWManager.Domain/Entities/Group.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,38 @@

namespace PWManager.Domain.Entities {
public class Group : Entity {
public string UserID { get; set; }
public string UserId { get; set; }
public List<Account> Accounts { get; set; }

public Group(string userID) : this(userID, new List<Account>()) {}
public Group(string id, DateTimeOffset created, DateTimeOffset updated, string userID) : this(id, created, updated, userID, new List<Account>()) {}
public Group(string userId) : this(userId, new List<Account>()) {}
public Group(string id, DateTimeOffset created, DateTimeOffset updated, string userId) : this(id, created, updated, userId, new List<Account>()) {}

public Group(string userID, List<Account> accounts) : base() {
UserID = userID;
public Group(string userId, List<Account> accounts) : base() {
UserId = userId;
Accounts = accounts;
}

public Group(string id, DateTimeOffset created, DateTimeOffset updated, string userID, List<Account> accounts) : base(id, created, updated) {
UserID = userID;
public Group(string id, DateTimeOffset created, DateTimeOffset updated, string userId, List<Account> accounts) : base(id, created, updated) {
UserId = userId;
Accounts = accounts;
}

public void AddAccount(Account acc) {
Accounts.Add(acc);
}

public Account? GetAccount(string accId) {
return Accounts.Find(e => e.Id.Equals(accId));
}

public bool RemoveAccount(string accId) {
var acc = GetAccount(accId);
return acc is not null && RemoveAccount(acc);
}

public bool RemoveAccount(Account acc) {
return Accounts.Remove(acc);
}

}
}
12 changes: 6 additions & 6 deletions PWManager.Domain/Entities/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@

namespace PWManager.Domain.Entities {
public class Settings : Entity {
public string UserID { get; set; }
public PasswordGeneratorCriteria PWGenCriteria { get; set; }
public string UserId { get; set; }
public PasswordGeneratorCriteria PwGenCriteria { get; set; }
public ClipboardTimeoutSetting ClipboardTimeout { get; set; }
public MainGroupSetting MainGroup { get; set; }

public Settings(string userId, PasswordGeneratorCriteria pwGenCriteria, ClipboardTimeoutSetting clipboardTimeout, MainGroupSetting mainGroup) : base(){
UserID = userId;
PWGenCriteria = pwGenCriteria;
UserId = userId;
PwGenCriteria = pwGenCriteria;
ClipboardTimeout = clipboardTimeout;
MainGroup = mainGroup;
}
public Settings(string id, DateTimeOffset created, DateTimeOffset updated, string userId, PasswordGeneratorCriteria pwGenCriteria, ClipboardTimeoutSetting clipboardTimeout, MainGroupSetting mainGroup) : base(id, created, updated) {
UserID = userId;
PWGenCriteria = pwGenCriteria;
UserId = userId;
PwGenCriteria = pwGenCriteria;
ClipboardTimeout = clipboardTimeout;
MainGroup = mainGroup;
}
Expand Down
96 changes: 96 additions & 0 deletions PWManager.UnitTests/Domain/Entities/GroupTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using PWManager.Domain.Entities;

namespace PWManager.UnitTests.Domain.Entities;

public class GroupTests {

private Group _sut;

public GroupTests() {
_sut = new Group("test-userId");
}

#region AddAccount
[Fact]
public void Group_Should_AddNewAccount() {
var acc = new Account("Hello", "Password");
_sut.AddAccount(acc);

Assert.True(_sut.Accounts.Count == 1);
Assert.Equal(acc, _sut.Accounts[0]);
}
#endregion
#region GetAccount
[Fact]
public void Group_Should_GetValidAccount() {
AddAccountsToGroup(_sut, 5);
var acc = _sut.Accounts[3];

var returnedAccount = _sut.GetAccount(acc.Id);

Assert.Equal(acc, returnedAccount);
}

[Fact]
public void Group_ShouldNot_GetNonExistingAccount() {
AddAccountsToGroup(_sut, 5);

var returnedAccount = _sut.GetAccount("$NON_EXISTENT_ID$");

Assert.Null(returnedAccount);
}

[Fact]
public void Group_ShouldNot_GetAccountOnEmptyAccounts() {
var returnedAccount = _sut.GetAccount("$NON_EXISTING_ID$");

Assert.Null(returnedAccount);
}
#endregion
#region Remove Account
[Fact]
public void Group_Should_RemoveAccountById() {
AddAccountsToGroup(_sut, 5);
var acc = _sut.Accounts[3];

bool ret = _sut.RemoveAccount(acc.Id);

Assert.True(ret);
Assert.True(_sut.Accounts.Count == 4);
Assert.DoesNotContain(_sut.Accounts, account => account.Id.Equals(acc.Id));
}
[Fact]
public void Group_Should_RemoveAccount() {
AddAccountsToGroup(_sut, 5);
var acc = _sut.Accounts[3];

var ret = _sut.RemoveAccount(acc);

Assert.True(ret);
Assert.True(_sut.Accounts.Count == 4);
Assert.DoesNotContain(_sut.Accounts, account => account.Equals(acc));
}

[Fact]
public void Group_ShouldNot_RemoveNonExistingAccount() {
AddAccountsToGroup(_sut, 5);

var ret = _sut.RemoveAccount("$NON_EXISTING_ID$");

Assert.False(ret);
Assert.True(_sut.Accounts.Count == 5);
}
[Fact]
public void Group_ShouldNot_RemoveEmptyAccounts() {
var ret = _sut.RemoveAccount("$NON_EXISTING_ID$");

Assert.False(ret);
}
#endregion

private static void AddAccountsToGroup(Group g, int numAccounts) {
for (int i = 0; i < numAccounts; ++i) {
g.Accounts.Add(new Account($"Cool Account Name {i}", "This is a very secure password"));
}
}
}
File renamed without changes.
7 changes: 5 additions & 2 deletions PWManager.UnitTests/PWManager.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand All @@ -24,6 +24,9 @@

<ItemGroup>
<ProjectReference Include="..\PWManager.Application\PWManager.Application.csproj" />
<ProjectReference Include="..\PWManager.CLI\PWManager.CLI.csproj" />
<ProjectReference Include="..\PWManager.Data\PWManager.Data.csproj" />
<ProjectReference Include="..\PWManager.Domain\PWManager.Domain.csproj" />
</ItemGroup>

</Project>
Loading
Loading