private static void addSearchChain(SearchChainResolver.Builder builder,
                                       FederationConfig.Target target,
                                       FederationConfig.Target.SearchChain searchChain)
    {
        String id = target.id();
        if (!id.equals(searchChain.searchChainId()))
            throw new RuntimeException("Invalid federation config, " + id + " != " + searchChain.searchChainId());

        ComponentId searchChainId = ComponentId.fromString(id);
        builder.addSearchChain(searchChainId, federationOptions(searchChain), searchChain.documentTypes());
        // Here we make synthetic SearchChain proxies for all cluster.schema combinations possible
        // Given a source on the form saerchcluster.schema will rewrite it to source=searchcluster and restrict to schema.
        // TODO Consider solving this in the config model by making many synthetic search clusters
        for (String schema : searchChain.documentTypes()) {
            String virtualChainId = id + "." + schema;
            builder.addSearchChain(ComponentId.fromString(virtualChainId),
                    new SearchChaininvocationProxy(searchChainId, federationOptions(searchChain).setUseByDefault(false), schema));
        }
    }
@Test
    void require_that_optional_search_chains_does_not_delay_federation() {
        BlockingSearcher blockingSearcher = new BlockingSearcher();

        FederationTester tester = new FederationTester();
        tester.addSearchChain("chain1", new AddHitSearcher());
        tester.addOptionalSearchChain("chain2", blockingSearcher);

        Result result = tester.searchAndFill();
        assertEquals(2, result.getHitCount());
        assertTrue(result.hits().get(0) instanceof HitGroup);
        assertTrue(result.hits().get(1) instanceof HitGroup);
        HitGroup chain1Result = (HitGroup) result.hits().get(0);
        HitGroup chain2Result = (HitGroup) result.hits().get(1);

        // Verify chain1 result: One filled hit
        assertEquals(1, chain1Result.size());
        assertFilled(getFirstHit(chain1Result));

        // Verify chain2 result: A timeout error
        assertEquals(1, chain2Result.size());
        assertNotNull(chain2Result.getErrorHit());
        ErrorHit errorHit = chain2Result.getErrorHit();
        assertEquals(1, errorHit.errors().size());
        ErrorMessage error = errorHit.errors().iterator().next();
        assertEquals("chain2", error.getSource());
        assertEquals(ErrorMessage.timeoutCode, error.getCode());
        assertEquals("Timed out", error.getMessage());
        assertEquals("Error in execution of chain 'chain2': Chain timed out.", error.getDetailedMessage());
    }