package be.iminds.ilabt.jfed.lowlevel.api.test;

import be.iminds.ilabt.jfed.log.Logger;
import be.iminds.ilabt.jfed.lowlevel.ApiInfo;
import be.iminds.ilabt.jfed.lowlevel.GeniUserProvider;
import be.iminds.ilabt.jfed.lowlevel.JFedException;
import be.iminds.ilabt.jfed.lowlevel.TestbedInfoSource;
import be.iminds.ilabt.jfed.lowlevel.api.AbstractFederationApi1;
import be.iminds.ilabt.jfed.lowlevel.api.FederationRegistryApi1;
import be.iminds.ilabt.jfed.lowlevel.connection.JFedConnectionProvider;
import be.iminds.ilabt.jfed.lowlevel.connection.SfaConnection;
import be.iminds.ilabt.jfed.preferences.JFedPreferences;
import be.iminds.ilabt.jfed.util.GeniUrn;
import be.iminds.ilabt.jfed.util.TargetAuthority;

import javax.inject.Inject;
import java.net.URL;
import java.util.*;

/**
 * TestFederationMemberAuthority1
 */
public class TestFederationRegistry1 extends AbstractFederationApi1Test {
    @Inject
    public TestFederationRegistry1(Logger logger, TargetAuthority testedAuthority, GeniUserProvider geniUserProvider, JFedConnectionProvider connectionProvider, TestbedInfoSource testbedInfoSource, JFedPreferences jFedPreferences) {
        super(logger, testedAuthority, geniUserProvider, connectionProvider, testbedInfoSource, jFedPreferences);
    }

    public String getTestDescription() {
        return "Test Federation API Federation Registry version 1";
    }

    private FederationRegistryApi1 fr;


    private GeniUrn testUserUrn;

    public SfaConnection getConnection() throws JFedException {
        return (SfaConnection) connectionProvider.getConnectionByAuthority(user, testedAuthority.getServerToConnect(),
                new ApiInfo.Api(ApiInfo.ApiName.GENI_CH, 1));
    }

    final List<String> serviceDefaultFieldNames = new ArrayList<>();
    List<AbstractFederationApi1.GetVersionResult.FieldInfo> serviceDefaultFields = new ArrayList<>();


    protected void checkLookupServiceCorrectness(AbstractFederationApi1.FederationApiReply reply) {
        setErrorsFatal();
        Object rawReplyValue = reply.getRawValue();
        Object[] rawReplyValueV = assertInstanceOf(rawReplyValue, Object[].class,
                "Lookup service result should be an array (=List), but is of type " + rawReplyValue.getClass().getName());
        ;
        setErrorsNotFatal();
        for (Object o : rawReplyValueV) {
            assertInstanceOf(o, Map.class, "Lookup service array should contain dictionaries (=Map) of string to object . But at least one value is not dict but " +
                    o.getClass().getName());
            Map ht = (Map) o;

            //not guaranteed if fields is specified
//            assertMapContainsNonemptyString(ht, "SERVICE_URN");
//            assertMapContainsNonemptyString(ht, "SERVICE_URL");


//            assertMapContainsNonemptyString(ht, "SERVICE_CERT");
//            assertMapContainsNonemptyString(ht, "SERVICE_NAME");
//            assertMapContainsNonemptyString(ht, "SERVICE_DESCRIPTION");
        }
        setErrorsFatal();
    }


    public void setUp() {

        fr = new FederationRegistryApi1(logger, jFedPreferences);
        fr.setHandleMalformedReplies(false); //throw exceptions instead of trying to recover from malformed replies

        testUserUrn = GeniUrn.parse(user.getUserUrnString());
        assertNotNull(testUserUrn, "Error in test user urn: " + user.getUserUrnString());

        serviceDefaultFields = fr.getMinimumFields("SERVICE");
        for (AbstractFederationApi1.GetVersionResult.FieldInfo fi : serviceDefaultFields)
            serviceDefaultFieldNames.add(fi.getName());
    }

    @Test
    public void getVersion() throws JFedException {
        AbstractFederationApi1.FederationApiReply<AbstractFederationApi1.GetVersionResult> reply =
                fr.getVersion(getConnection());
        checkGetVersion(reply, fr, false);
    }


