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

import be.iminds.ilabt.jfed.log.Logger;
import be.iminds.ilabt.jfed.lowlevel.*;
import be.iminds.ilabt.jfed.lowlevel.api.AbstractFederationApi;
import be.iminds.ilabt.jfed.lowlevel.api.AbstractFederationApi1;
import be.iminds.ilabt.jfed.lowlevel.api.FederationMemberAuthorityApi1;
import be.iminds.ilabt.jfed.lowlevel.api.ProtogeniSliceAuthority;
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.SSHKeyHelper;
import be.iminds.ilabt.jfed.util.TargetAuthority;

import javax.inject.Inject;
import java.security.NoSuchAlgorithmException;
import java.util.*;

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

    public String getTestDescription() {
        return "Test Uniform Federation API Member Authority";
    }

    private FederationMemberAuthorityApi1 ma;


    private GeniUrn testUserUrn;

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

    final List<String> memberDefaultFieldNames = new ArrayList<>();
    List<AbstractFederationApi1.GetVersionResult.FieldInfo> memberDefaultFields = new ArrayList<>();
    final List<String> keyDefaultFieldNames = new ArrayList<>();
    List<AbstractFederationApi1.GetVersionResult.FieldInfo> keyDefaultFields = new ArrayList<>();

    public void setUp() {

        ma = new FederationMemberAuthorityApi1(logger, jFedPreferences);
        ma.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());

        memberDefaultFields = ma.getMinimumFields("MEMBER");
        keyDefaultFields = ma.getMinimumFields("KEY");
        for (AbstractFederationApi1.GetVersionResult.FieldInfo fi : memberDefaultFields)
            memberDefaultFieldNames.add(fi.getName());
        for (AbstractFederationApi1.GetVersionResult.FieldInfo fi : keyDefaultFields)
            keyDefaultFieldNames.add(fi.getName());
    }

    private boolean hasKeyService = false;

    @Test(groups = {"getversion"})
    public void getVersion() throws JFedException {
        AbstractFederationApi1.FederationApiReply<AbstractFederationApi1.GetVersionResult> reply =
                ma.getVersion(getConnection());
        checkGetVersion(reply, ma, true);

        Map<String, Object> htRes = (Map<String, Object>) reply.getRawValue();
        List services = assertMapContainsList(htRes, "SERVICES");

        if (services != null) {
            hasKeyService = services.contains("KEY");
        }
    }

    AnyCredential maTestUserCredential;
    List<AnyCredential> maTestUserCredentialList;

    @Test(softDepends = {"getVersion"}, groups = {"get_redential"})
    public void getTestUserCredential() throws JFedException {
        AbstractFederationApi1.FederationApiReply<List<AnyCredential>> reply =
                ma.getCredentials(getConnection(), new ArrayList<>(), testUserUrn, null);
        checkCorrectnessXmlRpcResult(reply);
        assertTrue(reply.getGeniResponseCode().isSuccess(),
                "The tested call did not return code 0 (success), instead it returned " + reply.getGeniResponseCode());

        Object[] rawVal = assertInstanceOf(reply.getRawValue(), Object[].class,
                "The get_credential call should return an array (List) of dictionaries, instead it returned a " +
                        reply.getRawValue().getClass().getName());
        for (Object credO : rawVal)
            assertInstanceOf(credO, Map.class,
                    "The get_credential call should return an array containing dictionaries, " +
                            "instead one of the values in the array is a " + credO.getClass().getName());

        List<AnyCredential> testUserCreds = reply.getValue();
        assertNotNull(testUserCreds, "no credential returned");

        assertEquals(testUserCreds.size(), rawVal.length, "Error in jFed: raw credential list length (" +
                testUserCreds.size() + ") differs from converted credential list length (" + rawVal.length + ")");

        assertNotEmpty(testUserCreds,
                "empty list of credentials returned. Expecting a list with credentials, but got  " +
                        (reply.getRawValue() == null ?
                                "null" :
                                (reply.getRawValue() instanceof Object[] ?
                                        ("a list: " + reply.getRawValue()) :
                                        ("something of class:" + reply.getRawValue().getClass().getName())
                                )
                        ));
        maTestUserCredentialList = new ArrayList<>(testUserCreds);
        maTestUserCredential = testUserCreds.get(0);
    }

    AnyCredential testUserCredential;
    List<AnyCredential> testUserCredentialList;

    @Test(softDepends = {"getTestUserCredential"}, groups = {
            "get_redential"}, description = "Get A Credential for the test user. If the MA get_credential call did not work, this will try to fall back to the old PROTOGENI_SA API.")
    public void retrieveCredentialSomehow() throws JFedException {
        if (maTestUserCredentialList != null && maTestUserCredential != null) {
            testUserCredential = maTestUserCredential;
            testUserCredentialList = maTestUserCredentialList;
        } else {
            if (!ApiInfo.hasService(user.getUserAuthorityServer(), ApiInfo.ApiName.PROTOGENI_SA, 1)) {
                errorFatal("The Uniform Federation API Member Authority get_credential call failed to provide a credential. additionally, the test user authority does not have a PROTOGENI_SA available as fallback. => Cannot retrieve a credential for the test user.");
            } else {
                note("The Uniform Federation API Member Authority get_credential call failed to provide a credential: Falling back to the PROTOGENI_SA API to retrieve a credential for the remaining tests.");
                SfaConnection protogeniSaCon = (SfaConnection) connectionProvider.getConnectionByAuthority(user,
                        user.getUserAuthorityServer(), new ApiInfo.Api(ApiInfo.ApiName.PROTOGENI_SA, 1));
                ProtogeniSliceAuthority protogeniSa = new ProtogeniSliceAuthority(logger, jFedPreferences);

                ProtogeniSliceAuthority.SliceAuthorityReply<AnyCredential> pgSaReply =
                        protogeniSa.getCredential(protogeniSaCon);
                assertTrue(pgSaReply.getGeniResponseCode().isSuccess());
                AnyCredential pgCred = pgSaReply.getValue();

                assertNotNull(pgCred, "no credential returned by protogeni SA");
                testUserCredentialList = new ArrayList<>();
                testUserCredentialList.add(pgCred);
                testUserCredential = pgCred;
            }
        }
    }

    private void checkMemberLookupResult(AbstractFederationApi.FederationApiReply<AbstractFederationApi.MemberInfoList> reply, List<String> expectedFieldNames, List<String> excludedFieldNames) {
        setErrorsFatal();

        checkCorrectnessXmlRpcResult(reply);
        checkLookupCorrectness(reply);
        Map<String, Object> raw = assertStringObjectMap(reply.getRawValue());

        AbstractFederationApi.MemberInfoList lookupRes = reply.getValue();
        if (lookupRes == null)
            warn("lookup result is null. This is either due to receiving an incorrect result (will fail later in this test in that case), or due a a bug in the API client implementation");
//        assertNotNull(lookupRes, "lookup result is null");
//        assertNotNull(testUserUrn, "bug in test (testUserUrn is null)");
//
//        assertTrue(lookupRes.size() > 0, "Lookup result is empty for own urn: "+testUserUrn.getValue());

//        Map<String, Map<String, String>> raw = lookupRes.getRaw();
        errorNonFatalIfNot(raw.size() == 1, "lookup must return one user, but it returned " + raw.size());

        Map<String, Object> rawUser = assertStringObjectMap(raw.get(testUserUrn.getValue()));
        errorNonFatalIfNot(rawUser != null, "lookup must return requested test user (" + testUserUrn.getValue() + ") user.");

        String rawUrn = (String) rawUser.get("MEMBER_URN"); //check that if this users has a member urn, it matches.
        if (rawUrn != null && !Objects.equals(testUserUrn.getValue(), rawUrn))
            errorNonFatal("lookup must return requested test user (" + testUserUrn.getValue() + ") user, and it did, however its returned MEMBER_URN entry is not matching: " + rawUrn);

//        for (int i = 0; i < raw.size(); i++) {
//            Map<String, String> memberInfo = raw.get(i);
        for (Object mi : raw.entrySet()) {
            Map<String, Object> memberInfo = assertStringObjectMap(mi);
            for (String fieldName : expectedFieldNames) {
                if (!memberInfo.containsKey(fieldName))
                    errorNonFatal("returned result does not contain field: \"" + fieldName + "\"");
            }
            for (String fieldName : excludedFieldNames) {
                if (memberInfo.containsKey(fieldName))
                    errorNonFatal("returned result contains unexpected field: \"" + fieldName + "\"");
            }
        }
    }


    @Test(softDepends = {"retrieveCredentialSomehow"})
    public void lookupPublicMemberInfoNoFilter() throws JFedException {
        lookupPublicMemberInfoXFilterHelper(null);
    }

    @Test(softDepends = {"retrieveCredentialSomehow"})
    public void lookupPublicMemberInfoEmptyFilter() throws JFedException {
        List<String> filter = new ArrayList<>();
        note("this test calls a lookup with an EMPTY filter specified. So there is a filter, is just is empty. " +
                "This test is expecting NO fields to be returned.");
        lookupPublicMemberInfoXFilterHelper(filter);
    }

    public void lookupPublicMemberInfoXFilterHelper(List<String> filter) throws JFedException {
        Map<String, String> match = new HashMap<>();

        match.put("MEMBER_URN", testUserUrn.getValue());

        note("looking up member by MEMBER_URN: " + testUserUrn.getValue() + " with filter: " + filter);
        AbstractFederationApi.FederationApiReply<AbstractFederationApi.MemberInfoList> reply =
                ma.lookupPublicMemberInfo(getConnection(), new ArrayList<>()/*no credentials needed so we don't give them*/, match, filter, null);
        checkCorrectnessXmlRpcResult(reply);

        assertTrue(reply.getGeniResponseCode().isSuccess(), "The tested call did not return code 0 (success), instead it returned " + reply.getGeniResponseCode());

        List<String> expectedFieldNames = new ArrayList<>();
        List<String> excludedFieldNames = new ArrayList<>();
        for (AbstractFederationApi1.GetVersionResult.FieldInfo fi : memberDefaultFields)
            if (Objects.equals(fi.getProtect(), AbstractFederationApi1.GetVersionResult.FieldInfo.Protect.PUBLIC) && (filter == null || filter.contains(fi.getName())))
                expectedFieldNames.add(fi.getName());
            else
                excludedFieldNames.add(fi.getName());
        if (filter != null && filter.isEmpty())
            note("Empty filter provided, so expecting no fields to be returned: excludedFieldNames=" + excludedFieldNames);
        if (filter == null)
            note("No filter provided, so expecting all fields to be returned: " + expectedFieldNames);
        checkMemberLookupResult(reply, expectedFieldNames, excludedFieldNames);
    }

    @Test(hardDepends = {"retrieveCredentialSomehow"})
    public void lookupPrivateMemberInfoNoFilter() throws JFedException {
        lookupPrivateMemberInfoXFilterHelper(null);
    }

    @Test(hardDepends = {"retrieveCredentialSomehow"})
    public void lookupPrivateMemberInfoEmptyFilter() throws JFedException {
        List<String> filter = new ArrayList<>();
        note("this test calls a lookup with an EMPTY filter specified. So there is a filter, is just is empty. " +
                "This test is expecting NO fields to be returned.");
        lookupPrivateMemberInfoXFilterHelper(filter);
    }

    public void lookupPrivateMemberInfoXFilterHelper(List<String> filter) throws JFedException {
        Map<String, String> match = new HashMap<>();

        note("looking up member by MEMBER_URN: " + testUserUrn.getValue() + " with filter: " + filter);
        match.put("MEMBER_URN", testUserUrn.getValue());

        AbstractFederationApi.FederationApiReply<AbstractFederationApi.MemberInfoList> reply =
                ma.lookupPrivateMemberInfo(getConnection(), testUserCredentialList, match, filter, null);
        checkCorrectnessXmlRpcResult(reply);
        assertTrue(reply.getGeniResponseCode().isSuccess(), "The tested call did not return code 0 (success), instead it returned " + reply.getGeniResponseCode());

        List<String> expectedFieldNames = new ArrayList<>();
        List<String> excludedFieldNames = new ArrayList<>();
        for (AbstractFederationApi1.GetVersionResult.FieldInfo fi : memberDefaultFields)
            if (Objects.equals(fi.getProtect(), AbstractFederationApi1.GetVersionResult.FieldInfo.Protect.PRIVATE) && (filter == null || filter.contains(fi.getName())))
                expectedFieldNames.add(fi.getName());
            else
                excludedFieldNames.add(fi.getName());
        if (filter != null && filter.isEmpty())
            note("Empty filter provided, so expecting no fields to be returned: excludedFieldNames=" + excludedFieldNames);
        if (filter == null)
            note("No filter provided, so expecting all fields to be returned: " + expectedFieldNames);
        checkMemberLookupResult(reply, expectedFieldNames, excludedFieldNames);
    }


    @Test(hardDepends = {"retrieveCredentialSomehow"})
    public void lookupIdentifyingMemberInfoNoFilter() throws JFedException {
        List<String> filter = null;
        lookupIdentifyingMemberInfoXFilterHelper(filter);
    }

    @Test(hardDepends = {"retrieveCredentialSomehow"})
    public void lookupIdentifyingMemberInfoEmptyFilter() throws JFedException {
        List<String> filter = new ArrayList<>();
        note("this test calls a lookup with an EMPTY filter specified. So there is a filter, is just is empty. " +
                "This test is expecting NO fields to be returned.");
        lookupIdentifyingMemberInfoXFilterHelper(filter);
    }

    public void lookupIdentifyingMemberInfoXFilterHelper(List<String> filter) throws JFedException {
        Map<String, String> match = new HashMap<>();

        note("looking up member by MEMBER_URN: " + testUserUrn.getValue() + " with filter: " + filter);
        match.put("MEMBER_URN", testUserUrn.getValue());

        AbstractFederationApi.FederationApiReply<AbstractFederationApi.MemberInfoList> reply =
                ma.lookupIdentifyingMemberInfo(getConnection(), testUserCredentialList, match, filter, null);
        checkCorrectnessXmlRpcResult(reply);
        assertTrue(reply.getGeniResponseCode().isSuccess(), "The tested call did not return code 0 (success), instead it returned " + reply.getGeniResponseCode());

        List<String> expectedFieldNames = new ArrayList<>();
        List<String> excludedFieldNames = new ArrayList<>();
        for (AbstractFederationApi1.GetVersionResult.FieldInfo fi : memberDefaultFields)
            if (Objects.equals(fi.getProtect(), AbstractFederationApi1.GetVersionResult.FieldInfo.Protect.IDENTIFYING) && (filter == null || filter.contains(fi.getName())))
                expectedFieldNames.add(fi.getName());
            else
                excludedFieldNames.add(fi.getName());
        if (filter != null && filter.isEmpty())
            note("Empty filter provided, so expecting no fields to be returned: excludedFieldNames=" + excludedFieldNames);
        if (filter == null)
            note("No filter provided, so expecting all fields to be returned: " + expectedFieldNames);
        checkMemberLookupResult(reply, expectedFieldNames, excludedFieldNames);
    }


    @Test(softDepends = {"lookupPublicMemberInfoEmptyFilter"}, description = "")
    public void lookupPublicMemberInfoFiltered() throws JFedException {
        Map<String, String> match = new HashMap<>();
        List<String> filter = new ArrayList<>();
        filter.add("MEMBER_UID");

        note("looking up member by MEMBER_URN: " + testUserUrn.getValue());
        match.put("MEMBER_URN", testUserUrn.getValue());

        AbstractFederationApi.FederationApiReply<AbstractFederationApi.MemberInfoList> reply =
                ma.lookupPublicMemberInfo(getConnection(), new ArrayList<>()/*no credentials needed so we don't give them*/, match, filter, null);
        checkCorrectnessXmlRpcResult(reply);

        assertTrue(reply.getGeniResponseCode().isSuccess(), "The tested call did not return code 0 (success), instead it returned " + reply.getGeniResponseCode());

        List<String> excludedFieldNames = new ArrayList<>();
        for (AbstractFederationApi1.GetVersionResult.FieldInfo fi : memberDefaultFields)
            if (!filter.contains(fi.getName()))
                excludedFieldNames.add(fi.getName());
        note("filter provided, so expecting only these fields to be returned: " + filter + " while none of these fields should be returned: " + excludedFieldNames);
        checkMemberLookupResult(reply, filter, excludedFieldNames);
    }

    @Test(hardDepends = {"retrieveCredentialSomehow"}, softDepends = {"lookupIdentifyingMemberInfoEmptyFilter"})
    public void lookupIdentifyingMemberInfoFiltered() throws JFedException {
        Map<String, String> match = new HashMap<>();
        List<String> filter = new ArrayList<>();
        filter.add("MEMBER_EMAIL");
        filter.add("MEMBER_FIRSTNAME");

        note("looking up member by MEMBER_URN: " + testUserUrn.getValue());
        match.put("MEMBER_URN", testUserUrn.getValue());

        AbstractFederationApi.FederationApiReply<AbstractFederationApi.MemberInfoList> reply =
                ma.lookupIdentifyingMemberInfo(getConnection(), testUserCredentialList, match, filter, null);
        checkCorrectnessXmlRpcResult(reply);
        assertTrue(reply.getGeniResponseCode().isSuccess(), "The tested call did not return code 0 (success), instead it returned " + reply.getGeniResponseCode());

        List<String> excludedFieldNames = new ArrayList<>();
        for (AbstractFederationApi1.GetVersionResult.FieldInfo fi : memberDefaultFields)
            if (!filter.contains(fi.getName()))
                excludedFieldNames.add(fi.getName());
        note("filter provided, so expecting only these fields to be returned: " + filter + " while none of these fields should be returned: " + excludedFieldNames);
        checkMemberLookupResult(reply, filter, excludedFieldNames);
    }


    /////////////////////////////////////////////////////// KEYS ///////////////////////////////////////////////////////

    private SSHKeyHelper sshKeyHelper;
    private Map<String, Object> keyCreationDetails;
    private static final String initialKeyDescription = "Randomly generated public key used for automated testing";
    private String keyId;

    @Test(hardDepends = {"retrieveCredentialSomehow"}, softDepends = {
            //softDeps just for order (KEY methods after MEMBER methods):
            "lookupIdentifyingMemberInfoFiltered", "lookupPublicMemberInfoFiltered",
            "lookupPublicMemberInfoEmptyFilter", "lookupPublicMemberInfoNoFilter",
            "lookupPrivateMemberInfoEmptyFilter", "lookupPrivateMemberInfoNoFilter",
            "lookupIdentifyingMemberInfoEmptyFilter", "lookupIdentifyingMemberInfoNoFilter"})
    public void createKey() throws JFedException, NoSuchAlgorithmException {
        if (!hasKeyService) skip("No KEY service advertised in get_version");

        sshKeyHelper = new SSHKeyHelper();

        Map<String, String> fields = new HashMap<>();
        fields.put("KEY_MEMBER", user.getUserUrnString());
        fields.put("KEY_PUBLIC", sshKeyHelper.getSshPublicKeyString());
        fields.put("KEY_DESCRIPTION", initialKeyDescription);

        AbstractFederationApi.FederationApiReply<AbstractFederationApi.MemberKeyInfo> reply =
                ma.createKey(getConnection(), testUserCredentialList, testUserUrn, fields, null);
        assertTrue(reply.getGeniResponseCode().isSuccess(), "The tested call did not return code 0 (success), instead it returned " + reply.getGeniResponseCode());

        Object rawValue = reply.getXmlRpcCallDetailsWithCodeValueError().getResultValueObject();
        assertNotNull(rawValue, "The call did not return a value object");
        assertInstanceOf(rawValue, Map.class, "The call did not return a dictionary");
        Map rawValueHt = (Map) rawValue;
        for (Object key : rawValueHt.keySet()) {
            assertInstanceOf(key, String.class, "One of the dictionary keys in the result value is not a String: class=" + key.getClass().getName() + " value=" + key);
            Object v = rawValueHt.get(key);
            if (Objects.equals(key, "KEY_ID") && !(v instanceof String))
                errorNonFatal("The returned KEY_ID should be a String, but it is a " + v.getClass().getName());
            if (Objects.equals(key, "KEY_DESCRIPTION") && !(v instanceof String))
                errorNonFatal("The returned KEY_DESCRIPTION should be a String, but it is a " + v.getClass().getName());
            if (!(v instanceof String))
                note("One of the dictionary values in the result value is not a String: key=" + key + " class=" + v.getClass().getName() + " value=" + v);
        }

        keyCreationDetails = assertStringObjectMap(reply.getRawValue());
        assertNotNull(keyCreationDetails, "The jFed code did not return details about the created key. (reply.getValue() == null). internal jFed error?");
        note("key details contains the following fields: " + keyCreationDetails.keySet());

        keyId = assertMapContainsNonemptyString(keyCreationDetails, "KEY_ID");
    }


    private void checkKeyLookupResult(AbstractFederationApi1.FederationApiReply<Map<GeniUrn, List<FederationMemberAuthorityApi1.MemberKeyInfo>>> reply, List<String> expectedFieldNames, List<String> excludedFieldNames) {
        Map<String, Object> lookupRes = assertStringObjectMap(reply.getRawValue());
        checkCorrectnessXmlRpcResult(reply);
//        checkLookupCorrectness(reply);
        assertNotNull(lookupRes, "lookup result is null");

        assertTrue(lookupRes.size() > 0, "Lookup result is empty for keyId: " + keyId);

        errorNonFatalIfNot(lookupRes.size() == 1, "lookup must return one member with one key, but it returned " + lookupRes.size() + " members");

        List userKeys = assertMapContainsList(lookupRes, testUserUrn.getValue());
        errorNonFatalIfNot(userKeys.size() == 1, "lookup must return one member with one key, but it returned 1 member with " + userKeys.size() + " keys");

        Map<String, Object> key = assertStringObjectMap(userKeys.get(0));
        errorNonFatalIfNot(key != null, "lookup must return requested key (" + keyId + ") but 1 member with 1 key that was null.");

        String rawId = assertMapContainsString(key, "KEY_ID"); //check that if this key has a keyId, it matches.
        if (rawId != null && !Objects.equals(keyId, rawId))
            errorNonFatal("lookup must return requested key (" + keyId + "), and it did, however its returned KEY_ID entry is not matching: " + rawId);

        for (String fieldName : expectedFieldNames) {
            if (!key.containsKey(fieldName))
                errorNonFatal("returned result does not contain field: \"" + fieldName + "\"");
        }
        for (String fieldName : excludedFieldNames) {
            if (key.containsKey(fieldName))
                errorNonFatal("returned result contains unexpected field: \"" + fieldName + "\"");
        }

    }

    @Test(hardDepends = {"createKey"})
    public void lookupKeysNoFilter() throws JFedException {
        if (!hasKeyService) skip("No KEY service advertised in get_version");

        Map<String, String> match = new HashMap<>();
        List<String> filter = null;//new List<String>();

        match.put("KEY_ID", keyId);

        note("looking up key by KEY_ID: " + keyId);
        AbstractFederationApi.FederationApiReply<Map<GeniUrn, List<FederationMemberAuthorityApi1.MemberKeyInfo>>>
                reply =
                ma.lookupKeys(getConnection(), testUserCredentialList, match, filter, null);
        checkCorrectnessXmlRpcResult(reply);

        assertTrue(reply.getGeniResponseCode().isSuccess(), "The tested call did not return code 0 (success), instead it returned " + reply.getGeniResponseCode());

        List<String> expectedFieldNames = new ArrayList<>();
        List<String> excludedFieldNames = new ArrayList<>();
        for (AbstractFederationApi1.GetVersionResult.FieldInfo fi : keyDefaultFields)
            expectedFieldNames.add(fi.getName());
        note("No filter provided, so expecting all fields to be returned: " + expectedFieldNames);
        checkKeyLookupResult(reply, expectedFieldNames, excludedFieldNames);

        String desc = (String) reply.getValue().get(testUserUrn).get(0).getKeyDescription();
        assertNotNull(desc);
        assertEquals(desc, initialKeyDescription);
    }

    private static final String updatedKeyDescription =
            "Randomly generated public key used for automated testing with updated description";

    @Test(hardDepends = {"createKey"}, softDepends = {"lookupKeysNoFilter"})
    public void updateKey() throws JFedException {
        if (!hasKeyService) skip("No KEY service advertised in get_version");

        Map<String, String> fields = new HashMap<>();
        fields.put("KEY_DESCRIPTION", updatedKeyDescription);

        AbstractFederationApi1.FederationApiReply<String> reply =
                ma.updateKey(getConnection(), testUserCredentialList, testUserUrn, keyId, fields, null);
        checkCorrectnessXmlRpcResult(reply);

        assertTrue(reply.getGeniResponseCode().isSuccess(), "The tested call did not return code 0 (success), instead it returned " + reply.getGeniResponseCode());
    }

    @Test(hardDepends = {"updateKey"})
    public void lookupKeysNoFilterAfterUpdate() throws JFedException {
        if (!hasKeyService) skip("No KEY service advertised in get_version");

        Map<String, String> match = new HashMap<>();
        List<String> filter = null;// new List<String>();

        match.put("KEY_ID", keyId);

        note("looking up key by KEY_ID: " + keyId);
        AbstractFederationApi.FederationApiReply<Map<GeniUrn, List<FederationMemberAuthorityApi1.MemberKeyInfo>>>
                reply =
                ma.lookupKeys(getConnection(), testUserCredentialList, match, filter, null);
        checkCorrectnessXmlRpcResult(reply);

        assertTrue(reply.getGeniResponseCode().isSuccess(), "The tested call did not return code 0 (success), instead it returned " + reply.getGeniResponseCode());

        List<String> expectedFieldNames = new ArrayList<>();
        List<String> excludedFieldNames = new ArrayList<>();
        for (AbstractFederationApi1.GetVersionResult.FieldInfo fi : keyDefaultFields)
            expectedFieldNames.add(fi.getName());
        note("No filter provided, so expecting all fields to be returned: " + expectedFieldNames);
        checkKeyLookupResult(reply, expectedFieldNames, excludedFieldNames);

        String desc = (String) reply.getValue().get(testUserUrn).get(0).getKeyDescription();
        assertNotNull(desc);
        assertEquals(desc, updatedKeyDescription);
    }

    @Test(hardDepends = {"retrieveCredentialSomehow"}, softDepends = {"createKey",
                                                                      "lookupKeysNoFilterAfterUpdate"}, description = "Try to delete key. Always called to ensure an attempt to delete is always made.")
    public void deleteKey() throws JFedException {
        if (!hasKeyService) skip("No KEY service advertised in get_version");
        if (keyId == null)
            skip("No key ID to delete known");

        AbstractFederationApi1.FederationApiReply<Boolean> reply =
                ma.deleteKey(getConnection(), testUserCredentialList, testUserUrn, keyId, null);
        checkCorrectnessXmlRpcResult(reply);

        assertTrue(reply.getGeniResponseCode().isSuccess(), "delete_key unsuccessful");

        assertTrue(reply.getValue(), "delete_key returned false");
    }

    @Test(hardDepends = {"deleteKey"})
    public void lookupKeysNoFilterAfterDelete() throws JFedException {
        if (!hasKeyService) skip("No KEY service advertised in get_version");

        Map<String, String> match = new HashMap<>();
        List<String> filter = null;// new List<String>();

        match.put("KEY_ID", keyId);

        note("looking up key by KEY_ID: " + keyId);
        note("Expecting no key to be found, since we just deleted the key.");
        AbstractFederationApi.FederationApiReply<Map<GeniUrn, List<FederationMemberAuthorityApi1.MemberKeyInfo>>>
                reply =
                ma.lookupKeys(getConnection(), testUserCredentialList, match, filter, null);
        checkCorrectnessXmlRpcResult(reply);

        assertTrue(reply.getGeniResponseCode().isSuccess(), "The tested call did not return code 0 (success), instead it returned " + reply.getGeniResponseCode());

//        checkLookupCorrectness(reply);
        Map<GeniUrn, List<FederationMemberAuthorityApi1.MemberKeyInfo>> lookupRes = reply.getValue();
        assertNotNull(lookupRes);
        if (lookupRes.size() != 0) {
            List<FederationMemberAuthorityApi1.MemberKeyInfo> memberKeys = lookupRes.get(testUserUrn);
            assertTrue(memberKeys.size() == 0, "After delete, a key was still found: " + memberKeys);
        }
    }
}
