public async Task Should_Include_Closed_Milestone_If_Specified()
    {
        // Given
        var dbContextOptions = new DbContextOptionsBuilder <GitBucketDbContext>()
                               .UseInMemoryDatabase(databaseName: "Should_Include_Closed_Milestone_If_Specified")
                               .Options;

        var dbContext = new GitBucketDbContext(dbContextOptions);
        var options   = new MilestoneOptions {
            ExecutedDate = new DateTime(2018, 7, 1), IncludeClosed = true
        };

        dbContext.Milestones.Add(new Milestone
        {
            MilestoneId    = 1,
            Title          = "v0.1.0",
            RepositoryName = "test1",
            DueDate        = new DateTime(2018, 7, 1),
            ClosedDate     = new DateTime(2018, 7, 1),
            Description    = "Implement xxx feature",
            UserName       = "******",
            Issues         = new[]
            {
                new Issue
                {
                    IssueId          = 1,
                    AssignedUserName = "******",
                    UserName         = "******",
                    RepositoryName   = "test1",
                    OpenedUserName   = "******",
                    Title            = "Implement xxx feature",
                }
            }
        });

        dbContext.SaveChanges();

        var service = new MilestoneService(dbContext, FakeConsole);

        // When
        var result = await service.ShowMilestones(options);

        // Then
        Assert.Equal(0, result);
        Assert.Equal(3, FakeConsole.Messages.Count);
        Assert.Empty(FakeConsole.WarnMessages);
        Assert.Empty(FakeConsole.ErrorMessages);

        Assert.Equal("There are 1 milestone.", FakeConsole.Messages[0]);
        Assert.Equal(string.Empty, FakeConsole.Messages[1]);

        Assert.Equal("* [root/test1], [v0.1.0], [2018/07/01], [Implement xxx feature], [user1]", FakeConsole.Messages[2]);
    }
    public async Task <int> ShowMilestones(MilestoneOptions options)
    {
        if (options == null)
        {
            throw new ArgumentNullException(nameof(options));
        }

        var milestones = await FindMilestones(options);

        if (milestones.Count == 0)
        {
            _console.WriteLine("There are no milestone.");
            return(0);
        }

        var milestoneOrMilestones = milestones.Count == 1 ? "milestone." : "milestones.";

        _console.WriteLine(options.IncludeClosed ?
                           $"There are {milestones.Count} {milestoneOrMilestones}" :
                           $"There are {milestones.Count} open {milestoneOrMilestones}");
        _console.WriteLine(string.Empty);

        foreach (var milestone in milestones)
        {
            if (milestone.ClosedDate != null)
            {
                _console.WriteLine(milestone.Format());
            }
            else if (milestone.DueDate == null ||
                     (milestone.DueDate >= options.ExecutedDate) &&
                     (milestone.DueDate.Value.Date < options.ExecutedDate.Date.AddDays(7)))
            {
                _console.WriteWarnLine(milestone.Format());
            }
            else if (milestone.DueDate < options.ExecutedDate)
            {
                _console.WriteErrorLine(milestone.Format());
            }
            else
            {
                _console.WriteLine(milestone.Format());
            }
        }

        return(0);
    }
    private async Task <List <Milestone> > FindMilestones(MilestoneOptions options)
    {
#pragma warning disable CA1304 // Specify CultureInfo
        // "String.Equals(String, StringComparison)" causes client side evaluation.
        // https://github.com/aspnet/EntityFrameworkCore/issues/1222
        var owners       = options.Owners.Select(o => o.ToLower());
        var repositories = options.Repositories.Select(r => r.ToLower());

        return(await _context.Set <Milestone>()
               .WhereIf(options.Owners.Any(), m => owners.Contains(m.UserName.ToLower()))
               .WhereIf(options.Repositories.Any(), m => repositories.Contains(m.RepositoryName.ToLower()))
               .WhereIf(!options.IncludeClosed, m => m.ClosedDate == null)
               .OrderBy(m => m.DueDate)
               .ThenBy(m => m.UserName)
               .ThenBy(m => m.RepositoryName)
               .Include(m => m.Issues)
               .AsNoTracking()
               .ToListAsync());

#pragma warning restore CA1304 // Specify CultureInfo
    }
    public async Task Should_Return_If_No_Milestone()
    {
        // Given
        var dbContextOptions = new DbContextOptionsBuilder <GitBucketDbContext>()
                               .UseInMemoryDatabase(databaseName: "Should_Return_If_No_Milestone")
                               .Options;

        var dbContext = new GitBucketDbContext(dbContextOptions);
        var options   = new MilestoneOptions {
            ExecutedDate = new DateTime(2018, 7, 29)
        };
        var service = new MilestoneService(dbContext, FakeConsole);

        // When
        var result = await service.ShowMilestones(options);

        // Then
        Assert.Equal(0, result);
        Assert.Single(FakeConsole.Messages);
        Assert.Empty(FakeConsole.WarnMessages);
        Assert.Empty(FakeConsole.ErrorMessages);

        Assert.Equal("There are no milestone.", FakeConsole.Messages[0]);
    }
    public async Task Should_Show_Multiple_Milestones()
    {
        // Given
        var dbContextOptions = new DbContextOptionsBuilder <GitBucketDbContext>()
                               .UseInMemoryDatabase(databaseName: "Should_Show_Multiple_Milestones")
                               .Options;

        var dbContext = new GitBucketDbContext(dbContextOptions);
        var options   = new MilestoneOptions {
            ExecutedDate = new DateTime(2018, 7, 1), IncludeClosed = true
        };

        dbContext.Milestones.AddRange(new List <Core.Models.Milestone>
        {
            new Milestone
            {
                MilestoneId    = 1,
                Title          = "v0.1.0",
                RepositoryName = "test1",
                DueDate        = new DateTime(2018, 6, 30),
                ClosedDate     = null,
                Description    = "Error",
                UserName       = "******",
                Issues         = new[]
                {
                    new Issue
                    {
                        IssueId          = 1,
                        AssignedUserName = "******",
                        UserName         = "******",
                        RepositoryName   = "test1",
                        OpenedUserName   = "******",
                        Title            = "Implement xxx feature",
                    }
                }
            },
            new Milestone
            {
                MilestoneId    = 2,
                Title          = "v0.2.0",
                RepositoryName = "test1",
                DueDate        = new DateTime(2018, 7, 1),
                ClosedDate     = new DateTime(2018, 7, 1),
                Description    = "Closed",
                UserName       = "******",
                Issues         = new[]
                {
                    new Issue
                    {
                        IssueId          = 2,
                        AssignedUserName = "******",
                        UserName         = "******",
                        RepositoryName   = "test1",
                        OpenedUserName   = "******",
                        Title            = "Implement xxx feature",
                    }
                }
            },
            new Milestone
            {
                MilestoneId    = 3,
                Title          = "v0.3.0",
                RepositoryName = "test1",
                DueDate        = new DateTime(2018, 7, 7),
                ClosedDate     = null,
                Description    = "Warn",
                UserName       = "******",
                Issues         = new[]
                {
                    new Issue
                    {
                        IssueId          = 3,
                        AssignedUserName = "******",
                        UserName         = "******",
                        RepositoryName   = "test1",
                        OpenedUserName   = "******",
                        Title            = "Implement xxx feature",
                    }
                }
            },
            new Milestone
            {
                MilestoneId    = 4,
                Title          = "v0.4.0",
                RepositoryName = "test1",
                DueDate        = new DateTime(2018, 7, 8),
                ClosedDate     = null,
                Description    = "Info",
                UserName       = "******",
                Issues         = new[]
                {
                    new Issue
                    {
                        IssueId          = 4,
                        AssignedUserName = "******",
                        UserName         = "******",
                        RepositoryName   = "test1",
                        OpenedUserName   = "******",
                        Title            = "Implement xxx feature",
                    }
                }
            }
        });

        dbContext.SaveChanges();

        var service = new MilestoneService(dbContext, FakeConsole);

        // When
        var result = await service.ShowMilestones(options);

        // Then
        Assert.Equal(0, result);
        Assert.Equal(4, FakeConsole.Messages.Count);
        Assert.Single(FakeConsole.WarnMessages);
        Assert.Single(FakeConsole.ErrorMessages);

        Assert.Equal("There are 4 milestones.", FakeConsole.Messages[0]);
        Assert.Equal(string.Empty, FakeConsole.Messages[1]);

        Assert.Equal("* [root/test1], [v0.1.0], [2018/06/30], [Error], [user1]", FakeConsole.ErrorMessages[0]);
        Assert.Equal("* [root/test1], [v0.2.0], [2018/07/01], [Closed], [user1]", FakeConsole.Messages[2]);
        Assert.Equal("* [root/test1], [v0.3.0], [2018/07/07], [Warn], [user1]", FakeConsole.WarnMessages[0]);
        Assert.Equal("* [root/test1], [v0.4.0], [2018/07/08], [Info], [user1]", FakeConsole.Messages[3]);
    }