47e3d982创建于 3月23日历史提交
/*
 * Copyright (c) 2026 Huawei Device Co., Ltd.
 * 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.
 */

#define LOG_TAG "RdbSqlUtilsTest"
#include "rdb_sql_utils.h"

#include <gtest/gtest.h>

#include <climits>
#include <string>

#include "grd_type_export.h"
#include "rd_utils.h"
#include "logger.h"
#include "sqlite_sql_builder.h"
#include "values_buckets.h"
#include "rdb_predicates.h"

using namespace testing::ext;
using namespace OHOS::NativeRdb;
using namespace OHOS::Rdb;

class RdbSqlUtilsTest : public testing::Test {
public:
    static void SetUpTestCase(void);
    static void TearDownTestCase(void);
    void SetUp(void) {};
    void TearDown(void) {};
};

void RdbSqlUtilsTest::SetUpTestCase(void)
{
}

void RdbSqlUtilsTest::TearDownTestCase(void)
{
}

/**
 * @tc.name: RdbStore_SqliteUtils_001
 * @tc.desc: Normal testCase of sqlite_utils for IsSpecial, if sqlType is special
 * @tc.type: FUNC
 */
HWTEST_F(RdbSqlUtilsTest, RdbSqlUtils_Test_001, TestSize.Level1)
{
    auto [dataBasePath, errCode] = RdbSqlUtils::GetDefaultDatabasePath("/data/test", "RdbTest.db");
    EXPECT_EQ(dataBasePath, "/data/test/rdb/RdbTest.db");
    EXPECT_EQ(errCode, E_OK);

    auto [dataBasePath1, errCode1] = RdbSqlUtils::GetDefaultDatabasePath("/data/test", "RdbTest.db", "myself");
    EXPECT_EQ(dataBasePath1, "/data/test/rdb/myself/RdbTest.db");
    EXPECT_EQ(errCode1, E_OK);
}

/**
 * @tc.name: RdbStore_GetInsertSqlInfo_001
 * @tc.desc: test RdbStore GetInsertSqlInfo
 * @tc.type: FUNC
 */
HWTEST_F(RdbSqlUtilsTest, RdbSqlUtils_GetInsertSqlInfo_001, TestSize.Level1)
{
    ValuesBucket values;
    values.PutString("name", std::string("zhangsan"));
    values.PutInt("age", 20);
    values.PutDouble("salary", 300.5);
    std::pair<int, SqlInfo> result = RdbSqlUtils::GetInsertSqlInfo("", values, ConflictResolution::ON_CONFLICT_NONE);
    EXPECT_EQ(result.first, E_EMPTY_TABLE_NAME);
}

/**
 * @tc.name: RdbStore_GetInsertSqlInfo_002
 * @tc.desc: test RdbStore GetInsertSqlInfo
 * @tc.type: FUNC
 */
HWTEST_F(RdbSqlUtilsTest, RdbSqlUtils_GetInsertSqlInfo_002, TestSize.Level1)
{
    ValuesBucket values;
    values.PutString("name", std::string("zhangsan"));
    values.PutInt("age", 20);
    values.PutDouble("salary", 300.5);
    ValuesBucket emptyValues;

    std::pair<int, SqlInfo> result =
        RdbSqlUtils::GetInsertSqlInfo("temp", emptyValues, ConflictResolution::ON_CONFLICT_NONE);
    EXPECT_EQ(result.first, E_EMPTY_VALUES_BUCKET);
}

/**
 * @tc.name: RdbStore_GetInsertSqlInfo_003
 * @tc.desc: test RdbStore GetInsertSqlInfo
 * @tc.type: FUNC
 */
HWTEST_F(RdbSqlUtilsTest, RdbSqlUtils_GetInsertSqlInfo_003, TestSize.Level1)
{
    ValuesBucket values;
    values.PutString("name", std::string("zhangsan"));
    values.PutInt("age", 20);
    values.PutDouble("salary", 300.5);

    std::pair<int, SqlInfo> result =
        RdbSqlUtils::GetInsertSqlInfo("temp", values, ConflictResolution::ON_CONFLICT_NONE);
    LOG_INFO("INSERT SQL is %{public}s", result.second.sql.c_str());
    EXPECT_EQ(result.first, E_OK);
}

/**
 * @tc.name: RdbStore_GetInsertSqlInfo_004
 * @tc.desc: test RdbStore GetInsertSqlInfo
 * @tc.type: FUNC
 */