    private List<FederationRegistryApi1.ServiceDetails> amDetails;

    @Test()
    public void lookupAggregatesNoFilter() throws JFedException {
        Map<String, String> match = null;//new Map<String, String>();
        List<String> filter = null;// new List<String>();

        AbstractFederationApi1.FederationApiReply<List<FederationRegistryApi1.ServiceDetails>> reply =
                fr.lookupAggregates(getConnection(), filter, match, null);
        assertTrue(reply.getGeniResponseCode().isSuccess(), "The tested call did not return code 0 (success), instead it returned " + reply.getGeniResponseCode());

        checkLookupServiceCorrectness(reply);
        amDetails = reply.getValue();
        assertNotNull(amDetails, "lookup_aggregates returned null reply");
        for (FederationRegistryApi1.ServiceDetails sd : amDetails)
            note("lookup_aggregates returned aggregate: " + sd.getUrl());
    }

    private List<FederationRegistryApi1.ServiceDetails> maDetails;

    @Test()
    public void lookupMemberAuthoritiesNoFilter() throws JFedException {
        Map<String, String> match = null;//new Map<String, String>();
        List<String> filter = null;// new List<String>();

        AbstractFederationApi1.FederationApiReply<List<FederationRegistryApi1.ServiceDetails>> reply =
                fr.lookupMemberAuthorities(getConnection(), filter, match, null);
        assertTrue(reply.getGeniResponseCode().isSuccess());

        checkLookupServiceCorrectness(reply);
        maDetails = reply.getValue();
        assertNotNull(maDetails);
        for (FederationRegistryApi1.ServiceDetails sd : maDetails)
            note("lookup_member_authorities returned aggregate: " + sd.getUrl());
    }

    private List<FederationRegistryApi1.ServiceDetails> saDetails;

    @Test()
    public void lookupSliceAuthoritiesNoFilter() throws JFedException {
        Map<String, String> match = null;//new Map<String, String>();
        List<String> filter = null;// new List<String>();

        AbstractFederationApi1.FederationApiReply<List<FederationRegistryApi1.ServiceDetails>> reply =
                fr.lookupSliceAuthorities(getConnection(), filter, match, null);
        assertTrue(reply.getGeniResponseCode().isSuccess());

        checkLookupServiceCorrectness(reply);
        saDetails = reply.getValue();
        assertNotNull(saDetails);
        for (FederationRegistryApi1.ServiceDetails sd : saDetails)
            note("lookup_slice_authorities returned: " + sd.getUrl());
    }

    /////////////////////////// lookup with match ////////////////////////////////////////////

    private boolean hasTestedMulti = false;

    @Test(hardDepends = {"lookupAggregatesNoFilter"})
    public void lookupAggregatesWithMatchNoFilter() throws JFedException {
        if (amDetails.isEmpty()) skip("cannot test match if there are no AM");

        List<FederationRegistryApi1.ServiceDetails> toMatch = new ArrayList<>();
        List<String> urnToMatch = new ArrayList<>();
        List<String> urlToMatch = new ArrayList<>();
        if (!hasTestedMulti && amDetails.size() > 3) {
            note("Testing list of scalars, indicating an OR, as value in the match dictionary");
            toMatch.add(amDetails.get(1));
            toMatch.add(amDetails.get(2));
            hasTestedMulti = true;
        } else {
            toMatch.add(amDetails.get(0));
        }
        for (FederationRegistryApi1.ServiceDetails amDetail : toMatch) {
            assertNotNull(amDetail.getUrn(), "A service in a previous test call did not specify an URN");
            assertNotNull(amDetail.getUrl(), "A service in a previous test call did not specify an URL");
            urnToMatch.add(amDetail.getUrn().getValue());
            urlToMatch.add(amDetail.getUrl().toExternalForm());
        }

        note("Testing match by URL");
        Map<String, Object> match = new HashMap<>();
        List<String> filter = null;// new List<String>();
        if (urlToMatch.size() > 1)
            match.put("SERVICE_URL", urlToMatch);
        else
            match.put("SERVICE_URL", urlToMatch.get(0));

        AbstractFederationApi1.FederationApiReply<List<FederationRegistryApi1.ServiceDetails>> reply =
                fr.lookupAggregates(getConnection(), filter, match, null);
        assertTrue(reply.getGeniResponseCode().isSuccess());

        checkLookupServiceCorrectness(reply);
        amDetails = reply.getValue();
        assertNotNull(amDetails);
        assertEquals(amDetails.size(), toMatch.size(), "The search returned a different number of results than expected.");
        for (FederationRegistryApi1.ServiceDetails sd : amDetails) {
            assertTrue(urnToMatch.remove(sd.getUrn().getValue()), "returned a service that should not have been matched: " + sd.getUrn());
        }
        assertEmpty(urnToMatch, "Some services where not matched as expected: " + urnToMatch);
    }

