// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#ifdef __cplusplus
#include <cstdlib>
#include <cstddef>
#else
#include <stdlib.h>
#include <stddef.h>
#endif

static void* my_gballoc_malloc(size_t size)
{
    return malloc(size);
}

static void my_gballoc_free(void* ptr)
{
    free(ptr);
}

#include "testrunnerswitcher.h"
#include "umock_c/umock_c.h"
#include "umock_c/umocktypes_charptr.h"
#include "umock_c/umocktypes_stdint.h"
#include "umock_c/umock_c_negative_tests.h"
#include "azure_macro_utils/macro_utils.h"

#include "azure_prov_client/internal/iothub_auth_client.h"

#define ENABLE_MOCKS
#include "azure_c_shared_utility/gballoc.h"
#include "umock_c/umock_c_prod.h"
#include "azure_c_shared_utility/strings.h"
#include "azure_c_shared_utility/sastoken.h"
#include "azure_c_shared_utility/azure_base64.h"
#include "azure_c_shared_utility/crt_abstractions.h"
#include "azure_c_shared_utility/urlencode.h"
#include "azure_c_shared_utility/hmacsha256.h"

#include "azure_prov_client/iothub_security_factory.h"
#include "hsm_client_data.h"

MOCKABLE_FUNCTION(, HSM_CLIENT_HANDLE, hsm_client_create);
MOCKABLE_FUNCTION(, void, hsm_client_destroy, HSM_CLIENT_HANDLE, handle);
MOCKABLE_FUNCTION(, int, hsm_client_import_key, HSM_CLIENT_HANDLE, handle, const unsigned char*, data, size_t, data_len);
MOCKABLE_FUNCTION(, int, hsm_client_get_ek, HSM_CLIENT_HANDLE, handle, unsigned char**, key, size_t*, key_len);
MOCKABLE_FUNCTION(, int, hsm_client_get_srk, HSM_CLIENT_HANDLE, handle, unsigned char**, key, size_t*, key_len);
MOCKABLE_FUNCTION(, int, hsm_client_sign_data, HSM_CLIENT_HANDLE, handle, const unsigned char*, data, size_t, data_len, unsigned char**, key, size_t*, key_len);
MOCKABLE_FUNCTION(, char*, hsm_client_get_trust_bundle, HSM_CLIENT_HANDLE, handle);
MOCKABLE_FUNCTION(, char*, hsm_client_get_certificate, HSM_CLIENT_HANDLE, handle);
MOCKABLE_FUNCTION(, char*, hsm_client_get_alias_key, HSM_CLIENT_HANDLE, handle);
MOCKABLE_FUNCTION(, char*, hsm_client_get_symmetric_key, HSM_CLIENT_HANDLE, handle);
MOCKABLE_FUNCTION(, char*, hsm_client_get_common_name, HSM_CLIENT_HANDLE, handle);
MOCKABLE_FUNCTION(, int, hsm_client_set_symmetrical_key_info, HSM_CLIENT_HANDLE, handle, const char*, reg_name, const char*, symm_key);

MOCKABLE_FUNCTION(, const HSM_CLIENT_TPM_INTERFACE*, hsm_client_tpm_interface);
MOCKABLE_FUNCTION(, const HSM_CLIENT_X509_INTERFACE*, hsm_client_x509_interface);
MOCKABLE_FUNCTION(, const HSM_CLIENT_KEY_INTERFACE*, hsm_client_key_interface);

#ifdef HSM_TYPE_HTTP_EDGE
MOCKABLE_FUNCTION(, const HSM_CLIENT_HTTP_EDGE_INTERFACE*, hsm_client_http_edge_interface);
#endif

#undef ENABLE_MOCKS

#ifdef __cplusplus
extern "C"
{
#endif
    STRING_HANDLE STRING_construct_sprintf(const char* format, ...);

    STRING_HANDLE STRING_construct_sprintf(const char* format, ...)
    {
        (void)format;
        return (STRING_HANDLE)my_gballoc_malloc(1);
    }

    int my_size_tToString(char* destination, size_t destinationSize, size_t value)
    {
        (void)destinationSize;
        (void)value;
        sprintf(destination, "12345");
        return 0;
    }
#ifdef __cplusplus
}
#endif

#define TEST_CONCRETE_HANDLE (CONCRETE_XDA_HANDLE)0x11111111
#define TEST_PARAMETER_VALUE (void*)0x11111112
#define TEST_STRING_HANDLE_VALUE (STRING_HANDLE)0x11111113

static unsigned char TEST_DATA[] = { 'k', 'e', 'y' };
static const size_t TEST_DATA_LEN = 3;
static char* TEST_STRING_VALUE = "Test_String_Value";
static char* TEST_CERT_VALUE = "Test_Cert_Value";

static const char* TEST_PARAM_INFO_VALUE = "Test Param Info";

