public async Task UpdateQuestionAsync_BrokenTemplate_ReturnsError()
		{
			var database = GetDatabase().Build();
			var question = database.Context.GeneratedQuestions.First();

			database.Reload();
			question.GeneratorContents = "NewGeneratedContents";

			var questionGenerator = GetMockQuestionGenerator
			(
				question,
				new QuestionGenerationResult("QuestionGenerationError")
			);

			var errors = new MockErrorCollection();
			var updater = new GeneratedQuestionUpdater
			(
				database.Context, 
				question, 
				errors, 
				questionGenerator.Object
			);

			await updater.UpdateQuestionAsync();

			Assert.True(errors.HasErrors);
			Assert.True(errors.VerifyErrors("GeneratorContents"));
		}
		public async Task UpdateQuestionAsync_RemovesAllTests_Error()
		{
			var database = GetDatabase().Build();
			var question = database.Context.ClassQuestions
				.Include(q => q.Tests)
				.First();

			database.Reload();

			question.Tests.Clear();

			var errors = new MockErrorCollection();
			var updater = GetCodeQuestionUpdater(database, question, errors);
			await updater.UpdateQuestionAsync();
			database.Context.Questions.Update(question);
			database.Context.SaveChanges();

			Assert.True(errors.HasErrors);
			Assert.True(errors.VerifyErrors("Tests"));

			database.Reload();
			question = database.Context.ClassQuestions
				.Include(q => q.Tests)
				.First();

			Assert.Equal(1, question.Tests.Count);
			Assert.Equal("Test", question.Tests[0].Name);
		}
		public async Task UpdateQuestionAsync_IncorrectTemplate_Error()
		{
			var database = GetDatabase().Build();
			var question = database.Context.ClassQuestions
				.Include(q => q.Tests)
				.First();

			database.Reload();
			question.FileTemplate = "Missing Submission Expression";

			var errors = new MockErrorCollection();
			var loader = new ClassQuestionUpdater(database.Context, question, errors);
			await loader.UpdateQuestionAsync();

			Assert.True(errors.HasErrors);
			Assert.True(errors.VerifyErrors("FileTemplate"));

			database.Reload();
			question = database.Context.ClassQuestions.First();

			Assert.Equal("%SUBMISSION%", question.FileTemplate);
		}
		public async Task CreateQuestionAsync_NameCollision_QuestionNotCreated()
		{
			var database = new TestDatabaseBuilder()
				.AddClassroom("Class1")
				.AddQuestionCategory("Class1", "Category1")
				.AddQuestion("Class1", "Category1", new MethodQuestion() { Name = "Question1" })
				.Build();

			var questionCategoryId = database.Context.QuestionCategories.First().Id;
			var modelErrors = new MockErrorCollection();
			var updaterFactory = GetMockQuestionUpdaterFactory();

			var questionService = CreateQuestionService
			(
				database.Context,
				questionUpdaterFactory: updaterFactory.Object
			);

			var result = await questionService.CreateQuestionAsync
			(
				"Class1",
				new MethodQuestion()
				{
					Name = "Question1",
					QuestionCategoryId = questionCategoryId
				},
				modelErrors
			);

			database.Reload();

			var numQuestions = database.Context.Questions.Count();

			Assert.False(result);
			Assert.True(modelErrors.HasErrors);
			Assert.True(modelErrors.VerifyErrors("Name"));
			Assert.Equal(1, numQuestions);
		}
		public async Task UpdateCheckpointAsync_DuplicateTestClasses_CheckpointNotUpdated()
		{
			var database = GetDatabaseBuilderWithCheckpoint().Build();

			var checkpoint = database.Context.Checkpoints
				.Include(c => c.Project)
				.Include(c => c.Project.Classroom)
				.Include(c => c.TestClasses)
				.Include(c => c.SectionDates)
				.Single();
			
			var testClasses = database.Context.TestClasses.ToList();

			database.Reload();

			// Update the checkpoint
			checkpoint.TestClasses.Add
			(
				new CheckpointTestClass()
				{
					TestClassId = testClasses[0].Id,
					Required = false
				}
			);

			// Apply the update
			var modelErrors = new MockErrorCollection();
			var checkpointService = GetCheckpointService(database.Context);
			var result = await checkpointService.UpdateCheckpointAsync
			(
				"Class1",
				"Project1",
				checkpoint,
				modelErrors
			);

			database.Reload();

			checkpoint = database.Context.Checkpoints
				.Include(c => c.Project)
				.Include(c => c.Project.Classroom)
				.Include(c => c.TestClasses)
				.Include(c => c.SectionDates)
				.Single();

			Assert.False(result);
			Assert.True(modelErrors.HasErrors);
			Assert.True(modelErrors.VerifyErrors("TestClasses"));

			Assert.Equal(2, checkpoint.TestClasses.Count);
			Assert.Equal(testClasses[0].Id, checkpoint.TestClasses[0].TestClassId);
			Assert.True(checkpoint.TestClasses[0].Required);
			Assert.Equal(testClasses[1].Id, checkpoint.TestClasses[1].TestClassId);
			Assert.True(checkpoint.TestClasses[1].Required);
		}
		public async Task UpdateCheckpointAsync_DuplicateSectionDueDates_CheckpointNotUpdated()
		{
			var database = GetDatabaseBuilderWithCheckpoint().Build();

			var checkpoint = database.Context.Checkpoints
				.Include(c => c.Project)
				.Include(c => c.Project.Classroom)
				.Include(c => c.TestClasses)
				.Include(c => c.SectionDates)
				.Single();

			var sections = database.Context.Sections.ToList();

			database.Reload();

			// Update the checkpoint
			checkpoint.SectionDates.Add
			(
				new CheckpointDates()
				{
					SectionId = sections[0].Id,
					DueDate = DateTime.MinValue
				}
			);

			// Apply the update
			var modelErrors = new MockErrorCollection();
			var checkpointService = GetCheckpointService(database.Context);
			var result = await checkpointService.UpdateCheckpointAsync
			(
				"Class1",
				"Project1",
				checkpoint,
				modelErrors
			);

			database.Reload();

			checkpoint = database.Context.Checkpoints
				.Include(c => c.Project)
				.Include(c => c.Project.Classroom)
				.Include(c => c.TestClasses)
				.Include(c => c.SectionDates)
				.Single();

			Assert.False(result);
			Assert.True(modelErrors.HasErrors);
			Assert.True(modelErrors.VerifyErrors("SectionDates"));

			Assert.Equal(2, checkpoint.TestClasses.Count);
			Assert.Equal(sections[0].Id, checkpoint.SectionDates[0].SectionId);
			Assert.Equal(Period1DueDate, checkpoint.SectionDates[0].DueDate);
			Assert.Equal(sections[1].Id, checkpoint.SectionDates[1].SectionId);
			Assert.Equal(Period2DueDate, checkpoint.SectionDates[1].DueDate);
		}
		public async Task CreateCheckpointAsync_DuplicateSectionDueDates_CheckpointNotCreated()
		{
			var database = new TestDatabaseBuilder()
				.AddClassroom("Class1")
				.AddSection("Class1", "Period1")
				.AddSection("Class1", "Period2")
				.AddProject("Class1", "Project1")
				.Build();
			
			var sections = database.Context.Sections.ToList();
			database.Reload();

			var modelErrors = new MockErrorCollection();
			var checkpointService = GetCheckpointService(database.Context);

			var result = await checkpointService.CreateCheckpointAsync
			(
				"Class1",
				"Project1",
				new Checkpoint()
				{
					Name = "Checkpoint1",
					DisplayName = "Checkpoint1 DisplayName",
					SectionDates = Collections.CreateList
					(
						new CheckpointDates()
						{
							SectionId = sections[0].Id,
							DueDate = Period1DueDate
						},
						new CheckpointDates()
						{
							SectionId = sections[0].Id,
							DueDate = Period2DueDate
						}
					)
				},
				modelErrors
			);

			database.Reload();
			var checkpoint = database.Context.Checkpoints.SingleOrDefault();

			Assert.False(result);
			Assert.True(modelErrors.HasErrors);
			Assert.True(modelErrors.VerifyErrors("SectionDates"));
			Assert.Null(checkpoint);
		}
		public async Task RegisterNewStudentAsync_NonExistentGitHubUser_DoesNotRegister()
		{
			var database = new TestDatabaseBuilder()
				.AddClassroom("Class1")
				.AddSection("Class1", "Section1")
				.Build();


			var modelErrors = new MockErrorCollection();
			var identityProvider = GetMockIdentityProvider(GetIdentity("User1"));
			var gitHubUserClient = GetMockGitHubUserClient(userExists: false);

			var userService = GetUserService
			(
				database.Context,
				identityProvider.Object,
				gitHubUserClient.Object
			);

			var result = await userService.RegisterNewStudentAsync
			(
				"Class1",
				"Section1",
				new StudentRegistration() { GitHubLogin = "******" },
				"ConfirmationUrl",
				modelErrors
			);

			Assert.Equal(RegisterNewUserResult.Failed, result);
			Assert.True(modelErrors.VerifyErrors("GitHubLogin"));
			Assert.Equal(0, database.Context.Users.Count());
		}
		public async Task RegisterFirstSuperUserAsync_NonExistentGitHubUser_DoesNotRegister()
		{
			var database = new TestDatabaseBuilder().Build();

			var modelErrors = new MockErrorCollection();
			var activationToken = new ActivationToken("ActivationToken");
			var gitHubUserClient = GetMockGitHubUserClient(userExists: false);

			var userService = GetUserService
			(
				database.Context,
				activationToken: activationToken,
				gitHubUserClient: gitHubUserClient.Object
			);

			var result = await userService.RegisterFirstSuperUserAsync
			(
				new SuperUserRegistration()
				{
					ActivationToken = "ActivationToken",
					GitHubLogin = "******"
				},
				modelErrors
			);

			Assert.Equal(RegisterNewUserResult.Failed, result);
			Assert.True(modelErrors.VerifyErrors("GitHubLogin"));
			Assert.Equal(0, database.Context.Users.Count());
		}
		public async Task RegisterFirstSuperUserAsync_WrongActivationToken_DoesNotRegister()
		{
			var database = new TestDatabaseBuilder().Build();

			var modelErrors = new MockErrorCollection();
			var activationToken = new ActivationToken("ActivationToken");

			var userService = GetUserService
			(
				database.Context,
				activationToken: activationToken
			);

			var result = await userService.RegisterFirstSuperUserAsync
			(
				new SuperUserRegistration() { ActivationToken = "Wrong" },
				modelErrors
			);

			Assert.Equal(RegisterNewUserResult.Failed, result);
			Assert.True(modelErrors.VerifyErrors("ActivationToken"));
			Assert.Equal(0, database.Context.Users.Count());
		}
		public async Task UpdateUserAsync_InvalidGitHubUser_UserNotUpdated()
		{
			var database = new TestDatabaseBuilder()
			   .AddClassroom("Class1")
			   .AddSection("Class1", "Section1")
			   .AddStudent("User1", "LastName", "FirstName", "Class1", "Section1", "OldGitHubUser")
			   .Build();

			var user = database.Context.Users.Single();
			database.Reload();

			user.GitHubLogin = "******";

			var modelErrors = new MockErrorCollection();
			var userClient = GetMockGitHubUserClient(userExists: false);
			var userService = GetUserService
			(
				database.Context,
				gitHubUserClient: userClient.Object
			);

			var result = await userService.UpdateUserAsync
			(
				user,
				"ConfirmationUrlBuilder",
				modelErrors
			);

			database.Reload();
			user = database.Context.Users.Single();

			Assert.False(result);
			Assert.True(modelErrors.HasErrors);
			Assert.True(modelErrors.VerifyErrors("GitHubLogin"));
			Assert.Equal("OldGitHubUser", user.GitHubLogin);
		}