178 lines
6.7 KiB
C++
178 lines
6.7 KiB
C++
|
|
#include <gtest/gtest.h>
|
||
|
|
#include "vde/cloud/cloud_native.h"
|
||
|
|
#include <thread>
|
||
|
|
#include <chrono>
|
||
|
|
|
||
|
|
using namespace vde::cloud;
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// 1. DeltaSync 基本操作
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(CloudTest, DeltaSync_InitialVersionIsZero) {
|
||
|
|
DeltaSync sync;
|
||
|
|
EXPECT_EQ(sync.version(), 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, DeltaSync_CommitIncrementsVersion) {
|
||
|
|
DeltaSync sync;
|
||
|
|
DeltaOp op{OpType::MODIFY, 1001, 1, {}};
|
||
|
|
sync.commit_local({op}, "user1");
|
||
|
|
EXPECT_EQ(sync.version(), 1u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, DeltaSync_MultipleCommits) {
|
||
|
|
DeltaSync sync;
|
||
|
|
sync.commit_local({{OpType::INSERT, 1, 1, {}}}, "user1");
|
||
|
|
sync.commit_local({{OpType::MODIFY, 1, 2, {}}}, "user2");
|
||
|
|
sync.commit_local({{OpType::INSERT, 2, 3, {}}}, "user1");
|
||
|
|
EXPECT_EQ(sync.version(), 3u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, DeltaSync_DirtyEntitiesTracking) {
|
||
|
|
DeltaSync sync;
|
||
|
|
sync.commit_local({{OpType::INSERT, 100, 1, {}}}, "user1");
|
||
|
|
EXPECT_TRUE(sync.is_dirty(100));
|
||
|
|
EXPECT_FALSE(sync.is_dirty(200));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, DeltaSync_DeleteRemovesDirty) {
|
||
|
|
DeltaSync sync;
|
||
|
|
sync.commit_local({{OpType::INSERT, 100, 1, {}}}, "user1");
|
||
|
|
EXPECT_TRUE(sync.is_dirty(100));
|
||
|
|
sync.commit_local({{OpType::DELETE, 100, 2, {}}}, "user1");
|
||
|
|
EXPECT_FALSE(sync.is_dirty(100));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, DeltaSync_Rollback) {
|
||
|
|
DeltaSync sync;
|
||
|
|
sync.commit_local({{OpType::INSERT, 1, 1, {}}}, "user1");
|
||
|
|
sync.commit_local({{OpType::INSERT, 2, 2, {}}}, "user1");
|
||
|
|
EXPECT_EQ(sync.version(), 2u);
|
||
|
|
EXPECT_TRUE(sync.rollback(1));
|
||
|
|
EXPECT_EQ(sync.version(), 1u);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// 2. OperationalTransform 冲突检测
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(CloudTest, OT_NoConflictOnDifferentEntities) {
|
||
|
|
OperationalTransform ot;
|
||
|
|
DeltaOp op1{OpType::MODIFY, 1, 100, {}};
|
||
|
|
DeltaOp op2{OpType::MODIFY, 2, 200, {}};
|
||
|
|
EXPECT_FALSE(ot.conflicts(op1, op2));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, OT_ConflictOnSameEntityModify) {
|
||
|
|
OperationalTransform ot;
|
||
|
|
DeltaOp op1{OpType::MODIFY, 1, 100, {}};
|
||
|
|
DeltaOp op2{OpType::MODIFY, 1, 200, {}};
|
||
|
|
EXPECT_TRUE(ot.conflicts(op1, op2));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, OT_ConflictOnDelete) {
|
||
|
|
OperationalTransform ot;
|
||
|
|
DeltaOp op1{OpType::DELETE, 1, 100, {}};
|
||
|
|
DeltaOp op2{OpType::MODIFY, 1, 200, {}};
|
||
|
|
EXPECT_TRUE(ot.conflicts(op1, op2));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, OT_TransformDifferentEntitiesNoOp) {
|
||
|
|
OperationalTransform ot;
|
||
|
|
DeltaOp op1{OpType::MODIFY, 1, 100, {1,2,3}};
|
||
|
|
DeltaOp op2{OpType::MODIFY, 2, 200, {4,5,6}};
|
||
|
|
DeltaOp result = ot.transform(op1, op2);
|
||
|
|
EXPECT_EQ(result.entity_id, 1u);
|
||
|
|
EXPECT_EQ(result.type, OpType::MODIFY);
|
||
|
|
EXPECT_EQ(result.data, (std::vector<uint8_t>{1,2,3}));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, OT_MergeTwoChangeSets) {
|
||
|
|
OperationalTransform ot;
|
||
|
|
ChangeSet cs1;
|
||
|
|
cs1.base_version = 0;
|
||
|
|
cs1.new_version = 1;
|
||
|
|
cs1.ops = {{OpType::INSERT, 10, 1, {}}};
|
||
|
|
|
||
|
|
ChangeSet cs2;
|
||
|
|
cs2.base_version = 0;
|
||
|
|
cs2.new_version = 1;
|
||
|
|
cs2.ops = {{OpType::INSERT, 20, 2, {}}};
|
||
|
|
|
||
|
|
ChangeSet merged = ot.merge(cs1, cs2);
|
||
|
|
EXPECT_GE(merged.ops.size(), 2u);
|
||
|
|
EXPECT_EQ(merged.base_version, 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// 3. CloudSession 协作会话
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(CloudTest, Session_JoinAndLeave) {
|
||
|
|
CloudSession session("test-session-1");
|
||
|
|
UserInfo user{"user1", "Alice", 0xFF0000, true};
|
||
|
|
|
||
|
|
EXPECT_TRUE(session.join(user));
|
||
|
|
EXPECT_EQ(session.online_users().size(), 1u);
|
||
|
|
EXPECT_TRUE(session.leave("user1"));
|
||
|
|
EXPECT_EQ(session.online_users().size(), 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, Session_DuplicateJoinFails) {
|
||
|
|
CloudSession session("test-session-2");
|
||
|
|
UserInfo user{"user1", "Alice", 0xFF0000, true};
|
||
|
|
EXPECT_TRUE(session.join(user));
|
||
|
|
EXPECT_FALSE(session.join(user)); // 重复加入
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, Session_SubmitOperation) {
|
||
|
|
CloudSession session("test-session-3");
|
||
|
|
UserInfo user{"user1", "Bob", 0x00FF00, true};
|
||
|
|
session.join(user);
|
||
|
|
|
||
|
|
std::vector<DeltaOp> ops = {{OpType::INSERT, 42, 1, {1,2,3}}};
|
||
|
|
EXPECT_TRUE(session.submit_operation("user1", ops));
|
||
|
|
EXPECT_GT(session.version(), 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// 4. ObjectStorage 对象存储
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(CloudTest, ObjectStorage_Configure) {
|
||
|
|
ObjectStorage storage;
|
||
|
|
storage.configure("http://localhost:9000", "vde-models", "minioadmin", "minioadmin");
|
||
|
|
SUCCEED(); // 配置不抛出异常
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, ObjectStorage_StoreAndLoadModel) {
|
||
|
|
ObjectStorage storage;
|
||
|
|
storage.configure("http://localhost:9000", "vde-models", "key", "secret");
|
||
|
|
|
||
|
|
std::vector<uint8_t> model_data = {0x01, 0x02, 0x03, 0x04};
|
||
|
|
EXPECT_TRUE(storage.store_model("test_part", model_data, "stp"));
|
||
|
|
|
||
|
|
// Stub 返回 nullopt,但调用接口正常
|
||
|
|
auto loaded = storage.load_model("test_part", "stp");
|
||
|
|
// In stub mode, this returns nullopt — that's expected
|
||
|
|
SUCCEED();
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
// 5. ServerlessFunction
|
||
|
|
// ═══════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
TEST(CloudTest, Serverless_Deploy) {
|
||
|
|
ServerlessFunction fn;
|
||
|
|
EXPECT_TRUE(fn.deploy("validate-model", "/code/validate.py", "python3.9"));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(CloudTest, Serverless_Invoke) {
|
||
|
|
ServerlessFunction fn;
|
||
|
|
FunctionRequest req{"validate-model", {0x01, 0x02}, {}, std::chrono::milliseconds(5000)};
|
||
|
|
FunctionResponse resp = fn.invoke(req);
|
||
|
|
EXPECT_EQ(resp.status_code, 200);
|
||
|
|
EXPECT_TRUE(resp.ok);
|
||
|
|
}
|