Skip to content

Commit

Permalink
Complete tests for CreateTournament method #484
Browse files Browse the repository at this point in the history
  • Loading branch information
sussexrick committed Sep 22, 2022
1 parent 955e4f9 commit 0febd3e
Show file tree
Hide file tree
Showing 7 changed files with 386 additions and 154 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ private SqlServerPlayerRepository CreateRepository()
[Fact]
public async Task ProcessAsyncUpdates_allows_3_retries_for_SQL_timeouts_and_logs_warnings()
{
_dapperWrapper.Setup(x => x.QueryAsync<string>(_databaseConnection.Object, SqlServerPlayerRepository.PROCESS_ASYNC_STORED_PROCEDURE, CommandType.StoredProcedure)).Throws(SqlExceptionFactory.Create(SqlExceptionType.Timeout));
_dapperWrapper.Setup(x => x.QueryAsync<string>(SqlServerPlayerRepository.PROCESS_ASYNC_STORED_PROCEDURE, CommandType.StoredProcedure, _databaseConnection.Object)).Throws(SqlExceptionFactory.Create(SqlExceptionType.Timeout));

var repo = CreateRepository();

await repo.ProcessAsyncUpdatesForLinkingAndUnlinkingPlayersToMemberAccounts();

_dapperWrapper.Verify(x => x.QueryAsync<string>(_databaseConnection.Object, SqlServerPlayerRepository.PROCESS_ASYNC_STORED_PROCEDURE, CommandType.StoredProcedure), Times.Exactly(4));
_dapperWrapper.Verify(x => x.QueryAsync<string>(SqlServerPlayerRepository.PROCESS_ASYNC_STORED_PROCEDURE, CommandType.StoredProcedure, _databaseConnection.Object), Times.Exactly(4));
_logger.Verify(x => x.Warn(SqlServerPlayerRepository.LOG_TEMPLATE_WARN_SQL_TIMEOUT, It.IsAny<int>()), Times.Exactly(4));
}

Expand All @@ -63,7 +63,7 @@ public async Task ProcessAsyncUpdates_clears_cache_for_affected_routes()
var affectedRoutesFirstIteration = new[] { "/players/one", "/players/two" };
var affectedRoutesSecondIteration = new[] { "/players/three", "/players/four" };

