// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// AWS SDK
#include <aws/core/utils/StringUtils.h>

// GameKit
#include "user_gameplay_exports_tests.h"
#include "../core/dispatchers.h"
#include "aws/gamekit/user-gameplay-data/exports.h"
#include "aws/gamekit/user-gameplay-data/gamekit_user_gameplay_data_models.h"
#include "aws/gamekit/authentication/exports.h"

using namespace GameKit::Tests;
using namespace ::testing;

#define TEST_ID_TOKEN   "test_token123"
#define TEST_AUTH_HEADER    "Bearer test_token123"
#define CLIENT_CONFIG_FILE "../core/test_data/sampleplugin/instance/testgame/dev/awsGameKitClientConfig.yml"

void* GameKitUserGameplayDataExportsTestFixture::CreateDefault()
{
    return GameKitUserGameplayDataInstanceCreateWithSessionManager(sessionManagerInstance, nullptr);
}

void GameKitUserGameplayDataExportsTestFixture::SetMocks(void* handle, std::shared_ptr<Aws::Http::HttpClient> mockHttpClient)
{
    UserGameplayData::UserGameplayData* userGameplayDataInstance = static_cast<GameKit::UserGameplayData::UserGameplayData*>(handle);
    userGameplayDataInstance->setHttpClient(mockHttpClient);
}

bool GameKitUserGameplayDataExportsTestFixture::ValidateItemKeysProxy(const char* const* bundleItemKeys, int numKeys, std::stringstream& tempBuffer)
{
    // Need to wrap the method under test here because the tests created with the TEST_F macro are different classes.
    return UserGameplayData::UserGameplayData::validateBundleItemKeys(bundleItemKeys, numKeys, tempBuffer);
}

void GameKitUserGameplayDataExportsTestFixture::SetUp()
{
    testStackInitializer.Initialize();

    sessionManagerInstance = GameKitSessionManagerInstanceCreate(CLIENT_CONFIG_FILE, nullptr);
    static_cast<Authentication::GameKitSessionManager*>(sessionManagerInstance)->SetToken(TokenType::IdToken, TEST_ID_TOKEN);
}

void GameKitUserGameplayDataExportsTestFixture::TearDown()
{
    GameKitSessionManagerInstanceRelease(sessionManagerInstance);

    testStackInitializer.CleanupAndLog<TestLogger>();
    TestExecutionUtils::AbortOnFailureIfEnabled();
}

TEST_F(GameKitUserGameplayDataExportsTestFixture, TestCreate_Success)
{
    // act
    void* instance = GameKitUserGameplayDataInstanceCreateWithSessionManager(sessionManagerInstance, nullptr);

    // assert
    ASSERT_NE(instance, nullptr);

    GameKitUserGameplayDataInstanceRelease(instance);
}

/*
Test below verify that Gameplay data requests are well formed and that the responses are parsed as expected.
Background thread and network state callback are tested by UserGameplayDataClientTestFixture in user_gameplay_data_client_tests.cpp.
*/