TEST_DEFINE_ENUM_TYPE(DEVICE_AUTH_TYPE, DEVICE_AUTH_TYPE_VALUES);
IMPLEMENT_UMOCK_C_ENUM_TYPE(DEVICE_AUTH_TYPE, DEVICE_AUTH_TYPE_VALUES);

TEST_DEFINE_ENUM_TYPE(IOTHUB_SECURITY_TYPE, IOTHUB_SECURITY_TYPE_VALUES);
IMPLEMENT_UMOCK_C_ENUM_TYPE(IOTHUB_SECURITY_TYPE, IOTHUB_SECURITY_TYPE_VALUES);

TEST_DEFINE_ENUM_TYPE(HMACSHA256_RESULT, HMACSHA256_RESULT_VALUES);
IMPLEMENT_UMOCK_C_ENUM_TYPE(HMACSHA256_RESULT, HMACSHA256_RESULT_VALUES);

static const HSM_CLIENT_TPM_INTERFACE test_tpm_interface =
{
    hsm_client_create,
    hsm_client_destroy,
    hsm_client_import_key,
    hsm_client_get_ek,
    hsm_client_get_srk,
    hsm_client_sign_data
};

static const HSM_CLIENT_TPM_INTERFACE test_tpm_interface_fail =
{
    hsm_client_create,
    hsm_client_destroy,
    hsm_client_import_key,
    hsm_client_get_ek,
    hsm_client_get_srk,
    NULL
};

static const HSM_CLIENT_X509_INTERFACE test_x509_interface =
{
    hsm_client_create,
    hsm_client_destroy,
    hsm_client_get_certificate,
    hsm_client_get_alias_key,
    hsm_client_get_common_name
};

static const HSM_CLIENT_X509_INTERFACE test_x509_interface_fail =
{
    hsm_client_create,
    hsm_client_destroy,
    hsm_client_get_certificate,
    NULL,
    NULL
};

static const HSM_CLIENT_KEY_INTERFACE test_key_interface =
{
    hsm_client_create,
    hsm_client_destroy,
    hsm_client_get_symmetric_key,
    hsm_client_get_common_name,
    hsm_client_set_symmetrical_key_info
};

#ifdef HSM_TYPE_HTTP_EDGE
static const HSM_CLIENT_HTTP_EDGE_INTERFACE test_http_edge_interface =
{
    hsm_client_create,
    hsm_client_destroy,
    hsm_client_sign_data,
    hsm_client_get_trust_bundle
};
#endif

static BUFFER_HANDLE my_Azure_Base64_Decode(const char* source)
{
    (void)source;
    return (BUFFER_HANDLE)my_gballoc_malloc(1);
}

static void my_BUFFER_delete(BUFFER_HANDLE handle)
{
    my_gballoc_free((void*)handle);
}

static BUFFER_HANDLE my_BUFFER_new(void)
{
    return (BUFFER_HANDLE)my_gballoc_malloc(1);
}

static HSM_CLIENT_HANDLE my_hsm_client_create(void)
{
    return (HSM_CLIENT_HANDLE)my_gballoc_malloc(1);
}

static void my_hsm_client_destroy(HSM_CLIENT_HANDLE h)
{
    my_gballoc_free((void*)h);
}

static char* my_hsm_client_get_symmetric_key(HSM_CLIENT_HANDLE handle)
{
    (void)handle;
    size_t len = strlen(TEST_STRING_VALUE);
    char* result = (char*)my_gballoc_malloc(len + 1);
    strcpy(result, TEST_STRING_VALUE);
    return result;
}

static char* my_hsm_client_get_alias_key(HSM_CLIENT_HANDLE handle)
{
    (void)handle;
    size_t len = strlen(TEST_STRING_VALUE);
    char* result = (char*)my_gballoc_malloc(len + 1);
    strcpy(result, TEST_STRING_VALUE);
    return result;
}

static char* my_hsm_client_get_certificate(HSM_CLIENT_HANDLE handle)
{
    (void)handle;
    size_t len = strlen(TEST_CERT_VALUE);
    char* result = (char*)my_gballoc_malloc(len+1);
    strcpy(result, TEST_CERT_VALUE);
    return result;
}

static char* my_hsm_client_get_signer_cert(HSM_CLIENT_HANDLE handle)
{
    (void)handle;
    size_t len = strlen(TEST_STRING_VALUE);
    char* result = (char*)my_gballoc_malloc(len+1);
    strcpy(result, TEST_STRING_VALUE);
    return result;
}

static int my_hsm_client_sign_data(HSM_CLIENT_HANDLE handle, const unsigned char* data, size_t data_len, unsigned char** key, size_t* key_len)
{
    (void)handle;
    (void)data;
    (void)data_len;
    *key = (unsigned char*)my_gballoc_malloc(1);
    **key = 0;
    *key_len = 1;
    return 0;
}

static char* my_hsm_client_get_trust_bundle(HSM_CLIENT_HANDLE handle)
{
    (void)handle;
    return (char*)my_gballoc_malloc(1);
}


