TDengine/source/dnode/mnode/impl/test/xnode/xnodeSdbTest.cpp
guichuan zhang 2c62466aa0
feat(taosx): support distributed taosx (#34126)
* feat: add xnode syntax

* refactor(xnode): reduce function complexity

* chore: add lost xnode.h file

* feat(xnode): create xnode task

* chore: fix double free error

* add xnoded

* start xnoded as subprocess

* complete xnode task feature

* complete show xnode jobs feature

* complete with option feature

* complete alter xnode job feature

* complete alter xnode task feature

* complete user pass feature

* clean code

* modify status type as char

* fix leader ep null

* fix start task req null

* fix pass id for status

* support timeout msg

* drop xnode task relative jobs

* clean code

* wip

* chore: add test cases for xnode

* chore: fix 3.0 merge changes

* fix drain core dump and create task core dump

* add password check

* retrieve xnode status from xnoded

* pass integer as double to cjson

* add some debug log

* add some job log

* fix start task lock

* do not handle http response

* fix coredump drop xnode task by name

* support start/stop/drop task by name

* remove mock xnoded

* support unix socket

* kill pre-xnoded before start

* support dnode close xnoded

* test(xnode): add unit test cases for xnode

* rebalance support where clause

* fix some test issue

* unformat http post content json string

* add xnode zh doc

* modify drain description

* remove job create/stop/drop operation

* support rebalance all without where condition

* support alter task by name

* add NULL param for mndCheckOperPrivilege

* add xnode txnode module for libmnode.a

* code clean

* change parser len to 4096

* clean code

* chore: try to fix gtest/gtest.h not found

* chore: fix markdown files

* chore: fix markdown in zh

* chore: fix enum issue and add ci

* chore: fix test case problem

* chore: fix pKeyVal overflow

* chore: rename to 排空节点

* chore: external cmake remove parallel

* chore: add DEP_ext_gtest for xnode test

* chore: fix gtest errors

* chore: remove gtest pthread lib

* chore: fix data type not match

* chore: fix some lint errors

* chore: fix void unlink

* chore: fix return with null pointer check

* chore: fix pointer double free and xnodeMemoryTest strncpy null

* chore: fix xnode encode action invalid datelen

* chore: remove TD_LINUX condition

* chore: use PRIu64 denote long long

* chore: fix task parser NULL and allow no with clause

* fix(xnode): fix windows build error

* chore: fix windows curl error

* chore: fix test case ins_tables relative error

* chore: fix memory leak

* docs: update taosx docs

* chore: update taosx docs

* chore: add role priviledge table

* chore: fix error code doc

* chore: fix test_xnode.py

* chore: fix doc typo

* fix: ci error while run test_user_privilege_sysinfo.py

---------

Co-authored-by: Linhe Huo <linhehuo@gmail.com>
Co-authored-by: huohong <sallyhuo@taosdata.com>
Co-authored-by: Simon Guan <guanshengliang@qq.com>
2026-01-01 14:51:03 +08:00

1245 lines
37 KiB
C++

/**
* @file xnodeSdbTest.cpp
* @brief Unit tests for XNODE SDB (System Database) operations
* @version 1.0
* @date 2025-12-25
*
* Tests encode/decode, insert/update/delete operations for:
* - SXnodeObj
* - SXnodeTaskObj
* - SXnodeJobObj
* - SXnodeUserPassObj
*
* NOTE: This test uses simplified mock implementations of encode/decode functions
* that mirror the logic in production code (mndXnode.c lines 267-359 for XnodeObj,
* lines 630-768 for XnodeTaskObj). The mock functions use the same data structure
* layout and serialization order as production, ensuring compatibility.
*
* Using mocks instead of production code allows:
* 1. Faster compilation without complex library dependencies (GEOS, etc.)
* 2. Isolated testing of serialization logic
* 3. No need for static function exposure or wrapper files
*
* The production encode/decode functions in mndXnode.c are static and include
* dependencies on many other subsystems. These tests validate the same logical
* behavior using a simplified, standalone implementation.
*/
#include <gtest/gtest.h>
#include <cstdint>
#include <cstdlib>
#include <cstring>
extern "C" {
#include "mndXnode.h"
}
// Define reserve size for compatibility with production encode/decode
#define TSDB_XNODE_RESERVE_SIZE 64
// Helper functions
extern "C" {
SSdbRaw* mockAllocRaw(int32_t sdbType, int8_t sver, int32_t dataLen) {
// SSdbRaw uses flexible array member, allocate in one block
SSdbRaw* pRaw = (SSdbRaw*)taosMemMalloc(sizeof(SSdbRaw) + dataLen);
if (!pRaw) return nullptr;
pRaw->type = sdbType;
pRaw->sver = sver;
pRaw->dataLen = dataLen;
memset(pRaw->pData, 0, dataLen);
return pRaw;
}
void mockFreeRaw(SSdbRaw* pRaw) {
if (!pRaw) return;
taosMemFree(pRaw);
}
// Simplified encode function for XnodeObj
SSdbRaw* encodeXnodeObj(SXnodeObj* pObj) {
int32_t rawDataLen =
sizeof(int32_t) * 3 + sizeof(int64_t) * 2 + pObj->urlLen + pObj->statusLen + TSDB_XNODE_RESERVE_SIZE;
SSdbRaw* pRaw = mockAllocRaw(SDB_XNODE, TSDB_XNODE_VER_NUMBER, rawDataLen);
if (!pRaw) return nullptr;
int32_t pos = 0;
// Write id
memcpy(pRaw->pData + pos, &pObj->id, sizeof(int32_t));
pos += sizeof(int32_t);
// Write url
memcpy(pRaw->pData + pos, &pObj->urlLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->urlLen > 0) {
memcpy(pRaw->pData + pos, pObj->url, pObj->urlLen);
pos += pObj->urlLen;
}
// Write status
memcpy(pRaw->pData + pos, &pObj->statusLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->statusLen > 0) {
memcpy(pRaw->pData + pos, pObj->status, pObj->statusLen);
pos += pObj->statusLen;
}
// Write timestamps
memcpy(pRaw->pData + pos, &pObj->createTime, sizeof(int64_t));
pos += sizeof(int64_t);
memcpy(pRaw->pData + pos, &pObj->updateTime, sizeof(int64_t));
pos += sizeof(int64_t);
return pRaw;
}
// Simplified decode function for XnodeObj
SXnodeObj* decodeXnodeObj(SSdbRaw* pRaw) {
if (!pRaw || pRaw->sver != TSDB_XNODE_VER_NUMBER) return nullptr;
SXnodeObj* pObj = (SXnodeObj*)taosMemMalloc(sizeof(SXnodeObj));
if (!pObj) return nullptr;
memset(pObj, 0, sizeof(SXnodeObj));
int32_t pos = 0;
// Read id
memcpy(&pObj->id, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
// Read url
memcpy(&pObj->urlLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->urlLen > 0) {
pObj->url = (char*)taosMemMalloc(pObj->urlLen);
if (!pObj->url) {
taosMemFree(pObj);
return nullptr;
}
memcpy(pObj->url, pRaw->pData + pos, pObj->urlLen);
pos += pObj->urlLen;
}
// Read status
memcpy(&pObj->statusLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->statusLen > 0) {
pObj->status = (char*)taosMemMalloc(pObj->statusLen);
if (!pObj->status) {
if (pObj->url) taosMemFree(pObj->url);
taosMemFree(pObj);
return nullptr;
}
memcpy(pObj->status, pRaw->pData + pos, pObj->statusLen);
pos += pObj->statusLen;
}
// Read timestamps
memcpy(&pObj->createTime, pRaw->pData + pos, sizeof(int64_t));
pos += sizeof(int64_t);
memcpy(&pObj->updateTime, pRaw->pData + pos, sizeof(int64_t));
pos += sizeof(int64_t);
return pObj;
}
void freeXnodeObj(SXnodeObj* pObj) {
if (!pObj) return;
if (pObj->url) taosMemFree(pObj->url);
if (pObj->status) taosMemFree(pObj->status);
taosMemFree(pObj);
}
// Forward declarations for free functions used in decode
void freeXnodeTaskObj(SXnodeTaskObj* pObj);
void freeXnodeJobObj(SXnodeJobObj* pObj);
void freeXnodeUserPassObj(SXnodeUserPassObj* pObj);
// Encode function for XnodeTaskObj (mirrors mndXnode.c lines 630-674)
SSdbRaw* encodeXnodeTaskObj(SXnodeTaskObj* pObj) {
int32_t rawDataLen = sizeof(int32_t) * 10 + sizeof(int64_t) * 2 + pObj->nameLen + pObj->sourceDsnLen +
pObj->sinkDsnLen + pObj->parserLen + pObj->reasonLen + pObj->statusLen + TSDB_XNODE_RESERVE_SIZE;
SSdbRaw* pRaw = mockAllocRaw(SDB_XNODE_TASK, TSDB_XNODE_VER_NUMBER, rawDataLen);
if (!pRaw) return nullptr;
int32_t pos = 0;
// Write fields in the same order as production code (lines 641-659)
memcpy(pRaw->pData + pos, &pObj->id, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(pRaw->pData + pos, &pObj->createTime, sizeof(int64_t));
pos += sizeof(int64_t);
memcpy(pRaw->pData + pos, &pObj->updateTime, sizeof(int64_t));
pos += sizeof(int64_t);
memcpy(pRaw->pData + pos, &pObj->statusLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->statusLen > 0) {
memcpy(pRaw->pData + pos, pObj->status, pObj->statusLen);
pos += pObj->statusLen;
}
memcpy(pRaw->pData + pos, &pObj->via, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(pRaw->pData + pos, &pObj->xnodeId, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(pRaw->pData + pos, &pObj->nameLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->nameLen > 0) {
memcpy(pRaw->pData + pos, pObj->name, pObj->nameLen);
pos += pObj->nameLen;
}
memcpy(pRaw->pData + pos, &pObj->sourceType, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(pRaw->pData + pos, &pObj->sourceDsnLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->sourceDsnLen > 0) {
memcpy(pRaw->pData + pos, pObj->sourceDsn, pObj->sourceDsnLen);
pos += pObj->sourceDsnLen;
}
memcpy(pRaw->pData + pos, &pObj->sinkType, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(pRaw->pData + pos, &pObj->sinkDsnLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->sinkDsnLen > 0) {
memcpy(pRaw->pData + pos, pObj->sinkDsn, pObj->sinkDsnLen);
pos += pObj->sinkDsnLen;
}
memcpy(pRaw->pData + pos, &pObj->parserLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->parserLen > 0) {
memcpy(pRaw->pData + pos, pObj->parser, pObj->parserLen);
pos += pObj->parserLen;
}
memcpy(pRaw->pData + pos, &pObj->reasonLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->reasonLen > 0) {
memcpy(pRaw->pData + pos, pObj->reason, pObj->reasonLen);
pos += pObj->reasonLen;
}
return pRaw;
}
// Decode function for XnodeTaskObj (mirrors mndXnode.c lines 676-769)
SXnodeTaskObj* decodeXnodeTaskObj(SSdbRaw* pRaw) {
if (!pRaw || pRaw->sver != TSDB_XNODE_VER_NUMBER) return nullptr;
SXnodeTaskObj* pObj = (SXnodeTaskObj*)taosMemMalloc(sizeof(SXnodeTaskObj));
if (!pObj) return nullptr;
memset(pObj, 0, sizeof(SXnodeTaskObj));
int32_t pos = 0;
// Read fields in same order as encode
memcpy(&pObj->id, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(&pObj->createTime, pRaw->pData + pos, sizeof(int64_t));
pos += sizeof(int64_t);
memcpy(&pObj->updateTime, pRaw->pData + pos, sizeof(int64_t));
pos += sizeof(int64_t);
memcpy(&pObj->statusLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->statusLen > 0) {
pObj->status = (char*)taosMemMalloc(pObj->statusLen);
if (!pObj->status) {
taosMemFree(pObj);
return nullptr;
}
memcpy(pObj->status, pRaw->pData + pos, pObj->statusLen);
pos += pObj->statusLen;
}
memcpy(&pObj->via, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(&pObj->xnodeId, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(&pObj->nameLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->nameLen > 0) {
pObj->name = (char*)taosMemMalloc(pObj->nameLen);
if (!pObj->name) {
freeXnodeTaskObj(pObj);
return nullptr;
}
memcpy(pObj->name, pRaw->pData + pos, pObj->nameLen);
pos += pObj->nameLen;
}
memcpy(&pObj->sourceType, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(&pObj->sourceDsnLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->sourceDsnLen > 0) {
pObj->sourceDsn = (char*)taosMemMalloc(pObj->sourceDsnLen);
if (!pObj->sourceDsn) {
freeXnodeTaskObj(pObj);
return nullptr;
}
memcpy(pObj->sourceDsn, pRaw->pData + pos, pObj->sourceDsnLen);
pos += pObj->sourceDsnLen;
}
memcpy(&pObj->sinkType, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(&pObj->sinkDsnLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->sinkDsnLen > 0) {
pObj->sinkDsn = (char*)taosMemMalloc(pObj->sinkDsnLen);
if (!pObj->sinkDsn) {
freeXnodeTaskObj(pObj);
return nullptr;
}
memcpy(pObj->sinkDsn, pRaw->pData + pos, pObj->sinkDsnLen);
pos += pObj->sinkDsnLen;
}
memcpy(&pObj->parserLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->parserLen > 0) {
pObj->parser = (char*)taosMemMalloc(pObj->parserLen);
if (!pObj->parser) {
freeXnodeTaskObj(pObj);
return nullptr;
}
memcpy(pObj->parser, pRaw->pData + pos, pObj->parserLen);
pos += pObj->parserLen;
}
memcpy(&pObj->reasonLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->reasonLen > 0) {
pObj->reason = (char*)taosMemMalloc(pObj->reasonLen);
if (!pObj->reason) {
freeXnodeTaskObj(pObj);
return nullptr;
}
memcpy(pObj->reason, pRaw->pData + pos, pObj->reasonLen);
pos += pObj->reasonLen;
}
return pObj;
}
void freeXnodeTaskObj(SXnodeTaskObj* pObj) {
if (!pObj) return;
if (pObj->name) taosMemFree(pObj->name);
if (pObj->sourceDsn) taosMemFree(pObj->sourceDsn);
if (pObj->sinkDsn) taosMemFree(pObj->sinkDsn);
if (pObj->parser) taosMemFree(pObj->parser);
if (pObj->reason) taosMemFree(pObj->reason);
if (pObj->status) taosMemFree(pObj->status);
taosMemFree(pObj);
}
// Encode function for XnodeJobObj (mirrors mndXnode.c lines 2104-2143)
SSdbRaw* encodeXnodeJobObj(SXnodeJobObj* pObj) {
int32_t rawDataLen = sizeof(int32_t) * 6 + sizeof(int64_t) * 2 + pObj->configLen + pObj->statusLen + pObj->reasonLen +
TSDB_XNODE_RESERVE_SIZE;
SSdbRaw* pRaw = mockAllocRaw(SDB_XNODE_JOB, TSDB_XNODE_VER_NUMBER, rawDataLen);
if (!pRaw) return nullptr;
int32_t pos = 0;
memcpy(pRaw->pData + pos, &pObj->id, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(pRaw->pData + pos, &pObj->taskId, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(pRaw->pData + pos, &pObj->configLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->configLen > 0) {
memcpy(pRaw->pData + pos, pObj->config, pObj->configLen);
pos += pObj->configLen;
}
memcpy(pRaw->pData + pos, &pObj->via, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(pRaw->pData + pos, &pObj->xnodeId, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(pRaw->pData + pos, &pObj->statusLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->statusLen > 0) {
memcpy(pRaw->pData + pos, pObj->status, pObj->statusLen);
pos += pObj->statusLen;
}
memcpy(pRaw->pData + pos, &pObj->reasonLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->reasonLen > 0) {
memcpy(pRaw->pData + pos, pObj->reason, pObj->reasonLen);
pos += pObj->reasonLen;
}
memcpy(pRaw->pData + pos, &pObj->createTime, sizeof(int64_t));
pos += sizeof(int64_t);
memcpy(pRaw->pData + pos, &pObj->updateTime, sizeof(int64_t));
pos += sizeof(int64_t);
return pRaw;
}
// Decode function for XnodeJobObj (mirrors mndXnode.c lines 2145-2215)
SXnodeJobObj* decodeXnodeJobObj(SSdbRaw* pRaw) {
if (!pRaw || pRaw->sver != TSDB_XNODE_VER_NUMBER) return nullptr;
SXnodeJobObj* pObj = (SXnodeJobObj*)taosMemMalloc(sizeof(SXnodeJobObj));
if (!pObj) return nullptr;
memset(pObj, 0, sizeof(SXnodeJobObj));
int32_t pos = 0;
memcpy(&pObj->id, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(&pObj->taskId, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(&pObj->configLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->configLen > 0) {
pObj->config = (char*)taosMemMalloc(pObj->configLen);
if (!pObj->config) {
taosMemFree(pObj);
return nullptr;
}
memcpy(pObj->config, pRaw->pData + pos, pObj->configLen);
pos += pObj->configLen;
}
memcpy(&pObj->via, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(&pObj->xnodeId, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(&pObj->statusLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->statusLen > 0) {
pObj->status = (char*)taosMemMalloc(pObj->statusLen);
if (!pObj->status) {
if (pObj->config) taosMemFree(pObj->config);
taosMemFree(pObj);
return nullptr;
}
memcpy(pObj->status, pRaw->pData + pos, pObj->statusLen);
pos += pObj->statusLen;
}
memcpy(&pObj->reasonLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->reasonLen > 0) {
pObj->reason = (char*)taosMemMalloc(pObj->reasonLen);
if (!pObj->reason) {
if (pObj->config) taosMemFree(pObj->config);
if (pObj->status) taosMemFree(pObj->status);
taosMemFree(pObj);
return nullptr;
}
memcpy(pObj->reason, pRaw->pData + pos, pObj->reasonLen);
pos += pObj->reasonLen;
}
memcpy(&pObj->createTime, pRaw->pData + pos, sizeof(int64_t));
pos += sizeof(int64_t);
memcpy(&pObj->updateTime, pRaw->pData + pos, sizeof(int64_t));
pos += sizeof(int64_t);
return pObj;
}
void freeXnodeJobObj(SXnodeJobObj* pObj) {
if (!pObj) return;
if (pObj->config) taosMemFree(pObj->config);
if (pObj->status) taosMemFree(pObj->status);
if (pObj->reason) taosMemFree(pObj->reason);
taosMemFree(pObj);
}
// Encode function for XnodeUserPassObj (mirrors mndXnode.c lines 2243-2275)
SSdbRaw* encodeXnodeUserPassObj(SXnodeUserPassObj* pObj) {
int32_t rawDataLen =
sizeof(int32_t) * 3 + sizeof(int64_t) * 2 + pObj->userLen + pObj->passLen + TSDB_XNODE_RESERVE_SIZE;
SSdbRaw* pRaw = mockAllocRaw(SDB_XNODE_USER_PASS, TSDB_XNODE_VER_NUMBER, rawDataLen);
if (!pRaw) return nullptr;
int32_t pos = 0;
memcpy(pRaw->pData + pos, &pObj->id, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(pRaw->pData + pos, &pObj->userLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->userLen > 0) {
memcpy(pRaw->pData + pos, pObj->user, pObj->userLen);
pos += pObj->userLen;
}
memcpy(pRaw->pData + pos, &pObj->passLen, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->passLen > 0) {
memcpy(pRaw->pData + pos, pObj->pass, pObj->passLen);
pos += pObj->passLen;
}
memcpy(pRaw->pData + pos, &pObj->createTime, sizeof(int64_t));
pos += sizeof(int64_t);
memcpy(pRaw->pData + pos, &pObj->updateTime, sizeof(int64_t));
pos += sizeof(int64_t);
return pRaw;
}
// Decode function for XnodeUserPassObj (mirrors mndXnode.c lines 2276-2335)
SXnodeUserPassObj* decodeXnodeUserPassObj(SSdbRaw* pRaw) {
if (!pRaw || pRaw->sver != TSDB_XNODE_VER_NUMBER) return nullptr;
SXnodeUserPassObj* pObj = (SXnodeUserPassObj*)taosMemMalloc(sizeof(SXnodeUserPassObj));
if (!pObj) return nullptr;
memset(pObj, 0, sizeof(SXnodeUserPassObj));
int32_t pos = 0;
memcpy(&pObj->id, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
memcpy(&pObj->userLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->userLen > 0) {
pObj->user = (char*)taosMemMalloc(pObj->userLen);
if (!pObj->user) {
taosMemFree(pObj);
return nullptr;
}
memcpy(pObj->user, pRaw->pData + pos, pObj->userLen);
pos += pObj->userLen;
}
memcpy(&pObj->passLen, pRaw->pData + pos, sizeof(int32_t));
pos += sizeof(int32_t);
if (pObj->passLen > 0) {
pObj->pass = (char*)taosMemMalloc(pObj->passLen);
if (!pObj->pass) {
if (pObj->user) taosMemFree(pObj->user);
taosMemFree(pObj);
return nullptr;
}
memcpy(pObj->pass, pRaw->pData + pos, pObj->passLen);
pos += pObj->passLen;
}
memcpy(&pObj->createTime, pRaw->pData + pos, sizeof(int64_t));
pos += sizeof(int64_t);
memcpy(&pObj->updateTime, pRaw->pData + pos, sizeof(int64_t));
pos += sizeof(int64_t);
return pObj;
}
void freeXnodeUserPassObj(SXnodeUserPassObj* pObj) {
if (!pObj) return;
if (pObj->user) taosMemFree(pObj->user);
if (pObj->pass) taosMemFree(pObj->pass);
taosMemFree(pObj);
}
} // extern "C"
class XnodeSdbTest : public ::testing::Test {
protected:
void SetUp() override {}
void TearDown() override {}
};
// ============= SXnodeObj Tests =============
TEST_F(XnodeSdbTest, XnodeObj_BasicEncodeDecode) {
// Create a test object
SXnodeObj obj = {0};
obj.id = 1;
obj.urlLen = 20;
obj.url = (char*)taosMemMalloc(obj.urlLen);
strcpy(obj.url, "http://localhost:80");
obj.statusLen = 7;
obj.status = (char*)taosMemMalloc(obj.statusLen);
strcpy(obj.status, "online");
obj.createTime = 1234567890;
obj.updateTime = 1234567900;
// Encode
SSdbRaw* pRaw = encodeXnodeObj(&obj);
ASSERT_NE(pRaw, nullptr);
EXPECT_EQ(pRaw->type, SDB_XNODE);
EXPECT_EQ(pRaw->sver, TSDB_XNODE_VER_NUMBER);
// Decode
SXnodeObj* pDecoded = decodeXnodeObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->id, obj.id);
EXPECT_EQ(pDecoded->urlLen, obj.urlLen);
EXPECT_STREQ(pDecoded->url, obj.url);
EXPECT_EQ(pDecoded->statusLen, obj.statusLen);
EXPECT_STREQ(pDecoded->status, obj.status);
EXPECT_EQ(pDecoded->createTime, obj.createTime);
EXPECT_EQ(pDecoded->updateTime, obj.updateTime);
// Cleanup
taosMemFree(obj.url);
taosMemFree(obj.status);
mockFreeRaw(pRaw);
freeXnodeObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeObj_EmptyFields) {
SXnodeObj obj = {0};
obj.id = 2;
obj.urlLen = 0;
obj.url = nullptr;
obj.statusLen = 0;
obj.status = nullptr;
obj.createTime = 1000000;
obj.updateTime = 2000000;
SSdbRaw* pRaw = encodeXnodeObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeObj* pDecoded = decodeXnodeObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->id, obj.id);
EXPECT_EQ(pDecoded->urlLen, 0);
EXPECT_EQ(pDecoded->url, nullptr);
EXPECT_EQ(pDecoded->statusLen, 0);
EXPECT_EQ(pDecoded->status, nullptr);
mockFreeRaw(pRaw);
freeXnodeObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeObj_LongUrl) {
SXnodeObj obj = {0};
obj.id = 3;
obj.urlLen = 200;
obj.url = (char*)taosMemMalloc(obj.urlLen);
memset(obj.url, 'x', obj.urlLen - 1);
obj.url[obj.urlLen - 1] = '\0';
obj.statusLen = 10;
obj.status = (char*)taosMemMalloc(obj.statusLen);
strcpy(obj.status, "running");
obj.createTime = 111111;
obj.updateTime = 222222;
SSdbRaw* pRaw = encodeXnodeObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeObj* pDecoded = decodeXnodeObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->id, 3);
EXPECT_EQ(pDecoded->urlLen, 200);
EXPECT_EQ(memcmp(pDecoded->url, obj.url, obj.urlLen), 0);
taosMemFree(obj.url);
taosMemFree(obj.status);
mockFreeRaw(pRaw);
freeXnodeObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeObj_InvalidVersion) {
SXnodeObj obj = {0};
obj.id = 4;
obj.createTime = 1000;
obj.updateTime = 2000;
SSdbRaw* pRaw = mockAllocRaw(SDB_XNODE, 99, 1024); // Invalid version
ASSERT_NE(pRaw, nullptr);
SXnodeObj* pDecoded = decodeXnodeObj(pRaw);
EXPECT_EQ(pDecoded, nullptr); // Should fail due to version mismatch
mockFreeRaw(pRaw);
}
TEST_F(XnodeSdbTest, XnodeObj_MultipleEncodeDecode) {
// Test encoding/decoding multiple objects in sequence
for (int i = 1; i <= 5; i++) {
SXnodeObj obj = {0};
obj.id = i;
obj.urlLen = 10 + i;
obj.url = (char*)taosMemMalloc(obj.urlLen);
snprintf(obj.url, obj.urlLen, "url_%d", i);
obj.statusLen = 5;
obj.status = (char*)taosMemMalloc(obj.statusLen);
strcpy(obj.status, "ok");
obj.createTime = 1000 * i;
obj.updateTime = 2000 * i;
SSdbRaw* pRaw = encodeXnodeObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeObj* pDecoded = decodeXnodeObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->id, i);
EXPECT_EQ(pDecoded->createTime, 1000 * i);
taosMemFree(obj.url);
taosMemFree(obj.status);
mockFreeRaw(pRaw);
freeXnodeObj(pDecoded);
}
}
// ============= SXnodeTaskObj Tests =============
TEST_F(XnodeSdbTest, XnodeTaskObj_BasicEncodeDecode) {
SXnodeTaskObj obj = {0};
obj.id = 100;
obj.nameLen = 10;
obj.name = (char*)taosMemMalloc(obj.nameLen);
strcpy(obj.name, "task_test");
obj.sourceType = 1;
obj.sourceDsnLen = 15;
obj.sourceDsn = (char*)taosMemMalloc(obj.sourceDsnLen);
strcpy(obj.sourceDsn, "kafka://source");
obj.sinkType = 2;
obj.sinkDsnLen = 13;
obj.sinkDsn = (char*)taosMemMalloc(obj.sinkDsnLen);
strcpy(obj.sinkDsn, "tdengine://db");
obj.via = 1;
obj.xnodeId = 10;
obj.statusLen = 8;
obj.status = (char*)taosMemMalloc(obj.statusLen);
strcpy(obj.status, "running");
obj.createTime = 5555555;
obj.updateTime = 6666666;
SSdbRaw* pRaw = encodeXnodeTaskObj(&obj);
ASSERT_NE(pRaw, nullptr);
EXPECT_EQ(pRaw->type, SDB_XNODE_TASK);
EXPECT_EQ(pRaw->sver, TSDB_XNODE_VER_NUMBER);
// Decode and verify
SXnodeTaskObj* pDecoded = decodeXnodeTaskObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->id, obj.id);
EXPECT_EQ(pDecoded->nameLen, obj.nameLen);
EXPECT_EQ(memcmp(pDecoded->name, obj.name, obj.nameLen), 0);
EXPECT_EQ(pDecoded->sourceType, obj.sourceType);
EXPECT_EQ(pDecoded->sourceDsnLen, obj.sourceDsnLen);
EXPECT_EQ(memcmp(pDecoded->sourceDsn, obj.sourceDsn, obj.sourceDsnLen), 0);
EXPECT_EQ(pDecoded->sinkType, obj.sinkType);
EXPECT_EQ(pDecoded->sinkDsnLen, obj.sinkDsnLen);
EXPECT_EQ(memcmp(pDecoded->sinkDsn, obj.sinkDsn, obj.sinkDsnLen), 0);
EXPECT_EQ(pDecoded->via, obj.via);
EXPECT_EQ(pDecoded->xnodeId, obj.xnodeId);
EXPECT_EQ(pDecoded->createTime, obj.createTime);
EXPECT_EQ(pDecoded->updateTime, obj.updateTime);
taosMemFree(obj.name);
taosMemFree(obj.sourceDsn);
taosMemFree(obj.sinkDsn);
taosMemFree(obj.status);
mockFreeRaw(pRaw);
freeXnodeTaskObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeTaskObj_EmptyStrings) {
SXnodeTaskObj obj = {0};
obj.id = 101;
obj.nameLen = 0;
obj.name = nullptr;
obj.sourceType = 0;
obj.sourceDsnLen = 0;
obj.sourceDsn = nullptr;
obj.sinkType = 0;
obj.sinkDsnLen = 0;
obj.sinkDsn = nullptr;
obj.via = 0;
obj.xnodeId = 0;
obj.createTime = 111111;
obj.updateTime = 222222;
SSdbRaw* pRaw = encodeXnodeTaskObj(&obj);
ASSERT_NE(pRaw, nullptr);
EXPECT_GT(pRaw->dataLen, 0);
SXnodeTaskObj* pDecoded = decodeXnodeTaskObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->id, obj.id);
EXPECT_EQ(pDecoded->nameLen, 0);
EXPECT_EQ(pDecoded->name, nullptr);
mockFreeRaw(pRaw);
freeXnodeTaskObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeTaskObj_WithParser) {
SXnodeTaskObj obj = {0};
obj.id = 102;
obj.nameLen = 7;
obj.name = (char*)taosMemMalloc(obj.nameLen);
strcpy(obj.name, "task_p");
obj.sourceType = 1;
obj.sourceDsnLen = 10;
obj.sourceDsn = (char*)taosMemMalloc(obj.sourceDsnLen);
strcpy(obj.sourceDsn, "source123");
obj.sinkType = 2;
obj.sinkDsnLen = 8;
obj.sinkDsn = (char*)taosMemMalloc(obj.sinkDsnLen);
strcpy(obj.sinkDsn, "sink456");
obj.parserLen = 11;
obj.parser = (char*)taosMemMalloc(obj.parserLen);
strcpy(obj.parser, "csv_parser");
obj.via = 1;
obj.xnodeId = 5;
obj.createTime = 1111111;
obj.updateTime = 2222222;
SSdbRaw* pRaw = encodeXnodeTaskObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeTaskObj* pDecoded = decodeXnodeTaskObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->parserLen, obj.parserLen);
EXPECT_EQ(memcmp(pDecoded->parser, obj.parser, obj.parserLen), 0);
taosMemFree(obj.name);
taosMemFree(obj.sourceDsn);
taosMemFree(obj.sinkDsn);
taosMemFree(obj.parser);
mockFreeRaw(pRaw);
freeXnodeTaskObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeTaskObj_WithReason) {
SXnodeTaskObj obj = {0};
obj.id = 103;
obj.nameLen = 8;
obj.name = (char*)taosMemMalloc(obj.nameLen);
strcpy(obj.name, "task123");
obj.sourceType = 1;
obj.sourceDsnLen = 5;
obj.sourceDsn = (char*)taosMemMalloc(obj.sourceDsnLen);
strcpy(obj.sourceDsn, "src1");
obj.sinkType = 2;
obj.sinkDsnLen = 6;
obj.sinkDsn = (char*)taosMemMalloc(obj.sinkDsnLen);
strcpy(obj.sinkDsn, "sink1");
obj.reasonLen = 15;
obj.reason = (char*)taosMemMalloc(obj.reasonLen);
strcpy(obj.reason, "timeout_error");
obj.via = 2;
obj.xnodeId = 8;
obj.createTime = 3333333;
obj.updateTime = 4444444;
SSdbRaw* pRaw = encodeXnodeTaskObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeTaskObj* pDecoded = decodeXnodeTaskObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->reasonLen, obj.reasonLen);
EXPECT_EQ(memcmp(pDecoded->reason, obj.reason, obj.reasonLen), 0);
EXPECT_EQ(pDecoded->via, 2);
EXPECT_EQ(pDecoded->xnodeId, 8);
taosMemFree(obj.name);
taosMemFree(obj.sourceDsn);
taosMemFree(obj.sinkDsn);
taosMemFree(obj.reason);
mockFreeRaw(pRaw);
freeXnodeTaskObj(pDecoded);
}
// ============= SXnodeJobObj Tests =============
TEST_F(XnodeSdbTest, XnodeJobObj_BasicEncodeDecode) {
SXnodeJobObj obj = {0};
obj.id = 200;
obj.taskId = 100;
obj.configLen = 20;
obj.config = (char*)taosMemMalloc(obj.configLen);
strcpy(obj.config, "{\"threads\": 4}");
obj.via = 1;
obj.xnodeId = 10;
obj.statusLen = 8;
obj.status = (char*)taosMemMalloc(obj.statusLen);
strcpy(obj.status, "running");
obj.createTime = 7777777;
obj.updateTime = 8888888;
SSdbRaw* pRaw = encodeXnodeJobObj(&obj);
ASSERT_NE(pRaw, nullptr);
EXPECT_EQ(pRaw->type, SDB_XNODE_JOB);
EXPECT_EQ(pRaw->sver, TSDB_XNODE_VER_NUMBER);
SXnodeJobObj* pDecoded = decodeXnodeJobObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->id, obj.id);
EXPECT_EQ(pDecoded->taskId, obj.taskId);
EXPECT_EQ(pDecoded->configLen, obj.configLen);
EXPECT_EQ(memcmp(pDecoded->config, obj.config, obj.configLen), 0);
EXPECT_EQ(pDecoded->via, obj.via);
EXPECT_EQ(pDecoded->xnodeId, obj.xnodeId);
EXPECT_EQ(pDecoded->statusLen, obj.statusLen);
EXPECT_EQ(memcmp(pDecoded->status, obj.status, obj.statusLen), 0);
EXPECT_EQ(pDecoded->createTime, obj.createTime);
EXPECT_EQ(pDecoded->updateTime, obj.updateTime);
taosMemFree(obj.config);
taosMemFree(obj.status);
mockFreeRaw(pRaw);
freeXnodeJobObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeJobObj_EmptyFields) {
SXnodeJobObj obj = {0};
obj.id = 201;
obj.taskId = 101;
obj.configLen = 0;
obj.config = nullptr;
obj.via = 0;
obj.xnodeId = 0;
obj.statusLen = 0;
obj.status = nullptr;
obj.reasonLen = 0;
obj.reason = nullptr;
obj.createTime = 1000000;
obj.updateTime = 2000000;
SSdbRaw* pRaw = encodeXnodeJobObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeJobObj* pDecoded = decodeXnodeJobObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->id, obj.id);
EXPECT_EQ(pDecoded->taskId, obj.taskId);
EXPECT_EQ(pDecoded->configLen, 0);
EXPECT_EQ(pDecoded->config, nullptr);
mockFreeRaw(pRaw);
freeXnodeJobObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeJobObj_WithReason) {
SXnodeJobObj obj = {0};
obj.id = 202;
obj.taskId = 102;
obj.configLen = 15;
obj.config = (char*)taosMemMalloc(obj.configLen);
strcpy(obj.config, "{\"batch\": 10}");
obj.via = 2;
obj.xnodeId = 20;
obj.statusLen = 7;
obj.status = (char*)taosMemMalloc(obj.statusLen);
strcpy(obj.status, "failed");
obj.reasonLen = 18;
obj.reason = (char*)taosMemMalloc(obj.reasonLen);
strcpy(obj.reason, "connection_failed");
obj.createTime = 3333333;
obj.updateTime = 4444444;
SSdbRaw* pRaw = encodeXnodeJobObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeJobObj* pDecoded = decodeXnodeJobObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->reasonLen, obj.reasonLen);
EXPECT_EQ(memcmp(pDecoded->reason, obj.reason, obj.reasonLen), 0);
taosMemFree(obj.config);
taosMemFree(obj.status);
taosMemFree(obj.reason);
mockFreeRaw(pRaw);
freeXnodeJobObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeJobObj_LargeConfig) {
SXnodeJobObj obj = {0};
obj.id = 203;
obj.taskId = 103;
obj.configLen = 100;
obj.config = (char*)taosMemMalloc(obj.configLen);
memset(obj.config, 'c', obj.configLen - 1);
obj.config[obj.configLen - 1] = '\0';
obj.via = 1;
obj.xnodeId = 15;
obj.createTime = 5555555;
obj.updateTime = 6666666;
SSdbRaw* pRaw = encodeXnodeJobObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeJobObj* pDecoded = decodeXnodeJobObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->configLen, obj.configLen);
EXPECT_EQ(memcmp(pDecoded->config, obj.config, obj.configLen), 0);
taosMemFree(obj.config);
mockFreeRaw(pRaw);
freeXnodeJobObj(pDecoded);
}
// ============= SXnodeUserPassObj Tests =============
TEST_F(XnodeSdbTest, XnodeUserPassObj_BasicEncodeDecode) {
SXnodeUserPassObj obj = {0};
obj.id = 300;
obj.userLen = 10;
obj.user = (char*)taosMemMalloc(obj.userLen);
strcpy(obj.user, "testuser1");
obj.passLen = 15;
obj.pass = (char*)taosMemMalloc(obj.passLen);
strcpy(obj.pass, "securepass123");
obj.createTime = 9999999;
obj.updateTime = 1111111;
SSdbRaw* pRaw = encodeXnodeUserPassObj(&obj);
ASSERT_NE(pRaw, nullptr);
EXPECT_EQ(pRaw->type, SDB_XNODE_USER_PASS);
EXPECT_EQ(pRaw->sver, TSDB_XNODE_VER_NUMBER);
SXnodeUserPassObj* pDecoded = decodeXnodeUserPassObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->id, obj.id);
EXPECT_EQ(pDecoded->userLen, obj.userLen);
EXPECT_EQ(memcmp(pDecoded->user, obj.user, obj.userLen), 0);
EXPECT_EQ(pDecoded->passLen, obj.passLen);
EXPECT_EQ(memcmp(pDecoded->pass, obj.pass, obj.passLen), 0);
EXPECT_EQ(pDecoded->createTime, obj.createTime);
EXPECT_EQ(pDecoded->updateTime, obj.updateTime);
taosMemFree(obj.user);
taosMemFree(obj.pass);
mockFreeRaw(pRaw);
freeXnodeUserPassObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeUserPassObj_EmptyCredentials) {
SXnodeUserPassObj obj = {0};
obj.id = 301;
obj.userLen = 0;
obj.user = nullptr;
obj.passLen = 0;
obj.pass = nullptr;
obj.createTime = 1234567;
obj.updateTime = 7654321;
SSdbRaw* pRaw = encodeXnodeUserPassObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeUserPassObj* pDecoded = decodeXnodeUserPassObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->id, obj.id);
EXPECT_EQ(pDecoded->userLen, 0);
EXPECT_EQ(pDecoded->user, nullptr);
EXPECT_EQ(pDecoded->passLen, 0);
EXPECT_EQ(pDecoded->pass, nullptr);
mockFreeRaw(pRaw);
freeXnodeUserPassObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeUserPassObj_LongCredentials) {
SXnodeUserPassObj obj = {0};
obj.id = 302;
obj.userLen = 50;
obj.user = (char*)taosMemMalloc(obj.userLen);
memset(obj.user, 'u', obj.userLen - 1);
obj.user[obj.userLen - 1] = '\0';
obj.passLen = 100;
obj.pass = (char*)taosMemMalloc(obj.passLen);
memset(obj.pass, 'p', obj.passLen - 1);
obj.pass[obj.passLen - 1] = '\0';
obj.createTime = 5555555;
obj.updateTime = 6666666;
SSdbRaw* pRaw = encodeXnodeUserPassObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeUserPassObj* pDecoded = decodeXnodeUserPassObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->userLen, obj.userLen);
EXPECT_EQ(memcmp(pDecoded->user, obj.user, obj.userLen), 0);
EXPECT_EQ(pDecoded->passLen, obj.passLen);
EXPECT_EQ(memcmp(pDecoded->pass, obj.pass, obj.passLen), 0);
taosMemFree(obj.user);
taosMemFree(obj.pass);
mockFreeRaw(pRaw);
freeXnodeUserPassObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeUserPassObj_SpecialCharacters) {
SXnodeUserPassObj obj = {0};
obj.id = 303;
obj.userLen = 15;
obj.user = (char*)taosMemMalloc(obj.userLen);
strcpy(obj.user, "user@domain");
obj.passLen = 20;
obj.pass = (char*)taosMemMalloc(obj.passLen);
strcpy(obj.pass, "p@ss!w0rd#123");
obj.createTime = 1111111;
obj.updateTime = 2222222;
SSdbRaw* pRaw = encodeXnodeUserPassObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeUserPassObj* pDecoded = decodeXnodeUserPassObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_STREQ(pDecoded->user, obj.user);
EXPECT_STREQ(pDecoded->pass, obj.pass);
taosMemFree(obj.user);
taosMemFree(obj.pass);
mockFreeRaw(pRaw);
freeXnodeUserPassObj(pDecoded);
}
// ============= Cross-object Tests =============
TEST_F(XnodeSdbTest, AllObjects_InvalidVersion) {
// Test that all decode functions reject invalid versions
SSdbRaw* pRawXnode = mockAllocRaw(SDB_XNODE, 99, 1024);
SSdbRaw* pRawTask = mockAllocRaw(SDB_XNODE_TASK, 99, 1024);
SSdbRaw* pRawJob = mockAllocRaw(SDB_XNODE_JOB, 99, 1024);
SSdbRaw* pRawUser = mockAllocRaw(SDB_XNODE_USER_PASS, 99, 1024);
EXPECT_EQ(decodeXnodeObj(pRawXnode), nullptr);
EXPECT_EQ(decodeXnodeTaskObj(pRawTask), nullptr);
EXPECT_EQ(decodeXnodeJobObj(pRawJob), nullptr);
EXPECT_EQ(decodeXnodeUserPassObj(pRawUser), nullptr);
mockFreeRaw(pRawXnode);
mockFreeRaw(pRawTask);
mockFreeRaw(pRawJob);
mockFreeRaw(pRawUser);
}
TEST_F(XnodeSdbTest, AllObjects_NullInput) {
// Test that all decode/free functions handle null gracefully
EXPECT_EQ(decodeXnodeObj(nullptr), nullptr);
EXPECT_EQ(decodeXnodeTaskObj(nullptr), nullptr);
EXPECT_EQ(decodeXnodeJobObj(nullptr), nullptr);
EXPECT_EQ(decodeXnodeUserPassObj(nullptr), nullptr);
// Should not crash
freeXnodeObj(nullptr);
freeXnodeTaskObj(nullptr);
freeXnodeJobObj(nullptr);
freeXnodeUserPassObj(nullptr);
SUCCEED();
}
// ============= Memory and Edge Cases =============
TEST_F(XnodeSdbTest, XnodeObj_NullRawDecode) {
SXnodeObj* pDecoded = decodeXnodeObj(nullptr);
EXPECT_EQ(pDecoded, nullptr);
}
TEST_F(XnodeSdbTest, XnodeObj_FreeNull) {
// Should not crash
freeXnodeObj(nullptr);
SUCCEED();
}
TEST_F(XnodeSdbTest, XnodeTaskObj_FreeNull) {
// Should not crash
freeXnodeTaskObj(nullptr);
SUCCEED();
}
TEST_F(XnodeSdbTest, RawAllocation_OutOfMemory) {
// Test with very large allocation (simulating OOM)
SSdbRaw* pRaw = mockAllocRaw(SDB_XNODE, TSDB_XNODE_VER_NUMBER, 0);
// Should handle gracefully
if (pRaw) {
mockFreeRaw(pRaw);
}
SUCCEED();
}
TEST_F(XnodeSdbTest, XnodeObj_UpdateScenario) {
// Simulate an update: old object with one status, new object with another
SXnodeObj oldObj = {0};
oldObj.id = 50;
oldObj.urlLen = 10;
oldObj.url = (char*)taosMemMalloc(oldObj.urlLen);
strcpy(oldObj.url, "url_old");
oldObj.statusLen = 7;
oldObj.status = (char*)taosMemMalloc(oldObj.statusLen);
strcpy(oldObj.status, "online");
oldObj.createTime = 100;
oldObj.updateTime = 200;
SXnodeObj newObj = {0};
newObj.id = 50;
newObj.urlLen = 10;
newObj.url = (char*)taosMemMalloc(newObj.urlLen);
strcpy(newObj.url, "url_old");
newObj.statusLen = 8;
newObj.status = (char*)taosMemMalloc(newObj.statusLen);
strcpy(newObj.status, "offline");
newObj.createTime = 100;
newObj.updateTime = 300;
// Encode both
SSdbRaw* pRawOld = encodeXnodeObj(&oldObj);
SSdbRaw* pRawNew = encodeXnodeObj(&newObj);
ASSERT_NE(pRawOld, nullptr);
ASSERT_NE(pRawNew, nullptr);
// Decode and verify
SXnodeObj* pDecodedOld = decodeXnodeObj(pRawOld);
SXnodeObj* pDecodedNew = decodeXnodeObj(pRawNew);
ASSERT_NE(pDecodedOld, nullptr);
ASSERT_NE(pDecodedNew, nullptr);
EXPECT_STREQ(pDecodedOld->status, "online");
EXPECT_STREQ(pDecodedNew->status, "offline");
EXPECT_EQ(pDecodedNew->updateTime, 300);
taosMemFree(oldObj.url);
taosMemFree(oldObj.status);
taosMemFree(newObj.url);
taosMemFree(newObj.status);
mockFreeRaw(pRawOld);
mockFreeRaw(pRawNew);
freeXnodeObj(pDecodedOld);
freeXnodeObj(pDecodedNew);
}
TEST_F(XnodeSdbTest, XnodeObj_SpecialCharacters) {
SXnodeObj obj = {0};
obj.id = 999;
obj.urlLen = 30;
obj.url = (char*)taosMemMalloc(obj.urlLen);
strcpy(obj.url, "http://test.com/path?q=123");
obj.statusLen = 20;
obj.status = (char*)taosMemMalloc(obj.statusLen);
strcpy(obj.status, "status with spaces!");
obj.createTime = 777777;
obj.updateTime = 888888;
SSdbRaw* pRaw = encodeXnodeObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeObj* pDecoded = decodeXnodeObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_STREQ(pDecoded->url, obj.url);
EXPECT_STREQ(pDecoded->status, obj.status);
taosMemFree(obj.url);
taosMemFree(obj.status);
mockFreeRaw(pRaw);
freeXnodeObj(pDecoded);
}
TEST_F(XnodeSdbTest, XnodeObj_ZeroTimestamps) {
SXnodeObj obj = {0};
obj.id = 0;
obj.urlLen = 5;
obj.url = (char*)taosMemMalloc(obj.urlLen);
strcpy(obj.url, "test");
obj.statusLen = 3;
obj.status = (char*)taosMemMalloc(obj.statusLen);
strcpy(obj.status, "ok");
obj.createTime = 0;
obj.updateTime = 0;
SSdbRaw* pRaw = encodeXnodeObj(&obj);
ASSERT_NE(pRaw, nullptr);
SXnodeObj* pDecoded = decodeXnodeObj(pRaw);
ASSERT_NE(pDecoded, nullptr);
EXPECT_EQ(pDecoded->createTime, 0);
EXPECT_EQ(pDecoded->updateTime, 0);
taosMemFree(obj.url);
taosMemFree(obj.status);
mockFreeRaw(pRaw);
freeXnodeObj(pDecoded);
}