HWTEST_F(RdbSqlUtilsTest, RdbSqlUtils_GetInsertSqlInfo_004, TestSize.Level1)
{
    ValuesBucket values;
    values.PutString("name", std::string("zhangsan"));
    values.PutInt("age", 20);
    values.PutDouble("salary", 300.5);

    AssetValue assetVal;
    assetVal.id = "123";
    assetVal.name = "kkk";
    std::vector<AssetValue> assets;
    assets.emplace_back(assetVal);

    ValueObject vo(assets);
    values.Put("kkk", vo);
    std::pair<int, SqlInfo> result =
        RdbSqlUtils::GetInsertSqlInfo("temp", values, ConflictResolution::ON_CONFLICT_REPLACE);
    EXPECT_EQ(result.first, E_INVALID_ARGS);
}

/**
 * @tc.name: RdbStore_GetUpdateSqlInfo_001
 * @tc.desc: test RdbStore GetUpdateSqlInfo
 * @tc.type: FUNC
 */
HWTEST_F(RdbSqlUtilsTest, RdbSqlUtils_GetUpdateSqlInfo_001, TestSize.Level1)
{
    RdbPredicates predicates("");
    ValuesBucket bucketValues;
    bucketValues.PutString("name", std::string("zhangsan"));
    bucketValues.PutInt("age", 20);
    bucketValues.PutDouble("salary", 300.5);
    std::vector<ValueObject> emptyValues;
    std::pair<int, SqlInfo> result = RdbSqlUtils::GetUpdateSqlInfo(
        predicates, bucketValues, ConflictResolution::ON_CONFLICT_NONE);
    EXPECT_EQ(result.first, E_EMPTY_TABLE_NAME);
}

/**
 * @tc.name: RdbStore_GetUpdateSqlInfo_002
 * @tc.desc: test RdbStore GetUpdateSqlInfo
 * @tc.type: FUNC
 */
HWTEST_F(RdbSqlUtilsTest, RdbSqlUtils_GetUpdateSqlInfo_002, TestSize.Level1)
{
    RdbPredicates predicates("temp");
    ValuesBucket bucketValues;
    bucketValues.PutString("name", std::string("zhangsan"));
    bucketValues.PutInt("age", 20);
    bucketValues.PutDouble("salary", 300.5);
    ValuesBucket emptyBucketValues;
    std::vector<ValueObject> emptyValues;

    std::pair<int, SqlInfo> result = RdbSqlUtils::GetUpdateSqlInfo(
        predicates, emptyBucketValues, ConflictResolution::ON_CONFLICT_NONE);
    EXPECT_EQ(result.first, E_EMPTY_VALUES_BUCKET);
}

/**
 * @tc.name: RdbStore_GetUpdateSqlInfo_003
 * @tc.desc: test RdbStore GetUpdateSqlInfo
 * @tc.type: FUNC
 */
HWTEST_F(RdbSqlUtilsTest, RdbSqlUtils_GetUpdateSqlInfo_003, TestSize.Level1)
{
    RdbPredicates predicates("temp");
    ValuesBucket bucketValues;
    bucketValues.PutString("name", std::string("zhangsan"));
    bucketValues.PutInt("age", 20);
    bucketValues.PutDouble("salary", 300.5);
    std::vector<ValueObject> values;
    ValueObject object;
    values.push_back(object);
    predicates.EqualTo("name", "wangwu");
    predicates.SetBindArgs(values);
    std::pair<int, SqlInfo> result = RdbSqlUtils::GetUpdateSqlInfo(
        predicates, bucketValues, ConflictResolution::ON_CONFLICT_NONE);
    LOG_INFO("UPDATE SQL is %{public}s", result.second.sql.c_str());
    EXPECT_EQ(result.first, E_OK);
}

/**
 * @tc.name: RdbStore_GetDeleteSqlInfo_001
 * @tc.desc: test RdbStore GetDeleteSqlInfo
 * @tc.type: FUNC
 */
HWTEST_F(RdbSqlUtilsTest, RdbSqlUtils_GetDeleteSqlInfo_001, TestSize.Level1)
{
    RdbPredicates predicates("");
    std::pair<int, SqlInfo> result = RdbSqlUtils::GetDeleteSqlInfo(predicates);
    EXPECT_EQ(result.first, E_EMPTY_TABLE_NAME);
}

