@Test
  public void shouldScheduleIfAgentMatchingResources() throws Exception {
    JobConfig plan =
        evolveConfig
            .findBy(new CaseInsensitiveString(STAGE_NAME))
            .jobConfigByInstanceName("unit", true);
    plan.addResource("some-resource");

    scheduleHelper.schedule(evolveConfig, modifySomeFiles(evolveConfig), DEFAULT_APPROVED_BY);

    AgentConfig agentConfig = AgentMother.localAgent();
    agentConfig.addResource(new Resource("some-resource"));

    buildAssignmentService.onTimer();
    Work work = buildAssignmentService.assignWorkToAgent(agent(agentConfig));
    assertThat(work, is(not((Work) BuildAssignmentService.NO_WORK)));

    Pipeline pipeline =
        pipelineDao.mostRecentPipeline(CaseInsensitiveString.str(evolveConfig.name()));
    JobInstance job = pipeline.findStage(STAGE_NAME).findJob("unit");

    JobPlan loadedPlan = jobInstanceDao.loadPlan(job.getId());
    assertThat(loadedPlan.getResources(), is((List<Resource>) plan.resources()));

    assertThat(job.getState(), is(JobState.Assigned));
    assertThat(job.getAgentUuid(), is(agentConfig.getUuid()));
  }
  @Test
  public void shouldCancelOutOfDateBuilds() throws Exception {
    fixture.createPipelineWithFirstStageScheduled();
    buildAssignmentService.onTimer();
    configHelper.removeStage(fixture.pipelineName, fixture.devStage);

    buildAssignmentService.onConfigChange(goConfigService.getCurrentConfig());

    Pipeline pipeline = pipelineDao.mostRecentPipeline(fixture.pipelineName);
    JobInstance job = pipeline.getFirstStage().getJobInstances().first();
    assertThat(job.getState(), is(JobState.Completed));
    assertThat(job.getResult(), is(JobResult.Cancelled));
  }
  @Test
  public void shouldCancelBuildBelongingToNonExistentPipelineWhenCreatingWork() throws Exception {
    fixture.createPipelineWithFirstStageScheduled();
    Pipeline pipeline = pipelineDao.mostRecentPipeline(fixture.pipelineName);

    ScheduledPipelineLoader scheduledPipelineLoader = mock(ScheduledPipelineLoader.class);
    when(scheduledPipelineLoader.pipelineWithPasswordAwareBuildCauseByBuildId(
            pipeline.getFirstStage().getJobInstances().first().getId()))
        .thenThrow(new PipelineNotFoundException("thrown by mockPipelineService"));

    GoConfigService mockGoConfigService = mock(GoConfigService.class);
    CruiseConfig config = configHelper.currentConfig();
    configHelper.removePipeline(fixture.pipelineName, config);
    when(mockGoConfigService.getCurrentConfig()).thenReturn(config);

    buildAssignmentService =
        new BuildAssignmentService(
            mockGoConfigService,
            jobInstanceService,
            scheduleService,
            agentService,
            environmentConfigService,
            timeProvider,
            transactionTemplate,
            scheduledPipelineLoader,
            pipelineService,
            builderFactory,
            agentRemoteHandler);
    buildAssignmentService.onTimer();

    AgentConfig agentConfig = AgentMother.localAgent();
    agentConfig.addResource(new Resource("some-other-resource"));

    try {
      buildAssignmentService.assignWorkToAgent(agent(agentConfig));
      fail("should have thrown PipelineNotFoundException");
    } catch (PipelineNotFoundException e) {
      // ok
    }

    pipeline = pipelineDao.mostRecentPipeline(fixture.pipelineName);

    JobInstance job = pipeline.getFirstStage().getJobInstances().first();
    assertThat(job.getState(), is(JobState.Completed));
    assertThat(job.getResult(), is(JobResult.Cancelled));
    Stage stage = stageDao.findStageWithIdentifier(job.getIdentifier().getStageIdentifier());
    assertThat(stage.getState(), is(StageState.Cancelled));
    assertThat(stage.getResult(), is(StageResult.Cancelled));
  }
  @Test
  public void shouldNotScheduleIfAgentDoesNotHaveResources() throws Exception {
    JobConfig plan =
        evolveConfig
            .findBy(new CaseInsensitiveString(STAGE_NAME))
            .jobConfigByInstanceName("unit", true);
    plan.addResource("some-resource");

    scheduleHelper.schedule(evolveConfig, modifySomeFiles(evolveConfig), DEFAULT_APPROVED_BY);

    Work work = buildAssignmentService.assignWorkToAgent(agent(AgentMother.localAgent()));

    Pipeline pipeline =
        pipelineDao.mostRecentPipeline(CaseInsensitiveString.str(evolveConfig.name()));
    JobInstance job = pipeline.findStage(STAGE_NAME).findJob("unit");

    assertThat(work, is((Work) BuildAssignmentService.NO_WORK));
    assertThat(job.getState(), is(JobState.Scheduled));
    assertThat(job.getAgentUuid(), is(nullValue()));
  }
  @Test
  public void shouldNotAssignCancelledJob() throws Exception {
    AgentIdentifier instance = agent(AgentMother.localAgent());
    Pipeline pipeline =
        instanceFactory.createPipelineInstance(
            evolveConfig,
            modifyNoFiles(evolveConfig),
            new DefaultSchedulingContext(DEFAULT_APPROVED_BY),
            md5,
            new TimeProvider());
    dbHelper.savePipelineWithStagesAndMaterials(pipeline);
    buildAssignmentService.onConfigChange(goConfigService.getCurrentConfig());
    JobInstance job = buildOf(pipeline);
    job.cancel();
    jobInstanceDao.updateStateAndResult(job);

    assertThat(
        buildAssignmentService.assignWorkToAgent(instance),
        is((Work) BuildAssignmentService.NO_WORK));
  }
  @Test
  public void shouldCancelBuildBelongingToNonExistentPipeline() throws Exception {
    fixture.createPipelineWithFirstStageScheduled();
    buildAssignmentService.onTimer();

    configHelper.removePipeline(fixture.pipelineName);

    AgentConfig agentConfig = AgentMother.localAgent();
    agentConfig.addResource(new Resource("some-other-resource"));

    assertThat(
        (NoWork) buildAssignmentService.assignWorkToAgent(agent(agentConfig)),
        Matchers.is(BuildAssignmentService.NO_WORK));
    Pipeline pipeline = pipelineDao.mostRecentPipeline(fixture.pipelineName);
    JobInstance job = pipeline.getFirstStage().getJobInstances().first();
    assertThat(job.getState(), is(JobState.Completed));
    assertThat(job.getResult(), is(JobResult.Cancelled));
    Stage stage = stageDao.findStageWithIdentifier(job.getIdentifier().getStageIdentifier());
    assertThat(stage.getState(), is(StageState.Cancelled));
    assertThat(stage.getResult(), is(StageResult.Cancelled));
  }
  @Test
  public void shouldCancelBuildsForDeletedJobsWhenPipelineConfigChanges() throws Exception {
    fixture = new PipelineWithTwoStages(materialRepository, transactionTemplate).usingTwoJobs();
    fixture.usingConfigHelper(configHelper).usingDbHelper(dbHelper).onSetUp();
    fixture.createPipelineWithFirstStageScheduled();

    buildAssignmentService.onTimer();
    configHelper.removeJob(fixture.pipelineName, fixture.devStage, fixture.JOB_FOR_DEV_STAGE);

    buildAssignmentService.onPipelineConfigChange(
        goConfigService
            .getCurrentConfig()
            .getPipelineConfigByName(new CaseInsensitiveString(fixture.pipelineName)),
        "g1");

    Pipeline pipeline = pipelineDao.mostRecentPipeline(fixture.pipelineName);
    JobInstance deletedJob =
        pipeline.getFirstStage().getJobInstances().getByName(fixture.JOB_FOR_DEV_STAGE);
    assertThat(deletedJob.getState(), is(JobState.Completed));
    assertThat(deletedJob.getResult(), is(JobResult.Cancelled));
    JobInstance retainedJob =
        pipeline.getFirstStage().getJobInstances().getByName(fixture.DEV_STAGE_SECOND_JOB);
    assertThat(retainedJob.getState(), is(JobState.Scheduled));
    assertThat(retainedJob.getResult(), is(JobResult.Unknown));
  }
  @Test
  public void shouldRescheduleAbandonedBuild() throws SQLException {
    AgentIdentifier instance = agent(AgentMother.localAgent());
    Pipeline pipeline =
        instanceFactory.createPipelineInstance(
            evolveConfig,
            modifyNoFiles(evolveConfig),
            new DefaultSchedulingContext(DEFAULT_APPROVED_BY),
            md5,
            new TimeProvider());
    dbHelper.savePipelineWithStagesAndMaterials(pipeline);
    buildAssignmentService.onConfigChange(goConfigService.getCurrentConfig());
    buildAssignmentService.onTimer();
    buildAssignmentService.assignWorkToAgent(instance);
    long firstAssignedBuildId = buildOf(pipeline).getId();

    // somehow agent abandoned its original build...

    buildAssignmentService.assignWorkToAgent(instance);
    JobInstance reloaded = jobInstanceDao.buildByIdWithTransitions(firstAssignedBuildId);
    assertThat(reloaded.getState(), is(JobState.Rescheduled));
    assertThat(reloaded.isIgnored(), is(true));
  }
  @Test
  public void shouldReScheduleToCorrectAgent() throws Exception {
    JobConfig plan =
        evolveConfig
            .findBy(new CaseInsensitiveString(STAGE_NAME))
            .jobConfigByInstanceName("unit", true);
    plan.addResource("some-resource");

    scheduleHelper.schedule(evolveConfig, modifySomeFiles(evolveConfig), DEFAULT_APPROVED_BY);

    buildAssignmentService.onTimer();

    AgentConfig agentConfig = AgentMother.localAgent();
    agentConfig.addResource(new Resource("some-resource"));
    Work work = buildAssignmentService.assignWorkToAgent(agent(agentConfig));
    assertThat(work, is(not((Work) BuildAssignmentService.NO_WORK)));

    Pipeline pipeline =
        pipelineDao.mostRecentPipeline(CaseInsensitiveString.str(evolveConfig.name()));
    JobInstance job = pipeline.findStage(STAGE_NAME).findJob("unit");

    JobInstance runningJob = jobInstanceDao.buildByIdWithTransitions(job.getId());

    scheduleService.rescheduleJob(runningJob);

    pipeline = pipelineDao.mostRecentPipeline(CaseInsensitiveString.str(evolveConfig.name()));
    JobInstance rescheduledJob = pipeline.findStage(STAGE_NAME).findJob("unit");

    assertThat(rescheduledJob.getId(), not(runningJob.getId()));

    buildAssignmentService.onTimer();
    Work noResourcesWork =
        buildAssignmentService.assignWorkToAgent(
            agent(AgentMother.localAgentWithResources("WITHOUT_RESOURCES")));
    assertThat(noResourcesWork, is((Work) BuildAssignmentService.NO_WORK));

    buildAssignmentService.onTimer();
    Work correctAgentWork = buildAssignmentService.assignWorkToAgent(agent(agentConfig));
    assertThat(correctAgentWork, is(not((Work) BuildAssignmentService.NO_WORK)));
  }
  @Test
  public void shouldNotReloadScheduledJobPlansWhenAgentWorkAssignmentIsInProgress()
      throws Exception {
    fixture.createPipelineWithFirstStageScheduled();
    Pipeline pipeline = pipelineDao.mostRecentPipeline(fixture.pipelineName);
    JobInstance job = pipeline.getFirstStage().getJobInstances().first();

    final JobInstanceService mockJobInstanceService = mock(JobInstanceService.class);

    final Pipeline pipeline1 = pipeline;
    final Semaphore sem = new Semaphore(1);
    sem.acquire();
    when(mockJobInstanceService.orderedScheduledBuilds())
        .thenReturn(jobInstanceService.orderedScheduledBuilds());
    when(mockJobInstanceService.buildByIdWithTransitions(job.getId()))
        .thenReturn(jobInstanceService.buildByIdWithTransitions(job.getId()));

    ScheduledPipelineLoader scheduledPipelineLoader =
        new ScheduledPipelineLoader(null, null, null, null, null, null, null, null) {
          @Override
          public Pipeline pipelineWithPasswordAwareBuildCauseByBuildId(long buildId) {
            sem.release();
            sleepQuietly(1000);
            verify(mockJobInstanceService, times(1)).orderedScheduledBuilds();
            return pipeline1;
          }
        };

    final BuildAssignmentService buildAssignmentServiceUnderTest =
        new BuildAssignmentService(
            goConfigService,
            mockJobInstanceService,
            scheduleService,
            agentService,
            environmentConfigService,
            timeProvider,
            transactionTemplate,
            scheduledPipelineLoader,
            pipelineService,
            builderFactory,
            agentRemoteHandler);

    final Throwable[] fromThread = new Throwable[1];
    buildAssignmentServiceUnderTest.onTimer();

    Thread assigner =
        new Thread(
            new Runnable() {
              public void run() {
                try {
                  final AgentConfig agentConfig =
                      AgentMother.localAgentWithResources("some-other-resource");

                  buildAssignmentServiceUnderTest.assignWorkToAgent(agent(agentConfig));
                } catch (Throwable e) {
                  e.printStackTrace();
                  fromThread[0] = e;
                } finally {

                }
              }
            },
            "assignmentThread");
    assigner.start();

    sem.acquire();
    buildAssignmentServiceUnderTest.onTimer();

    assigner.join();
    assertThat(fromThread[0], is(nullValue()));
  }