Skip to content

Commit

Permalink
Implement a basic user record, also improves integration test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
MattRyder committed Mar 10, 2019
1 parent 19783e1 commit 6461ff5
Show file tree
Hide file tree
Showing 51 changed files with 1,351 additions and 190 deletions.
43 changes: 39 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,45 @@ jobs:
build:
docker:
- image: microsoft/dotnet:2.2-sdk
- image: mysql/mysql-server:8.0
environment:
MYSQL_ROOT_HOST: '%'
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_USER: socialite_dbuser
MYSQL_PASSWORD: socialite_pass
MYSQL_DATABASE: socialite_test
steps:
- checkout

- setup_remote_docker

- run:
name: Installing dockerize
command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
environment:
DOCKERIZE_VERSION: v0.6.1

- run:
name: Waiting for MySQL to be ready
command: dockerize -wait tcp://127.0.0.1:3306 -timeout 120s

- run: find .
- run: dotnet restore
- run: dotnet build
- run: dotnet test --no-build /p:CollectCoverage=true /p:Include="[Socialite.*]*" /p:Exclude="[*]Socialite.Infrastructure.Migrations.*" /p:CoverletOutputFormat=lcov /p:CoverletOutput=./lcov.info Socialite.UnitTests
- run: bash <(curl -s https://codecov.io/bash)

- run:
name: Building Socialite
command: dotnet build


- run:
name: Executing unit tests
command: dotnet test /p:CollectCoverage=true /p:Include=\"[Socialite.Domain]*,[Socialite.WebAPI]*\" /p:CoverletOutputFormat=json /p:CoverletOutput=./../coverage/unit.tests.json Socialite.UnitTests

- run:
name: Executing integration tests
command: dotnet test /p:CollectCoverage=true /p:Include=\"[Socialite.Domain]*,[Socialite.WebAPI]*\" /p:CoverletOutputFormat=opencover /p:CoverletOutput=./../coverage/Socialite_coverage.xml /p:MergeWith="./../coverage/unit.tests.json" Socialite.IntegrationTests
environment:
ConnectionStrings__SocialiteIntegrationTests: Server=localhost;Database=socialite_test;Uid=root;AllowUserVariables=True;

- run:
name: Uploading code coverage to CodeCov
command: bash <(curl -s https://codecov.io/bash)
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Socialite
Socialite.WebAPI/Properties/launchSettings.json

lcov.info
coverage/

# User-specific files
*.suo
Expand Down
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Socialite.WebAPI/bin/Debug/netcoreapp2.1/Socialite.WebAPI.dll",
"program": "${workspaceFolder}/Socialite.WebAPI/bin/Debug/netcoreapp2.2/Socialite.WebAPI.dll",
"args": [],
"cwd": "${workspaceFolder}/Socialite.WebAPI",
"stopAtEntry": false,
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM microsoft/dotnet:2.1-sdk AS base
FROM microsoft/dotnet:2.2-sdk AS base
WORKDIR /app
COPY . .

Expand Down
13 changes: 5 additions & 8 deletions Socialite.Domain/AggregateModels/PostAggregate/Post.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
using System;
using Socialite.Domain.Common;
using Socialite.Domain.Events;
using Socialite.Domain.Events.Posts;
using Socialite.Domain.Exceptions;

namespace Socialite.Domain.AggregateModels.PostAggregate
{
public class Post : BaseEntity, IAggregateRoot
{
public DateTime CreatedAt { get; private set; }

/// <summary>
/// Title of this Post
/// </summary>
/// <value>This getter/setter returns a String</value>
public String Title { get; set; }
public String Title { get; private set; }

/// <summary>
/// The condition of this Post
/// </summary>
/// <value>This getter/setter returns an enumeration of PostState</value>
private int? _stateId;
public PostState State
{
get
{
return PostState.FromValue<PostState>(_stateId.Value);
}
get { return PostState.FromValue<PostState>(_stateId); }
private set { _stateId = value.Id; }
}
private int _stateId;

/// <summary>
/// The textual content of this Post
Expand Down
3 changes: 0 additions & 3 deletions Socialite.Domain/AggregateModels/StatusAggregate/Status.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ public class Status : BaseEntity, IAggregateRoot

public string Text { get; private set; }

private DateTime _createdAt;
public DateTime CreatedAt { get { return _createdAt; } }

public Status(string mood, string text)
{
Mood = !String.IsNullOrEmpty(mood) ? mood : throw new StatusDomainException(nameof(mood));
Expand Down
17 changes: 17 additions & 0 deletions Socialite.Domain/AggregateModels/UserAggregate/IUserRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Socialite.Domain.Interfaces;

namespace Socialite.Domain.AggregateModels.UsersAggregate
{
public interface IUserRepository : IRepository<User>
{
List<User> FindAll();

Task<User> FindAsync(int userId);

User Add(User user);

void Delete(User user);
}
}
59 changes: 59 additions & 0 deletions Socialite.Domain/AggregateModels/UserAggregate/User.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using Socialite.Domain.Common;
using Socialite.Domain.Events.Users;

namespace Socialite.Domain.AggregateModels.UsersAggregate
{
public class User : BaseEntity, IAggregateRoot
{
public string Email { get; private set; }

public string Name { get; private set; }

public string PhoneNumber { get; private set; }

public DateTime? BirthDate { get; private set; }

public User(string email, string name)
{
Email = !string.IsNullOrEmpty(email) ? email : throw new UserDomainException(nameof(email));
Name = !string.IsNullOrEmpty(name) ? name : throw new UserDomainException(nameof(name));
}

public void UpdateName(string name)
{
if(string.IsNullOrEmpty(name))
{
throw new UserDomainException(nameof(name));
}

Name = name;

AddEvent(new UserNameChangedEvent(this));
}

public void UpdatePhoneNumber(string phoneNumber)
{
if(string.IsNullOrEmpty(phoneNumber))
{
throw new UserDomainException(nameof(phoneNumber));
}

PhoneNumber = phoneNumber;

AddEvent(new UserPhoneNumberChangedEvent(this));
}

public void UpdateBirthDate(DateTime birthDate)
{
if(birthDate.Subtract(DateTime.Now).Milliseconds > 0)
{
throw new UserDomainException(nameof(birthDate));
}

BirthDate = birthDate;

AddEvent(new UserBirthDateChangedEvent(this));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace Socialite.Domain.AggregateModels.UsersAggregate
{
public class UserDomainException : Exception
{
public UserDomainException(string message) : base(message)
{
}
}
}
7 changes: 6 additions & 1 deletion Socialite.Domain/Common/BaseEntity.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;

namespace Socialite.Domain.Common
{
Expand All @@ -9,6 +10,10 @@ public abstract class BaseEntity
/// </summary>
public int Id { get; set; }

public DateTime CreatedAt { get; private set; }

public DateTime UpdatedAt { get; private set; }

/// <summary>
/// List of Domain Events being raised for this BaseEntity
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Socialite.Domain.AggregateModels.PostAggregate;
using Socialite.Domain.Common;

namespace Socialite.Domain.Events
namespace Socialite.Domain.Events.Posts
{
/// <summary>
/// Event raised when a Post is initially drafted
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Socialite.Domain.AggregateModels.PostAggregate;
using Socialite.Domain.Common;

namespace Socialite.Domain.Events
namespace Socialite.Domain.Events.Posts
{
/// <summary>
/// Raised when a Post is published publically by a user
Expand Down
15 changes: 15 additions & 0 deletions Socialite.Domain/Events/Users/UserBirthDateChangedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Socialite.Domain.AggregateModels.UsersAggregate;
using Socialite.Domain.Common;

namespace Socialite.Domain.Events.Users
{
public class UserBirthDateChangedEvent : BaseEvent
{
public User User { get; private set; }

public UserBirthDateChangedEvent(User user)
{
User = user;
}
}
}
15 changes: 15 additions & 0 deletions Socialite.Domain/Events/Users/UserEmailChangedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Socialite.Domain.AggregateModels.UsersAggregate;
using Socialite.Domain.Common;

namespace Socialite.Domain.Events.Users
{
public class UserEmailChangedEvent : BaseEvent
{
public User User { get; private set; }

public UserEmailChangedEvent(User user)
{
User = user;
}
}
}
15 changes: 15 additions & 0 deletions Socialite.Domain/Events/Users/UserNameChangedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Socialite.Domain.AggregateModels.UsersAggregate;
using Socialite.Domain.Common;

namespace Socialite.Domain.Events.Users
{
public class UserNameChangedEvent : BaseEvent
{
public User User { get; private set; }

public UserNameChangedEvent(User user)
{
User = user;
}
}
}
15 changes: 15 additions & 0 deletions Socialite.Domain/Events/Users/UserPhoneNumberChangedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Socialite.Domain.AggregateModels.UsersAggregate;
using Socialite.Domain.Common;

namespace Socialite.Domain.Events.Users
{
public class UserPhoneNumberChangedEvent : BaseEvent
{
public User User { get; private set; }

public UserPhoneNumberChangedEvent(User user)
{
User = user;
}
}
}
35 changes: 24 additions & 11 deletions Socialite.Infrastructure/Data/SocialiteDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using Socialite.Domain.AggregateModels.StatusAggregate;
using Socialite.Domain.Common;
using Socialite.Infrastructure.Exensions;
using Socialite.Domain.AggregateModels.UsersAggregate;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace Socialite.Infrastructure.Data
{
Expand All @@ -19,7 +21,9 @@ public class SocialiteDbContext : DbContext, IUnitOfWork

public DbSet<Post> Posts { get; set; }

public DbSet<PostState> PostStates { get; set;}
public DbSet<PostState> PostStates { get; set; }

public DbSet<User> Users { get; set; }

public SocialiteDbContext(DbContextOptions<SocialiteDbContext> contextOptions, IConfiguration configuration, IMediator mediator)
: base(contextOptions)
Expand All @@ -30,25 +34,34 @@ public SocialiteDbContext(DbContextOptions<SocialiteDbContext> contextOptions, I

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
const string DATETIME_NOW_FUNC = "CURRENT_TIMESTAMP(6)";

modelBuilder.Entity<Status>().Ignore(s => s.Events);
modelBuilder.Entity<Status>().Property(s => s.Id).ValueGeneratedOnAdd();
modelBuilder.Entity<Status>().Property(s => s.CreatedAt).HasDefaultValueSql(DATETIME_NOW_FUNC);
SetupEntityModel<Status>(modelBuilder);
SetupEntityModel<Post>(modelBuilder);
SetupEntityModel<User>(modelBuilder);

modelBuilder.Entity<Post>().Ignore(p => p.Events);
modelBuilder.Entity<Post>().Property(p => p.Id).ValueGeneratedOnAdd();
modelBuilder.Entity<Post>().Property(p => p.CreatedAt).HasDefaultValueSql(DATETIME_NOW_FUNC);
modelBuilder.Entity<User>().Property(u => u.Email).IsRequired();
modelBuilder.Entity<User>().Property(u => u.Name).IsRequired();

var postStateSeedData = PostState.List().ToArray();
var postStateSeedData = Socialite.Domain.AggregateModels.PostAggregate.PostState.List().ToArray();
modelBuilder.Entity<PostState>().HasData(postStateSeedData);
}

private void SetupEntityModel<T>(ModelBuilder modelBuilder) where T : BaseEntity
{
const string DATETIME_NOW_FUNC = "CURRENT_TIMESTAMP(6)";

var entityModel = modelBuilder.Entity<T>();

entityModel.Ignore(m => m.Events);
entityModel.Property(m => m.Id).ValueGeneratedOnAdd();
entityModel.Property(m => m.CreatedAt).HasDefaultValueSql(DATETIME_NOW_FUNC).ValueGeneratedOnAdd();
entityModel.Property(m => m.UpdatedAt).HasDefaultValueSql(DATETIME_NOW_FUNC).ValueGeneratedOnAddOrUpdate();
}

public async Task<bool> SaveEntitiesAsync()
{
await _mediator.DispatchDomainEventsAsync(this);

var result = await base.SaveChangesAsync();
await base.SaveChangesAsync();

return true;
}
Expand Down
Loading

0 comments on commit 6461ff5

Please sign in to comment.