static int my_mallocAndStrcpy_s(char** destination, const char* source)
{
    (void)source;
    size_t src_len = strlen(source);
    *destination = (char*)my_gballoc_malloc(src_len+1);
    strcpy(*destination, source);
    return 0;
}

static STRING_HANDLE my_Azure_Base64_Encode_Bytes(const unsigned char* source, size_t length)
{
    (void)source;
    (void)length;
    return (STRING_HANDLE)my_gballoc_malloc(1);
}

static STRING_HANDLE my_STRING_construct(const char* textEncode)
{
    (void)textEncode;
    return (STRING_HANDLE)my_gballoc_malloc(1);
}

static STRING_HANDLE my_URL_Encode(STRING_HANDLE input)
{
    (void)input;
    return (STRING_HANDLE)my_gballoc_malloc(1);
}

static STRING_HANDLE my_URL_EncodeString(const char* textEncode)
{
    (void)textEncode;
    return (STRING_HANDLE)my_gballoc_malloc(1);
}

static void my_STRING_delete(STRING_HANDLE h)
{
    my_gballoc_free((void*)h);
}

static DEVICE_AUTH_CREDENTIAL_INFO g_test_sas_cred;
static DEVICE_AUTH_CREDENTIAL_INFO g_test_sas_cred_no_keyname;
static DEVICE_AUTH_CREDENTIAL_INFO g_test_x509_cred;
static DEVICE_AUTH_CREDENTIAL_INFO g_test_key_cred;

MU_DEFINE_ENUM_STRINGS(UMOCK_C_ERROR_CODE, UMOCK_C_ERROR_CODE_VALUES)

static void on_umock_c_error(UMOCK_C_ERROR_CODE error_code)
{
    char temp_str[256];
    (void)snprintf(temp_str, sizeof(temp_str), "umock_c reported error :%s", MU_ENUM_TO_STRING(UMOCK_C_ERROR_CODE, error_code));
    ASSERT_FAIL(temp_str);
}

static TEST_MUTEX_HANDLE g_testByTest;