    @Test(hardDepends = {"lookupMemberAuthoritiesNoFilter"})
    public void lookupMemberAuthoritiesWithMatchNoFilter() throws JFedException {
        if (maDetails.isEmpty()) skip("cannot test match if there are no MA");

        List<FederationRegistryApi1.ServiceDetails> toMatch = new ArrayList<>();
        List<String> urnToMatch = new ArrayList<>();
        List<String> nameToMatch = new ArrayList<>();
        if (!hasTestedMulti && maDetails.size() > 3) {
            note("Testing list of scalars, indicating an OR, as value in the match dictionary");
            toMatch.add(maDetails.get(1));
            toMatch.add(maDetails.get(2));
            hasTestedMulti = true;
        } else {
            toMatch.add(maDetails.get(0));
        }
        for (FederationRegistryApi1.ServiceDetails maDetail : toMatch) {
            assertNotNull(maDetail.getUrn(), "A service in a previous test call did not specify an URN");
            assertNotNull(maDetail.getName(), "A service in a previous test call did not specify an name");
            urnToMatch.add(maDetail.getUrn().getValue());
            nameToMatch.add(maDetail.getName());
        }

        note("Testing match by NAME");
        Map<String, Object> match = new HashMap<>();
        List<String> filter = null;// new List<String>();
        if (nameToMatch.size() > 1)
            match.put("SERVICE_NAME", nameToMatch);
        else
            match.put("SERVICE_NAME", nameToMatch.get(0));

        AbstractFederationApi1.FederationApiReply<List<FederationRegistryApi1.ServiceDetails>> reply =
                fr.lookupMemberAuthorities(getConnection(), filter, match, null);
        assertTrue(reply.getGeniResponseCode().isSuccess());

        checkLookupServiceCorrectness(reply);
        maDetails = reply.getValue();
        assertNotNull(maDetails);
        assertEquals(maDetails.size(), toMatch.size(), "The search returned a different number of results than expected.");
        for (FederationRegistryApi1.ServiceDetails sd : maDetails) {
            assertTrue(urnToMatch.remove(sd.getUrn().getValue()), "returned a service that should not have been matched: " + sd.getUrn());
        }
        assertEmpty(urnToMatch, "Some services where not matched as expected: " + urnToMatch);
    }

    @Test(hardDepends = {"lookupSliceAuthoritiesNoFilter"})
    public void lookupSliceAuthoritiesWithMatchNoFilter() throws JFedException {
        if (saDetails.isEmpty()) skip("cannot test match if there are no SA");

        List<FederationRegistryApi1.ServiceDetails> toMatch = new ArrayList<>();
        List<String> urnToMatch = new ArrayList<>();
        if (!hasTestedMulti && saDetails.size() > 3) {
            note("Testing list of scalars, indicating an OR, as value in the match dictionary");
            toMatch.add(saDetails.get(1));
            toMatch.add(saDetails.get(2));
            hasTestedMulti = true;
        } else {
            toMatch.add(saDetails.get(0));
        }
        for (FederationRegistryApi1.ServiceDetails saDetail : toMatch) {
            assertNotNull(saDetail.getUrn(), "A service in a previous test call did not specify an URN");
            urnToMatch.add(saDetail.getUrn().getValue());
        }

        note("Testing match by URN");
        Map<String, Object> match = new HashMap<>();
        List<String> filter = null;// new List<String>();
        if (urnToMatch.size() > 1)
            match.put("SERVICE_URN", urnToMatch);
        else
            match.put("SERVICE_URN", urnToMatch.get(0));

        AbstractFederationApi1.FederationApiReply<List<FederationRegistryApi1.ServiceDetails>> reply =
                fr.lookupSliceAuthorities(getConnection(), filter, match, null);
        assertTrue(reply.getGeniResponseCode().isSuccess());

        checkLookupServiceCorrectness(reply);
        saDetails = reply.getValue();
        assertNotNull(saDetails);
        assertEquals(saDetails.size(), toMatch.size(), "The search returned a different number of results than expected.");
        for (FederationRegistryApi1.ServiceDetails sd : saDetails) {
            assertTrue(urnToMatch.remove(sd.getUrn().getValue()), "returned a service that should not have been matched: " + sd.getUrn());
        }
        assertEmpty(urnToMatch, "Some services where not matched as expected: " + urnToMatch);
    }