_dapperWrapper.SetupSequence(x => x.QueryAsync<string>(_databaseConnection.Object, SqlServerPlayerRepository.PROCESS_ASYNC_STORED_PROCEDURE, CommandType.StoredProcedure))
_dapperWrapper.SetupSequence(x => x.QueryAsync<string>(SqlServerPlayerRepository.PROCESS_ASYNC_STORED_PROCEDURE, CommandType.StoredProcedure, _databaseConnection.Object))
.Returns(Task.FromResult(affectedRoutesFirstIteration as IEnumerable<string>))
.Returns(Task.FromResult(affectedRoutesSecondIteration as IEnumerable<string>))
.Returns(Task.FromResult(Array.Empty<string>() as IEnumerable<string>));
Expand All @@ -88,7 +88,7 @@ public async Task ProcessAsyncUpdates_logs_affected_routes()
{
var affectedRoutes = new[] { "/players/one", "/players/two" };

_dapperWrapper.SetupSequence(x => x.QueryAsync<string>(_databaseConnection.Object, SqlServerPlayerRepository.PROCESS_ASYNC_STORED_PROCEDURE, CommandType.StoredProcedure))
_dapperWrapper.SetupSequence(x => x.QueryAsync<string>(SqlServerPlayerRepository.PROCESS_ASYNC_STORED_PROCEDURE, CommandType.StoredProcedure, _databaseConnection.Object))
.Returns(Task.FromResult(affectedRoutes as IEnumerable<string>))
.Returns(Task.FromResult(Array.Empty<string>() as IEnumerable<string>));

Expand All @@ -103,13 +103,13 @@ public async Task ProcessAsyncUpdates_logs_affected_routes()
[Fact]
public async Task ProcessAsyncUpdates_logs_SQL_connection_error_and_exits()
{
_dapperWrapper.Setup(x => x.QueryAsync<string>(_databaseConnection.Object, SqlServerPlayerRepository.PROCESS_ASYNC_STORED_PROCEDURE, CommandType.StoredProcedure)).Throws(SqlExceptionFactory.Create(SqlExceptionType.Connection));
_dapperWrapper.Setup(x => x.QueryAsync<string>(SqlServerPlayerRepository.PROCESS_ASYNC_STORED_PROCEDURE, CommandType.StoredProcedure, _databaseConnection.Object)).Throws(SqlExceptionFactory.Create(SqlExceptionType.Connection));

var repo = CreateRepository();

await repo.ProcessAsyncUpdatesForLinkingAndUnlinkingPlayersToMemberAccounts();

_dapperWrapper.Verify(x => x.QueryAsync<string>(_databaseConnection.Object, SqlServerPlayerRepository.PROCESS_ASYNC_STORED_PROCEDURE, CommandType.StoredProcedure), Times.Exactly(1));
_dapperWrapper.Verify(x => x.QueryAsync<string>(SqlServerPlayerRepository.PROCESS_ASYNC_STORED_PROCEDURE, CommandType.StoredProcedure, _databaseConnection.Object), Times.Exactly(1));
_logger.Verify(x => x.Error(
SqlServerPlayerRepository.LOG_TEMPLATE_ERROR_SQL_EXCEPTION,
SqlExceptionFactory.ERROR_ESTABLISHING_CONNECTION
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using AngleSharp.Css.Dom;
using Ganss.XSS;
using Moq;
using Stoolball.Logging;
using Stoolball.Matches;
using Stoolball.Routing;
using Stoolball.Teams;
using Xunit;
using static Stoolball.Constants;

namespace Stoolball.Data.SqlServer.UnitTests
{
public class SqlServerTournamentRepositoryUnitTests
{
private readonly Mock<IDatabaseConnectionFactory> _connectionFactory = new();
private readonly Mock<IDbConnection> _databaseConnection = new();
private readonly Mock<IDbTransaction> _databaseTransaction = new();
private readonly Mock<IDapperWrapper> _dapperWrapper = new();
private readonly Mock<IAuditRepository> _auditRepository = new();
private readonly Mock<ILogger<SqlServerTournamentRepository>> _logger = new();
private readonly Mock<IRouteGenerator> _routeGenerator = new();
private readonly Mock<IRedirectsRepository> _redirectsRepository = new();
private readonly Mock<ITeamRepository> _teamRepository = new();
private readonly Mock<IMatchRepository> _matchRepository = new();
private readonly Mock<IHtmlSanitizer> _htmlSanitizer = new();
private readonly Mock<IStoolballEntityCopier> _copier = new();

public SqlServerTournamentRepositoryUnitTests()
{
_connectionFactory.Setup(x => x.CreateDatabaseConnection()).Returns(_databaseConnection.Object);
_databaseConnection.Setup(x => x.BeginTransaction()).Returns(_databaseTransaction.Object);

_htmlSanitizer.Setup(x => x.AllowedTags).Returns(new HashSet<string>());
_htmlSanitizer.Setup(x => x.AllowedAttributes).Returns(new HashSet<string>());
_htmlSanitizer.Setup(x => x.AllowedCssProperties).Returns(new HashSet<string>());
_htmlSanitizer.Setup(x => x.AllowedAtRules).Returns(new HashSet<CssRuleType>());
}

private SqlServerTournamentRepository CreateRepository()
{
return new SqlServerTournamentRepository(
_connectionFactory.Object,
_dapperWrapper.Object,
_auditRepository.Object,
_logger.Object,
_routeGenerator.Object,
_redirectsRepository.Object,
_teamRepository.Object,
_matchRepository.Object,
_htmlSanitizer.Object,
_copier.Object);
}

[Fact]
public async Task Create_tournament_audits_and_logs()
{
var memberKey = Guid.NewGuid();
var memberName = "Member name";

var original = new Tournament
{
TournamentId = Guid.NewGuid(),
TournamentName = "Example tournament",
StartTime = DateTime.Now.AddDays(1),
TournamentRoute = "/tournaments/example-tournament",
MemberKey = memberKey
};

var auditable = new Tournament
{
TournamentId = Guid.NewGuid(),
TournamentName = original.TournamentName,
StartTime = original.StartTime,
TournamentRoute = original.TournamentRoute,
MemberKey = memberKey
};

var redacted = new Tournament
{
TournamentId = Guid.NewGuid(),
TournamentName = original.TournamentName,
StartTime = original.StartTime,
TournamentRoute = original.TournamentRoute,
MemberKey = memberKey
};

_copier.Setup(x => x.CreateAuditableCopy(original)).Returns(auditable);
_copier.Setup(x => x.CreateRedactedCopy(auditable)).Returns(redacted);

var repo = CreateRepository();

var createdTournament = await repo.CreateTournament(original, memberKey, memberName);

_auditRepository.Verify(x => x.CreateAudit(It.Is<AuditRecord>(audit =>
audit.Action == AuditAction.Create &&
audit.MemberKey == memberKey &&
audit.ActorName == memberName &&
audit.EntityUri == auditable.EntityUri &&
audit.State.Contains(auditable.TournamentId.Value.ToString()) &&
audit.RedactedState.Contains(redacted.TournamentId.Value.ToString()) &&
audit.AuditDate.Date == DateTime.UtcNow.Date
), _databaseTransaction.Object), Times.Once);

_logger.Verify(x => x.Info(LoggingTemplates.Created, redacted, memberName, memberKey, typeof(SqlServerTournamentRepository), nameof(SqlServerTournamentRepository.CreateTournament)), Times.Once);

}
}
}
15 changes: 14 additions & 1 deletion Stoolball.Data.SqlServer/DapperWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,22 @@ namespace Stoolball.Data.SqlServer
public class DapperWrapper : IDapperWrapper
{
/// <inheritdoc/>
public async Task<IEnumerable<T>> QueryAsync<T>(IDbConnection connection, string sql, CommandType commandType)
public async Task<IEnumerable<T>> QueryAsync<T>(string sql, CommandType commandType, IDbConnection connection)
{
return await connection.QueryAsync<T>(sql, commandType);
}

/// <inheritdoc/>
public async Task<IEnumerable<T>> QueryAsync<T>(string sql, object param, IDbTransaction transaction)
{
return await transaction.Connection.QueryAsync<T>(sql, param, transaction);
}

/// <inheritdoc/>
public async Task<int> ExecuteAsync(string sql, object param, IDbTransaction transaction)
{
return await transaction.Connection.ExecuteAsync(sql, param, transaction);
}

}
}
24 changes: 22 additions & 2 deletions Stoolball.Data.SqlServer/IDapperWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,30 @@ public interface IDapperWrapper
/// Execute a query asynchronously using Task
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="connection"></param>
/// <param name="sql"></param>
/// <param name="commandType"></param>
/// <param name="connection"></param>
/// <returns></returns>
Task<IEnumerable<T>> QueryAsync<T>(string sql, CommandType commandType, IDbConnection connection);

/// <summary>
/// Execute a query asynchronously using Task
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sql"></param>
/// <param name="param"></param>
/// <param name="transaction"></param>
/// <returns></returns>
Task<IEnumerable<T>> QueryAsync<T>(IDbConnection connection, string sql, CommandType commandType);
Task<IEnumerable<T>> QueryAsync<T>(string sql, object param, IDbTransaction transaction);

/// <summary>
/// Execute a command asynchronously using Task
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sql"></param>
/// <param name="param"></param>
/// <param name="transaction"></param>
/// <returns>The number of rows affected</returns>
Task<int> ExecuteAsync(string sql, object param, IDbTransaction transaction);
}
}
2 changes: 1 addition & 1 deletion Stoolball.Data.SqlServer/SqlServerPlayerRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ public async Task ProcessAsyncUpdatesForLinkingAndUnlinkingPlayersToMemberAccoun
try
{
retry = false;
affectedRoutes = await _dapperWrapper.QueryAsync<string>(connection, "usp_Link_Player_To_Member_Async_Update", commandType: CommandType.StoredProcedure).ConfigureAwait(false);
affectedRoutes = await _dapperWrapper.QueryAsync<string>("usp_Link_Player_To_Member_Async_Update", commandType: CommandType.StoredProcedure, connection: connection).ConfigureAwait(false);
foreach (var route in affectedRoutes)
{
await _playerCacheClearer.ClearCacheFor(new Player { PlayerRoute = route });
Expand Down
Loading

0 comments on commit 0febd3e

Please sign in to comment.