public Task StartAsync(CancellationToken cancellationToken)
        {
            _coordinator.Initialize(new ISubmissionCrawler[]
            {
                // _zojSubmissionCrawler,
            });
            _timer = new Timer(DoWork, null, TimeSpan.Zero,
                               TimeSpan.FromHours(0.5));

            return(Task.CompletedTask);
        }
        public void It_ShouldWork()
        {
            "Given a coordinator".x(() => { });

            "When it is initialized"
            .x(() => _coordinator.Initialize(new[] { _crawlerMock }));

            "But crawler does not immediately work"
            .x(() => _crawlerMock.CalledCount.Should().Be(0));

            $"When calling {nameof(_coordinator.StartAllCrawlers)}"
            .x(() => _coordinator.StartAllCrawlers());

            $"Then crawler's {nameof(_crawlerMock.WorkAsync)} is called"
            .x(() => _crawlerMock.CalledCount.Should().Be(1));

            "And there is nothing in the database"
            .x(() => WithDb(context =>
            {
                context.Submission.Should().BeEmpty();
                context.CrawlerErrors.Should().BeEmpty();
            }));

            "And lastSubmissionId sent to crawler should be null"
            .x(() => _crawlerMock.LastSubmissionId.Should().BeNull());

            "When crawler sends data"
            .x(async() =>
            {
                await _crawlerMock.Pipeline.SendAsync(new CrawlerMessage
                {
                    Submission = new Submission
                    {
                        Status        = RunResult.Accepted,
                        Time          = new DateTime(2020, 4, 1, 0, 0, 0),
                        ProblemLabel  = "1001",
                        SubmissionId  = 42,
                        UserName      = "******",
                        OnlineJudgeId = OnlineJudge.ZOJ,
                    },
                });
                await _crawlerMock.Pipeline.SendAsync(new CrawlerMessage
                {
                    CrawlerError = new CrawlerError
                    {
                        Crawler = "zoj",
                        Data    = null,
                        Message = "An error",
                        Time    = new DateTime(2020, 4, 1, 1, 0, 0),
                    },
                });
                await Utils.WaitSecond();
            });

            "But data are not saved to database immediately"
            .x(() => WithDb(context =>
            {
                context.Submission.Should().BeEmpty();
                context.CrawlerErrors.Should().BeEmpty();
            }));

            "When crawler sends a checkpoint"
            .x(async() =>
            {
                await _crawlerMock.Pipeline.SendAsync(new CrawlerMessage
                {
                    Checkpoint = true,
                });
                await Utils.WaitSecond();
            });

            "But data are not saved to database immediately"
            .x(() => WithDb(context =>
            {
                context.Submission.Should().BeEmpty();
                context.CrawlerErrors.Should().BeEmpty();
            }));

            "When crawler finished"
            .x(async() =>
            {
                _crawlerMock.TaskSource.SetResult(1);
                // wait longer to insert database
                await Utils.WaitSecond();
            });

            "Then data are saved to database"
            .x(() => WithDb(context =>
            {
                context.Submission.Should().HaveCount(1);
                context.Submission.Should().AllBeEquivalentTo(new Submission
                {
                    Status        = RunResult.Accepted,
                    Time          = new DateTime(2020, 4, 1, 0, 0, 0),
                    ProblemLabel  = "1001",
                    SubmissionId  = 42,
                    UserName      = "******",
                    OnlineJudgeId = OnlineJudge.ZOJ,
                });

                context.CrawlerErrors.Should().HaveCount(1);
                context.CrawlerErrors.Single().Invoking(it =>
                {
                    it.Crawler.Should().Be("zoj");
                    it.Data.Should().BeNull();
                    it.Message.Should().Be("An error");
                    it.Time.Should().Be(new DateTime(2020, 4, 1, 1, 0, 0));
                    it.Id.Should().NotBe(0);
                });
            }));

            "When cancelling a coordinator, it should not throw"
            .x(() => _coordinator.Cancel());
        }