    /////////////////////////// lookup with filter ////////////////////////////////////////////


    private void checkRawLookupResult(List<Map<String, String>> rawLookupResult, List<String> expectedFieldNames, List<String> excludedFieldNames) {
        assertNotNull(rawLookupResult);

        for (Map<String, String> serviceInfo : rawLookupResult) {
            for (String fieldName : expectedFieldNames) {
                if (!serviceInfo.containsKey(fieldName))
                    errorNonFatal("returned result does not contain field: \"" + fieldName + "\"");
            }
            for (String fieldName : excludedFieldNames) {
                if (serviceInfo.containsKey(fieldName))
                    errorNonFatal("returned result contains unexpected field: \"" + fieldName + "\"");
            }
        }
    }

    @Test(softDepends = {"lookupAggregatesWithMatchNoFilter"})
    public void lookupAggregatesWithFilter() throws JFedException {
        Map<String, String> match = null;//new Map<String, String>();
        List<String> filter = new ArrayList<>();
        filter.add("SERVICE_URL");
        filter.add("SERVICE_CERT");
        filter.add("SERVICE_DESCRIPTION");

        AbstractFederationApi1.FederationApiReply<List<FederationRegistryApi1.ServiceDetails>> reply =
                fr.lookupAggregates(getConnection(), filter, match, null);
        assertTrue(reply.getGeniResponseCode().isSuccess());

        checkLookupServiceCorrectness(reply); //was commented, but does not need to be

        List<Map<String, String>> rawResult = (List<Map<String, String>>) reply.getRawValue();
        assertNotNull(rawResult);
        List<String> expectedFieldNames = new ArrayList<>(filter);
        List<String> excludedFieldNames = new ArrayList<>(serviceDefaultFieldNames);
        excludedFieldNames.removeAll(expectedFieldNames);
        checkRawLookupResult(rawResult, expectedFieldNames, excludedFieldNames);
    }

    @Test(softDepends = {"lookupMemberAuthoritiesWithMatchNoFilter"})
    public void lookupMemberAuthoritiesWithFilter() throws JFedException {
        Map<String, String> match = null;//new Map<String, String>();
        List<String> filter = new ArrayList<>();
        filter.add("SERVICE_NAME");

        AbstractFederationApi1.FederationApiReply<List<FederationRegistryApi1.ServiceDetails>> reply =
                fr.lookupMemberAuthorities(getConnection(), filter, match, null);
        assertTrue(reply.getGeniResponseCode().isSuccess());

        checkLookupServiceCorrectness(reply); //was commented, but does not need to be

        List<Map<String, String>> rawResult = (List<Map<String, String>>) reply.getRawValue();
        assertNotNull(rawResult);
        List<String> expectedFieldNames = new ArrayList<>(filter);
        List<String> excludedFieldNames = new ArrayList<>(serviceDefaultFieldNames);
        excludedFieldNames.removeAll(expectedFieldNames);
        checkRawLookupResult(rawResult, expectedFieldNames, excludedFieldNames);
    }

