### Question:
ReadReplicaStartupProcess implements Lifecycle { @Override public void start() throws IOException, DatabaseShutdownException { boolean syncedWithUpstream = false; TimeoutStrategy.Timeout timeout = timeoutStrategy.newTimeout(); int attempt = 0; while ( !syncedWithUpstream ) { attempt++; MemberId source = null; try { source = selectionStrategy.bestUpstreamDatabase(); syncStoreWithUpstream( source ); syncedWithUpstream = true; } catch ( UpstreamDatabaseSelectionException e ) { lastIssue = issueOf( "finding upstream member", attempt ); debugLog.warn( lastIssue ); } catch ( StoreCopyFailedException e ) { lastIssue = issueOf( format( "copying store files from %s", source ), attempt ); debugLog.warn( lastIssue ); } catch ( StoreIdDownloadFailedException e ) { lastIssue = issueOf( format( "getting store id from %s", source ), attempt ); debugLog.warn( lastIssue ); } catch ( TopologyLookupException e ) { lastIssue = issueOf( format( "getting address of %s", source ), attempt ); debugLog.warn( lastIssue ); } try { Thread.sleep( timeout.getMillis() ); timeout.increment(); } catch ( InterruptedException e ) { Thread.interrupted(); lastIssue = "Interrupted while trying to start read replica"; debugLog.warn( lastIssue ); break; } } if ( !syncedWithUpstream ) { userLog.error( lastIssue ); throw new RuntimeException( lastIssue ); } try { localDatabase.start(); txPulling.start(); } catch ( Throwable e ) { throw new RuntimeException( e ); } } ReadReplicaStartupProcess( RemoteStore remoteStore, LocalDatabase localDatabase, Lifecycle txPulling,
            UpstreamDatabaseStrategySelector selectionStrategy, TimeoutStrategy timeoutStrategy, LogProvider debugLogProvider,
            LogProvider userLogProvider, StoreCopyProcess storeCopyProcess, TopologyService topologyService ); @Override void init(); @Override void start(); @Override void stop(); @Override void shutdown();  }### Answer:
@Test public void shouldReplaceEmptyStoreWithRemote() throws Throwable { when( localDatabase.isEmpty() ).thenReturn( true ); when( topologyService.findCatchupAddress( any() )).thenReturn( Optional.of( fromAddress ) ); when( remoteStore.getStoreId( any() ) ).thenReturn( otherStoreId ); ReadReplicaStartupProcess readReplicaStartupProcess = new ReadReplicaStartupProcess( remoteStore, localDatabase, txPulling, chooseFirstMember(), retryStrategy, NullLogProvider.getInstance(), NullLogProvider.getInstance(), storeCopyProcess, topologyService ); readReplicaStartupProcess.start(); verify( storeCopyProcess ).replaceWithStoreFrom( any(), any() ); verify( localDatabase ).start(); verify( txPulling ).start(); }
@Test public void shouldNotStartWithMismatchedNonEmptyStore() throws Throwable { when( localDatabase.isEmpty() ).thenReturn( false ); when( remoteStore.getStoreId( any() ) ).thenReturn( otherStoreId ); ReadReplicaStartupProcess readReplicaStartupProcess = new ReadReplicaStartupProcess( remoteStore, localDatabase, txPulling, chooseFirstMember(), retryStrategy, NullLogProvider.getInstance(), NullLogProvider.getInstance(), storeCopyProcess, topologyService ); try { readReplicaStartupProcess.start(); fail( "should have thrown" ); } catch ( Exception ex ) { assertThat( ex.getMessage(), containsString( "This read replica cannot join the cluster. The local database is not empty and has a " + "mismatching storeId" ) ); } verify( txPulling, never() ).start(); }
@Test public void shouldStartWithMatchingDatabase() throws Throwable { when( remoteStore.getStoreId( any() ) ).thenReturn( localStoreId ); when( localDatabase.isEmpty() ).thenReturn( false ); ReadReplicaStartupProcess readReplicaStartupProcess = new ReadReplicaStartupProcess( remoteStore, localDatabase, txPulling, chooseFirstMember(), retryStrategy, NullLogProvider.getInstance(), NullLogProvider.getInstance(), storeCopyProcess, topologyService ); readReplicaStartupProcess.start(); verify( localDatabase ).start(); verify( txPulling ).start(); }