BEGIN_TEST_SUITE(iothub_auth_client_ut)

    TEST_SUITE_INITIALIZE(suite_init)
    {
        int result;

        g_testByTest = TEST_MUTEX_CREATE();
        ASSERT_IS_NOT_NULL(g_testByTest);

        (void)umock_c_init(on_umock_c_error);

        result = umocktypes_charptr_register_types();
        ASSERT_ARE_EQUAL(int, 0, result);
        result = umocktypes_stdint_register_types();
        ASSERT_ARE_EQUAL(int, 0, result);

        REGISTER_TYPE(IOTHUB_SECURITY_TYPE, IOTHUB_SECURITY_TYPE);
        REGISTER_TYPE(HMACSHA256_RESULT, HMACSHA256_RESULT);

        REGISTER_UMOCK_ALIAS_TYPE(IOTHUB_SECURITY_HANDLE, void*);
        REGISTER_UMOCK_ALIAS_TYPE(BUFFER_HANDLE, void*);
        REGISTER_UMOCK_ALIAS_TYPE(STRING_HANDLE, void*);
        REGISTER_UMOCK_ALIAS_TYPE(HSM_CLIENT_HANDLE, void*);

        REGISTER_GLOBAL_MOCK_HOOK(hsm_client_create, my_hsm_client_create);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(hsm_client_create, NULL);
        REGISTER_GLOBAL_MOCK_HOOK(hsm_client_destroy, my_hsm_client_destroy);

        REGISTER_GLOBAL_MOCK_HOOK(hsm_client_get_alias_key, my_hsm_client_get_alias_key);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(hsm_client_get_alias_key, NULL);
        REGISTER_GLOBAL_MOCK_HOOK(hsm_client_sign_data, my_hsm_client_sign_data);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(hsm_client_sign_data, __LINE__);
        REGISTER_GLOBAL_MOCK_HOOK(hsm_client_get_certificate, my_hsm_client_get_certificate);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(hsm_client_get_certificate, NULL);
        REGISTER_GLOBAL_MOCK_HOOK(hsm_client_get_trust_bundle, my_hsm_client_get_trust_bundle);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(hsm_client_get_trust_bundle, NULL);

        REGISTER_GLOBAL_MOCK_HOOK(hsm_client_get_symmetric_key, my_hsm_client_get_symmetric_key);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(hsm_client_get_symmetric_key, NULL);
        REGISTER_GLOBAL_MOCK_RETURN(hsm_client_set_symmetrical_key_info, 0);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(hsm_client_set_symmetrical_key_info, __LINE__);

        REGISTER_GLOBAL_MOCK_HOOK(gballoc_malloc, my_gballoc_malloc);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(gballoc_malloc, NULL);
        REGISTER_GLOBAL_MOCK_HOOK(gballoc_free, my_gballoc_free);

        REGISTER_GLOBAL_MOCK_HOOK(mallocAndStrcpy_s, my_mallocAndStrcpy_s);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(mallocAndStrcpy_s, __LINE__);
        REGISTER_GLOBAL_MOCK_HOOK(Azure_Base64_Encode_Bytes, my_Azure_Base64_Encode_Bytes);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(Azure_Base64_Encode_Bytes, NULL);
        REGISTER_GLOBAL_MOCK_HOOK(URL_Encode, my_URL_Encode);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(URL_Encode, NULL);
        REGISTER_GLOBAL_MOCK_RETURN(STRING_c_str, TEST_STRING_VALUE);
        REGISTER_GLOBAL_MOCK_HOOK(STRING_delete, my_STRING_delete);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(size_tToString, __LINE__);

        REGISTER_GLOBAL_MOCK_HOOK(URL_EncodeString, my_URL_EncodeString);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(URL_EncodeString, NULL);

        REGISTER_GLOBAL_MOCK_HOOK(Azure_Base64_Decode, my_Azure_Base64_Decode);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(Azure_Base64_Decode, NULL);
        REGISTER_GLOBAL_MOCK_HOOK(BUFFER_new, my_BUFFER_new);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(BUFFER_new, NULL);
        REGISTER_GLOBAL_MOCK_RETURN(BUFFER_length, TEST_DATA_LEN);
        REGISTER_GLOBAL_MOCK_RETURN(BUFFER_u_char, TEST_DATA);
        REGISTER_GLOBAL_MOCK_HOOK(BUFFER_delete, my_BUFFER_delete);

        REGISTER_GLOBAL_MOCK_HOOK(STRING_construct, my_STRING_construct);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(STRING_construct, NULL);

        REGISTER_GLOBAL_MOCK_RETURN(HMACSHA256_ComputeHash, HMACSHA256_OK);
        REGISTER_GLOBAL_MOCK_FAIL_RETURN(HMACSHA256_ComputeHash, HMACSHA256_ERROR);

#if defined(HSM_TYPE_X509) || defined(HSM_TYPE_RIOT) || defined(HSM_AUTH_TYPE_CUSTOM) // Edge Module cmake shouldn't bring in TPM or RIOT.
        REGISTER_GLOBAL_MOCK_RETURN(iothub_security_type, IOTHUB_SECURITY_TYPE_SAS);
#else
        REGISTER_GLOBAL_MOCK_RETURN(iothub_security_type, IOTHUB_SECURITY_TYPE_HTTP_EDGE);
#endif

        REGISTER_GLOBAL_MOCK_RETURN(hsm_client_tpm_interface, &test_tpm_interface);
        REGISTER_GLOBAL_MOCK_RETURN(hsm_client_x509_interface, &test_x509_interface);
        REGISTER_GLOBAL_MOCK_RETURN(hsm_client_key_interface, &test_key_interface);

        g_test_sas_cred.dev_auth_type = AUTH_TYPE_SAS;
        g_test_sas_cred.sas_info.token_scope = "scope";
        g_test_sas_cred.sas_info.expiry_seconds = 123;
        g_test_sas_cred.sas_info.key_name = "key_name";

        g_test_sas_cred_no_keyname.dev_auth_type = AUTH_TYPE_SAS;
        g_test_sas_cred_no_keyname.sas_info.token_scope = "scope";
        g_test_sas_cred_no_keyname.sas_info.expiry_seconds = 123;
        g_test_sas_cred_no_keyname.sas_info.key_name = NULL;

        g_test_x509_cred.dev_auth_type = AUTH_TYPE_X509;

        g_test_key_cred.dev_auth_type = AUTH_TYPE_SYMM_KEY;
        g_test_key_cred.sas_info.token_scope = "scope";
        g_test_key_cred.sas_info.expiry_seconds = 123;
        g_test_key_cred.sas_info.key_name = NULL;

#ifdef HSM_TYPE_HTTP_EDGE
        REGISTER_GLOBAL_MOCK_RETURN(hsm_client_http_edge_interface, &test_http_edge_interface);
#endif

    }

    TEST_SUITE_CLEANUP(suite_cleanup)
    {
        umock_c_deinit();

        TEST_MUTEX_DESTROY(g_testByTest);
    }

    TEST_FUNCTION_INITIALIZE(method_init)
    {
        if (TEST_MUTEX_ACQUIRE(g_testByTest))
        {
            ASSERT_FAIL("Could not acquire test serialization mutex.");
        }
        umock_c_reset_all_calls();
    }

    TEST_FUNCTION_CLEANUP(method_cleanup)
    {
        TEST_MUTEX_RELEASE(g_testByTest);
    }

    static void setup_sign_sas_data_mocks(bool use_key)
    {
        if (use_key)
        {
            STRICT_EXPECTED_CALL(hsm_client_get_symmetric_key(IGNORED_PTR_ARG));
            STRICT_EXPECTED_CALL(Azure_Base64_Decode(IGNORED_PTR_ARG));
            STRICT_EXPECTED_CALL(BUFFER_new());
            STRICT_EXPECTED_CALL(BUFFER_length(IGNORED_PTR_ARG)).CallCannotFail();
            STRICT_EXPECTED_CALL(BUFFER_u_char(IGNORED_PTR_ARG)).CallCannotFail();
            STRICT_EXPECTED_CALL(HMACSHA256_ComputeHash(IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG));
            STRICT_EXPECTED_CALL(BUFFER_length(IGNORED_PTR_ARG)).CallCannotFail();
            STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
            STRICT_EXPECTED_CALL(BUFFER_u_char(IGNORED_PTR_ARG)).CallCannotFail();
            STRICT_EXPECTED_CALL(BUFFER_delete(IGNORED_PTR_ARG));
            STRICT_EXPECTED_CALL(BUFFER_delete(IGNORED_PTR_ARG));
            STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
        }
        else
        {
            STRICT_EXPECTED_CALL(hsm_client_sign_data(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG));
        }
    }

    static void setup_iothub_device_auth_generate_credentials_mocks(bool base64_encode_signature, bool urlencode_token_scope, bool use_key)
    {
        STRICT_EXPECTED_CALL(size_tToString(IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_NUM_ARG)).CallCannotFail();
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));

        setup_sign_sas_data_mocks(use_key);
        if (base64_encode_signature)
        {
            STRICT_EXPECTED_CALL(Azure_Base64_Encode_Bytes(IGNORED_PTR_ARG, IGNORED_NUM_ARG));
        }
        else
        {
            STRICT_EXPECTED_CALL(STRING_construct(IGNORED_PTR_ARG));
        }
        STRICT_EXPECTED_CALL(URL_Encode(IGNORED_PTR_ARG));
        if (urlencode_token_scope)
        {
            STRICT_EXPECTED_CALL(URL_EncodeString(IGNORED_PTR_ARG));
            STRICT_EXPECTED_CALL(STRING_c_str(IGNORED_PTR_ARG)).CallCannotFail();
            STRICT_EXPECTED_CALL(STRING_c_str(IGNORED_PTR_ARG)).CallCannotFail();
            STRICT_EXPECTED_CALL(STRING_delete(IGNORED_PTR_ARG));
        }
        else
        {
            STRICT_EXPECTED_CALL(STRING_c_str(IGNORED_PTR_ARG)).CallCannotFail();
        }
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
        STRICT_EXPECTED_CALL(STRING_c_str(IGNORED_PTR_ARG)).CallCannotFail();
        STRICT_EXPECTED_CALL(mallocAndStrcpy_s(IGNORED_PTR_ARG, IGNORED_PTR_ARG));
        STRICT_EXPECTED_CALL(STRING_delete(IGNORED_PTR_ARG));
        STRICT_EXPECTED_CALL(STRING_delete(IGNORED_PTR_ARG));
        STRICT_EXPECTED_CALL(STRING_delete(IGNORED_PTR_ARG));
        STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
        STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
    }

    static void setup_iothub_device_auth_generate_credentials_x509_mocks(void)
    {
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
        STRICT_EXPECTED_CALL(hsm_client_get_certificate(IGNORED_NUM_ARG));
        STRICT_EXPECTED_CALL(hsm_client_get_alias_key(IGNORED_NUM_ARG));
    }