    @Test(softDepends = {"lookupSliceAuthoritiesWithMatchNoFilter"})
    public void lookupSliceAuthoritiesWithFilter() throws JFedException {
        Map<String, String> match = null;//new Map<String, String>();
        List<String> filter = new ArrayList<>();
        filter.add("SERVICE_URN");
        filter.add("SERVICE_CERT");
        filter.add("SERVICE_DESCRIPTION");

        AbstractFederationApi1.FederationApiReply<List<FederationRegistryApi1.ServiceDetails>> reply =
                fr.lookupSliceAuthorities(getConnection(), filter, match, null);
        assertTrue(reply.getGeniResponseCode().isSuccess());

        checkLookupServiceCorrectness(reply); //was commented, but does not need to be

        List<Map<String, String>> rawResult = (List<Map<String, String>>) reply.getRawValue();
        assertNotNull(rawResult);
        List<String> expectedFieldNames = new ArrayList<>(filter);
        List<String> excludedFieldNames = new ArrayList<>(serviceDefaultFieldNames);
        excludedFieldNames.removeAll(expectedFieldNames);
        checkRawLookupResult(rawResult, expectedFieldNames, excludedFieldNames);
    }


    @Test(hardDepends = {"lookupAggregatesNoFilter"})
    public void lookupAMForUrns() throws JFedException {
        assertNotNull(amDetails);

        {
            //test URNs of (fake) objects on the authorities
            List<GeniUrn> urns = new ArrayList<>();
            for (FederationRegistryApi1.ServiceDetails amDetail : amDetails) {
                assertNotNull(amDetail.getUrn(), "A service in a previous test call did not specify an URN");
                urns.add(GeniUrn.createGeniUrnFromEncodedParts(amDetail.getUrn().getEncodedTopLevelAuthority(), "sliver", "fakesliver"));
            }
            note("Looking up " + urns.size() + " AM urns: " + urns);
            try {
                AbstractFederationApi1.FederationApiReply<Map<GeniUrn, URL>> reply =
                        fr.lookupAuthoritiesForUrns(getConnection(), urns);
                assertTrue(reply.getGeniResponseCode().isSuccess());

                Map<GeniUrn, URL> mapping = reply.getValue();
                assertNotNull(mapping);

                setErrorsNotFatal();
                assertEquals(mapping.size(), urns.size(), "Size of returned Map differs from number of requested urns. returned Map=" + mapping);
                for (FederationRegistryApi1.ServiceDetails amDetail : amDetails) {
                    URL returnedUrl =
                            mapping.get(GeniUrn.createGeniUrnFromEncodedParts(amDetail.getUrn().getEncodedTopLevelAuthority(), "sliver", "fakesliver"));
                    assertNotNull(returnedUrl, "lookup_authorities_for_urns did not return an entry for " + amDetail.getUrn());
                    if (returnedUrl != null) {
                        assertEquals(returnedUrl, amDetail.getUrl(), "lookup_authorities_for_urns did not return the same URL for " + amDetail.getUrn());
                    }
                }
                note("first test done.");
            } catch (JFedException e) {
                errorNonFatal("Error calling lookup_authorities_for_urns: probably malformed URN or URL in reply. error message:" + e.getMessage());
            }
            setErrorsFatal();
        }


        //test URNs of the authorities themself
        {
            List<GeniUrn> urns = new ArrayList<>();
            for (FederationRegistryApi1.ServiceDetails amDetail : amDetails) {
                assertNotNull(amDetail.getUrn(), "A service in a previous test call did not specify an URN");
                urns.add(amDetail.getUrn());
            }
            note("Looking up " + urns.size() + " AM urns: " + urns);

            try {
                AbstractFederationApi1.FederationApiReply<Map<GeniUrn, URL>> reply =
                        fr.lookupAuthoritiesForUrns(getConnection(), urns);
                assertTrue(reply.getGeniResponseCode().isSuccess());

                Map<GeniUrn, URL> mapping = reply.getValue();
                assertNotNull(mapping);

                setErrorsNotFatal();
                assertEquals(mapping.size(), urns.size(), "Size of returned Map differs from number of requested urns. returned Map=" + mapping);
                for (FederationRegistryApi1.ServiceDetails amDetail : amDetails) {
                    URL returnedUrl = mapping.get(amDetail.getUrn());
                    assertNotNull(returnedUrl, "lookup_authorities_for_urns did not return an entry for " + amDetail.getUrn());
                    if (returnedUrl != null) {
                        assertEquals(returnedUrl, amDetail.getUrl(), "lookup_authorities_for_urns did not return the same URL for " + amDetail.getUrn());
                    }
                }

            } catch (JFedException e) {
                errorNonFatal("Error calling lookup_authorities_for_urns: probably malformed URN or URL in reply. error message:" + e.getMessage());
            }
            setErrorsFatal();
        }
    }