/**
 * @tc.name: RdbStore_GetDeleteSqlInfo_002
 * @tc.desc: test RdbStore GetDeleteSqlInfo
 * @tc.type: FUNC
 */
HWTEST_F(RdbSqlUtilsTest, GetDeleteSqlInfo_002, TestSize.Level1)
{
    std::vector<ValueObject> emptyValues;
    RdbPredicates predicates("temp");
    predicates.EqualTo("name", "wangwu");
    predicates.SetBindArgs(emptyValues);
    std::pair<int, SqlInfo> result =
        RdbSqlUtils::GetDeleteSqlInfo(predicates);
    LOG_INFO("DELETE SQL is %{public}s", result.second.sql.c_str());
    EXPECT_EQ(result.first, E_OK);
}

/**
 * @tc.name: GetDataBaseDir_001
 * @tc.desc: test RdbStore GetDataBaseDirFromRealPath
 * @tc.type: FUNC
 */
HWTEST_F(RdbSqlUtilsTest, GetDataBaseDir_001, TestSize.Level1)
{
    auto dataBaseDir = RdbSqlUtils::GetDataBaseDirFromRealPath("/data/app/rdb/test.db", false, "", "test.db");
    EXPECT_EQ(dataBaseDir, "/data/app/rdb/test.db");
    dataBaseDir = RdbSqlUtils::GetDataBaseDirFromRealPath("/data/app/rdb/test.db", true, "", "test.db");
    EXPECT_EQ(dataBaseDir, "/data/app");
    dataBaseDir = RdbSqlUtils::GetDataBaseDirFromRealPath("/data/app/rdb/custom/test.db", true, "custom", "test.db");
    EXPECT_EQ(dataBaseDir, "/data/app");
    dataBaseDir =
        RdbSqlUtils::GetDataBaseDirFromRealPath("/data/app/rdb/custom/rdb/test.db", true, "custom/rdb", "test.db");
    EXPECT_EQ(dataBaseDir, "/data/app");
    dataBaseDir = RdbSqlUtils::GetDataBaseDirFromRealPath(
        "/data/app/rdb/custom/rdb/rdbtest.db", true, "custom/rdb", "rdbtest.db");
    EXPECT_EQ(dataBaseDir, "/data/app");
}

/**
 * @tc.name: GetDataBaseDir_002
 * @tc.desc: test RdbStore GetDataBaseDirFromRealPath
 * @tc.type: FUNC
 */
HWTEST_F(RdbSqlUtilsTest, GetDataBaseDir_002, TestSize.Level1)
{
    auto dataBaseDir = RdbSqlUtils::GetDataBaseDirFromRealPath("", true, "custom", "test.db");
    EXPECT_EQ(dataBaseDir, "");
    dataBaseDir = RdbSqlUtils::GetDataBaseDirFromRealPath("/data/app/rdb/test.db", true, "", "");
    EXPECT_EQ(dataBaseDir, "");
    dataBaseDir = RdbSqlUtils::GetDataBaseDirFromRealPath("test.db", true, "", "test.db");
    EXPECT_EQ(dataBaseDir, "");
    dataBaseDir = RdbSqlUtils::GetDataBaseDirFromRealPath("/data/app/rdb/abc.db", true, "", "test.db");
    EXPECT_EQ(dataBaseDir, "");
    dataBaseDir = RdbSqlUtils::GetDataBaseDirFromRealPath("data/app/rdb/cs/test.db", true, "custom", "test.db");
    EXPECT_EQ(dataBaseDir, "");
    dataBaseDir = RdbSqlUtils::GetDataBaseDirFromRealPath("/data/app/rdb/other/test.db", true, "custom", "test.db");
    EXPECT_EQ(dataBaseDir, "");
    dataBaseDir = RdbSqlUtils::GetDataBaseDirFromRealPath("/rd/custom/test.db", true, "custom", "test.db");
    EXPECT_EQ(dataBaseDir, "");
    dataBaseDir = RdbSqlUtils::GetDataBaseDirFromRealPath("/data/app/custom/test.db", true, "custom", "test.db");
    EXPECT_EQ(dataBaseDir, "");
}