#if defined(HSM_TYPE_SAS_TOKEN)  || defined(HSM_AUTH_TYPE_CUSTOM)
    TEST_FUNCTION(iothub_device_auth_create_tpm_succeed)
    {
        //arrange
        STRICT_EXPECTED_CALL(iothub_security_type()).SetReturn(IOTHUB_SECURITY_TYPE_SAS);
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
        STRICT_EXPECTED_CALL(hsm_client_tpm_interface()).SetReturn(&test_tpm_interface);
        STRICT_EXPECTED_CALL(hsm_client_create());

        //act
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();

        //assert
        ASSERT_IS_NOT_NULL(xda_handle);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        iothub_device_auth_destroy(xda_handle);
    }

    TEST_FUNCTION(iothub_device_auth_create_tpm_interface_NULL_fail)
    {
        //arrange
        STRICT_EXPECTED_CALL(iothub_security_type()).SetReturn(IOTHUB_SECURITY_TYPE_SAS);
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
        STRICT_EXPECTED_CALL(hsm_client_tpm_interface()).SetReturn(&test_tpm_interface_fail);
        STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));

        //act
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();

        //assert
        ASSERT_IS_NULL(xda_handle);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        iothub_device_auth_destroy(xda_handle);
    }
#endif

    TEST_FUNCTION(iothub_device_auth_create_malloc_fail)
    {
        //arrange
        STRICT_EXPECTED_CALL(iothub_security_type());
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG)).SetReturn(NULL);

        //act
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();

        //assert
        ASSERT_IS_NULL(xda_handle);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
    }