    @Test(hardDepends = {"lookupSliceAuthoritiesNoFilter"})
    public void lookupSAForUrns() throws JFedException {

        assertNotNull(saDetails);
        Random r = new Random(System.currentTimeMillis());

        {
            List<GeniUrn> urns = new ArrayList<>();
            Map<GeniUrn, URL> requestUrnToExpectedUrl = new HashMap<>();
            for (FederationRegistryApi1.ServiceDetails saDetail : saDetails) {
                assertNotNull(saDetail.getUrn(), "A service in a previous test call did not specify an URN");
                String type;
                switch (r.nextInt(2)) {
                    case 0: {
                        type = "slice";
                        break;
                    }
                    default: {
                        type = "project";
                        break;
                    }
                }
                GeniUrn urn =
                        GeniUrn.createGeniUrnFromEncodedParts(saDetail.getUrn().getEncodedTopLevelAuthority(), type, "fake" + type);
                requestUrnToExpectedUrl.put(urn, saDetail.getUrl());
                urns.add(urn);
            }
            note("Looking up " + urns.size() + " SA urns: " + urns);

            try {
                AbstractFederationApi1.FederationApiReply<Map<GeniUrn, URL>> reply =
                        fr.lookupAuthoritiesForUrns(getConnection(), urns);
                assertTrue(reply.getGeniResponseCode().isSuccess());

                Map<GeniUrn, URL> mapping = reply.getValue();
                assertNotNull(mapping);

                setErrorsNotFatal();
                assertEquals(mapping.size(), urns.size(), "Size of returned Map differs from number of requested urns. returned Map=" + mapping);
                for (Map.Entry<GeniUrn, URL> e : requestUrnToExpectedUrl.entrySet()) {
                    URL returnedUrl = mapping.get(e.getKey());
                    URL expectedURL = e.getValue();

                    assertNotNull(returnedUrl, "lookup_authorities_for_urns did not return an entry for " + e.getKey());
                    if (returnedUrl != null) {
                        assertEquals(returnedUrl, expectedURL, "lookup_authorities_for_urns did not return the same URL for " + e.getKey());
                    }
                }

                note("first test done.");
            } catch (JFedException e) {
                errorNonFatal("Error calling lookup_authorities_for_urns: probably malformed URN or URL in reply. error message:" + e.getMessage());
            }
            setErrorsFatal();
        }


        {
            List<GeniUrn> urns = new ArrayList<>();

            for (FederationRegistryApi1.ServiceDetails saDetail : saDetails) {
                assertNotNull(saDetail.getUrn(), "A service in a previous test call did not specify an URN");
                urns.add(saDetail.getUrn());
            }
            note("Looking up " + urns.size() + " SA urns: " + urns);
            try {
                AbstractFederationApi1.FederationApiReply<Map<GeniUrn, URL>> reply =
                        fr.lookupAuthoritiesForUrns(getConnection(), urns);
                assertTrue(reply.getGeniResponseCode().isSuccess());

                Map<GeniUrn, URL> mapping = reply.getValue();
                assertNotNull(mapping);

                setErrorsNotFatal();
                assertEquals(mapping.size(), urns.size(), "Size of returned Map differs from number of requested urns. returned Map=" + mapping);
                for (FederationRegistryApi1.ServiceDetails saDetail : saDetails) {
                    URL returnedUrl = mapping.get(saDetail.getUrn());

                    assertNotNull(returnedUrl, "lookup_authorities_for_urns did not return an entry for " + saDetail.getUrn());
                    if (returnedUrl != null) {
                        assertEquals(returnedUrl, saDetail.getUrl(), "lookup_authorities_for_urns did not return the same URL for " + saDetail.getUrn());
                    }
                }

            } catch (JFedException e) {
                errorNonFatal("Error calling lookup_authorities_for_urns: probably malformed URN or URL in reply. error message:" + e.getMessage());
            }
            setErrorsFatal();
        }
    }