TEST_F(GameKitUserGameplayDataExportsTestFixture, TestAddBundle_RequestIsWellFormed_Success)
{
    // arrange
    GameKit::UserGameplayDataBundle bundle;
    bundle.bundleName = "TestBundle";
    bundle.numKeys = 2;
    const char* keys[2] = { "k1", "k2" };
    bundle.bundleItemKeys = keys;
    const char* values[2] = { "v1", "v2" };
    bundle.bundleItemValues = values;

    void* instance = CreateDefault();
    const std::shared_ptr<MockHttpClient> mockHttpClient = std::make_shared<MockHttpClient>();
    SetMocks(instance, mockHttpClient);

    std::shared_ptr<Aws::Http::HttpResponse> successResponse = std::make_shared<FakeHttpResponse>();
    successResponse->SetResponseCode(Aws::Http::HttpResponseCode::CREATED);
    static_cast<FakeHttpResponse*>(successResponse.get())->SetResponseBody(
        "{\"data\":{\"unprocessed_items\":[]}}");


    std::shared_ptr<Aws::Http::HttpRequest> actualRequest;

    auto saveRequestAndReturnResponse = [&actualRequest, &successResponse](const std::shared_ptr<Aws::Http::HttpRequest>& request)
    {
        actualRequest = request;

        return successResponse;
    };

    EXPECT_CALL(
        *mockHttpClient,
        MakeRequest(_, _, _)).
        WillOnce(WithArg<0>(saveRequestAndReturnResponse));

    std::map<std::string, std::string> retrievedPairs;
    auto unprocessedItemsSetter = [&retrievedPairs](const char* key, const char* value)
    {
        retrievedPairs[key] = value;
    };
    typedef LambdaDispatcher<decltype(unprocessedItemsSetter), void, const char*, const char*> UnprocessedItemsSetter;

    // act
    unsigned int result = GameKitAddUserGameplayData(instance, bundle, &unprocessedItemsSetter, UnprocessedItemsSetter::Dispatch);
    GameKitUserGameplayDataInstanceRelease(instance);

    // assert
    ASSERT_EQ(result, GameKit::GAMEKIT_SUCCESS);
    
    ASSERT_STREQ("https://domain.tld/usergamedata/bundles/TestBundle", actualRequest->GetURIString().c_str());
    ASSERT_EQ(Aws::Http::HttpMethod::HTTP_POST, actualRequest->GetMethod());
    ASSERT_STREQ(TEST_AUTH_HEADER, actualRequest->GetAuthorization().c_str());
    ASSERT_STRCASEEQ("application/json", actualRequest->GetContentType().c_str());
    std::stringstream bodyStream;
    bodyStream << actualRequest->GetContentBody()->rdbuf();
    ASSERT_STRCASEEQ("{\"k1\":\"v1\",\"k2\":\"v2\"}", bodyStream.str().c_str());

    // assert we have no unprocessed values
    ASSERT_TRUE(retrievedPairs.empty());

    ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockHttpClient.get()));
}

TEST_F(GameKitUserGameplayDataExportsTestFixture, TestAddBundles_RequestIsWellFormed_FailedProcessingSome)
{
    // arrange
    GameKit::UserGameplayDataBundle bundle;
    bundle.bundleName = "TestBundle";
    bundle.numKeys = 2;
    const char* keys[2] = { "k1", "k2" };
    bundle.bundleItemKeys = keys;
    const char* values[2] = { "v1", "v2" };
    bundle.bundleItemValues = values;

    // arrange
    void* instance = CreateDefault();
    const std::shared_ptr<MockHttpClient> mockHttpClient = std::make_shared<MockHttpClient>();
    SetMocks(instance, mockHttpClient);

    std::shared_ptr<Aws::Http::HttpResponse> successResponse = std::make_shared<FakeHttpResponse>();
    successResponse->SetResponseCode(Aws::Http::HttpResponseCode::CREATED);
    static_cast<FakeHttpResponse*>(successResponse.get())->SetResponseBody(
        "{\"data\":{\"unprocessed_items\":[{\"bundle_item_key\": \"k2\", \"bundle_item_value\": \"v2\"}]}}");

    std::shared_ptr<Aws::Http::HttpRequest> actualRequest;

    auto saveRequestAndReturnResponse = [&actualRequest, &successResponse](const std::shared_ptr<Aws::Http::HttpRequest>& request)
    {
        actualRequest = request;

        return successResponse;
    };

    EXPECT_CALL(
        *mockHttpClient,
        MakeRequest(_, _, _)).
        WillOnce(WithArg<0>(saveRequestAndReturnResponse));

    std::map<std::string, std::string> retrievedPairs;
    auto unprocessedItemsSetter = [&retrievedPairs](const char* key, const char* value)
    {
        retrievedPairs[key] = value;
    };
    typedef LambdaDispatcher<decltype(unprocessedItemsSetter), void, const char*, const char*> UnprocessedItemsSetter;

    // act
    unsigned int result = GameKitAddUserGameplayData(instance, bundle, &unprocessedItemsSetter, UnprocessedItemsSetter::Dispatch);
    GameKitUserGameplayDataInstanceRelease(instance);

    // assert
    ASSERT_EQ(result, GameKit::GAMEKIT_ERROR_USER_GAMEPLAY_DATA_UNPROCESSED_ITEMS);

    ASSERT_STREQ("https://domain.tld/usergamedata/bundles/TestBundle", actualRequest->GetURIString().c_str());
    ASSERT_EQ(Aws::Http::HttpMethod::HTTP_POST, actualRequest->GetMethod());
    ASSERT_STREQ(TEST_AUTH_HEADER, actualRequest->GetAuthorization().c_str());
    ASSERT_STRCASEEQ("application/json", actualRequest->GetContentType().c_str());
    std::stringstream bodyStream;
    bodyStream << actualRequest->GetContentBody()->rdbuf();
    ASSERT_STRCASEEQ("{\"k1\":\"v1\",\"k2\":\"v2\"}", bodyStream.str().c_str());

    // assert we have the expected unprocessed values
    ASSERT_EQ(1, retrievedPairs.size());
    ASSERT_EQ(1, retrievedPairs.count("k2"));
    ASSERT_STREQ("v2", retrievedPairs["k2"].c_str());

    ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockHttpClient.get()));
}

