public async Task Update_competition_does_not_add_competition_version_if_name_is_unchanged()
        {
            var competition = _databaseFixture.TestData.Competitions.First(x => !x.UntilYear.HasValue);

            var auditable = new Competition
            {
                CompetitionId   = competition.CompetitionId,
                CompetitionName = competition.CompetitionName,
            };

            int?existingVersions = null;

            (Guid? competitionVersionId, string competitionName, string comparableName, DateTimeOffset? fromDate)currentVersion = (null, null, null, null);
            using (var connection = _databaseFixture.ConnectionFactory.CreateDatabaseConnection())
            {
                existingVersions = await connection.ExecuteScalarAsync <int>($"SELECT COUNT(*) FROM {Tables.CompetitionVersion} WHERE CompetitionId = @CompetitionId", auditable).ConfigureAwait(false);

                currentVersion = await connection.QuerySingleAsync <(Guid competitionVersionId, string competitionName, string comparableName, DateTimeOffset?fromDate)>(
                    $"SELECT CompetitionVersionId, CompetitionName, ComparableName, FromDate FROM {Tables.CompetitionVersion} WHERE CompetitionId = @CompetitionId AND UntilDate IS NULL", auditable).ConfigureAwait(false);
            }

            var routeGenerator = new Mock <IRouteGenerator>();

            routeGenerator.Setup(x => x.GenerateUniqueRoute(competition.CompetitionRoute, "/competitions", auditable.CompetitionName, NoiseWords.CompetitionRoute, It.IsAny <Func <string, Task <int> > >())).Returns(Task.FromResult(competition.CompetitionRoute));

            var copier = new Mock <IStoolballEntityCopier>();

            copier.Setup(x => x.CreateAuditableCopy(competition)).Returns(auditable);

            var repo = new SqlServerCompetitionRepository(
                _databaseFixture.ConnectionFactory,
                Mock.Of <IAuditRepository>(),
                Mock.Of <ILogger>(),
                Mock.Of <ISeasonRepository>(),
                routeGenerator.Object,
                Mock.Of <IRedirectsRepository>(),
                Mock.Of <IHtmlSanitizer>(),
                copier.Object,
                Mock.Of <IUrlFormatter>(),
                Mock.Of <ISocialMediaAccountFormatter>());

            var updated = await repo.UpdateCompetition(competition, Guid.NewGuid(), "Person 1").ConfigureAwait(false);

            using (var connection = _databaseFixture.ConnectionFactory.CreateDatabaseConnection())
            {
                var totalVersions = await connection.ExecuteScalarAsync <int>($"SELECT COUNT(*) FROM {Tables.CompetitionVersion} WHERE CompetitionId = @CompetitionId", auditable).ConfigureAwait(false);

                Assert.Equal(existingVersions, totalVersions);

                var versionResult = await connection.QuerySingleAsync <(Guid competitionVersionId, string competitionName, string comparableName, DateTimeOffset?fromDate)>(
                    $"SELECT CompetitionVersionId, CompetitionName, ComparableName, FromDate FROM {Tables.CompetitionVersion} WHERE CompetitionId = @CompetitionId AND UntilDate IS NULL", auditable).ConfigureAwait(false);

                Assert.Equal(currentVersion.competitionVersionId, versionResult.competitionVersionId);
                Assert.Equal(auditable.CompetitionName, versionResult.competitionName);
                Assert.Equal(auditable.ComparableName(), versionResult.comparableName);
                Assert.Equal(currentVersion.fromDate.Value, versionResult.fromDate.Value);
            }
        }
        public async Task Create_minimal_competition_succeeds()
        {
            var competition = new Competition
            {
                CompetitionName = "Example competition",
                PlayerType      = PlayerType.JuniorMixed,
                MemberGroupKey  = Guid.NewGuid(),
                MemberGroupName = "Test group"
            };

            var route          = "/competitions/" + Guid.NewGuid();
            var routeGenerator = new Mock <IRouteGenerator>();

            routeGenerator.Setup(x => x.GenerateUniqueRoute("/competitions", competition.CompetitionName, NoiseWords.CompetitionRoute, It.IsAny <Func <string, Task <int> > >())).Returns(Task.FromResult(route));

            var copier = new Mock <IStoolballEntityCopier>();

            copier.Setup(x => x.CreateAuditableCopy(competition)).Returns(competition);

            var repo = new SqlServerCompetitionRepository(
                _databaseFixture.ConnectionFactory,
                Mock.Of <IAuditRepository>(),
                Mock.Of <ILogger>(),
                Mock.Of <ISeasonRepository>(),
                routeGenerator.Object,
                Mock.Of <IRedirectsRepository>(),
                Mock.Of <IHtmlSanitizer>(),
                copier.Object,
                Mock.Of <IUrlFormatter>(),
                Mock.Of <ISocialMediaAccountFormatter>());
            var created = await repo.CreateCompetition(competition, Guid.NewGuid(), "Member name").ConfigureAwait(false);

            routeGenerator.Verify(x => x.GenerateUniqueRoute("/competitions", competition.CompetitionName, NoiseWords.CompetitionRoute, It.IsAny <Func <string, Task <int> > >()), Times.Once);

            using (var connection = _databaseFixture.ConnectionFactory.CreateDatabaseConnection())
            {
                var competitionResult = await connection.QuerySingleOrDefaultAsync <Competition>(
                    @$ "SELECT MemberGroupKey, MemberGroupName, CompetitionRoute, PlayerType
                        FROM {Tables.Competition} 
                        WHERE CompetitionId = @CompetitionId",
                    new
                {
                    created.CompetitionId
                }).ConfigureAwait(false);

                Assert.NotNull(competitionResult);
                Assert.Equal(competition.MemberGroupKey, competitionResult.MemberGroupKey);
                Assert.Equal(competition.MemberGroupName, competitionResult.MemberGroupName);
                Assert.Equal(competition.CompetitionRoute, competitionResult.CompetitionRoute);
                Assert.Equal(competition.PlayerType, competitionResult.PlayerType);

                var versionResult = await connection.QuerySingleAsync <(string competitionName, string comparableName, DateTimeOffset?fromDate)>(
                    $"SELECT CompetitionName, ComparableName, FromDate FROM {Tables.CompetitionVersion} WHERE CompetitionId = @CompetitionId",
                    new { created.CompetitionId }
                    ).ConfigureAwait(false);

                Assert.Equal(competition.CompetitionName, versionResult.competitionName);
                Assert.Equal(competition.ComparableName(), versionResult.comparableName);
                Assert.Equal(DateTime.UtcNow.Date, versionResult.fromDate.Value.Date);
            }
        }
        public async Task Update_competition_succeeds()
        {
            var competition = _databaseFixture.TestData.Competitions.First();

            var originalIntro           = string.IsNullOrEmpty(competition.Introduction) ? "Unsanitised introduction" : competition.Introduction;
            var originalPrivateContact  = string.IsNullOrEmpty(competition.PrivateContactDetails) ? "Unsanitised private details" : competition.PrivateContactDetails;
            var originalPublicContact   = string.IsNullOrEmpty(competition.PublicContactDetails) ? "Unsanitised public details" : competition.PublicContactDetails;
            var sanitisedIntro          = "<p>This is the sanitised intro</p>";
            var sanitisedPrivateContact = "<p>Sanitised private details</p>";
            var sanitisedPublicContact  = "<p>Sanitised public details</p>";
            var originalFacebook        = "facebook.com/" + Guid.NewGuid().ToString();
            var originalInstagram       = Guid.NewGuid().ToString();
            var originalYouTube         = "youtube.com/example" + Guid.NewGuid().ToString();
            var originalTwitter         = Guid.NewGuid().ToString();
            var originalWebsite         = "example.org/" + Guid.NewGuid().ToString();

            var auditable = new Competition
            {
                CompetitionId = competition.CompetitionId,

                Introduction          = originalIntro,
                PlayerType            = competition.PlayerType == PlayerType.JuniorGirls ? PlayerType.Ladies : PlayerType.JuniorGirls,
                CompetitionName       = Guid.NewGuid().ToString(),
                FromYear              = competition.FromYear.HasValue ? competition.FromYear + 1 : 1999,
                UntilYear             = competition.UntilYear.HasValue ? competition.UntilYear + 1 : 2021,
                Facebook              = originalFacebook,
                Instagram             = originalInstagram,
                YouTube               = originalYouTube,
                Twitter               = originalTwitter,
                Website               = originalWebsite,
                PrivateContactDetails = originalPrivateContact,
                PublicContactDetails  = originalPublicContact,
            };

            var updatedRoute = competition.CompetitionRoute + Guid.NewGuid();

            var routeGenerator = new Mock <IRouteGenerator>();

            routeGenerator.Setup(x => x.GenerateUniqueRoute(competition.CompetitionRoute, "/competitions", auditable.CompetitionName, NoiseWords.CompetitionRoute, It.IsAny <Func <string, Task <int> > >())).Returns(Task.FromResult(updatedRoute));

            var copier = new Mock <IStoolballEntityCopier>();

            copier.Setup(x => x.CreateAuditableCopy(competition)).Returns(auditable);

            var sanitizer = new Mock <IHtmlSanitizer>();

            sanitizer.Setup(x => x.Sanitize(auditable.PrivateContactDetails)).Returns(sanitisedPrivateContact);
            sanitizer.Setup(x => x.Sanitize(auditable.PublicContactDetails)).Returns(sanitisedPublicContact);
            sanitizer.Setup(x => x.Sanitize(auditable.Introduction)).Returns(sanitisedIntro);

            var urlFormatter = new Mock <IUrlFormatter>();

            urlFormatter.Setup(x => x.PrefixHttpsProtocol(auditable.Facebook)).Returns(new Uri("https://" + auditable.Facebook));
            urlFormatter.Setup(x => x.PrefixHttpsProtocol(auditable.YouTube)).Returns(new Uri("https://" + auditable.YouTube));
            urlFormatter.Setup(x => x.PrefixHttpsProtocol(auditable.Website)).Returns(new Uri("https://" + auditable.Website));

            var socialMediaFormatter = new Mock <ISocialMediaAccountFormatter>();

            socialMediaFormatter.Setup(x => x.PrefixAtSign(auditable.Instagram)).Returns("@" + auditable.Instagram);
            socialMediaFormatter.Setup(x => x.PrefixAtSign(auditable.Twitter)).Returns("@" + auditable.Twitter);

            var repo = new SqlServerCompetitionRepository(
                _databaseFixture.ConnectionFactory,
                Mock.Of <IAuditRepository>(),
                Mock.Of <ILogger>(),
                Mock.Of <ISeasonRepository>(),
                routeGenerator.Object,
                Mock.Of <IRedirectsRepository>(),
                sanitizer.Object,
                copier.Object,
                urlFormatter.Object,
                socialMediaFormatter.Object);

            var updated = await repo.UpdateCompetition(competition, Guid.NewGuid(), "Person 1").ConfigureAwait(false);

            using (var connection = _databaseFixture.ConnectionFactory.CreateDatabaseConnection())
            {
                var competitionResult = await connection.QuerySingleOrDefaultAsync <Competition>(
                    $@"SELECT PlayerType, Introduction, Facebook, Instagram, YouTube, Twitter, Website, 
                                PrivateContactDetails, PublicContactDetails, CompetitionRoute
                                FROM {Tables.Competition} 
                                WHERE CompetitionId = @CompetitionId",
                    new { updated.CompetitionId }).ConfigureAwait(false);

                Assert.NotNull(competitionResult);

                Assert.Equal(auditable.PlayerType, competitionResult.PlayerType);
                sanitizer.Verify(x => x.Sanitize(originalIntro));
                Assert.Equal(sanitisedIntro, competitionResult.Introduction);
                urlFormatter.Verify(x => x.PrefixHttpsProtocol(originalFacebook), Times.Once);
                Assert.Equal(auditable.Facebook, competitionResult.Facebook);
                Assert.StartsWith("https://", auditable.Facebook);
                socialMediaFormatter.Verify(x => x.PrefixAtSign(originalInstagram), Times.Once);
                Assert.Equal(auditable.Instagram, competitionResult.Instagram);
                Assert.StartsWith("@", auditable.Instagram);
                urlFormatter.Verify(x => x.PrefixHttpsProtocol(originalYouTube), Times.Once);
                Assert.Equal(auditable.YouTube, competitionResult.YouTube);
                Assert.StartsWith("https://", auditable.YouTube);
                socialMediaFormatter.Verify(x => x.PrefixAtSign(originalTwitter), Times.Once);
                Assert.Equal(auditable.Twitter, competitionResult.Twitter);
                Assert.StartsWith("@", auditable.Twitter);
                urlFormatter.Verify(x => x.PrefixHttpsProtocol(originalWebsite), Times.Once);
                Assert.Equal(auditable.Website, competitionResult.Website);
                Assert.StartsWith("https://", auditable.Website);
                sanitizer.Verify(x => x.Sanitize(originalPrivateContact));
                Assert.Equal(sanitisedPrivateContact, competitionResult.PrivateContactDetails);
                sanitizer.Verify(x => x.Sanitize(originalPublicContact));
                Assert.Equal(sanitisedPublicContact, competitionResult.PublicContactDetails);
                Assert.Equal(updatedRoute, competitionResult.CompetitionRoute);

                var versionResult = await connection.QuerySingleOrDefaultAsync <(string competitionName, string comparableName)>(
                    $"SELECT CompetitionName, ComparableName FROM {Tables.CompetitionVersion} WHERE CompetitionId = @CompetitionId", new { updated.CompetitionId }).ConfigureAwait(false);

                Assert.Equal(auditable.CompetitionName, versionResult.competitionName);
                Assert.Equal(auditable.ComparableName(), versionResult.comparableName);
            }
        }