/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2011-2020 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include "config.h"
#include "iotests.h"
#include "internal.h"

class EerrsUnitTest : public MockUnitTest
{
  protected:
    virtual void createEerrConnection(HandleWrap &hw, lcb_INSTANCE **instance)
    {
        MockEnvironment::getInstance()->createConnection(hw, instance);
        ASSERT_EQ(LCB_SUCCESS, lcb_connect(*instance));
        lcb_wait(*instance, LCB_WAIT_DEFAULT);
        ASSERT_EQ(LCB_SUCCESS, lcb_get_bootstrap_status(*instance));
    }

    void enableEnhancedErrors()
    {
        MockEnvironment::getInstance()->setEnhancedErrors(true);
    }

    void disableEnhancedErrors()
    {
        MockEnvironment::getInstance()->setEnhancedErrors(false);
    }

    void checkRetryVerify(uint16_t errcode);

    void TearDown()
    {
        if (!MockEnvironment::getInstance()->isRealCluster()) {
            MockOpFailClearCommand clearCmd(MockEnvironment::getInstance()->getNumNodes());
            doMockTxn(clearCmd);
        }
        MockUnitTest::TearDown();
    }
};

struct EerrsCookie {
    lcb_STATUS rc;
    bool called;
    char *err_ref;
    char *err_ctx;

    void reset()
    {
        rc = LCB_SUCCESS;
        called = false;
        free(err_ref);
        err_ref = NULL;
        free(err_ctx);
        err_ctx = NULL;
    }
    EerrsCookie() : rc(LCB_SUCCESS), called(false), err_ref(NULL), err_ctx(NULL) {}

    ~EerrsCookie()
    {
        free(err_ref);
        free(err_ctx);
    }
};

extern "C" {
static void opcb(lcb_INSTANCE *, int cbtype, const lcb_RESPBASE *rb)
{
    EerrsCookie *cookie = reinterpret_cast< EerrsCookie * >(rb->cookie);
    cookie->called = true;
    cookie->rc = rb->ctx.rc;

    const char *ref = lcb_resp_get_error_ref(cbtype, rb);
    if (ref != NULL) {
        cookie->err_ref = strdup(ref);
    }

    const char *ctx = lcb_resp_get_error_context(cbtype, rb);
    if (ctx != NULL) {
        cookie->err_ctx = strdup(ctx);
    }
}
}

TEST_F(EerrsUnitTest, testInCallbackWhenEnabled)
{
    SKIP_UNLESS_MOCK();
    HandleWrap hw;
    lcb_INSTANCE *instance;

    enableEnhancedErrors();
    createEerrConnection(hw, &instance);
    lcb_install_callback(instance, LCB_CALLBACK_DEFAULT, opcb);

    EerrsCookie cookie;

    std::string key("hello");
    lcb_CMDGET *cmd;
    lcb_cmdget_create(&cmd);
    lcb_cmdget_key(cmd, key.c_str(), key.size());
    lcb_cmdget_locktime(cmd, 10);
    lcb_get(instance, &cookie, cmd);
    lcb_cmdget_destroy(cmd);

    lcb_wait(instance, LCB_WAIT_DEFAULT);
    ASSERT_TRUE(cookie.called);
    ASSERT_EQ(LCB_ERR_DOCUMENT_NOT_FOUND, cookie.rc);
    ASSERT_NE((char *)NULL, cookie.err_ref);
    ASSERT_EQ(36, strlen(cookie.err_ref)); // java.util.UUID generates 36-bytes long strings
    ASSERT_STREQ("Failed to lookup item", cookie.err_ctx);
}

TEST_F(EerrsUnitTest, testInCallbackWhenDisabled)
{
    SKIP_UNLESS_MOCK();
    HandleWrap hw;
    lcb_INSTANCE *instance;

    disableEnhancedErrors();
    createEerrConnection(hw, &instance);
    lcb_install_callback(instance, LCB_CALLBACK_DEFAULT, opcb);

    EerrsCookie cookie;

    std::string key("hello");
    lcb_CMDGET *cmd;
    lcb_cmdget_create(&cmd);
    lcb_cmdget_key(cmd, key.c_str(), key.size());
    lcb_cmdget_locktime(cmd, 10);
    lcb_get(instance, &cookie, cmd);
    lcb_cmdget_destroy(cmd);

    lcb_wait(instance, LCB_WAIT_DEFAULT);
    ASSERT_TRUE(cookie.called);
    ASSERT_EQ((char *)NULL, cookie.err_ref);
    ASSERT_EQ((char *)NULL, cookie.err_ctx);
}