    @Test(hardDepends = {"lookupMemberAuthoritiesNoFilter"})
    public void lookupMAForUrns() throws JFedException {
        assertNotNull(maDetails);

        Random r = new Random(System.currentTimeMillis());

        {
            List<GeniUrn> urns = new ArrayList<>();
            Map<GeniUrn, URL> requestUrnToExpectedUrl = new HashMap<>();
            for (FederationRegistryApi1.ServiceDetails maDetail : maDetails) {
                assertNotNull(maDetail.getUrn(), "A service in a previous test call did not specify an URN");
                String type;
                switch (r.nextInt(2)) {
                    case 0: {
                        type = "member";
                        break;
                    }
                    default: {
                        type = "key";
                        break;
                    }
                }
                GeniUrn urn =
                        GeniUrn.createGeniUrnFromEncodedParts(maDetail.getUrn().getEncodedTopLevelAuthority(), type, "fake" + type);
                requestUrnToExpectedUrl.put(urn, maDetail.getUrl());
                urns.add(urn);
            }
            note("Looking up " + urns.size() + " MA urns: " + urns);

            try {
                AbstractFederationApi1.FederationApiReply<Map<GeniUrn, URL>> reply =
                        fr.lookupAuthoritiesForUrns(getConnection(), urns);
                assertTrue(reply.getGeniResponseCode().isSuccess());

                Map<GeniUrn, URL> mapping = reply.getValue();
                assertNotNull(mapping);

                setErrorsNotFatal();
                assertEquals(mapping.size(), urns.size(), "Size of returned Map differs from number of requested urns. returned Map=" + mapping);
                for (Map.Entry<GeniUrn, URL> e : requestUrnToExpectedUrl.entrySet()) {
                    URL returnedUrl = mapping.get(e.getKey());
                    URL expectedURL = e.getValue();

                    assertNotNull(returnedUrl, "lookup_authorities_for_urns did not return an entry for " + e.getKey());
                    if (returnedUrl != null) {
                        assertEquals(returnedUrl, expectedURL, "lookup_authorities_for_urns did not return the same URL for " + e.getKey());
                    }
                }

                note("first test done.");
            } catch (JFedException e) {
                errorNonFatal("Error calling lookup_authorities_for_urns: probably malformed URN or URL in reply. error message:" + e.getMessage());
            }
            setErrorsFatal();
        }

        {
            List<GeniUrn> urns = new ArrayList<>();
            for (FederationRegistryApi1.ServiceDetails maDetail : maDetails) {
                assertNotNull(maDetail.getUrn(), "A service in a previous test call did not specify an URN");
                urns.add(maDetail.getUrn());
            }
            note("Looking up " + urns.size() + " MA urns: " + urns);

            try {
                AbstractFederationApi1.FederationApiReply<Map<GeniUrn, URL>> reply =
                        fr.lookupAuthoritiesForUrns(getConnection(), urns);
                assertTrue(reply.getGeniResponseCode().isSuccess());

                Map<GeniUrn, URL> mapping = reply.getValue();
                assertNotNull(mapping);

                setErrorsNotFatal();
                assertEquals(mapping.size(), urns.size(), "Size of returned Map differs from number of requested urns. returned Map=" + mapping);
                for (FederationRegistryApi1.ServiceDetails maDetail : maDetails) {
                    URL returnedUrl = mapping.get(maDetail.getUrn());

                    assertNotNull(returnedUrl, "lookup_authorities_for_urns did not return an entry for " + maDetail.getUrn());
                    if (returnedUrl != null) {
                        assertEquals(returnedUrl, maDetail.getUrl(), "lookup_authorities_for_urns did not return the same URL for " + maDetail.getUrn());
                    }
                }
            } catch (JFedException e) {
                errorNonFatal("Error calling lookup_authorities_for_urns: probably malformed URN or URL in reply. error message:" + e.getMessage());
            }
            setErrorsFatal();
        }
    }


    @Test()
    public void lookupTrustRoots() throws JFedException {
        AbstractFederationApi1.FederationApiReply<List<String>> reply =
                fr.getTrustRoots(getConnection());
        assertTrue(reply.getGeniResponseCode().isSuccess());

        List<String> trustRoots = reply.getValue();
        assertNotNull(trustRoots);
        for (String trustRoot : trustRoots) {
            String tr = trustRoot;
            if (tr.length() > 30) tr = trustRoot.substring(0, 30) + "...";
            note("get_trust_roots returned (showing maximum of 30 chars): " + tr);
        }
    }
}