TEST_F(GameKitUserGameplayDataExportsTestFixture, TestListBundles_RequestIsWellFormed_Success)
{
    // arrange
    std::string bundle = "TestBundle";
    void* instance = CreateDefault();
    const std::shared_ptr<MockHttpClient> mockHttpClient = std::make_shared<MockHttpClient>();
    SetMocks(instance, mockHttpClient);

    std::shared_ptr<Aws::Http::HttpResponse> successResponse = std::make_shared<FakeHttpResponse>();
    successResponse->SetResponseCode(Aws::Http::HttpResponseCode::OK);
    static_cast<FakeHttpResponse*>(successResponse.get())->SetResponseBody(
        "{\"data\":{\"bundle_names\":[{\"bundle_name\":\"b1\"},{\"bundle_name\":\"b2\"}]}}");

    std::shared_ptr<Aws::Http::HttpRequest> actualRequest;

    auto saveRequestAndReturnResponse = [&actualRequest, &successResponse](const std::shared_ptr<Aws::Http::HttpRequest>& request)
    {
        actualRequest = request;

        return successResponse;
    };

    EXPECT_CALL(
        *mockHttpClient,
        MakeRequest(_, _, _)).
        WillOnce(WithArg<0>(saveRequestAndReturnResponse));

    std::vector<std::string> retrievedNames;
    auto bundleNamesSetter = [&retrievedNames](const char* bundleName)
    {
        retrievedNames.push_back(bundleName);
    };
    typedef LambdaDispatcher<decltype(bundleNamesSetter), void, const char*> BundleNamesSetter;

    // act
    const unsigned int result = GameKitListUserGameplayDataBundles(instance, &bundleNamesSetter, BundleNamesSetter::Dispatch);
    GameKitUserGameplayDataInstanceRelease(instance);

    // assert
    ASSERT_EQ(GameKit::GAMEKIT_SUCCESS, result);
    ASSERT_STREQ("b1", retrievedNames[0].c_str());
    ASSERT_STREQ("b2", retrievedNames[1].c_str());

    ASSERT_STREQ("https://domain.tld/usergamedata/bundles?limit=100", actualRequest->GetURIString().c_str());
    ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, actualRequest->GetMethod());
    ASSERT_STREQ(TEST_AUTH_HEADER, actualRequest->GetAuthorization().c_str());

    ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockHttpClient.get()));
}