#if defined(HSM_TYPE_X509) || defined(HSM_TYPE_RIOT) || defined(HSM_AUTH_TYPE_CUSTOM) // Edge Module cmake shouldn't bring in TPM or RIOT.
    TEST_FUNCTION(iothub_device_auth_create_x509_succeed)
    {
        //arrange
        STRICT_EXPECTED_CALL(iothub_security_type()).SetReturn(IOTHUB_SECURITY_TYPE_X509);
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
        STRICT_EXPECTED_CALL(hsm_client_x509_interface()).SetReturn(&test_x509_interface);
        STRICT_EXPECTED_CALL(hsm_client_create());

        //act
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();

        //assert
        ASSERT_IS_NOT_NULL(xda_handle);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        iothub_device_auth_destroy(xda_handle);
    }

    TEST_FUNCTION(iothub_device_auth_create_x509_Interface_NULL_fail)
    {
        //arrange
        STRICT_EXPECTED_CALL(iothub_security_type()).SetReturn(IOTHUB_SECURITY_TYPE_X509);
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
        STRICT_EXPECTED_CALL(hsm_client_x509_interface()).SetReturn(&test_x509_interface_fail);
        STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));

        //act
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();

        //assert
        ASSERT_IS_NULL(xda_handle);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        iothub_device_auth_destroy(xda_handle);
    }

    TEST_FUNCTION(iothub_device_auth_create_concrete_iothub_device_auth_create_fail)
    {
        //arrange
        STRICT_EXPECTED_CALL(iothub_security_type()).SetReturn(IOTHUB_SECURITY_TYPE_X509).CallCannotFail();
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
        STRICT_EXPECTED_CALL(hsm_client_x509_interface()).SetReturn(&test_x509_interface);
        STRICT_EXPECTED_CALL(hsm_client_create()).SetReturn(NULL);
        STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));

        //act
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();

        //assert
        ASSERT_IS_NULL(xda_handle);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
    }
#endif

    TEST_FUNCTION(iothub_device_auth_destroy_succeed)
    {
        //arrange
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
        umock_c_reset_all_calls();

        STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
        STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
        STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG));
        STRICT_EXPECTED_CALL(hsm_client_destroy(IGNORED_PTR_ARG));
        STRICT_EXPECTED_CALL(gballoc_free(xda_handle));

        //act
        iothub_device_auth_destroy(xda_handle);

        //assert
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
    }

    TEST_FUNCTION(iothub_device_auth_destroy_handle_NULL_succeed)
    {
        //arrange

        //act
        iothub_device_auth_destroy(NULL);

        //assert
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
    }

    TEST_FUNCTION(iothub_device_auth_get_auth_type_handle_NULL_fail)
    {
        //arrange

        //act
        DEVICE_AUTH_TYPE dev_auth_type = iothub_device_auth_get_type(NULL);

        //assert
        ASSERT_ARE_EQUAL(DEVICE_AUTH_TYPE, AUTH_TYPE_UNKNOWN, dev_auth_type);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
    }

    TEST_FUNCTION(iothub_device_auth_get_auth_type_succeed)
    {
        //arrange
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
        umock_c_reset_all_calls();

        //act
        DEVICE_AUTH_TYPE dev_auth_type = iothub_device_auth_get_type(xda_handle);

        //assert
        ASSERT_ARE_EQUAL(DEVICE_AUTH_TYPE, AUTH_TYPE_SAS, dev_auth_type);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        iothub_device_auth_destroy(xda_handle);
    }

    TEST_FUNCTION(iothub_device_auth_generate_credentials_handle_NULL_fail)
    {
        //arrange

        //act
        void* result = iothub_device_auth_generate_credentials(NULL, &g_test_sas_cred);

        //assert
        ASSERT_IS_NULL(result);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
    }

    TEST_FUNCTION(iothub_device_auth_generate_credentials_cred_info_NULL_fail)
    {
        //arrange
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
        umock_c_reset_all_calls();

        //act
        void* result = iothub_device_auth_generate_credentials(xda_handle, NULL);

        //assert
        ASSERT_IS_NULL(result);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        iothub_device_auth_destroy(xda_handle);
    }

    TEST_FUNCTION(iothub_device_auth_generate_credentials_invalid_param_type_fail)
    {
        //arrange
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
        umock_c_reset_all_calls();

        DEVICE_AUTH_CREDENTIAL_INFO test_iothub_device_auth_credentials;
        test_iothub_device_auth_credentials.dev_auth_type = AUTH_TYPE_X509;
        test_iothub_device_auth_credentials.sas_info.expiry_seconds = 0;
        test_iothub_device_auth_credentials.sas_info.token_scope = NULL;
        test_iothub_device_auth_credentials.sas_info.key_name = NULL;
        //act
        void* result = iothub_device_auth_generate_credentials(xda_handle, &test_iothub_device_auth_credentials);

        //assert
        ASSERT_IS_NULL(result);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        iothub_device_auth_destroy(xda_handle);
    }

