@Test
public void synchronizationWithPeerNodeListAndDuplicates() throws Throwable {

    VerificationHost h = null;
    TemporaryFolder tmpFolder = new TemporaryFolder();
    tmpFolder.create();
    try {
        setUp(this.nodeCount);
        // the hosts are started, but not joined. We need to relax the quorum for any updates
        // to go through
        this.host.setNodeGroupQuorum(1);
        Map<String, ExampleServiceState> exampleStatesPerSelfLink = new HashMap<>();
        // add the *same* service instance, all *all* peers, so we force synchronization and epoch
        // change on an instance that exists everywhere
        int dupServiceCount = this.serviceCount;
        AtomicInteger counter = new AtomicInteger();
        Map<URI, ExampleServiceState> dupStates = new HashMap<>();
        for (VerificationHost v : this.host.getInProcessHostMap().values()) {
            counter.set(0);
            URI factoryUri = UriUtils.buildFactoryUri(v,
                    ExampleService.class);
            dupStates = this.host.doFactoryChildServiceStart(
                    null,
                    dupServiceCount,
                    ExampleServiceState.class,
                    (o) -> {
                        ExampleServiceState s = new ExampleServiceState();
                        s.documentSelfLink = "duplicateExampleInstance-"
                                + counter.incrementAndGet();
                        s.name = s.documentSelfLink;
                        o.setBody(s);
                    }, factoryUri);
        }
        for (ExampleServiceState s : dupStates.values()) {
            exampleStatesPerSelfLink.put(s.documentSelfLink, s);
        }
        // increment to account for link found on all nodes
        this.serviceCount = exampleStatesPerSelfLink.size();
        // create peer argument list, all the nodes join.
        Collection<URI> peerNodeGroupUris = new ArrayList<>();
        StringBuilder peerNodes = new StringBuilder();
        for (VerificationHost peer : this.host.getInProcessHostMap().values()) {
            peerNodeGroupUris.add(UriUtils.buildUri(peer, ServiceUriPaths.DEFAULT_NODE_GROUP));
            peerNodes.append(peer.getUri().toString()).append(",");
        }
        for (URI nodeGroup : this.host.getNodeGroupMap().values()) {
            this.host.subscribeForNodeGroupConvergence(nodeGroup, this.nodeCount + 1,
                    (o, e) -> {
                        if (e != null) {
                            this.host.log("Error in notification: %s", Utils.toString(e));
                        }
                    });
        }
        // now start a new Host and supply the already created peer, then observe the automatic
        // join
        h = new VerificationHost();
        int quorum = this.host.getPeerCount() + 1;
        String mainHostId = "main-" + VerificationHost.hostNumber.incrementAndGet();
        String[] args = {
                "--port=0",
                "--id=" + mainHostId,
                "--bindAddress=127.0.0.1",
                "--sandbox="
                        + tmpFolder.getRoot().getAbsolutePath(),
                "--peerNodes=" + peerNodes.toString()
        };
        h.initialize(args);
        h.setPeerSynchronizationEnabled(this.isPeerSynchronizationEnabled);
        h.setMaintenanceIntervalMicros(TimeUnit.MILLISECONDS
                .toMicros(VerificationHost.FAST_MAINT_INTERVAL_MILLIS));
        h.start();
        URI mainHostNodeGroupUri = UriUtils.buildUri(h, ServiceUriPaths.DEFAULT_NODE_GROUP);
        int totalCount = this.nodeCount + 1;
        peerNodeGroupUris.add(mainHostNodeGroupUri);
        this.host.waitForNodeGroupIsAvailableConvergence();
        this.host.waitForNodeGroupConvergence(peerNodeGroupUris, totalCount,
                totalCount, true);
        this.host.setNodeGroupQuorum(quorum, mainHostNodeGroupUri);
        this.host.setNodeGroupQuorum(quorum);
        this.host.scheduleSynchronizationIfAutoSyncDisabled(this.replicationNodeSelector);
        int peerNodeCount = h.getInitialPeerHosts().size();
        // include self in peers
        assertTrue(totalCount >= peerNodeCount + 1);
        // Before factory synch is complete, make sure POSTs to existing services fail,
        // since they are not marked idempotent.
        verifyReplicatedInConflictPost(dupStates);
        // now verify all nodes synchronize and see the example service instances that existed on the single
        // host
        this.host.addPeerNode(h);
        this.host.waitForReplicatedFactoryChildServiceConvergence(
                getFactoriesPerNodeGroup(this.replicationTargetFactoryLink),
                exampleStatesPerSelfLink,
                this.exampleStateConvergenceChecker,
                this.serviceCount, 0,
                this.replicationFactor);
        // Send some updates after the full group has formed  and verify the updates are seen by services on all nodes
        doStateUpdateReplicationTest(Action.PATCH, this.serviceCount, this.updateCount, 0,
                this.exampleStateUpdateBodySetter,
                this.exampleStateConvergenceChecker,
                exampleStatesPerSelfLink);
        URI exampleFactoryUri = this.host.getPeerServiceUri(ExampleService.FACTORY_LINK);
        waitForReplicatedFactoryServiceAvailable(
                UriUtils.buildUri(exampleFactoryUri, ExampleService.FACTORY_LINK),
                ServiceUriPaths.DEFAULT_NODE_SELECTOR);
    } finally {
        this.host.log("test finished");
        if (h != null) {
            h.stop();
            tmpFolder.delete();
        }
    }
}