TEST_F(GameKitUserGameplayDataExportsTestFixture, TestGetBundle_RequestIsWellFormed_Success)
{
    // arrange
    char* bundle = "TestBundle";
    void* instance = CreateDefault();
    const std::shared_ptr<MockHttpClient> mockHttpClient = std::make_shared<MockHttpClient>();
    SetMocks(instance, mockHttpClient);

    std::shared_ptr<Aws::Http::HttpResponse> successResponse = std::make_shared<FakeHttpResponse>();
    successResponse->SetResponseCode(Aws::Http::HttpResponseCode::OK);
    static_cast<FakeHttpResponse*>(successResponse.get())->SetResponseBody(
        "{\"data\":{\"bundle_items\":[{\"bundle_item_key\":\"k1\",\"bundle_item_value\":\"v1\"},{\"bundle_item_key\":\"k2\",\"bundle_item_value\":\"v2\"}]}}");

    std::shared_ptr<Aws::Http::HttpRequest> actualRequest;

    auto saveRequestAndReturnResponse = [&actualRequest, &successResponse](const std::shared_ptr<Aws::Http::HttpRequest>& request)
    {
        actualRequest = request;

        return successResponse;
    };

    EXPECT_CALL(
        *mockHttpClient,
        MakeRequest(_, _, _)).
        WillOnce(WithArg<0>(saveRequestAndReturnResponse));

    std::map<std::string, std::string> retrievedPairs;
    auto bundleSetter = [&retrievedPairs](const char* key, const char* value)
    {
        retrievedPairs[key] = value;
    };
    typedef LambdaDispatcher<decltype(bundleSetter), void, const char*, const char*> BundleSetter;

    // act
    unsigned int result = GameKitGetUserGameplayDataBundle(instance, bundle, &bundleSetter, BundleSetter::Dispatch);
    GameKitUserGameplayDataInstanceRelease(instance);

    // assert
    ASSERT_EQ(GameKit::GAMEKIT_SUCCESS, result);
    ASSERT_STREQ("v1", retrievedPairs["k1"].c_str());
    ASSERT_STREQ("v2", retrievedPairs["k2"].c_str());

    ASSERT_STREQ("https://domain.tld/usergamedata/bundles/TestBundle?limit=100", actualRequest->GetURIString().c_str());
    ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, actualRequest->GetMethod());
    ASSERT_STREQ(TEST_AUTH_HEADER, actualRequest->GetAuthorization().c_str());

    ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockHttpClient.get()));
}

TEST_F(GameKitUserGameplayDataExportsTestFixture, TestGetBundleItem_RequestIsWellFormed_Success)
{
    // arrange
    GameKit::UserGameplayDataBundleItem bundleItem;
    bundleItem.bundleName = "TestBundle";
    bundleItem.bundleItemKey = "k1";
    void* instance = CreateDefault();
    std::shared_ptr<MockHttpClient> mockHttpClient = std::make_shared<MockHttpClient>();
    SetMocks(instance, mockHttpClient);

    std::shared_ptr<Aws::Http::HttpResponse> successResponse = std::make_shared<FakeHttpResponse>();
    successResponse->SetResponseCode(Aws::Http::HttpResponseCode::OK);
    static_cast<FakeHttpResponse*>(successResponse.get())->SetResponseBody(
        "{\"data\":{\"bundle_item_value\":\"123\"}}");

    std::shared_ptr<Aws::Http::HttpRequest> actualRequest;

    auto saveRequestAndReturnResponse = [&actualRequest, &successResponse](const std::shared_ptr<Aws::Http::HttpRequest>& request)
    {
        actualRequest = request;

        return successResponse;
    };

    EXPECT_CALL(
        *mockHttpClient,
        MakeRequest(_, _, _)).
        WillOnce(WithArg<0>(saveRequestAndReturnResponse));

    std::string retrievedValue;
    auto valueSetter = [&retrievedValue](const char* value)
    {
        retrievedValue = value;
    };
    typedef LambdaDispatcher<decltype(valueSetter), void, const char*> ValueSetter;

    // act
    const unsigned int result = GameKitGetUserGameplayDataBundleItem(instance, bundleItem, &valueSetter, ValueSetter::Dispatch);
    GameKitUserGameplayDataInstanceRelease(instance);

    // assert
    ASSERT_EQ(GameKit::GAMEKIT_SUCCESS, result);
    ASSERT_STREQ("123", retrievedValue.c_str());

    ASSERT_STREQ("https://domain.tld/usergamedata/bundles/TestBundle/items/k1", actualRequest->GetURIString().c_str());
    ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, actualRequest->GetMethod());
    ASSERT_STREQ(TEST_AUTH_HEADER, actualRequest->GetAuthorization().c_str());

    ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockHttpClient.get()));
}

