Skip to content

Commit

Permalink
Update API to return error code when database is missing required obj…
Browse files Browse the repository at this point in the history
…ects. Update documentation. Change the stats download database class to return a DatabaseFailedReason instead of reusing the FailedReason enum.
  • Loading branch information
Miguel Molina Jr committed Aug 20, 2018
1 parent 9b3f8af commit b047df0
Show file tree
Hide file tree
Showing 17 changed files with 153 additions and 40 deletions.
34 changes: 28 additions & 6 deletions Api/StatsDownloadApi.Core.Tests/TestStatsDownloadApiProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using NSubstitute;
using NUnit.Framework;
using StatsDownload.Core.Interfaces;
using StatsDownload.Core.Interfaces.Enums;

[TestFixture]
public class TestStatsDownloadApiProvider
Expand All @@ -14,7 +15,7 @@ public class TestStatsDownloadApiProvider
public void SetUp()
{
statsDownloadApiDatabaseServiceMock = Substitute.For<IStatsDownloadApiDatabaseService>();
statsDownloadApiDatabaseServiceMock.IsAvailable().Returns(true);
statsDownloadApiDatabaseServiceMock.IsAvailable().Returns((true, DatabaseFailedReason.None));

statsDownloadApiTokenDistributionServiceMock = Substitute.For<IStatsDownloadApiTokenDistributionService>();

Expand Down Expand Up @@ -54,7 +55,8 @@ public void Constructor_WhenNullDependencyProvided_ThrowsException()
[Test]
public void GetDistro_WhenDatabaseIsUnavailable_ReturnsDatabaseUnavailableResponse()
{
statsDownloadApiDatabaseServiceMock.IsAvailable().Returns(false);
statsDownloadApiDatabaseServiceMock
.IsAvailable().Returns((false, DatabaseFailedReason.DatabaseUnavailable));

GetDistroResponse actual = InvokeGetDistro();

Expand All @@ -81,7 +83,8 @@ public void GetDistro_WhenEndDateInputIsTodayOrFutureDate_ReturnsEndDateUnsearch
[Test]
public void GetDistro_WhenErrorsOccurs_ReturnsErrorResponse()
{
statsDownloadApiDatabaseServiceMock.IsAvailable().Returns(false);
statsDownloadApiDatabaseServiceMock
.IsAvailable().Returns((false, DatabaseFailedReason.DatabaseUnavailable));

GetDistroResponse actual = InvokeGetDistro(null, null, amountMock);

Expand Down Expand Up @@ -213,7 +216,8 @@ public void GetDistro_WhenZeroAmountIsProvided_ReturnsZeroAmountResponse()
[Test]
public void GetMemberStats_WhenDatabaseIsUnavailable_ReturnsDatabaseUnavailableResponse()
{
statsDownloadApiDatabaseServiceMock.IsAvailable().Returns(false);
statsDownloadApiDatabaseServiceMock
.IsAvailable().Returns((false, DatabaseFailedReason.DatabaseUnavailable));

GetMemberStatsResponse actual = InvokeGetMemberStats();

Expand Down Expand Up @@ -241,7 +245,8 @@ public void GetMemberStats_WhenEndDateInputIsTodayOrFutureDate_ReturnsEndDateUns
[Test]
public void GetMemberStats_WhenErrorsOccurs_ReturnsErrorsResponse()
{
statsDownloadApiDatabaseServiceMock.IsAvailable().Returns(false);
statsDownloadApiDatabaseServiceMock
.IsAvailable().Returns((false, DatabaseFailedReason.DatabaseUnavailable));

GetMemberStatsResponse actual = InvokeGetMemberStats(null, null);

Expand Down Expand Up @@ -323,7 +328,8 @@ public void GetMemberStats_WhenStartDateIsLaterThanEndDate_ReturnsInvalidDateRan
[Test]
public void GetTeams_WhenDatabaseIsUnavailable_ReturnsDatabaseUnavailableResponse()
{
statsDownloadApiDatabaseServiceMock.IsAvailable().Returns(false);
statsDownloadApiDatabaseServiceMock
.IsAvailable().Returns((false, DatabaseFailedReason.DatabaseUnavailable));

GetTeamsResponse actual = InvokeGetTeams();

Expand All @@ -335,6 +341,22 @@ public void GetTeams_WhenDatabaseIsUnavailable_ReturnsDatabaseUnavailableRespons
Constants.ErrorMessages.DatabaseUnavailableMessage));
}

[Test]
public void GetTeams_WhenDatabaseMissingRequiredObjects_ReturnsDatabaseMissingRequiredObjectsResponse()
{
statsDownloadApiDatabaseServiceMock
.IsAvailable().Returns((false, DatabaseFailedReason.DatabaseMissingRequiredObjects));

GetTeamsResponse actual = InvokeGetTeams();

Assert.That(actual.Success, Is.False);
Assert.That(actual.Errors?.Count, Is.EqualTo(1));
Assert.That(actual.Errors?[0].ErrorCode, Is.EqualTo(ApiErrorCode.DatabaseMissingRequiredObjects));
Assert.That(actual.Errors?[0].ErrorMessage,
Is.EqualTo(
Constants.ErrorMessages.DatabaseMissingRequiredObjectsMessage));
}

[Test]
public void GetTeams_WhenInvoked_ReturnsSuccessGetTeamsResponse()
{
Expand Down
14 changes: 12 additions & 2 deletions Api/StatsDownloadApi.Core/StatsDownloadApiProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Interfaces;
using Interfaces.DataTransfer;
using StatsDownload.Core.Interfaces;
using StatsDownload.Core.Interfaces.Enums;

public class StatsDownloadApiProvider : IStatsDownloadApiService
{
Expand Down Expand Up @@ -133,9 +134,18 @@ private void ValidateAmount(int? amount, IList<ApiError> errors)

private void ValidateDatabaseIsAvailable(IList<ApiError> errors)
{
if (!statsDownloadApiDatabaseService.IsAvailable())
(bool isAvailable, DatabaseFailedReason reason) = statsDownloadApiDatabaseService.IsAvailable();

if (!isAvailable)
{
errors.Add(Constants.ApiErrors.DatabaseUnavailable);
if (reason == DatabaseFailedReason.DatabaseUnavailable)
{
errors.Add(Constants.ApiErrors.DatabaseUnavailable);
}
else if (reason == DatabaseFailedReason.DatabaseMissingRequiredObjects)
{
errors.Add(Constants.ApiErrors.DatabaseMissingRequiredObjects);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
using NSubstitute;
using NUnit.Framework;
using StatsDownload.Core.Interfaces;
using StatsDownload.Core.Interfaces.Enums;
using StatsDownload.Database.Tests;
using Constants = Database.Constants;

[TestFixture]
public class TestStatsDownloadApiDatabaseProvider
Expand Down Expand Up @@ -224,15 +226,18 @@ public void GetTeams_WhenInvoked_GetsTeams()
Assert.That(actual[1].TeamName, Is.EqualTo("TeamName2"));
}

[TestCase(true)]
[TestCase(false)]
public void IsAvailable_WhenInvoked_ReturnsIsAvailable(bool expected)
[TestCase(true, DatabaseFailedReason.None)]
[TestCase(false, DatabaseFailedReason.DatabaseMissingRequiredObjects)]
public void IsAvailable_WhenInvoked_ReturnsIsAvailable(bool expectedIsAvailable,
DatabaseFailedReason expectedReason)
{
statsDownloadDatabaseServiceMock.IsAvailable().Returns(expected);
statsDownloadDatabaseServiceMock.IsAvailable(Constants.StatsDownloadApiDatabase.ApiObjects)
.Returns((expectedIsAvailable, expectedReason));

bool actual = systemUnderTest.IsAvailable();
(bool isAvailable, DatabaseFailedReason reason) actual = systemUnderTest.IsAvailable();

Assert.That(actual, Is.EqualTo(expected));
Assert.That(actual.isAvailable, Is.EqualTo(expectedIsAvailable));
Assert.That(actual.reason, Is.EqualTo(expectedReason));
}

private IStatsDownloadApiDatabaseService NewStatsDownloadApiDatabaseProvider(
Expand Down
7 changes: 7 additions & 0 deletions Api/StatsDownloadApi.Database/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ public static class StatsDownloadApiDatabase

public static readonly string GetTeamsProcedureName =
$"{StatsDownloadConstants.StatsDownloadDatabase.DatabaseSchema}{StatsDownloadConstants.StatsDownloadDatabase.SchemaSeparator}[GetTeams]";

public static readonly string[] ApiObjects =
{
GetFoldingMembersProcedureName,
GetMembersProcedureName,
GetTeamsProcedureName
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Interfaces;
using Interfaces.DataTransfer;
using StatsDownload.Core.Interfaces;
using StatsDownload.Core.Interfaces.Enums;

public class StatsDownloadApiDatabaseProvider : IStatsDownloadApiDatabaseService
{
Expand Down Expand Up @@ -94,9 +95,9 @@ public IList<Team> GetTeams()
return users;
}

public bool IsAvailable()
public (bool isAvailable, DatabaseFailedReason reason) IsAvailable()
{
return statsDownloadDatabaseService.IsAvailable();
return statsDownloadDatabaseService.IsAvailable(Constants.StatsDownloadApiDatabase.ApiObjects);
}
}
}
2 changes: 2 additions & 0 deletions Api/StatsDownloadApi.Interfaces/ApiErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public enum ApiErrorCode

DatabaseUnavailable = 8000,

DatabaseMissingRequiredObjects = 8005,

UnexpectedException = 9000
}
}
7 changes: 7 additions & 0 deletions Api/StatsDownloadApi.Interfaces/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ public class Constants
{
public static class ApiErrors
{
public static ApiError DatabaseMissingRequiredObjects => new ApiError(
ApiErrorCode.DatabaseMissingRequiredObjects,
ErrorMessages.DatabaseMissingRequiredObjectsMessage);

public static ApiError DatabaseUnavailable => new ApiError(ApiErrorCode.DatabaseUnavailable,
ErrorMessages.DatabaseUnavailableMessage);

Expand Down Expand Up @@ -37,6 +41,9 @@ public static class ApiErrors

public static class ErrorMessages
{
public static string DatabaseMissingRequiredObjectsMessage =>
"The database is available but missing required objects. Try again in a short period of time. If the problem continues, then contact the technical team about the problem.";

public static string DatabaseUnavailableMessage =>
"The database is unavailable. Try again in a short period of time. If the problem continues, then contact the technical team about the problem.";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using DataTransfer;
using StatsDownload.Core.Interfaces.Enums;

public interface IStatsDownloadApiDatabaseService
{
Expand All @@ -12,6 +13,6 @@ public interface IStatsDownloadApiDatabaseService

IList<Team> GetTeams();

bool IsAvailable();
(bool isAvailable, DatabaseFailedReason reason) IsAvailable();
}
}
11 changes: 11 additions & 0 deletions Shared/StatsDownload.Core.Interfaces/Enums/DatabaseFailedReason.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace StatsDownload.Core.Interfaces.Enums
{
public enum DatabaseFailedReason
{
None,

DatabaseUnavailable,

DatabaseMissingRequiredObjects
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public interface IStatsDownloadDatabaseService

DbTransaction CreateTransaction();

(bool isAvailable, FailedReason reason) IsAvailable(string[] requiredObjects);
(bool isAvailable, DatabaseFailedReason reason) IsAvailable(string[] requiredObjects);

void Rollback(DbTransaction transaction);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,14 @@ public void GetLastFileDownloadDateTime_WhenNoRowsReturned_ReturnsDefaultDateTim
Assert.That(actual, Is.EqualTo(default(DateTime)));
}

[TestCase(true, FailedReason.None)]
[TestCase(false, FailedReason.DatabaseUnavailable)]
[TestCase(true, DatabaseFailedReason.None, FailedReason.None)]
[TestCase(false, DatabaseFailedReason.DatabaseUnavailable, FailedReason.DatabaseUnavailable)]
public void IsAvailable_WhenInvoked_ReturnsDatabaseAvailability(bool expectedIsAvailable,
DatabaseFailedReason failedReason,
FailedReason expectedReason)
{
statsDownloadDatabaseServiceMock.IsAvailable(Constants.FileDownloadDatabase.FileDownloadObjects)
.Returns((expectedIsAvailable, expectedReason));
.Returns((expectedIsAvailable, failedReason));

(bool isAvailable, FailedReason reason) actual = InvokeIsAvailable();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,10 @@ public void IsAvailable_WhenDatabaseConnectionFails_ReturnsFalse()
{
databaseConnectionServiceMock.When(mock => mock.Open()).Throw<Exception>();

(bool isAvailable, FailedReason reason) actual = InvokeIsAvailable();
(bool isAvailable, DatabaseFailedReason reason) actual = InvokeIsAvailable();

Assert.That(actual.isAvailable, Is.False);
Assert.That(actual.reason, Is.EqualTo(FailedReason.DatabaseUnavailable));
Assert.That(actual.reason, Is.EqualTo(DatabaseFailedReason.DatabaseUnavailable));
}

[TestCase(null)]
Expand All @@ -162,10 +162,10 @@ public void IsAvailable_WhenInvalidConnectionString_ReturnsFalse(string connecti
{
databaseConnectionSettingsServiceMock.GetConnectionString().Returns(connectionString);

(bool isAvailable, FailedReason reason) actual = InvokeIsAvailable();
(bool isAvailable, DatabaseFailedReason reason) actual = InvokeIsAvailable();

Assert.That(actual.isAvailable, Is.False);
Assert.That(actual.reason, Is.EqualTo(FailedReason.DatabaseUnavailable));
Assert.That(actual.reason, Is.EqualTo(DatabaseFailedReason.DatabaseUnavailable));
}

[Test]
Expand All @@ -180,10 +180,10 @@ public void IsAvailable_WhenInvoked_ChecksForRequiredObjects()
[Test]
public void IsAvailable_WhenInvoked_ReturnsTrue()
{
(bool isAvailable, FailedReason reason) actual = InvokeIsAvailable();
(bool isAvailable, DatabaseFailedReason reason) actual = InvokeIsAvailable();

Assert.That(actual.isAvailable, Is.True);
Assert.That(actual.reason, Is.EqualTo(FailedReason.None));
Assert.That(actual.reason, Is.EqualTo(DatabaseFailedReason.None));
}

[Test]
Expand All @@ -204,10 +204,10 @@ public void IsAvailable_WhenRequiredObjectsMissing_ReturnsFailedReason()
{
databaseConnectionServiceMock.ExecuteScalar("OBJECT_ID('object1')").Returns(DBNull.Value);

(bool isAvailable, FailedReason reason) actual = InvokeIsAvailable(new[] { "object1" });
(bool isAvailable, DatabaseFailedReason reason) actual = InvokeIsAvailable(new[] { "object1" });

Assert.That(actual.isAvailable, Is.False);
Assert.That(actual.reason, Is.EqualTo(FailedReason.DatabaseMissingRequiredObjects));
Assert.That(actual.reason, Is.EqualTo(DatabaseFailedReason.DatabaseMissingRequiredObjects));
}

[Test]
Expand All @@ -220,7 +220,7 @@ public void Rollback_WhenInvoked_RollsBackTransaction()
transaction.Received(1).Rollback();
}

private (bool isAvailable, FailedReason reason) InvokeIsAvailable(string[] requiredObjects = null)
private (bool isAvailable, DatabaseFailedReason reason) InvokeIsAvailable(string[] requiredObjects = null)
{
return systemUnderTest.IsAvailable(requiredObjects);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,13 +625,14 @@ public void GetFileData_WhenInvoked_ReturnsFileData()
Assert.That(actual, Is.EqualTo("FileData"));
}

[TestCase(true, FailedReason.None)]
[TestCase(false, FailedReason.DatabaseUnavailable)]
[TestCase(true, DatabaseFailedReason.None, FailedReason.None)]
[TestCase(false, DatabaseFailedReason.DatabaseUnavailable, FailedReason.DatabaseUnavailable)]
public void IsAvailable_WhenInvoked_ReturnsDatabaseAvailability(bool expectedIsAvailable,
DatabaseFailedReason failedReason,
FailedReason expectedReason)
{
statsDownloadDatabaseServiceMock.IsAvailable(Constants.StatsUploadDatabase.StatsUploadObjects)
.Returns((expectedIsAvailable, expectedReason));
.Returns((expectedIsAvailable, failedReason));

(bool isAvailable, FailedReason reason) actual = InvokeIsAvailable();

Expand Down
24 changes: 23 additions & 1 deletion Shared/StatsDownload.Database/FileDownloadDatabaseProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,29 @@ public DateTime GetLastFileDownloadDateTime()

public (bool isAvailable, FailedReason reason) IsAvailable()
{
return statsDownloadDatabaseService.IsAvailable(Constants.FileDownloadDatabase.FileDownloadObjects);
(bool isAvailable, DatabaseFailedReason reason) =
statsDownloadDatabaseService.IsAvailable(Constants.FileDownloadDatabase.FileDownloadObjects);

FailedReason failedReason;

if (reason == DatabaseFailedReason.None)
{
failedReason = FailedReason.None;
}
else if (reason == DatabaseFailedReason.DatabaseUnavailable)
{
failedReason = FailedReason.DatabaseUnavailable;
}
else if (reason == DatabaseFailedReason.DatabaseMissingRequiredObjects)
{
failedReason = FailedReason.DatabaseMissingRequiredObjects;
}
else
{
throw new ArgumentOutOfRangeException();
}

return (isAvailable, failedReason);
}

public void NewFileDownloadStarted(FilePayload filePayload)
Expand Down
Loading

0 comments on commit b047df0

Please sign in to comment.