/**
 * @tc.name: IsValidTableName_ValidateFormat
 * @tc.desc: Test validation of table name format in IsValidTableName function
 * @tc.type: FUNC
 * Test Point:
 * Verify that IsValidTableName correctly validates table name formats according to SQL standards
 * Test Steps:
 * 1. Test with empty string - should return false
 * 2. Test with numeric string "12345" - should return false
 * 3. Test with invalid double dot format "a..b" - should return false
 * 4. Test with leading dot ".a" - should return false
 * 5. Test with trailing dot "a." - should return false
 * 6. Test with valid underscore format "abc_def" - should return true
 * 7. Test with valid dot notation "abc.def" - should return true
 * 8. Test with alphanumeric string "abc123" - should return true
 */
HWTEST_F(RdbSqlUtilsTest, IsValidTableName_ValidateFormat, TestSize.Level1)
{
    auto res = RdbSqlUtils::IsValidTableName("");
    EXPECT_FALSE(res);
    res = RdbSqlUtils::IsValidTableName("12345");
    EXPECT_FALSE(res);
    res = RdbSqlUtils::IsValidTableName("a..b");
    EXPECT_FALSE(res);
    res = RdbSqlUtils::IsValidTableName(".a");
    EXPECT_FALSE(res);
    res = RdbSqlUtils::IsValidTableName("a.");
    EXPECT_FALSE(res);
    res = RdbSqlUtils::IsValidTableName("abc_def");
    EXPECT_TRUE(res);
    res = RdbSqlUtils::IsValidTableName("abc.def");
    EXPECT_TRUE(res);
    res = RdbSqlUtils::IsValidTableName("abc123");
    EXPECT_TRUE(res);
}

/**
 * @tc.name: IsValidFields_ValidateNames
 * @tc.desc: Test validation of field names in IsValidFields function
 * @tc.type: FUNC
 * Test Point:
 * Verify that IsValidFields correctly validates field names according to SQL standards
 * Test Steps:
 * 1. Test with empty fields vector - should return false
 * 2. Test with fields exceeding limit - should return false
 * 3. Test with illegal field names containing special characters - should return false
 * 4. Test with field names containing spaces - should return false
 * 5. Test with field names containing commas - should return false
 * 6. Test with valid field names including duplicates - should return true
 */
HWTEST_F(RdbSqlUtilsTest, IsValidFields_ValidateNames, TestSize.Level1)
{
    const int32_t limit = 4;
    EXPECT_FALSE(RdbSqlUtils::IsValidFields(std::vector<std::string>()));
    EXPECT_FALSE(RdbSqlUtils::IsValidFields(std::vector<std::string>(limit + 1, "")));
    EXPECT_FALSE(RdbSqlUtils::IsValidFields({"field1", "field2", "***illegal_field"}));
    EXPECT_FALSE(RdbSqlUtils::IsValidFields({"field1", "field2", "illegal field"}));
    EXPECT_FALSE(RdbSqlUtils::IsValidFields({"field1", "field2", "illegal,field"}));
    EXPECT_TRUE(RdbSqlUtils::IsValidFields({"field1", "field2", "field2"}));
}

/**
 * @tc.name: HasDuplicateAssets_CheckDuplicates
 * @tc.desc: Test detection of duplicate assets in ValueObject
 * @tc.type: FUNC
 * Test Point:
 * Verify that HasDuplicateAssets correctly detects duplicate assets within a ValueObject
 * Test Steps:
 * 1. Test with empty ValueObject - should return false
 * 2. Create two assets with identical names
 * 3. Put them into a ValueObject
 * 4. Check if HasDuplicateAssets detects duplicates - should return true
 * 5. Test with duplicated ValueObjects - should return true
 * 6. Test with ValuesBucket containing duplicate assets - should return true
 * 7. Test with ValuesBuckets containing duplicate assets - should return true
 */
HWTEST_F(RdbSqlUtilsTest, HasDuplicateAssets_CheckDuplicates, TestSize.Level1)
{
    ValueObject value;
    auto res = RdbSqlUtils::HasDuplicateAssets(value);
    EXPECT_FALSE(res);
    AssetValue asset1;
    asset1.name = "asset1";
    AssetValue asset2;
    asset2.name = "asset1";
    value = ValueObject({asset1, asset2});
    res = RdbSqlUtils::HasDuplicateAssets(value);
    EXPECT_TRUE(res);
    res = RdbSqlUtils::HasDuplicateAssets({value, value});
    EXPECT_TRUE(res);
    ValuesBucket bucket;
    bucket.PutDouble("double", 2.2);
    bucket.Put("assets", value);
    res = RdbSqlUtils::HasDuplicateAssets(bucket);
    EXPECT_TRUE(res);
    ValuesBuckets Buckets({bucket, bucket});
    res = RdbSqlUtils::HasDuplicateAssets(Buckets);
    EXPECT_TRUE(res);
}