TEST_F(GameKitUserGameplayDataExportsTestFixture, TestUpdateBundleItem_RequestIsWellFormed_Success)
{
    // arrange
    GameKit::UserGameplayDataBundleItemValue bundleItemValue;
    bundleItemValue.bundleName = "TestBundle";
    bundleItemValue.bundleItemKey = "k123";
    bundleItemValue.bundleItemValue = "v123.1";
    void* instance = CreateDefault();
    const std::shared_ptr<MockHttpClient> mockHttpClient = std::make_shared<MockHttpClient>();
    SetMocks(instance, mockHttpClient);

    std::shared_ptr<Aws::Http::HttpResponse> successResponse = std::make_shared<FakeHttpResponse>();
    successResponse->SetResponseCode(Aws::Http::HttpResponseCode::NO_CONTENT);

    std::shared_ptr<Aws::Http::HttpRequest> actualRequest;

    auto saveRequestAndReturnResponse = [&actualRequest, &successResponse](const std::shared_ptr<Aws::Http::HttpRequest>& request)
    {
        actualRequest = request;

        return successResponse;
    };

    EXPECT_CALL(
        *mockHttpClient,
        MakeRequest(_, _, _)).
        WillOnce(WithArg<0>(saveRequestAndReturnResponse));

    // act
    unsigned int result = GameKitUpdateUserGameplayDataBundleItem(instance, bundleItemValue);
    GameKitUserGameplayDataInstanceRelease(instance);

    // assert
    ASSERT_EQ(GameKit::GAMEKIT_SUCCESS, result);

    ASSERT_STREQ("https://domain.tld/usergamedata/bundles/TestBundle/items/k123", actualRequest->GetURIString().c_str());
    ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, actualRequest->GetMethod());
    ASSERT_STREQ(TEST_AUTH_HEADER, actualRequest->GetAuthorization().c_str());
    ASSERT_STRCASEEQ("application/json", actualRequest->GetContentType().c_str());
    std::stringstream bodyStream;
    bodyStream << actualRequest->GetContentBody()->rdbuf();
    ASSERT_STRCASEEQ("{\"bundle_item_value\":\"v123.1\"}", bodyStream.str().c_str());

    ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockHttpClient.get()));
}

TEST_F(GameKitUserGameplayDataExportsTestFixture, TestDeleteAll_RequestIsWellFormed_Success)
{
    // arrange
    void* instance = CreateDefault();
    const std::shared_ptr<MockHttpClient> mockHttpClient = std::make_shared<MockHttpClient>();
    SetMocks(instance, mockHttpClient);

    std::shared_ptr<Aws::Http::HttpResponse> successResponse = std::make_shared<FakeHttpResponse>();
    successResponse->SetResponseCode(Aws::Http::HttpResponseCode::NO_CONTENT);

    std::shared_ptr<Aws::Http::HttpRequest> actualRequest;

    auto saveRequestAndReturnResponse = [&actualRequest, &successResponse](const std::shared_ptr<Aws::Http::HttpRequest>& request)
    {
        actualRequest = request;

        return successResponse;
    };

    EXPECT_CALL(
        *mockHttpClient,
        MakeRequest(_, _, _)).
        WillOnce(WithArg<0>(saveRequestAndReturnResponse));

    // act
    unsigned int result = GameKitDeleteAllUserGameplayData(instance);
    GameKitUserGameplayDataInstanceRelease(instance);

    // assert
    ASSERT_EQ(GameKit::GAMEKIT_SUCCESS, result);

    ASSERT_STREQ("https://domain.tld/usergamedata", actualRequest->GetURIString().c_str());
    ASSERT_EQ(Aws::Http::HttpMethod::HTTP_DELETE, actualRequest->GetMethod());
    ASSERT_STREQ(TEST_AUTH_HEADER, actualRequest->GetAuthorization().c_str());

    ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockHttpClient.get()));
}

TEST_F(GameKitUserGameplayDataExportsTestFixture, TestDeleteBundle_RequestIsWellFormed_Success)
{
    // arrange
    char* bundle = "TestBundle";
    void* instance = CreateDefault();
    const std::shared_ptr<MockHttpClient> mockHttpClient = std::make_shared<MockHttpClient>();
    SetMocks(instance, mockHttpClient);

    std::shared_ptr<Aws::Http::HttpResponse> successResponse = std::make_shared<FakeHttpResponse>();
    successResponse->SetResponseCode(Aws::Http::HttpResponseCode::NO_CONTENT);

    std::shared_ptr<Aws::Http::HttpRequest> actualRequest;

    auto saveRequestAndReturnResponse = [&actualRequest, &successResponse](const std::shared_ptr<Aws::Http::HttpRequest>& request)
    {
        actualRequest = request;

        return successResponse;
    };

    EXPECT_CALL(
        *mockHttpClient,
        MakeRequest(_, _, _)).
        WillOnce(WithArg<0>(saveRequestAndReturnResponse));

    // act
    unsigned int result = GameKitDeleteUserGameplayDataBundle(instance, bundle);
    GameKitUserGameplayDataInstanceRelease(instance);

    // assert
    ASSERT_EQ(GameKit::GAMEKIT_SUCCESS, result);
    ASSERT_STREQ("https://domain.tld/usergamedata/bundles/TestBundle", actualRequest->GetURIString().c_str());
    ASSERT_EQ(Aws::Http::HttpMethod::HTTP_DELETE, actualRequest->GetMethod());
    ASSERT_STREQ(TEST_AUTH_HEADER, actualRequest->GetAuthorization().c_str());

    ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockHttpClient.get()));
}

