@Test
  public void shouldNotSnapshotPopulatingIndexes() throws Exception {
    // GIVEN
    CountDownLatch populatorLatch = new CountDownLatch(1);
    IndexAccessor indexAccessor = mock(IndexAccessor.class);
    IndexingService indexing =
        newIndexingServiceWithMockedDependencies(
            populator, indexAccessor, new DataUpdates(new NodePropertyUpdate[0]));
    int indexId = 1;
    int indexId2 = 2;
    File theFile = new File("Blah");

    IndexRule rule1 = indexRule(indexId, 2, 3, PROVIDER_DESCRIPTOR);
    IndexRule rule2 = indexRule(indexId2, 4, 5, PROVIDER_DESCRIPTOR);

    doAnswer(waitForLatch(populatorLatch)).when(populator).create();
    when(indexAccessor.snapshotFiles()).thenAnswer(newResourceIterator(theFile));
    when(indexProvider.getInitialState(indexId)).thenReturn(POPULATING);
    when(indexProvider.getInitialState(indexId2)).thenReturn(ONLINE);

    indexing.initIndexes(iterator(rule1, rule2));
    life.start();

    // WHEN
    ResourceIterator<File> files = indexing.snapshotStoreFiles();
    populatorLatch
        .countDown(); // only now, after the snapshot, is the population job allowed to finish

    // THEN
    // We get a snapshot from the online index, but no snapshot from the populating one
    assertThat(asCollection(files), equalTo(asCollection(iterator(theFile))));
  }
  @Test
  public void shouldBringIndexOnlineAndFlipOverToIndexAccessor() throws Exception {
    // given
    when(accessor.newUpdater(any(IndexUpdateMode.class))).thenReturn(updater);

    IndexingService indexingService =
        newIndexingServiceWithMockedDependencies(populator, accessor, withData());

    life.start();

    // when
    indexingService.createIndex(indexRule(0, labelId, propertyKeyId, PROVIDER_DESCRIPTOR));
    IndexProxy proxy = indexingService.getProxyForRule(0);

    verify(populator, timeout(1000)).close(true);

    try (IndexUpdater updater = proxy.newUpdater(IndexUpdateMode.ONLINE)) {
      updater.process(add(10, "foo"));
    }

    // then
    assertEquals(InternalIndexState.ONLINE, proxy.getState());
    InOrder order = inOrder(populator, accessor, updater);
    order.verify(populator).create();
    order.verify(populator).close(true);
    order.verify(accessor).newUpdater(IndexUpdateMode.ONLINE);
    order.verify(updater).process(add(10, "foo"));
    order.verify(updater).close();
  }
  @Test
  public void shouldStillReportInternalIndexStateAsPopulatingWhenConstraintIndexIsDonePopulating()
      throws Exception {
    // given
    when(accessor.newUpdater(any(IndexUpdateMode.class))).thenReturn(updater);

    IndexingService indexingService =
        newIndexingServiceWithMockedDependencies(populator, accessor, withData());

    life.start();

    // when
    indexingService.createIndex(
        IndexRule.constraintIndexRule(0, labelId, propertyKeyId, PROVIDER_DESCRIPTOR, null));
    IndexProxy proxy = indexingService.getProxyForRule(0);

    verify(populator, timeout(1000)).close(true);

    try (IndexUpdater updater = proxy.newUpdater(IndexUpdateMode.ONLINE)) {
      updater.process(add(10, "foo"));
    }

    // then
    assertEquals(InternalIndexState.POPULATING, proxy.getState());
    InOrder order = inOrder(populator, accessor, updater);
    order.verify(populator).create();
    order.verify(populator).close(true);
    order.verify(accessor).newUpdater(IndexUpdateMode.ONLINE);
    order.verify(updater).process(add(10, "foo"));
    order.verify(updater).close();
  }
  @Test
  public void shouldSnapshotOnlineIndexes() throws Exception {
    // GIVEN
    IndexAccessor indexAccessor = mock(IndexAccessor.class);
    IndexingService indexing =
        newIndexingServiceWithMockedDependencies(
            mock(IndexPopulator.class), indexAccessor, new DataUpdates(new NodePropertyUpdate[0]));
    int indexId = 1;
    int indexId2 = 2;
    File theFile = new File("Blah");

    IndexRule rule1 = indexRule(indexId, 2, 3, PROVIDER_DESCRIPTOR);
    IndexRule rule2 = indexRule(indexId2, 4, 5, PROVIDER_DESCRIPTOR);

    when(indexAccessor.snapshotFiles()).thenAnswer(newResourceIterator(theFile));
    when(indexProvider.getInitialState(indexId)).thenReturn(ONLINE);
    when(indexProvider.getInitialState(indexId2)).thenReturn(ONLINE);

    indexing.initIndexes(iterator(rule1, rule2));
    life.start();

    // WHEN
    ResourceIterator<File> files = indexing.snapshotStoreFiles();

    // THEN
    // We get a snapshot per online index
    assertThat(asCollection(files), equalTo(asCollection(iterator(theFile, theFile))));
  }
  @Test
  public void shouldLogIndexStateOnStart() throws Exception {
    // given
    TestLogger logger = new TestLogger();
    SchemaIndexProvider provider = mock(SchemaIndexProvider.class);
    when(provider.getProviderDescriptor()).thenReturn(PROVIDER_DESCRIPTOR);
    SchemaIndexProviderMap providerMap = new DefaultSchemaIndexProviderMap(provider);
    TokenNameLookup mockLookup = mock(TokenNameLookup.class);
    IndexingService indexingService =
        new IndexingService(
            mock(JobScheduler.class),
            providerMap,
            mock(IndexStoreView.class),
            mockLookup,
            mock(UpdateableSchemaState.class),
            mockLogging(logger));

    IndexRule onlineIndex = indexRule(1, 1, 1, PROVIDER_DESCRIPTOR);
    IndexRule populatingIndex = indexRule(2, 1, 2, PROVIDER_DESCRIPTOR);
    IndexRule failedIndex = indexRule(3, 2, 2, PROVIDER_DESCRIPTOR);

    when(provider.getInitialState(onlineIndex.getId())).thenReturn(ONLINE);
    when(provider.getInitialState(populatingIndex.getId()))
        .thenReturn(InternalIndexState.POPULATING);
    when(provider.getInitialState(failedIndex.getId())).thenReturn(InternalIndexState.FAILED);
    indexingService.initIndexes(asList(onlineIndex, populatingIndex, failedIndex).iterator());

    when(mockLookup.labelGetName(1)).thenReturn("LabelOne");
    when(mockLookup.labelGetName(2)).thenReturn("LabelTwo");
    when(mockLookup.propertyKeyGetName(1)).thenReturn("propertyOne");
    when(mockLookup.propertyKeyGetName(2)).thenReturn("propertyTwo");

    logger.clear();

    // when
    indexingService.start();

    // then
    verify(provider).getPopulationFailure(3);
    logger.assertAtLeastOnce(
        info("IndexingService.start: index on :LabelOne(propertyOne) is ONLINE"));
    logger.assertAtLeastOnce(
        info("IndexingService.start: index on :LabelOne(propertyTwo) is POPULATING"));
    logger.assertAtLeastOnce(
        info("IndexingService.start: index on :LabelTwo(propertyTwo) is FAILED"));
  }
  @Test
  public void shouldDeliverUpdatesThatOccurDuringPopulationToPopulator() throws Exception {
    // given
    when(populator.newPopulatingUpdater()).thenReturn(updater);

    CountDownLatch latch = new CountDownLatch(1);
    doAnswer(afterAwaiting(latch)).when(populator).add(anyLong(), any());

    IndexingService indexingService =
        newIndexingServiceWithMockedDependencies(populator, accessor, withData(add(1, "value1")));

    life.start();

    // when
    indexingService.createIndex(indexRule(0, labelId, propertyKeyId, PROVIDER_DESCRIPTOR));
    IndexProxy proxy = indexingService.getProxyForRule(0);
    assertEquals(InternalIndexState.POPULATING, proxy.getState());

    try (IndexUpdater updater = proxy.newUpdater(IndexUpdateMode.ONLINE)) {
      updater.process(add(2, "value2"));
    }

    latch.countDown();

    verify(populator, timeout(1000)).close(true);

    // then
    assertEquals(InternalIndexState.ONLINE, proxy.getState());
    InOrder order = inOrder(populator, accessor, updater);
    order.verify(populator).create();
    order.verify(populator).add(1, "value1");

    // this is invoked from indexAllNodes(),
    // empty because the id we added (2) is bigger than the one we indexed (1)
    order.verify(populator).newPopulatingUpdater();
    order.verify(updater).close();

    order.verify(populator).newPopulatingUpdater();
    order.verify(updater).process(add(2, "value2"));
    order.verify(updater).close();

    order.verify(populator).close(true);
    verifyNoMoreInteractions(updater);
    verifyNoMoreInteractions(populator);
    verifyZeroInteractions(accessor);
  }
  @Test
  public void indexCreationShouldBeIdempotent() throws Exception {
    // given
    when(accessor.newUpdater(any(IndexUpdateMode.class))).thenReturn(updater);

    IndexingService indexingService =
        newIndexingServiceWithMockedDependencies(populator, accessor, withData());

    life.start();

    // when
    indexingService.createIndex(
        IndexRule.indexRule(0, labelId, propertyKeyId, PROVIDER_DESCRIPTOR));
    indexingService.createIndex(
        IndexRule.indexRule(0, labelId, propertyKeyId, PROVIDER_DESCRIPTOR));

    // We are asserting that the second call to createIndex does not throw an exception.
  }
  @Test
  public void shouldBringConstraintIndexOnlineWhenExplicitlyToldTo() throws Exception {
    // given
    IndexingService indexingService =
        newIndexingServiceWithMockedDependencies(populator, accessor, withData());

    life.start();

    // when
    indexingService.createIndex(
        IndexRule.constraintIndexRule(0, labelId, propertyKeyId, PROVIDER_DESCRIPTOR, null));
    IndexProxy proxy = indexingService.getProxyForRule(0);

    indexingService.activateIndex(0);

    // then
    assertEquals(ONLINE, proxy.getState());
    InOrder order = inOrder(populator, accessor);
    order.verify(populator).create();
    order.verify(populator).close(true);
  }
  @Test
  public void shouldFailToStartIfMissingIndexProvider() throws Exception {
    // GIVEN an indexing service that has a schema index provider X
    IndexingService indexing =
        newIndexingServiceWithMockedDependencies(
            mock(IndexPopulator.class),
            mock(IndexAccessor.class),
            new DataUpdates(new NodePropertyUpdate[0]));
    String otherProviderKey = "something-completely-different";
    SchemaIndexProvider.Descriptor otherDescriptor =
        new SchemaIndexProvider.Descriptor(otherProviderKey, "no-version");
    IndexRule rule = indexRule(1, 2, 3, otherDescriptor);

    // WHEN trying to start up and initialize it with an index from provider Y
    try {
      indexing.initIndexes(iterator(rule));
      fail("initIndexes with mismatching index provider should fail");
    } catch (IllegalArgumentException e) { // THEN starting up should fail
      assertThat(e.getMessage(), containsString("existing index"));
      assertThat(e.getMessage(), containsString(otherProviderKey));
    }
  }