/**
 * @tc.name: HasDuplicateAssets_CheckNonDuplicates
 * @tc.desc: Test detection of non-duplicate assets in ValueObject
 * @tc.type: FUNC
 * Test Point:
 * Verify that HasDuplicateAssets correctly identifies when there are no duplicate assets within a ValueObject
 * Test Steps:
 * 1. Test with empty ValueObject - should return false
 * 2. Create two assets with different names
 * 3. Put them into a ValueObject
 * 4. Check if HasDuplicateAssets detects duplicates - should return false
 * 5. Test with non-duplicated ValueObjects - should return false
 * 6. Test with ValuesBucket containing non-duplicate assets - should return false
 * 7. Test with ValuesBuckets containing non-duplicate assets - should return false
 */
HWTEST_F(RdbSqlUtilsTest, HasDuplicateAssets_CheckNonDuplicates, TestSize.Level1)
{
    ValueObject value;
    auto res = RdbSqlUtils::HasDuplicateAssets(value);
    EXPECT_FALSE(res);
    AssetValue asset1;
    asset1.name = "asset1";
    AssetValue asset2;
    asset2.name = "asset2";
    value = ValueObject({asset1, asset2});
    res = RdbSqlUtils::HasDuplicateAssets(value);
    EXPECT_FALSE(res);
    res = RdbSqlUtils::HasDuplicateAssets({value, value});
    EXPECT_FALSE(res);
    ValuesBucket bucket;
    bucket.PutDouble("double", 2.2);
    bucket.Put("assets", value);
    res = RdbSqlUtils::HasDuplicateAssets(bucket);
    EXPECT_FALSE(res);
    ValuesBuckets Buckets({bucket, bucket});
    res = RdbSqlUtils::HasDuplicateAssets(Buckets);
    EXPECT_FALSE(res);
}

/**
 * @tc.name: BatchTrim_TrimWhitespace
 * @tc.desc: Test trimming whitespace from strings in BatchTrim function
 * @tc.type: FUNC
 * Test Point:
 * Verify that BatchTrim correctly trims whitespace from a vector of strings
 *
 * Test Steps:
 * 1. Create a vector of strings with various whitespace patterns
 * 2. Apply BatchTrim to the vector
 * 3. Verify that whitespace is correctly trimmed from each string
 * 4. Verify that empty strings remain empty
 */
HWTEST_F(RdbSqlUtilsTest, BatchTrim_TrimWhitespace, TestSize.Level1)
{
    std::vector<std::string> strs = {" str1 ", "str2", ""};
    std::vector<std::string> res = RdbSqlUtils::BatchTrim(strs);
    EXPECT_EQ(res.size(), 3);
    EXPECT_EQ(res[0], "str1");
    EXPECT_EQ(res[1], "str2");
    EXPECT_EQ(res[2], "");
}

/**
 * @tc.name: IsValidMaxCount_ValidateValues
 * @tc.desc: Test RdbSqlUtils IsValidReturningMaxCount function with various max returning count values
 * @tc.type: FUNC
 * Test Point:
 * Verify that IsValidReturningMaxCount correctly validates maximum count values for returning operations
 * Test Steps:
 * 1. Test with a positive valid count (5) - should return true
 * 2. Test with a negative count (-5) - should return false
 * 3. Test with zero count (0) - should return false
 * 4. Test with maximum allowed count (ReturningConfig::MAX_RETURNING_COUNT) - should return true
 * 5. Test with exceeding maximum count (ReturningConfig::MAX_RETURNING_COUNT + 1) - should return false
 */
HWTEST_F(RdbSqlUtilsTest, IsValidMaxCount_ValidateValues, TestSize.Level1)
{
    EXPECT_TRUE(RdbSqlUtils::IsValidReturningMaxCount(5));
    EXPECT_FALSE(RdbSqlUtils::IsValidReturningMaxCount(-5));
    EXPECT_FALSE(RdbSqlUtils::IsValidReturningMaxCount(0));
    EXPECT_TRUE(RdbSqlUtils::IsValidReturningMaxCount(ReturningConfig::MAX_RETURNING_COUNT));
    EXPECT_FALSE(RdbSqlUtils::IsValidReturningMaxCount(ReturningConfig::MAX_RETURNING_COUNT + 1));
}