TEST_F(GameKitUserGameplayDataExportsTestFixture, TestDeleteBundleItems_RequestIsWellFormed_Success)
{
    // arrange
    GameKit::UserGameplayDataDeleteItemsRequest bundleItems;
    bundleItems.bundleName = "TestBundle";
    const char* keys[2] = { "k1", "k2" };
    bundleItems.bundleItemKeys = keys;
    bundleItems.numKeys = 2;
    void* instance = CreateDefault();
    const std::shared_ptr<MockHttpClient> mockHttpClient = std::make_shared<MockHttpClient>();
    SetMocks(instance, mockHttpClient);

    std::shared_ptr<Aws::Http::HttpResponse> successResponse = std::make_shared<FakeHttpResponse>();
    successResponse->SetResponseCode(Aws::Http::HttpResponseCode::NO_CONTENT);

    std::shared_ptr<Aws::Http::HttpRequest> actualRequest;

    auto saveRequestAndReturnResponse = [&actualRequest, &successResponse](const std::shared_ptr<Aws::Http::HttpRequest>& request)
    {
        actualRequest = request;

        return successResponse;
    };

    EXPECT_CALL(
        *mockHttpClient,
        MakeRequest(_, _, _)).
        WillOnce(WithArg<0>(saveRequestAndReturnResponse));

    // act
    unsigned int result = GameKitDeleteUserGameplayDataBundleItems(instance, bundleItems);
    GameKitUserGameplayDataInstanceRelease(instance);

    // assert
    ASSERT_EQ(GameKit::GAMEKIT_SUCCESS, result);

    ASSERT_STREQ("https://domain.tld/usergamedata/bundles/TestBundle", actualRequest->GetURIString(false).c_str());
    ASSERT_EQ(Aws::Http::HttpMethod::HTTP_DELETE, actualRequest->GetMethod());
    ASSERT_STREQ(TEST_AUTH_HEADER, actualRequest->GetAuthorization().c_str());
    ASSERT_FALSE(actualRequest->HasContentType());
    ASSERT_FALSE(actualRequest->HasContentLength());
    
    Aws::Utils::Json::JsonValue payload;
    bundleItems.ToJson(payload);
    Aws::String serialized = payload.View().WriteCompact();
    Aws::String urlEncoded = Aws::Utils::StringUtils::URLEncode(serialized.c_str());
    Aws::Http::QueryStringParameterCollection params = actualRequest->GetQueryStringParameters();
    ASSERT_EQ(1, params.size());
    ASSERT_EQ(1, params.count("payload"));
    ASSERT_STREQ(urlEncoded.c_str(), params.find("payload")->second.c_str());
    
    ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockHttpClient.get()));
}

TEST_F(GameKitUserGameplayDataExportsTestFixture, TestValidateItemKeys_ValidKeys_ReturnsTrue)
{
    // arrange
    const char* keys[3] = { "Valid", "Another-Valid", "This.one_too"};

    // act
    std::stringstream buffer;
    const bool valid = ValidateItemKeysProxy(keys, 3, buffer);

    // assert
    ASSERT_TRUE(valid);
    ASSERT_STREQ("", buffer.str().c_str());
}
TEST_F(GameKitUserGameplayDataExportsTestFixture, TestValidateItemKeys_InvalidKeys_ReturnsFalse)
{
    // arrange
    const char* keys[4] = { "Valid", "not valid", "Another-Valid", "~not>valid" };

    // act
    std::stringstream buffer;
    const bool valid = ValidateItemKeysProxy(keys, 4, buffer);

    // assert
    ASSERT_FALSE(valid);
    ASSERT_STREQ("not valid, ~not>valid", buffer.str().c_str());
}