public void parse_error_message_is_null_if_a_document_is_successfully_parsed_after_one_fails_to_parse()
        {
            var badDocument = @"# First Program

## First Exercise

 whatever!";
            var goodDocument = "# First Program";
            var documents = new Subject<string>();
            var exerciseDocumentService = new ExerciseDocumentServiceMock();
            var scheduler = new TestScheduler();

            exerciseDocumentService
                .When(x => x.ExerciseDocument)
                .Return(documents);

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithExerciseDocumentService(exerciseDocumentService)
                .WithScheduler(scheduler)
                .Build();

            documents.OnNext(badDocument);

            scheduler.AdvanceMinimal();
            Assert.NotNull(sut.ParseErrorMessage);

            documents.OnNext(goodDocument);
            scheduler.AdvanceMinimal();
            Assert.Null(sut.ParseErrorMessage);
        }
        public void programs_is_populated_from_cache_whilst_document_from_cloud_loads()
        {
            var cacheDocument = "# First Program";
            var cloudDocument = @"
# First Program
# Second Program";
            var scheduler = new TestScheduler();

            var exerciseDocumentService = new ExerciseDocumentServiceMock(MockBehavior.Loose);

            exerciseDocumentService
                .When(x => x.ExerciseDocument)
                .Return(
                    Observable
                        .Return(cloudDocument)
                        .Delay(TimeSpan.FromSeconds(3), scheduler));

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithExerciseDocumentService(exerciseDocumentService)
                .WithCachedDocument(cacheDocument)
                .WithScheduler(scheduler)
                .Build();

            scheduler.AdvanceMinimal();

            Assert.NotNull(sut.Programs);
            Assert.Equal(1, sut.Programs.Count);

            scheduler.AdvanceBy(TimeSpan.FromSeconds(2));
            Assert.NotNull(sut.Programs);
            Assert.Equal(1, sut.Programs.Count);

            scheduler.AdvanceBy(TimeSpan.FromSeconds(2));
            Assert.NotNull(sut.Programs);
            Assert.Equal(2, sut.Programs.Count);
        }
        public void status_is_load_failed_if_the_document_fails_to_load_altogether()
        {
            var exerciseDocumentService = new ExerciseDocumentServiceMock();
            var scheduler = new TestScheduler();

            exerciseDocumentService
                .When(x => x.ExerciseDocument)
                .Return(Observable.Throw<string>(new InvalidOperationException()));

            var sut = new ExerciseProgramsViewModelBuilder()
                .WithExerciseDocumentService(exerciseDocumentService)
                .WithScheduler(scheduler)
                .Build();

            scheduler.AdvanceMinimal();
            Assert.Equal(ExerciseProgramsViewModelStatus.LoadFailed, sut.Status);
        }
        public ExerciseProgramsViewModelBuilder WithCloudDocument(string cloudDocument)
        {
            var exerciseDocumentService = new ExerciseDocumentServiceMock(MockBehavior.Loose);

            exerciseDocumentService
                .When(x => x.ExerciseDocument)
                .Return(Observable.Return(cloudDocument));

            return this.WithExerciseDocumentService(exerciseDocumentService);
        }