#if defined(HSM_TYPE_X509) || defined(HSM_TYPE_RIOT) || defined(HSM_AUTH_TYPE_CUSTOM) // Edge Module cmake shouldn't bring in TPM or RIOT.
    TEST_FUNCTION(iothub_device_auth_generate_credentials_succeed)
    {
        //arrange
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
        umock_c_reset_all_calls();

        setup_iothub_device_auth_generate_credentials_mocks(true, false, false);

        //act
        void* result = iothub_device_auth_generate_credentials(xda_handle, &g_test_sas_cred);

        //assert
        ASSERT_IS_NOT_NULL(result);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        my_gballoc_free(result);
        iothub_device_auth_destroy(xda_handle);
    }

#ifndef __APPLE__   // Disabled for Apple builds.
    TEST_FUNCTION(iothub_device_auth_generate_credentials_key_succeed)
    {
        //arrange
        STRICT_EXPECTED_CALL(iothub_security_type()).SetReturn(IOTHUB_SECURITY_TYPE_SYMMETRIC_KEY);
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
        STRICT_EXPECTED_CALL(hsm_client_key_interface());

        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
        umock_c_reset_all_calls();

        setup_iothub_device_auth_generate_credentials_mocks(true, false, true);

        //act
        void* result = iothub_device_auth_generate_credentials(xda_handle, &g_test_key_cred);

        //assert
        ASSERT_IS_NOT_NULL(result);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        my_gballoc_free(result);
        iothub_device_auth_destroy(xda_handle);
    }
#endif

    TEST_FUNCTION(iothub_device_auth_generate_credentials_no_key_succeed)
    {
        //arrange
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
        umock_c_reset_all_calls();

        setup_iothub_device_auth_generate_credentials_mocks(true, false, false);

        //act
        void* result = iothub_device_auth_generate_credentials(xda_handle, &g_test_sas_cred_no_keyname);

        //assert
        ASSERT_IS_NOT_NULL(result);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        my_gballoc_free(result);
        iothub_device_auth_destroy(xda_handle);
    }

    TEST_FUNCTION(iothub_device_auth_generate_credentials_x509_succeed)
    {
        //arrange
        STRICT_EXPECTED_CALL(iothub_security_type()).SetReturn(IOTHUB_SECURITY_TYPE_X509);
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
        STRICT_EXPECTED_CALL(hsm_client_x509_interface()).SetReturn(&test_x509_interface);

        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
        umock_c_reset_all_calls();

        setup_iothub_device_auth_generate_credentials_x509_mocks();

        //act
        CREDENTIAL_RESULT* result = iothub_device_auth_generate_credentials(xda_handle, &g_test_x509_cred);

        //assert
        ASSERT_IS_NOT_NULL(result);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        my_gballoc_free(result);
        iothub_device_auth_destroy(xda_handle);
    }

    TEST_FUNCTION(iothub_device_auth_generate_credentials_fail)
    {
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
        umock_c_reset_all_calls();

        int negativeTestsInitResult = umock_c_negative_tests_init();
        ASSERT_ARE_EQUAL(int, 0, negativeTestsInitResult);

        setup_iothub_device_auth_generate_credentials_mocks(true, false, false);

        umock_c_negative_tests_snapshot();

        //act
        size_t count = umock_c_negative_tests_call_count();
        for (size_t index = 0; index < count; index++)
        {
            if (umock_c_negative_tests_can_call_fail(index))
            {
                umock_c_negative_tests_reset();
                umock_c_negative_tests_fail_call(index);

                void* result = iothub_device_auth_generate_credentials(xda_handle, &g_test_sas_cred);

                ASSERT_IS_NULL(result, "iothub_device_auth_generate_credentials failure in test %zu/%zu", index, count);
            }
        }

        //cleanup
        iothub_device_auth_destroy(xda_handle);
        umock_c_negative_tests_deinit();
    }

    TEST_FUNCTION(iothub_device_auth_generate_credentials_x509_fail)
    {
        STRICT_EXPECTED_CALL(iothub_security_type()).SetReturn(IOTHUB_SECURITY_TYPE_X509);
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
        STRICT_EXPECTED_CALL(hsm_client_x509_interface()).SetReturn(&test_x509_interface);

        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
        umock_c_reset_all_calls();

        int negativeTestsInitResult = umock_c_negative_tests_init();
        ASSERT_ARE_EQUAL(int, 0, negativeTestsInitResult);

        setup_iothub_device_auth_generate_credentials_x509_mocks();

        umock_c_negative_tests_snapshot();

        //act
        size_t count = umock_c_negative_tests_call_count();
        for (size_t index = 0; index < count; index++)
        {
            if (umock_c_negative_tests_can_call_fail(index))
            {
                umock_c_negative_tests_reset();
                umock_c_negative_tests_fail_call(index);

                void* result = iothub_device_auth_generate_credentials(xda_handle, &g_test_sas_cred);

                ASSERT_IS_NULL(result, "iothub_device_auth_generate_credentials riot failure in test %zu/%zu", index, count);
            }
        }

        //cleanup
        iothub_device_auth_destroy(xda_handle);
        umock_c_negative_tests_deinit();
    }
