From 380e19ebd6101b28f3da50fae2631b49d659cc95 Mon Sep 17 00:00:00 2001 From: lobty Date: Sat, 31 Jan 2026 17:49:55 +0800 Subject: [PATCH] ai test Signed-off-by: lobty --- frameworks/libs/distributeddb/test/BUILD.gn | 7 + .../distributeddb_rdb_cloud_download_test.cpp | 384 ++++++++++++++++++ 2 files changed, 391 insertions(+) create mode 100644 frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_cloud_download_test.cpp diff --git a/frameworks/libs/distributeddb/test/BUILD.gn b/frameworks/libs/distributeddb/test/BUILD.gn index 818f8bd6aa..add67cb965 100644 --- a/frameworks/libs/distributeddb/test/BUILD.gn +++ b/frameworks/libs/distributeddb/test/BUILD.gn @@ -924,6 +924,12 @@ distributeddb_unittest("DistributedDBRDBComplexCloudTest") { ] } +distributeddb_unittest("DistributedDBRDBCloudDownloadTest") { + sources = [ + "unittest/common/store_test/rdb/distributeddb_rdb_cloud_download_test.cpp", + ] +} + distributeddb_unittest("DistributedDBRDBSqliteUtilsTest") { sources = [ "unittest/common/store_test/rdb/distributeddb_rdb_sqlite_utils_test.cpp", @@ -1089,6 +1095,7 @@ group("unittest") { ":DistributedDBQueryObjectHelperTest", ":DistributedDBRDBCollaborationTest", ":DistributedDBRDBComplexCloudTest", + ":DistributedDBRDBCloudDownloadTest", ":DistributedDBRDBCompressTest", ":DistributedDBRDBDataStatusTest", ":DistributedDBRDBDropTableTest", diff --git a/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_cloud_download_test.cpp b/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_cloud_download_test.cpp new file mode 100644 index 0000000000..cbb849c9d2 --- /dev/null +++ b/frameworks/libs/distributeddb/test/unittest/common/store_test/rdb/distributeddb_rdb_cloud_download_test.cpp @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2025 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. + */ + +#include "rdb_general_ut.h" + +#ifdef USE_DISTRIBUTEDDB_CLOUD +using namespace testing::ext; +using namespace DistributedDB; +using namespace DistributedDBUnitTest; + +namespace { +// 测试用的云同步表名 +constexpr const char *CLOUD_DOWNLOAD_TABLE = "CLOUD_DOWNLOAD_TABLE"; + +class DistributedDBRDBCloudDownloadTest : public RDBGeneralUt { +public: + void SetUp() override; + void TearDown() override; + +protected: + // 初始化测试表 + void InitTestTable(const std::string &table = CLOUD_DOWNLOAD_TABLE); + + // 初始化 Schema + void InitSchema(const StoreInfo &info, const std::string &table = CLOUD_DOWNLOAD_TABLE); + + // 初始化云同步表 + void InitCloudSyncTable(const StoreInfo &info, const std::string &table = CLOUD_DOWNLOAD_TABLE); + + // 向云端插入指定数量的数据 + DBStatus InsertDataToCloud(int64_t count); + + StoreInfo info_ = {USER_ID, APP_ID, STORE_ID_1}; +}; + +void DistributedDBRDBCloudDownloadTest::SetUp() +{ + RDBGeneralUt::SetUp(); +} + +void DistributedDBRDBCloudDownloadTest::TearDown() +{ + RDBGeneralUt::TearDown(); +} + +void DistributedDBRDBCloudDownloadTest::InitTestTable(const std::string &table) +{ + // 创建测试表:id 是主键,intCol 是整数,stringCol 是字符串 + std::string sql = "CREATE TABLE IF NOT EXISTS " + table + "(" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "intCol INTEGER," + "stringCol TEXT)"; + EXPECT_EQ(ExecuteSQL(sql, info_), E_OK); +} + +void DistributedDBRDBCloudDownloadTest::InitSchema(const StoreInfo &info, const std::string &table) +{ + // 定义表的 Schema 信息 + const std::vector fieldInfo = { + {{"id", TYPE_INDEX, true, true}, false}, + {{"intCol", TYPE_INDEX, false, true}, false}, + {{"stringCol", TYPE_INDEX, false, true}, false}, + }; + UtDateBaseSchemaInfo schemaInfo = { + .tablesInfo = { + {.name = table, .fieldInfo = fieldInfo} + } + }; + RDBGeneralUt::SetSchemaInfo(info, schemaInfo); +} + +void DistributedDBRDBCloudDownloadTest::InitCloudSyncTable(const StoreInfo &info, const std::string &table) +{ + // 设置云数据库配置 + RDBGeneralUt::SetCloudDbConfig(info); + // 创建云同步类型的分布式表 + ASSERT_EQ(SetDistributedTables(info, {table}, TableSyncType::CLOUD_COOPERATION), E_OK); +} + +DBStatus DistributedDBRDBCloudDownloadTest::InsertDataToCloud(int64_t count) +{ + // 获取虚拟云数据库 + auto cloudDB = GetVirtualCloudDb(); + if (cloudDB == nullptr) { + return DBStatus::DB_ERROR; + } + + // 构造云端数据 + std::vector records; + std::vector extends; + Timestamp now = TimeHelper::GetSysCurrentTime(); + auto time = static_cast(now / CloudDbConstant::TEN_THOUSAND); + + for (int64_t i = 1; i <= count; i++) { + // 构造记录数据 + VBucket record; + record["id"] = i; + record["intCol"] = i * 10; + record["stringCol"] = "cloud_data_" + std::to_string(i); + records.push_back(record); + + // 构造扩展数据(包含 GID、时间戳等元信息) + VBucket extend; + extend.insert_or_assign(CloudDbConstant::CREATE_FIELD, time); + extend.insert_or_assign(CloudDbConstant::MODIFY_FIELD, time); + extend.insert_or_assign(CloudDbConstant::DELETE_FIELD, false); + extend.insert_or_assign(CloudDbConstant::GID_FIELD, std::to_string(i)); + extends.push_back(extend); + + time++; + } + + // 批量插入云端数据 + return cloudDB->BatchInsertWithGid(CLOUD_DOWNLOAD_TABLE, std::move(records), extends); +} + +/** + * @tc.name: RdbCloudDownload001 + * @tc.desc: Test RDB open database, insert 10 records to cloud, trigger sync, and verify download. + * @tc.type: FUNC + * @tc.require: + * @tc.author: Claude + */ +HWTEST_F(DistributedDBRDBCloudDownloadTest, RdbCloudDownload001, TestSize.Level0) +{ + /** + * @tc.steps: step1. Initialize delegate (open RDB database) + * @tc.expected: step1. Open success + */ + EXPECT_EQ(BasicUnitTest::InitDelegate(info_, "dev1"), E_OK); + + /** + * @tc.steps: step2. Create test table + * @tc.expected: step2. Table created success + */ + EXPECT_NO_FATAL_FAILURE(InitTestTable()); + + /** + * @tc.steps: step3. Set schema for the table + * @tc.expected: step3. Schema set success + */ + EXPECT_NO_FATAL_FAILURE(InitSchema(info_)); + + /** + * @tc.steps: step4. Set cloud sync table + * @tc.expected: step4. Cloud sync table created success + */ + EXPECT_NO_FATAL_FAILURE(InitCloudSyncTable(info_)); + + /** + * @tc.steps: step5. Verify local database is empty + * @tc.expected: step5. Local data count is 0 + */ + int localCount = CountTableData(info_, CLOUD_DOWNLOAD_TABLE); + EXPECT_EQ(localCount, 0); + + /** + * @tc.steps: step6. Insert 10 records to cloud + * @tc.expected: step6. Cloud insert success + */ + constexpr int64_t CLOUD_DATA_COUNT = 10; + DBStatus status = InsertDataToCloud(CLOUD_DATA_COUNT); + EXPECT_EQ(status, DBStatus::OK); + + /** + * @tc.steps: step7. Verify cloud has 10 records + * @tc.expected: step7. Cloud data count is 10 + */ + int cloudCount = GetCloudDataCount(CLOUD_DOWNLOAD_TABLE); + EXPECT_EQ(cloudCount, CLOUD_DATA_COUNT); + + /** + * @tc.steps: step8. Trigger cloud sync (download from cloud to local) + * @tc.expected: step8. Sync success + */ + Query query = Query::Select().FromTable({CLOUD_DOWNLOAD_TABLE}); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info_, query, SyncMode::SYNC_MODE_CLOUD_FORCE_PULL, OK, OK)); + + /** + * @tc.steps: step9. Verify local database has 10 records after sync + * @tc.expected: step9. Local data count is 10 + */ + localCount = CountTableData(info_, CLOUD_DOWNLOAD_TABLE); + EXPECT_EQ(localCount, CLOUD_DATA_COUNT); + + /** + * @tc.steps: step10. Verify the data content is correct + * @tc.expected: step10. Data matches cloud data + */ + // 查询本地数据验证内容 + std::string sql = "SELECT * FROM " + CLOUD_DOWNLOAD_TABLE + " ORDER BY id"; + sqlite3 *db = GetSqliteHandle(info_); + ASSERT_NE(db, nullptr); + + sqlite3_stmt *stmt = nullptr; + int errCode = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr); + ASSERT_EQ(errCode, SQLITE_OK); + + int id = 0; + while ((errCode = sqlite3_step(stmt)) == SQLITE_ROW) { + id++; + int colId = sqlite3_column_int(stmt, 0); // id + int colInt = sqlite3_column_int(stmt, 1); // intCol + const char *colStr = reinterpret_cast(sqlite3_column_text(stmt, 2)); // stringCol + + // 验证数据内容 + EXPECT_EQ(colId, id); + EXPECT_EQ(colInt, id * 10); + std::string expectedStr = "cloud_data_" + std::to_string(id); + EXPECT_STREQ(colStr, expectedStr.c_str()); + } + sqlite3_finalize(stmt); + + // 验证总共查到了 10 条数据 + EXPECT_EQ(id, CLOUD_DATA_COUNT); +} + +/** + * @tc.name: RdbCloudDownload002 + * @tc.desc: Test cloud sync with merge mode when local has existing data. + * @tc.type: FUNC + * @tc.require: + * @tc.author: Claude + */ +HWTEST_F(DistributedDBRDBCloudDownloadTest, RdbCloudDownload002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Initialize delegate and create table + * @tc.expected: step1. Success + */ + EXPECT_EQ(BasicUnitTest::InitDelegate(info_, "dev1"), E_OK); + EXPECT_NO_FATAL_FAILURE(InitTestTable()); + EXPECT_NO_FATAL_FAILURE(InitSchema(info_)); + EXPECT_NO_FATAL_FAILURE(InitCloudSyncTable(info_)); + + /** + * @tc.steps: step2. Insert 3 records to local database + * @tc.expected: step2. Insert success + */ + for (int i = 1; i <= 3; i++) { + std::string sql = "INSERT INTO " + std::string(CLOUD_DOWNLOAD_TABLE) + + " VALUES(" + std::to_string(i) + ", " + std::to_string(i * 100) + + ", 'local_data_" + std::to_string(i) + "')"; + EXPECT_EQ(ExecuteSQL(sql, info_), E_OK); + } + EXPECT_EQ(CountTableData(info_, CLOUD_DOWNLOAD_TABLE), 3); + + /** + * @tc.steps: step3. Insert 10 records to cloud (with some overlapping IDs) + * @tc.expected: step3. Insert success + */ + constexpr int64_t CLOUD_DATA_COUNT = 10; + DBStatus status = InsertDataToCloud(CLOUD_DATA_COUNT); + EXPECT_EQ(status, DBStatus::OK); + + /** + * @tc.steps: step4. Trigger cloud sync with merge mode + * @tc.expected: step4. Sync success + */ + Query query = Query::Select().FromTable({CLOUD_DOWNLOAD_TABLE}); + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info_, query, SyncMode::SYNC_MODE_CLOUD_MERGE, OK, OK)); + + /** + * @tc.steps: step5. Verify all cloud data is downloaded + * @tc.expected: step5. Local has 10 records (cloud data should overwrite local data) + */ + int localCount = CountTableData(info_, CLOUD_DOWNLOAD_TABLE); + EXPECT_EQ(localCount, CLOUD_DATA_COUNT); + + /** + * @tc.steps: step6. Verify cloud data overwrote local data for overlapping IDs + * @tc.expected: step6. Data matches cloud (not local) + */ + std::string sql = "SELECT stringCol FROM " + std::string(CLOUD_DOWNLOAD_TABLE) + " WHERE id = 1"; + sqlite3 *db = GetSqliteHandle(info_); + ASSERT_NE(db, nullptr); + + sqlite3_stmt *stmt = nullptr; + int errCode = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr); + ASSERT_EQ(errCode, SQLITE_OK); + + errCode = sqlite3_step(stmt); + ASSERT_EQ(errCode, SQLITE_ROW); + const char *colStr = reinterpret_cast(sqlite3_column_text(stmt, 0)); + // 应该是云数据 "cloud_data_1" 而不是本地数据 "local_data_1" + EXPECT_STREQ(colStr, "cloud_data_1"); + sqlite3_finalize(stmt); +} + +/** + * @tc.name: RdbCloudDownload003 + * @tc.desc: Test multiple sync cycles download data correctly. + * @tc.type: FUNC + * @tc.require: + * @tc.author: Claude + */ +HWTEST_F(DistributedDBRDBCloudDownloadTest, RdbCloudDownload003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Initialize delegate and create table + * @tc.expected: step1. Success + */ + EXPECT_EQ(BasicUnitTest::InitDelegate(info_, "dev1"), E_OK); + EXPECT_NO_FATAL_FAILURE(InitTestTable()); + EXPECT_NO_FATAL_FAILURE(InitSchema(info_)); + EXPECT_NO_FATAL_FAILURE(InitCloudSyncTable(info_)); + + Query query = Query::Select().FromTable({CLOUD_DOWNLOAD_TABLE}); + + /** + * @tc.steps: step2. First sync: Insert 5 records to cloud and sync + * @tc.expected: step2. Local has 5 records + */ + { + // 插入 5 条云端数据 + auto cloudDB = GetVirtualCloudDb(); + ASSERT_NE(cloudDB, nullptr); + + std::vector records; + std::vector extends; + Timestamp now = TimeHelper::GetSysCurrentTime(); + auto time = static_cast(now / CloudDbConstant::TEN_THOUSAND); + + for (int64_t i = 1; i <= 5; i++) { + VBucket record; + record["id"] = i; + record["intCol"] = i * 10; + record["stringCol"] = "batch1_data_" + std::to_string(i); + records.push_back(record); + + VBucket extend; + extend.insert_or_assign(CloudDbConstant::CREATE_FIELD, time); + extend.insert_or_assign(CloudDbConstant::MODIFY_FIELD, time); + extend.insert_or_assign(CloudDbConstant::DELETE_FIELD, false); + extend.insert_or_assign(CloudDbConstant::GID_FIELD, std::to_string(i)); + extends.push_back(extend); + time++; + } + EXPECT_EQ(cloudDB->BatchInsertWithGid(CLOUD_DOWNLOAD_TABLE, std::move(records), extends), DBStatus::OK); + + // 第一次同步 + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info_, query, SyncMode::SYNC_MODE_CLOUD_FORCE_PULL, OK, OK)); + EXPECT_EQ(CountTableData(info_, CLOUD_DOWNLOAD_TABLE), 5); + } + + /** + * @tc.steps: step3. Second sync: Insert 5 more records to cloud (id 6-10) and sync + * @tc.expected: step3. Local has 10 records total + */ + { + constexpr int64_t CLOUD_DATA_COUNT = 10; + DBStatus status = InsertDataToCloud(CLOUD_DATA_COUNT); + EXPECT_EQ(status, DBStatus::OK); + + EXPECT_NO_FATAL_FAILURE(CloudBlockSync(info_, query, SyncMode::SYNC_MODE_CLOUD_FORCE_PULL, OK, OK)); + EXPECT_EQ(CountTableData(info_, CLOUD_DOWNLOAD_TABLE), CLOUD_DATA_COUNT); + } + + /** + * @tc.steps: step4. Verify all data is correct + * @tc.expected: step4. All 10 records with correct content + */ + int localCount = CountTableData(info_, CLOUD_DOWNLOAD_TABLE, "stringCol LIKE 'cloud_data_%'"); + EXPECT_EQ(localCount, 5); // 第二批的 5 条数据 + + localCount = CountTableData(info_, CLOUD_DOWNLOAD_TABLE, "stringCol LIKE 'batch1_data_%'"); + EXPECT_EQ(localCount, 5); // 第一批的 5 条数据 +} + +} // namespace + +#endif // USE_DISTRIBUTEDDB_CLOUD -- Gitee