TDengine/source/util/test/cfgTest.cpp
Bomin Zhang 876979b5f5
feat[ts-6107]: shared storage (#31552)
* add API to use s3 as shared storage

* support using local file system as shared storage

* upload file to shared storage

* support read, compact and drop

* finish basic mnode & vnode msg processing

* follower sync migration state

* implement mnode transaction, and improve log

* send migration progress msg to dnode to avoid deadlock

* implement following migration

* remove mcount

* avoid redo migration on startup

* avoid follower deadlock when leader is down

* trigger migrate by timer,  avoid compact after migration

* comment out the usage of 'tcs' functions in stream

* change config item prefix from s3 to ss

* change db option prefix from s3 to ss

* rename s3 data struct, function, file to ss

* rename s3 macro to ss

* update s3 sql to ss

* rename remaining s3 items to ss

* check ss configruation, improve s3 retry

* grant object storage -> shared storage,  check ssEnabled

* fix memory leaks

* update build options

* omit sensitive information when dump config

* fix backward compatibility issue

* fix issues found in ci-checks

* fix some failed test cases

* avoid follower timeout and improve log

* fix: follower timeout because migration status not updated

* refuse migration if there's an in progress one

* fix ss test case

* remove garbage files and other minor improvement

* fix failed test cases

* update unit test

* fix failed test case

* fix failed test case

* update document

* update document and fix failed test cases

* fix minor issues in code, test and document

* check new commit after migration task is scheduled

* fix several issus

1. migrate information cannot be dropped sometimes because progress response was put into read queue.
2. memory leak in rare cases
3. data corruption in rare cases
4. failed test case

* add shared storage upgrade tool

* fix compile error
2025-07-14 16:33:53 +08:00

538 lines
No EOL
19 KiB
C++

/**
* @file cfgTest.cpp
* @author slguan (slguan@taosdata.com)
* @brief config module tests
* @version 1.0
* @date 2022-02-20
*
* @copyright Copyright (c) 2022
*
*/
#include <gtest/gtest.h>
#include "tconfig.h"
#ifndef WINDOWS
#include "osFile.h"
#endif
class CfgTest : public ::testing::Test {
protected:
static void SetUpTestSuite() {}
static void TearDownTestSuite() {}
public:
void SetUp() override {}
void TearDown() override {}
void InitCfg(SConfig *pConfig);
static const char *pConfig;
};
const char *CfgTest::pConfig;
TEST_F(CfgTest, 01_Str) {
EXPECT_STREQ(cfgStypeStr(CFG_STYPE_DEFAULT), "default");
EXPECT_STREQ(cfgStypeStr(CFG_STYPE_CFG_FILE), "cfg_file");
EXPECT_STREQ(cfgStypeStr(CFG_STYPE_ENV_FILE), "env_file");
EXPECT_STREQ(cfgStypeStr(CFG_STYPE_ENV_VAR), "env_var");
EXPECT_STREQ(cfgStypeStr(CFG_STYPE_ENV_CMD), "env_cmd");
EXPECT_STREQ(cfgStypeStr(CFG_STYPE_APOLLO_URL), "apollo_url");
EXPECT_STREQ(cfgStypeStr(CFG_STYPE_ARG_LIST), "arg_list");
EXPECT_STREQ(cfgStypeStr(CFG_STYPE_TAOS_OPTIONS), "taos_options");
EXPECT_STREQ(cfgStypeStr(CFG_STYPE_ALTER_CLIENT_CMD), "alter_client_cmd");
EXPECT_STREQ(cfgStypeStr(CFG_STYPE_ALTER_SERVER_CMD), "alter_server_cmd");
EXPECT_STREQ(cfgStypeStr(ECfgSrcType(1024)), "invalid");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_NONE), "none");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_BOOL), "bool");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_INT32), "int32");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_INT64), "int64");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_FLOAT), "float");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_STRING), "string");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_DIR), "dir");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_DIR), "dir");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_DIR), "dir");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_DIR), "dir");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_DOUBLE), "double");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_LOCALE), "locale");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_CHARSET), "charset");
EXPECT_STREQ(cfgDtypeStr(CFG_DTYPE_TIMEZONE), "timezone");
EXPECT_STREQ(cfgDtypeStr(ECfgDataType(1024)), "invalid");
}
TEST_F(CfgTest, 02_Basic) {
SConfig *pConfig = NULL;
int32_t code = cfgInit(&pConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pConfig, nullptr);
EXPECT_EQ(cfgAddBool(pConfig, "test_bool", 0, 0, 6, 0), 0);
EXPECT_EQ(cfgAddInt32(pConfig, "test_int32", 21, 0, 16, 0, 1, 0), TSDB_CODE_OUT_OF_RANGE);
EXPECT_EQ(cfgAddInt32(pConfig, "test_int32", 1, 0, 16, 0, 1, 0), 0);
EXPECT_EQ(cfgAddInt64(pConfig, "test_int64", 21, 0, 16, 0, 2, 0), TSDB_CODE_OUT_OF_RANGE);
EXPECT_EQ(cfgAddInt64(pConfig, "test_int64", 2, 0, 16, 0, 2, 0), 0);
EXPECT_EQ(cfgAddFloat(pConfig, "test_float", 21, 0, 16, 0, 6, 0), TSDB_CODE_OUT_OF_RANGE);
EXPECT_EQ(cfgAddFloat(pConfig, "test_float", 3, 0, 16, 0, 6, 0), 0);
EXPECT_EQ(cfgAddString(pConfig, "test_string", "4", 0, 6, 0), 0);
EXPECT_EQ(cfgAddDir(pConfig, "test_dir", TD_TMP_DIR_PATH, 0, 6, 0), 0);
EXPECT_EQ(cfgGetSize(pConfig), 6);
int32_t size = cfgGetSize(pConfig);
SConfigItem *pItem = NULL;
SConfigIter *pIter = NULL;
code = cfgCreateIter(pConfig, &pIter);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pIter, nullptr);
while ((pItem = cfgNextIter(pIter)) != NULL) {
switch (pItem->dtype) {
case CFG_DTYPE_BOOL:
printf("index:%d, cfg:%s value:%d\n", size, pItem->name, pItem->bval);
break;
case CFG_DTYPE_INT32:
printf("index:%d, cfg:%s value:%d\n", size, pItem->name, pItem->i32);
break;
case CFG_DTYPE_INT64:
printf("index:%d, cfg:%s value:%" PRId64 "\n", size, pItem->name, pItem->i64);
break;
case CFG_DTYPE_FLOAT:
printf("index:%d, cfg:%s value:%f\n", size, pItem->name, pItem->fval);
break;
case CFG_DTYPE_STRING:
printf("index:%d, cfg:%s value:%s\n", size, pItem->name, pItem->str);
break;
case CFG_DTYPE_DIR:
printf("index:%d, cfg:%s value:%s\n", size, pItem->name, pItem->str);
break;
default:
printf("index:%d, cfg:%s invalid cfg dtype:%d\n", size, pItem->name, pItem->dtype);
break;
}
}
cfgDestroyIter(pIter);
EXPECT_EQ(cfgGetSize(pConfig), 6);
pItem = cfgGetItem(pConfig, "test_bool");
EXPECT_EQ(pItem->stype, CFG_STYPE_DEFAULT);
EXPECT_EQ(pItem->dtype, CFG_DTYPE_BOOL);
EXPECT_STREQ(pItem->name, "test_bool");
EXPECT_EQ(pItem->bval, 0);
pItem = cfgGetItem(pConfig, "test_int32");
EXPECT_EQ(pItem->stype, CFG_STYPE_DEFAULT);
EXPECT_EQ(pItem->dtype, CFG_DTYPE_INT32);
EXPECT_STREQ(pItem->name, "test_int32");
EXPECT_EQ(pItem->i32, 1);
code = cfgSetItem(pConfig, "test_int32", "21", CFG_STYPE_DEFAULT, true);
ASSERT_EQ(code, TSDB_CODE_OUT_OF_RANGE);
pItem = cfgGetItem(pConfig, "test_int64");
EXPECT_EQ(pItem->stype, CFG_STYPE_DEFAULT);
EXPECT_EQ(pItem->dtype, CFG_DTYPE_INT64);
EXPECT_STREQ(pItem->name, "test_int64");
EXPECT_EQ(pItem->i64, 2);
code = cfgSetItem(pConfig, "test_int64", "21", CFG_STYPE_DEFAULT, true);
ASSERT_EQ(code, TSDB_CODE_OUT_OF_RANGE);
pItem = cfgGetItem(pConfig, "test_float");
EXPECT_EQ(pItem->stype, CFG_STYPE_DEFAULT);
EXPECT_EQ(pItem->dtype, CFG_DTYPE_FLOAT);
EXPECT_STREQ(pItem->name, "test_float");
EXPECT_EQ(pItem->fval, 3);
pItem = cfgGetItem(pConfig, "test_string");
EXPECT_EQ(pItem->stype, CFG_STYPE_DEFAULT);
EXPECT_EQ(pItem->dtype, CFG_DTYPE_STRING);
EXPECT_STREQ(pItem->name, "test_string");
EXPECT_STREQ(pItem->str, "4");
pItem = cfgGetItem(pConfig, "test_dir");
EXPECT_EQ(pItem->stype, CFG_STYPE_DEFAULT);
EXPECT_EQ(pItem->dtype, CFG_DTYPE_DIR);
EXPECT_STREQ(pItem->name, "test_dir");
EXPECT_STREQ(pItem->str, TD_TMP_DIR_PATH);
code = cfgGetAndSetItem(pConfig, &pItem, "err_cfg", "err_val", CFG_STYPE_DEFAULT, true);
ASSERT_EQ(code, TSDB_CODE_CFG_NOT_FOUND);
code = cfgCheckRangeForDynUpdate(pConfig, "test_int32", "4", false, CFG_ALTER_LOCAL);
ASSERT_EQ(code, TSDB_CODE_INVALID_CFG);
code = cfgCheckRangeForDynUpdate(pConfig, "test_int64", "4", true, CFG_ALTER_LOCAL);
ASSERT_EQ(code, TSDB_CODE_INVALID_CFG);
code = cfgCheckRangeForDynUpdate(pConfig, "test_bool", "3", false, CFG_ALTER_LOCAL);
ASSERT_EQ(code, TSDB_CODE_OUT_OF_RANGE);
code = cfgCheckRangeForDynUpdate(pConfig, "test_int32", "74", true, CFG_ALTER_LOCAL);
ASSERT_EQ(code, TSDB_CODE_OUT_OF_RANGE);
code = cfgCheckRangeForDynUpdate(pConfig, "test_int64", "74", false, CFG_ALTER_LOCAL);
ASSERT_EQ(code, TSDB_CODE_OUT_OF_RANGE);
code = cfgCheckRangeForDynUpdate(pConfig, "test_float", "74", false, CFG_ALTER_LOCAL);
ASSERT_EQ(code, TSDB_CODE_OUT_OF_RANGE);
cfgCleanup(pConfig);
}
TEST_F(CfgTest, initWithArray) {
SConfig *pConfig = NULL;
int32_t code = cfgInit(&pConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pConfig, nullptr);
EXPECT_EQ(cfgAddBool(pConfig, "test_bool", 0, 0, 0, 0), 0);
EXPECT_EQ(cfgAddInt32(pConfig, "test_int32", 1, 0, 16, 0, 0, 0), 0);
EXPECT_EQ(cfgAddInt64(pConfig, "test_int64", 2, 0, 16, 0, 0, 0), 0);
EXPECT_EQ(cfgAddFloat(pConfig, "test_float", 3, 0, 16, 0, 0, 0), 0);
EXPECT_EQ(cfgAddString(pConfig, "test_string", "4", 0, 0, 0), 0);
EXPECT_EQ(cfgAddDir(pConfig, "test_dir", TD_TMP_DIR_PATH, 0, 0, 0), 0);
SArray *pArgs = taosArrayInit(6, sizeof(SConfigPair));
SConfigPair *pPair = (SConfigPair *)taosMemoryMalloc(sizeof(SConfigPair));
pPair->name = "test_bool";
pPair->value = "1";
taosArrayPush(pArgs, pPair);
SConfigPair *pPair1 = (SConfigPair *)taosMemoryMalloc(sizeof(SConfigPair));
pPair1->name = "test_int32";
pPair1->value = "2";
taosArrayPush(pArgs, pPair1);
SConfigPair *pPair2 = (SConfigPair *)taosMemoryMalloc(sizeof(SConfigPair));
pPair2->name = "test_int64";
pPair2->value = "3";
taosArrayPush(pArgs, pPair2);
SConfigPair *pPair3 = (SConfigPair *)taosMemoryMalloc(sizeof(SConfigPair));
pPair3->name = "test_float";
pPair3->value = "4";
taosArrayPush(pArgs, pPair3);
SConfigPair *pPair4 = (SConfigPair *)taosMemoryMalloc(sizeof(SConfigPair));
pPair4->name = "test_string";
pPair4->value = "5";
taosArrayPush(pArgs, pPair4);
SConfigPair *pPair5 = (SConfigPair *)taosMemoryMalloc(sizeof(SConfigPair));
pPair5->name = "test_dir";
pPair5->value = TD_TMP_DIR_PATH;
taosArrayPush(pArgs, pPair5);
code = cfgLoadFromArray(pConfig, pArgs);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
}
TEST_F(CfgTest, cfgDumpItemCategory) {
SConfig *pConfig = NULL;
int32_t code = cfgInit(&pConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pConfig, nullptr);
EXPECT_EQ(cfgAddBool(pConfig, "test_bool", 0, 0, 6, 100), 0);
SConfigItem *pItem = NULL;
pItem = cfgGetItem(pConfig, "test_bool");
EXPECT_EQ(pItem->stype, CFG_STYPE_DEFAULT);
EXPECT_EQ(pItem->dtype, CFG_DTYPE_BOOL);
EXPECT_STREQ(pItem->name, "test_bool");
EXPECT_EQ(pItem->bval, 0);
EXPECT_EQ(cfgDumpItemCategory(pItem, NULL, 0, 0), TSDB_CODE_INVALID_CFG);
}
#ifndef WINDOWS
TEST_F(CfgTest, cfgLoadFromEnvVar) {
SConfig *pConfig = NULL;
int32_t code = cfgInit(&pConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pConfig, nullptr);
EXPECT_EQ(cfgAddBool(pConfig, "test_bool", 0, 0, 6, 0), 0);
EXPECT_EQ(cfgAddInt32(pConfig, "test_int32", 21, 0, 16, 0, 1, 0), TSDB_CODE_OUT_OF_RANGE);
EXPECT_EQ(cfgAddInt32(pConfig, "test_int32", 1, 0, 16, 0, 1, 0), 0);
EXPECT_EQ(cfgAddInt64(pConfig, "test_int64", 21, 0, 16, 0, 2, 0), TSDB_CODE_OUT_OF_RANGE);
EXPECT_EQ(cfgAddInt64(pConfig, "test_int64", 2, 0, 16, 0, 2, 0), 0);
EXPECT_EQ(cfgAddFloat(pConfig, "test_float", 21, 0, 16, 0, 6, 0), TSDB_CODE_OUT_OF_RANGE);
EXPECT_EQ(cfgAddFloat(pConfig, "test_float", 3, 0, 16, 0, 6, 0), 0);
EXPECT_EQ(cfgAddString(pConfig, "test_string", "4", 0, 6, 0), 0);
EXPECT_EQ(cfgAddDir(pConfig, "test_dir", TD_TMP_DIR_PATH, 0, 6, 0), 0);
setenv("test_bool", "1", 1);
setenv("test_int32", "2", 1);
setenv("test_int64", "3", 1);
setenv("test_float", "4", 1);
setenv("test_string", "5", 1);
setenv("test_dir", TD_TMP_DIR_PATH, 1);
ASSERT_EQ(cfgLoad(pConfig, CFG_STYPE_ENV_VAR, "test_bool"), TSDB_CODE_SUCCESS);
}
TEST_F(CfgTest, cfgLoadFromEnvCmd) {
SConfig *pConfig = NULL;
int32_t code = cfgInit(&pConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pConfig, nullptr);
EXPECT_EQ(cfgAddBool(pConfig, "test_bool", 0, 0, 6, 0), 0);
EXPECT_EQ(cfgAddInt32(pConfig, "test_int32", 21, 0, 16, 0, 1, 0), TSDB_CODE_OUT_OF_RANGE);
EXPECT_EQ(cfgAddInt32(pConfig, "test_int32", 1, 0, 16, 0, 1, 0), 0);
EXPECT_EQ(cfgAddInt64(pConfig, "test_int64", 21, 0, 16, 0, 2, 0), TSDB_CODE_OUT_OF_RANGE);
EXPECT_EQ(cfgAddInt64(pConfig, "test_int64", 2, 0, 16, 0, 2, 0), 0);
EXPECT_EQ(cfgAddFloat(pConfig, "test_float", 21, 0, 16, 0, 6, 0), TSDB_CODE_OUT_OF_RANGE);
EXPECT_EQ(cfgAddFloat(pConfig, "test_float", 3, 0, 16, 0, 6, 0), 0);
EXPECT_EQ(cfgAddString(pConfig, "test_string", "4", 0, 6, 0), 0);
const char *envCmd[] = {"test_bool=1", "test_int32=2", "test_int64=3", "test_float=4", "test_string=5", NULL};
ASSERT_EQ(cfgLoad(pConfig, CFG_STYPE_ENV_CMD, envCmd), TSDB_CODE_SUCCESS);
}
TEST_F(CfgTest, cfgLoadFromEnvFile) {
SConfig *pConfig = NULL;
int32_t code = cfgInit(&pConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pConfig, nullptr);
EXPECT_EQ(cfgAddBool(pConfig, "test_bool", 0, 0, 0, 0), 0);
EXPECT_EQ(cfgAddInt32(pConfig, "test_int32", 1, 0, 16, 0, 0, 0), 0);
EXPECT_EQ(cfgAddInt64(pConfig, "test_int64", 2, 0, 16, 0, 0, 0), 0);
EXPECT_EQ(cfgAddFloat(pConfig, "test_float", 3, 0, 16, 0, 0, 0), 0);
EXPECT_EQ(cfgAddString(pConfig, "test_string", "4", 0, 0, 0), 0);
TdFilePtr envFile = NULL;
const char *envFilePath = TD_TMP_DIR_PATH "envFile";
envFile = taosOpenFile(envFilePath, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_APPEND);
const char *buf = "test_bool=1\ntest_int32=2\ntest_int64=3\ntest_float=4\ntest_string=5\n";
taosWriteFile(envFile, buf, strlen(buf));
taosCloseFile(&envFile);
ASSERT_EQ(cfgLoad(pConfig, CFG_STYPE_ENV_FILE, envFilePath), TSDB_CODE_SUCCESS);
taosRemoveFile(envFilePath);
}
TEST_F(CfgTest, cfgLoadFromApollUrl) {
SConfig *pConfig = NULL;
int32_t code = cfgInit(&pConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pConfig, nullptr);
EXPECT_EQ(cfgAddBool(pConfig, "test_bool", 0, 0, 0, 0), 0);
EXPECT_EQ(cfgAddInt32(pConfig, "test_int32", 1, 0, 16, 0, 0, 0), 0);
EXPECT_EQ(cfgAddInt64(pConfig, "test_int64", 2, 0, 16, 0, 0, 0), 0);
EXPECT_EQ(cfgAddFloat(pConfig, "test_float", 3, 0, 16, 0, 0, 0), 0);
EXPECT_EQ(cfgAddString(pConfig, "test_string", "4", 0, 0, 0), 0);
TdFilePtr jsonFile = NULL;
const char *jsonFilePath = TD_TMP_DIR_PATH "envJson.json";
jsonFile = taosOpenFile(jsonFilePath, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_APPEND);
const char *buf =
"{\"test_bool\":\"1\",\"test_int32\":\"2\",\"test_int64\":\"3\",\"test_float\":\"4\",\"test_string\":\"5\"}";
taosWriteFile(jsonFile, buf, strlen(buf));
taosCloseFile(&jsonFile);
char str[256];
snprintf(str, sizeof(str), "jsonFile:%s", jsonFilePath);
ASSERT_EQ(cfgLoad(pConfig, CFG_STYPE_APOLLO_URL, str), 0);
taosRemoveFile(jsonFilePath);
}
#endif
TEST_F(CfgTest, configSyncAddDelete) {
SConfig *pGlobalConfig = NULL;
SConfig *pSdbConfig = NULL;
int32_t code = cfgInit(&pGlobalConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pGlobalConfig, nullptr);
code = cfgInit(&pSdbConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pSdbConfig, nullptr);
// Setup global config with some items
EXPECT_EQ(cfgAddBool(pGlobalConfig, "globalBool", true, 0, 0, 0), 0);
EXPECT_EQ(cfgAddInt32(pGlobalConfig, "globalInt32", 100, 0, 1000, 0, 0, 0), 0);
EXPECT_EQ(cfgAddString(pGlobalConfig, "globalString", "test", 0, 0, 0), 0);
EXPECT_EQ(cfgAddString(pGlobalConfig, "sharedConfig", "shared", 0, 0, 0), 0);
// Setup SDB config with some different items
EXPECT_EQ(cfgAddBool(pSdbConfig, "sdbBool", false, 0, 0, 0), 0);
EXPECT_EQ(cfgAddInt64(pSdbConfig, "sdbInt64", 200, 0, 2000, 0, 0, 0), 0);
EXPECT_EQ(cfgAddString(pSdbConfig, "sdbString", "sdb", 0, 0, 0), 0);
EXPECT_EQ(cfgAddString(pSdbConfig, "sharedConfig", "shared", 0, 0, 0), 0);
// Simulate the sync logic: find items to add (in global but not in SDB)
SArray *itemsToAdd = taosArrayInit(4, sizeof(char*));
SArray *itemsToDelete = taosArrayInit(4, sizeof(char*));
int32_t globalSize = cfgGetSize(pGlobalConfig);
int32_t sdbSize = cfgGetSize(pSdbConfig);
// Find items in global config not in SDB config (to add)
SConfigIter *pGlobalIter = NULL;
code = cfgCreateIter(pGlobalConfig, &pGlobalIter);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
SConfigItem *pItem = NULL;
while ((pItem = cfgNextIter(pGlobalIter)) != NULL) {
SConfigItem *pSdbItem = cfgGetItem(pSdbConfig, pItem->name);
if (pSdbItem == NULL) {
char *itemName = taosStrdup(pItem->name);
taosArrayPush(itemsToAdd, &itemName);
}
}
cfgDestroyIter(pGlobalIter);
// Find items in SDB config not in global config (to delete)
SConfigIter *pSdbIter = NULL;
code = cfgCreateIter(pSdbConfig, &pSdbIter);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
while ((pItem = cfgNextIter(pSdbIter)) != NULL) {
SConfigItem *pGlobalItem = cfgGetItem(pGlobalConfig, pItem->name);
if (pGlobalItem == NULL) {
char *itemName = taosStrdup(pItem->name);
taosArrayPush(itemsToDelete, &itemName);
}
}
cfgDestroyIter(pSdbIter);
// Verify the results
EXPECT_EQ(taosArrayGetSize(itemsToAdd), 3); // globalBool, globalInt32, globalString
EXPECT_EQ(taosArrayGetSize(itemsToDelete), 3); // sdbBool, sdbInt64, sdbString
// Check specific items to add
char **pItemName = (char**)taosArrayGet(itemsToAdd, 0);
EXPECT_TRUE(strcmp(*pItemName, "globalBool") == 0 ||
strcmp(*pItemName, "globalInt32") == 0 ||
strcmp(*pItemName, "globalString") == 0);
// Check specific items to delete
pItemName = (char**)taosArrayGet(itemsToDelete, 0);
EXPECT_TRUE(strcmp(*pItemName, "sdbBool") == 0 ||
strcmp(*pItemName, "sdbInt64") == 0 ||
strcmp(*pItemName, "sdbString") == 0);
// Cleanup
for (int i = 0; i < taosArrayGetSize(itemsToAdd); ++i) {
char **pName = (char**)taosArrayGet(itemsToAdd, i);
taosMemoryFree(*pName);
}
for (int i = 0; i < taosArrayGetSize(itemsToDelete); ++i) {
char **pName = (char**)taosArrayGet(itemsToDelete, i);
taosMemoryFree(*pName);
}
taosArrayDestroy(itemsToAdd);
taosArrayDestroy(itemsToDelete);
cfgCleanup(pGlobalConfig);
cfgCleanup(pSdbConfig);
}
TEST_F(CfgTest, configSyncEmpty) {
SConfig *pGlobalConfig = NULL;
SConfig *pSdbConfig = NULL;
int32_t code = cfgInit(&pGlobalConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pGlobalConfig, nullptr);
code = cfgInit(&pSdbConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pSdbConfig, nullptr);
// Test with empty configs
SArray *itemsToAdd = taosArrayInit(4, sizeof(char*));
SArray *itemsToDelete = taosArrayInit(4, sizeof(char*));
// Since both configs are empty, no items should be found
EXPECT_EQ(taosArrayGetSize(itemsToAdd), 0);
EXPECT_EQ(taosArrayGetSize(itemsToDelete), 0);
taosArrayDestroy(itemsToAdd);
taosArrayDestroy(itemsToDelete);
cfgCleanup(pGlobalConfig);
cfgCleanup(pSdbConfig);
}
TEST_F(CfgTest, configSyncSameContent) {
SConfig *pGlobalConfig = NULL;
SConfig *pSdbConfig = NULL;
int32_t code = cfgInit(&pGlobalConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pGlobalConfig, nullptr);
code = cfgInit(&pSdbConfig);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
ASSERT_NE(pSdbConfig, nullptr);
// Setup identical configs
EXPECT_EQ(cfgAddBool(pGlobalConfig, "testBool", true, 0, 0, 0), 0);
EXPECT_EQ(cfgAddInt32(pGlobalConfig, "testInt32", 100, 0, 1000, 0, 0, 0), 0);
EXPECT_EQ(cfgAddString(pGlobalConfig, "testString", "test", 0, 0, 0), 0);
EXPECT_EQ(cfgAddBool(pSdbConfig, "testBool", true, 0, 0, 0), 0);
EXPECT_EQ(cfgAddInt32(pSdbConfig, "testInt32", 100, 0, 1000, 0, 0, 0), 0);
EXPECT_EQ(cfgAddString(pSdbConfig, "testString", "test", 0, 0, 0), 0);
// Simulate the sync logic
SArray *itemsToAdd = taosArrayInit(4, sizeof(char*));
SArray *itemsToDelete = taosArrayInit(4, sizeof(char*));
// Find items in global config not in SDB config (to add)
SConfigIter *pGlobalIter = NULL;
code = cfgCreateIter(pGlobalConfig, &pGlobalIter);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
SConfigItem *pItem = NULL;
while ((pItem = cfgNextIter(pGlobalIter)) != NULL) {
SConfigItem *pSdbItem = cfgGetItem(pSdbConfig, pItem->name);
if (pSdbItem == NULL) {
char *itemName = taosStrdup(pItem->name);
taosArrayPush(itemsToAdd, &itemName);
}
}
cfgDestroyIter(pGlobalIter);
// Find items in SDB config not in global config (to delete)
SConfigIter *pSdbIter = NULL;
code = cfgCreateIter(pSdbConfig, &pSdbIter);
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
while ((pItem = cfgNextIter(pSdbIter)) != NULL) {
SConfigItem *pGlobalItem = cfgGetItem(pGlobalConfig, pItem->name);
if (pGlobalItem == NULL) {
char *itemName = taosStrdup(pItem->name);
taosArrayPush(itemsToDelete, &itemName);
}
}
cfgDestroyIter(pSdbIter);
// Since configs are identical, no items should need to be added or deleted
EXPECT_EQ(taosArrayGetSize(itemsToAdd), 0);
EXPECT_EQ(taosArrayGetSize(itemsToDelete), 0);
taosArrayDestroy(itemsToAdd);
taosArrayDestroy(itemsToDelete);
cfgCleanup(pGlobalConfig);
cfgCleanup(pSdbConfig);
}