#endif

#ifdef HSM_TYPE_HTTP_EDGE
    static void set_expected_calls_for_device_auth_create_http_edge()
    {
        STRICT_EXPECTED_CALL(iothub_security_type()).SetReturn(IOTHUB_SECURITY_TYPE_HTTP_EDGE);
        STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));
        STRICT_EXPECTED_CALL(hsm_client_http_edge_interface()).SetReturn(&test_http_edge_interface);
        STRICT_EXPECTED_CALL(hsm_client_create());
    }

    TEST_FUNCTION(iothub_device_auth_create_http_edge_succeed)
    {
        //arrange
        set_expected_calls_for_device_auth_create_http_edge();

        //act
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();

        //assert
        ASSERT_IS_NOT_NULL(xda_handle);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        iothub_device_auth_destroy(xda_handle);
    }

    TEST_FUNCTION(iothub_device_auth_generate_credentials_http_edge_succeed)
    {
        //arrange
        set_expected_calls_for_device_auth_create_http_edge();
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
        umock_c_reset_all_calls();

        setup_iothub_device_auth_generate_credentials_mocks(false, true, false);

        //act
        void* result = iothub_device_auth_generate_credentials(xda_handle, &g_test_sas_cred);

        //assert
        ASSERT_IS_NOT_NULL(result);
        ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

        //cleanup
        my_gballoc_free(result);
        iothub_device_auth_destroy(xda_handle);
    }

    TEST_FUNCTION(iothub_device_auth_generate_credentials_http_edge_fail)
    {
        set_expected_calls_for_device_auth_create_http_edge();
        IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
        umock_c_reset_all_calls();

        int negativeTestsInitResult = umock_c_negative_tests_init();
        ASSERT_ARE_EQUAL(int, 0, negativeTestsInitResult);

        setup_iothub_device_auth_generate_credentials_mocks(false, true, false);

        umock_c_negative_tests_snapshot();

        //act
        size_t count = umock_c_negative_tests_call_count();
        for (size_t index = 0; index < count; index++)
        {
            if (umock_c_negative_tests_can_call_fail(index))
            {
                umock_c_negative_tests_reset();
                umock_c_negative_tests_fail_call(index);

                void* result = iothub_device_auth_generate_credentials(xda_handle, &g_test_sas_cred);

                ASSERT_IS_NULL(result, "iothub_device_auth_generate_credentials http edge failure in test %zu/%zu", index, count);
            }
        }

        //cleanup
        iothub_device_auth_destroy(xda_handle);
        umock_c_negative_tests_deinit();
    }

TEST_FUNCTION(IoTHubClient_Auth_Get_TrustedBundle_succeed)
{
    //arrange
    set_expected_calls_for_device_auth_create_http_edge();
    IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
    umock_c_reset_all_calls();

    STRICT_EXPECTED_CALL(hsm_client_get_trust_bundle(IGNORED_PTR_ARG));

    //act
    char* result = iothub_device_auth_get_trust_bundle(xda_handle);

    //assert
    ASSERT_IS_NOT_NULL(result);
    ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());

    //cleanup
    my_gballoc_free(result);
    iothub_device_auth_destroy(xda_handle);
}


#if defined(HSM_TYPE_X509) || defined(HSM_TYPE_RIOT) || defined(HSM_AUTH_TYPE_CUSTOM) // Edge Module cmake shouldn't bring in TPM or RIOT.
// IoTHubClient_Auth_Get_TrustBundle only supports Edge based auth.  Verify that others fail.  Only can get this far if Edge & X509 enabled at same time.
TEST_FUNCTION(IoTHubClient_Auth_Get_TrustedBundle_unsupported_authtype_fail)
{
    //arrange
    STRICT_EXPECTED_CALL(iothub_security_type()).SetReturn(IOTHUB_SECURITY_TYPE_X509);
    STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG));

    STRICT_EXPECTED_CALL(hsm_client_x509_interface()).SetReturn(&test_x509_interface);
    STRICT_EXPECTED_CALL(hsm_client_create());

    IOTHUB_SECURITY_HANDLE xda_handle = iothub_device_auth_create();
    umock_c_reset_all_calls();

    //act
    char* result = iothub_device_auth_get_trust_bundle(xda_handle);

    //assert
    ASSERT_IS_NULL(result);

    //cleanup
    iothub_device_auth_destroy(xda_handle);
}
#endif // defined(HSM_TYPE_X509) || defined(HSM_TYPE_RIOT) || defined(HSM_AUTH_TYPE_CUSTOM)

#endif // HSM_TYPE_HTTP_EDGE

END_TEST_SUITE(iothub_auth_client_ut)

