1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef OMIT_JSON
17 #include <gtest/gtest.h>
18 #include "distributeddb_tools_unit_test.h"
19 #include "kv_store_delegate_manager.h"
20 #include "schema_constant.h"
21 #include "schema_object.h"
22 #include "schema_utils.h"
23
24 using namespace testing::ext;
25 using namespace DistributedDB;
26 using namespace DistributedDBUnitTest;
27
28 namespace {
29 const std::string APP_ID = "SCHEMA";
30 const std::string USER_ID = "UPGRADE";
31 std::string g_testDir;
32 KvStoreDelegateManager g_manager(APP_ID, USER_ID);
33
34 DBStatus g_kvDelegateStatus = INVALID_ARGS;
35 KvStoreNbDelegate *g_kvDelegatePtr = nullptr;
36 auto g_kvDelegateCallback = bind(&DistributedDBToolsUnitTest::KvStoreNbDelegateCallback,
37 std::placeholders::_1, std::placeholders::_2, std::ref(g_kvDelegateStatus), std::ref(g_kvDelegatePtr));
38
39 std::string g_baseSchema;
40 DBStatus g_expectError = SCHEMA_VIOLATE_VALUE;
41
StringEraser(const std::string & oriString,const std::string & toErase)42 std::string StringEraser(const std::string &oriString, const std::string &toErase)
43 {
44 std::string resStr = oriString;
45 auto iter = std::search(resStr.begin(), resStr.end(), toErase.begin(), toErase.end());
46 resStr.erase(iter, iter + toErase.size());
47 return resStr;
48 }
StringReplacer(const std::string & oriString,const std::string & toErase,const std::string & toRepalce)49 std::string StringReplacer(const std::string &oriString, const std::string &toErase, const std::string &toRepalce)
50 {
51 std::string resStr = oriString;
52 auto iter = std::search(resStr.begin(), resStr.end(), toErase.begin(), toErase.end());
53 resStr.replace(iter, iter + toErase.size(), toRepalce);
54 return resStr;
55 }
56
57 const std::string SCHEMA_INC_FIELD = "{\"SCHEMA_VERSION\":\"1.0\","
58 "\"SCHEMA_MODE\":\"COMPATIBLE\","
59 "\"SCHEMA_DEFINE\":{"
60 "\"field_1\":\"LONG, NOT NULL, DEFAULT 100\","
61 "\"field_2\":{"
62 "\"field_3\":\"STRING, DEFAULT 'OpenHarmony'\","
63 "\"field_4\":\"INTEGER\""
64 "}"
65 "},"
66 "\"SCHEMA_INDEXES\":[\"field_1\", [\"field_2.field_3\", \"field_2.field_4\"]]}";
67 const std::string SCHEMA_BASE = StringReplacer(StringEraser(SCHEMA_INC_FIELD, ",\"field_4\":\"INTEGER\""),
68 "[\"field_2.field_3\", \"field_2.field_4\"]", "\"field_2.field_3\"");
69 const std::string SCHEMA_INC_FIELD_NOTNULL = StringReplacer(SCHEMA_INC_FIELD, "\"INTEGER\"",
70 "\"INTEGER, NOT NULL\"");
71 const std::string SCHEMA_INC_FIELD_DEFAULT = StringReplacer(SCHEMA_INC_FIELD, "\"INTEGER\"",
72 "\"INTEGER, DEFAULT 88\"");
73 const std::string SCHEMA_INC_FIELD_NOTNULL_DEFAULT = StringReplacer(SCHEMA_INC_FIELD, "\"INTEGER\"",
74 "\"INTEGER, NOT NULL, DEFAULT 88\"");
75
76 const std::string VALUE_BASE_LACK = "{\"field_1\":LONG_VAL}";
77 const std::string VALUE_BASE = "{\"field_1\":LONG_VAL,\"field_2\":{\"field_3\":STR_VAL}}";
78 const std::string VALUE_FIELD_FULL = "{\"field_1\":LONG_VAL,\"field_2\":{\"field_3\":STR_VAL,\"field_4\":INT_VAL}}";
79
SchemaSwitchMode(const std::string & oriSchemaStr)80 std::string SchemaSwitchMode(const std::string &oriSchemaStr)
81 {
82 std::string resStr = oriSchemaStr;
83 auto iterStrict = std::search(resStr.begin(), resStr.end(), SchemaConstant::KEYWORD_MODE_STRICT,
84 SchemaConstant::KEYWORD_MODE_STRICT + strlen(SchemaConstant::KEYWORD_MODE_STRICT));
85 auto iterCompatible = std::search(resStr.begin(), resStr.end(), SchemaConstant::KEYWORD_MODE_COMPATIBLE,
86 SchemaConstant::KEYWORD_MODE_COMPATIBLE + strlen(SchemaConstant::KEYWORD_MODE_COMPATIBLE));
87 if (iterStrict != resStr.end()) {
88 resStr.replace(iterStrict, iterStrict + strlen(SchemaConstant::KEYWORD_MODE_STRICT),
89 SchemaConstant::KEYWORD_MODE_COMPATIBLE,
90 SchemaConstant::KEYWORD_MODE_COMPATIBLE + strlen(SchemaConstant::KEYWORD_MODE_COMPATIBLE));
91 return resStr;
92 }
93 if (iterCompatible != resStr.end()) {
94 resStr.replace(iterCompatible, iterCompatible + strlen(SchemaConstant::KEYWORD_MODE_COMPATIBLE),
95 SchemaConstant::KEYWORD_MODE_STRICT,
96 SchemaConstant::KEYWORD_MODE_STRICT + strlen(SchemaConstant::KEYWORD_MODE_STRICT));
97 return resStr;
98 }
99 return oriSchemaStr;
100 }
SchemaChecker(const std::string & schema)101 bool SchemaChecker(const std::string &schema)
102 {
103 SchemaObject schemaObj;
104 return (schemaObj.ParseFromSchemaString(schema) == E_OK);
105 }
ToVec(const std::string & inStr)106 std::vector<uint8_t> ToVec(const std::string &inStr)
107 {
108 std::vector<uint8_t> outVec(inStr.begin(), inStr.end());
109 return outVec;
110 }
ToStr(const std::vector<uint8_t> & inVec)111 std::string ToStr(const std::vector<uint8_t> &inVec)
112 {
113 std::string outStr(inVec.begin(), inVec.end());
114 return outStr;
115 }
116
117 const std::map<std::string, std::vector<uint8_t>> VALUE_MAP {
118 {"LACK", ToVec(StringReplacer(VALUE_BASE_LACK, "LONG_VAL", "1"))},
119 {"BASE", ToVec(StringReplacer(StringReplacer(VALUE_BASE, "LONG_VAL", "2"), "STR_VAL", "\"OS\""))},
120 {"FULL", ToVec(StringReplacer(StringReplacer(StringReplacer(VALUE_FIELD_FULL, "LONG_VAL", "3"), "STR_VAL",
121 "\"TEST\""), "INT_VAL", "33"))},
122 {"BASE_WRONG_TYPE", ToVec(StringReplacer(StringReplacer(VALUE_BASE, "LONG_VAL", "2"), "STR_VAL", "10086"))},
123 {"FULL_NULL", ToVec(StringReplacer(StringReplacer(StringReplacer(VALUE_FIELD_FULL, "LONG_VAL", "3"),
124 "STR_VAL", "\"TEST\""), "INT_VAL", "null"))},
125 {"FULL_WRONG_TYPE", ToVec(StringReplacer(StringReplacer(StringReplacer(VALUE_FIELD_FULL, "LONG_VAL", "3"),
126 "STR_VAL", "\"TEST\""), "INT_VAL", "\"UT\""))},
127 };
128
129 // Key begin from "KEY_1"
InsertPresetEntry(KvStoreNbDelegate & delegate,const std::vector<std::string> & selection)130 void InsertPresetEntry(KvStoreNbDelegate &delegate, const std::vector<std::string> &selection)
131 {
132 int count = 0;
133 for (const auto &eachSel : selection) {
134 ASSERT_NE(VALUE_MAP.count(eachSel), 0ul);
135 DBStatus ret = delegate.Put(ToVec(std::string("KEY_") + std::to_string(++count)), VALUE_MAP.at(eachSel));
136 ASSERT_EQ(ret, OK);
137 }
138 }
139 }
140
141 class DistributedDBInterfacesSchemaDatabaseUpgradeTest : public testing::Test {
142 public:
143 static void SetUpTestCase(void);
144 static void TearDownTestCase(void);
145 void SetUp();
146 void TearDown();
147 };
148
SetUpTestCase(void)149 void DistributedDBInterfacesSchemaDatabaseUpgradeTest::SetUpTestCase(void)
150 {
151 DistributedDBToolsUnitTest::TestDirInit(g_testDir);
152 KvStoreConfig config{g_testDir};
153 g_manager.SetKvStoreConfig(config);
154 ASSERT_EQ(SchemaChecker(SCHEMA_BASE), true);
155 ASSERT_EQ(SchemaChecker(SCHEMA_INC_FIELD), true);
156 ASSERT_EQ(SchemaChecker(SCHEMA_INC_FIELD_NOTNULL), true);
157 ASSERT_EQ(SchemaChecker(SCHEMA_INC_FIELD_DEFAULT), true);
158 ASSERT_EQ(SchemaChecker(SCHEMA_INC_FIELD_NOTNULL_DEFAULT), true);
159 }
160
TearDownTestCase(void)161 void DistributedDBInterfacesSchemaDatabaseUpgradeTest::TearDownTestCase(void)
162 {
163 if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) {
164 LOGE("[TestSchemaUpgrade] Remove test directory error.");
165 }
166 }
167
SetUp(void)168 void DistributedDBInterfacesSchemaDatabaseUpgradeTest::SetUp(void)
169 {
170 DistributedDBToolsUnitTest::PrintTestCaseInfo();
171 g_kvDelegateStatus = INVALID_ARGS;
172 g_kvDelegatePtr = nullptr;
173 }
174
TearDown(void)175 void DistributedDBInterfacesSchemaDatabaseUpgradeTest::TearDown(void)
176 {
177 if (g_kvDelegatePtr != nullptr) {
178 ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK);
179 g_kvDelegatePtr = nullptr;
180 }
181 }
182
183 /**
184 * @tc.name: UpgradeFromKv001
185 * @tc.desc: Schema database upgrade from kv database, exist value match compatible schema(mismatch strict schema)
186 * @tc.type: FUNC
187 * @tc.require:
188 * @tc.author: xiaozhenjian
189 */
190 HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromKv001, TestSize.Level1)
191 {
192 /**
193 * @tc.steps: step1. Prepare kv database with value match compatible schema(mismatch strict schema) then close
194 * @tc.expected: step1. E_OK
195 */
196 std::string storeId = "UpgradeFromKv001";
197 KvStoreNbDelegate::Option option;
198 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
199 ASSERT_TRUE(g_kvDelegatePtr != nullptr);
200 ASSERT_EQ(g_kvDelegateStatus, OK);
201
202 InsertPresetEntry(*g_kvDelegatePtr, std::vector<std::string>{"LACK", "BASE", "LACK", "BASE", "FULL"});
203 DBStatus ret = g_kvDelegatePtr->Delete(ToVec("KEY_4"));
204 ASSERT_EQ(ret, OK);
205 ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK);
206 g_kvDelegatePtr = nullptr;
207
208 /**
209 * @tc.steps: step2. Upgrade to schema(strict) database
210 * @tc.expected: step2. SCHEMA_VIOLATE_VALUE
211 */
212 option.schema = SchemaSwitchMode(SCHEMA_BASE);
213 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
214 ASSERT_TRUE(g_kvDelegatePtr == nullptr);
215 ASSERT_EQ(g_kvDelegateStatus, SCHEMA_VIOLATE_VALUE);
216
217 /**
218 * @tc.steps: step3. Upgrade to schema(compatible) database
219 * @tc.expected: step3. E_OK
220 */
221 option.schema = SCHEMA_BASE;
222 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
223 ASSERT_TRUE(g_kvDelegatePtr != nullptr);
224 ASSERT_EQ(g_kvDelegateStatus, OK);
225
226 /**
227 * @tc.steps: step4. Query field_2.field_3 equal OpenHarmony
228 * @tc.expected: step4. E_OK, KEY_1
229 */
230 Query query = Query::Select().EqualTo("field_2.field_3", "OpenHarmony");
231 std::vector<Entry> entries;
232 EXPECT_EQ(g_kvDelegatePtr->GetEntries(query, entries), OK);
233 ASSERT_EQ(entries.size(), 2ul);
234 EXPECT_EQ(ToStr(entries[0].key), std::string("KEY_1"));
235 EXPECT_EQ(ToStr(entries[1].key), std::string("KEY_3"));
236 std::string valStr = ToStr(entries[0].value);
237 std::string defaultVal = "OpenHarmony";
238 auto iter = std::search(valStr.begin(), valStr.end(), defaultVal.begin(), defaultVal.end());
239 EXPECT_TRUE(iter != valStr.end());
240 }
241
242 /**
243 * @tc.name: UpgradeFromKv002
244 * @tc.desc: Schema database upgrade from kv database, exist value mismatch compatible schema
245 * @tc.type: FUNC
246 * @tc.require:
247 * @tc.author: xiaozhenjian
248 */
249 HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromKv002, TestSize.Level1)
250 {
251 /**
252 * @tc.steps: step1. Prepare kv database with value mismatch compatible schema then close
253 * @tc.expected: step1. E_OK
254 */
255 std::string storeId = "UpgradeFromKv002";
256 KvStoreNbDelegate::Option option;
257 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
258 ASSERT_TRUE(g_kvDelegatePtr != nullptr);
259 ASSERT_EQ(g_kvDelegateStatus, OK);
260
261 InsertPresetEntry(*g_kvDelegatePtr, std::vector<std::string>{"BASE_WRONG_TYPE"});
262 ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK);
263 g_kvDelegatePtr = nullptr;
264
265 /**
266 * @tc.steps: step2. Upgrade to schema(compatible) database
267 * @tc.expected: step2. SCHEMA_VIOLATE_VALUE
268 */
269 option.schema = SCHEMA_BASE;
270 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
271 ASSERT_TRUE(g_kvDelegatePtr == nullptr);
272 ASSERT_EQ(g_kvDelegateStatus, SCHEMA_VIOLATE_VALUE);
273 }
274
275 namespace {
TestUpgradeFromSchema(const std::string & storeId,const std::vector<std::string> & selection,const std::string & newSchema,bool expectMatch,uint32_t expectCount)276 void TestUpgradeFromSchema(const std::string &storeId, const std::vector<std::string> &selection,
277 const std::string &newSchema, bool expectMatch, uint32_t expectCount)
278 {
279 LOGI("[TestUpgradeFromSchema] StoreId=%s", storeId.c_str());
280 /**
281 * @tc.steps: step1. Prepare kv database with value then close
282 * @tc.expected: step1. E_OK
283 */
284 KvStoreNbDelegate::Option option;
285 option.schema = g_baseSchema;
286 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
287 ASSERT_TRUE(g_kvDelegatePtr != nullptr);
288 ASSERT_EQ(g_kvDelegateStatus, OK);
289
290 InsertPresetEntry(*g_kvDelegatePtr, selection);
291 ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK);
292 g_kvDelegatePtr = nullptr;
293
294 /**
295 * @tc.steps: step2. Upgrade to schema database
296 * @tc.expected: step2. OK or SCHEMA_VIOLATE_VALUE
297 */
298 option.schema = newSchema;
299 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
300 if (expectMatch) {
301 ASSERT_TRUE(g_kvDelegatePtr != nullptr);
302 ASSERT_EQ(g_kvDelegateStatus, OK);
303 } else {
304 ASSERT_TRUE(g_kvDelegatePtr == nullptr);
305 ASSERT_EQ(g_kvDelegateStatus, g_expectError);
306 }
307
308 /**
309 * @tc.steps: step3. Query field_2.field_3
310 * @tc.expected: step3. E_OK
311 */
312 if (expectMatch) {
313 Query query = Query::Select().EqualTo("field_2.field_4", 88); // 88 is the default value in the testcase.
314 std::vector<Entry> entries;
315 if (expectCount == 0) {
316 EXPECT_EQ(g_kvDelegatePtr->GetEntries(query, entries), NOT_FOUND);
317 } else {
318 ASSERT_EQ(g_kvDelegatePtr->GetEntries(query, entries), OK);
319 EXPECT_EQ(entries.size(), expectCount);
320 }
321 ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK);
322 g_kvDelegatePtr = nullptr;
323 }
324 }
325 }
326
327 /**
328 * @tc.name: UpgradeFromSchema001
329 * @tc.desc: Schema database upgrade from kv database, exist value match new schema
330 * @tc.type: FUNC
331 * @tc.require:
332 * @tc.author: xiaozhenjian
333 */
334 HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromSchema001, TestSize.Level1)
335 {
336 g_baseSchema = SCHEMA_BASE;
337 g_expectError = SCHEMA_VIOLATE_VALUE;
338 TestUpgradeFromSchema("UpgradeFromSchema001_1", std::vector<std::string>{"LACK", "BASE", "FULL", "FULL_NULL"},
339 SCHEMA_INC_FIELD, true, 0);
340 TestUpgradeFromSchema("UpgradeFromSchema001_2", std::vector<std::string>{"LACK", "BASE", "FULL", "FULL_NULL"},
341 SCHEMA_INC_FIELD_DEFAULT, true, 2);
342 TestUpgradeFromSchema("UpgradeFromSchema001_3", std::vector<std::string>{"LACK", "BASE", "FULL"},
343 SCHEMA_INC_FIELD_NOTNULL_DEFAULT, true, 2);
344 }
345
346 /**
347 * @tc.name: UpgradeFromSchema002
348 * @tc.desc: Schema database upgrade from kv database, exist value mismatch new schema
349 * @tc.type: FUNC
350 * @tc.require:
351 * @tc.author: xiaozhenjian
352 */
353 HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromSchema002, TestSize.Level1)
354 {
355 g_baseSchema = SCHEMA_BASE;
356 g_expectError = SCHEMA_VIOLATE_VALUE;
357 TestUpgradeFromSchema("UpgradeFromSchema002_1", std::vector<std::string>{"LACK", "BASE", "FULL", "FULL_WRONG_TYPE"},
358 SCHEMA_INC_FIELD, false, 0);
359 TestUpgradeFromSchema("UpgradeFromSchema002_2", std::vector<std::string>{"LACK", "BASE", "FULL", "FULL_WRONG_TYPE"},
360 SCHEMA_INC_FIELD_DEFAULT, false, 0);
361 TestUpgradeFromSchema("UpgradeFromSchema002_3", std::vector<std::string>{"LACK", "BASE", "FULL", "FULL_NULL"},
362 SCHEMA_INC_FIELD_NOTNULL_DEFAULT, false, 0);
363 }
364
365 /**
366 * @tc.name: UpgradeFromSchema003
367 * @tc.desc: Schema database upgrade from kv database, new schema incompatible with old schema
368 * @tc.type: FUNC
369 * @tc.require:
370 * @tc.author: xiaozhenjian
371 */
372 HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromSchema003, TestSize.Level1)
373 {
374 // Compatible schema can increase field, but must not be null without default.
375 g_baseSchema = SCHEMA_BASE;
376 g_expectError = SCHEMA_MISMATCH;
377 TestUpgradeFromSchema("UpgradeFromSchema003_1", std::vector<std::string>{"LACK", "BASE", "FULL", "FULL_NULL"},
378 SCHEMA_INC_FIELD_NOTNULL, false, 0);
379 // Strict schema can not incrase field
380 g_baseSchema = SchemaSwitchMode(SCHEMA_BASE);
381 TestUpgradeFromSchema("UpgradeFromSchema003_2", std::vector<std::string>{"LACK", "BASE"},
382 SchemaSwitchMode(SCHEMA_INC_FIELD), false, 0);
383 }
384 #endif