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.begin(),
84 SchemaConstant::KEYWORD_MODE_STRICT.end());
85 auto iterCompatible = std::search(resStr.begin(), resStr.end(), SchemaConstant::KEYWORD_MODE_COMPATIBLE.begin(),
86 SchemaConstant::KEYWORD_MODE_COMPATIBLE.end());
87 if (iterStrict != resStr.end()) {
88 resStr.replace(iterStrict, iterStrict + SchemaConstant::KEYWORD_MODE_STRICT.size(),
89 SchemaConstant::KEYWORD_MODE_COMPATIBLE.begin(), SchemaConstant::KEYWORD_MODE_COMPATIBLE.end());
90 return resStr;
91 }
92 if (iterCompatible != resStr.end()) {
93 resStr.replace(iterCompatible, iterCompatible + SchemaConstant::KEYWORD_MODE_COMPATIBLE.size(),
94 SchemaConstant::KEYWORD_MODE_STRICT.begin(), SchemaConstant::KEYWORD_MODE_STRICT.end());
95 return resStr;
96 }
97 return oriSchemaStr;
98 }
SchemaChecker(const std::string & schema)99 bool SchemaChecker(const std::string &schema)
100 {
101 SchemaObject schemaObj;
102 return (schemaObj.ParseFromSchemaString(schema) == E_OK);
103 }
ToVec(const std::string & inStr)104 std::vector<uint8_t> ToVec(const std::string &inStr)
105 {
106 std::vector<uint8_t> outVec(inStr.begin(), inStr.end());
107 return outVec;
108 }
ToStr(const std::vector<uint8_t> & inVec)109 std::string ToStr(const std::vector<uint8_t> &inVec)
110 {
111 std::string outStr(inVec.begin(), inVec.end());
112 return outStr;
113 }
114
115 const std::map<std::string, std::vector<uint8_t>> VALUE_MAP {
116 {"LACK", ToVec(StringReplacer(VALUE_BASE_LACK, "LONG_VAL", "1"))},
117 {"BASE", ToVec(StringReplacer(StringReplacer(VALUE_BASE, "LONG_VAL", "2"), "STR_VAL", "\"OS\""))},
118 {"FULL", ToVec(StringReplacer(StringReplacer(StringReplacer(VALUE_FIELD_FULL, "LONG_VAL", "3"), "STR_VAL",
119 "\"TEST\""), "INT_VAL", "33"))},
120 {"BASE_WRONG_TYPE", ToVec(StringReplacer(StringReplacer(VALUE_BASE, "LONG_VAL", "2"), "STR_VAL", "10086"))},
121 {"FULL_NULL", ToVec(StringReplacer(StringReplacer(StringReplacer(VALUE_FIELD_FULL, "LONG_VAL", "3"),
122 "STR_VAL", "\"TEST\""), "INT_VAL", "null"))},
123 {"FULL_WRONG_TYPE", ToVec(StringReplacer(StringReplacer(StringReplacer(VALUE_FIELD_FULL, "LONG_VAL", "3"),
124 "STR_VAL", "\"TEST\""), "INT_VAL", "\"UT\""))},
125 };
126
127 // Key begin from "KEY_1"
InsertPresetEntry(KvStoreNbDelegate & delegate,const std::vector<std::string> & selection)128 void InsertPresetEntry(KvStoreNbDelegate &delegate, const std::vector<std::string> &selection)
129 {
130 int count = 0;
131 for (const auto &eachSel : selection) {
132 ASSERT_NE(VALUE_MAP.count(eachSel), 0ul);
133 DBStatus ret = delegate.Put(ToVec(std::string("KEY_") + std::to_string(++count)), VALUE_MAP.at(eachSel));
134 ASSERT_EQ(ret, OK);
135 }
136 }
137 }
138
139 class DistributedDBInterfacesSchemaDatabaseUpgradeTest : public testing::Test {
140 public:
141 static void SetUpTestCase(void);
142 static void TearDownTestCase(void);
143 void SetUp();
144 void TearDown();
145 };
146
SetUpTestCase(void)147 void DistributedDBInterfacesSchemaDatabaseUpgradeTest::SetUpTestCase(void)
148 {
149 DistributedDBToolsUnitTest::TestDirInit(g_testDir);
150 KvStoreConfig config{g_testDir};
151 g_manager.SetKvStoreConfig(config);
152 ASSERT_EQ(SchemaChecker(SCHEMA_BASE), true);
153 ASSERT_EQ(SchemaChecker(SCHEMA_INC_FIELD), true);
154 ASSERT_EQ(SchemaChecker(SCHEMA_INC_FIELD_NOTNULL), true);
155 ASSERT_EQ(SchemaChecker(SCHEMA_INC_FIELD_DEFAULT), true);
156 ASSERT_EQ(SchemaChecker(SCHEMA_INC_FIELD_NOTNULL_DEFAULT), true);
157 }
158
TearDownTestCase(void)159 void DistributedDBInterfacesSchemaDatabaseUpgradeTest::TearDownTestCase(void)
160 {
161 if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) {
162 LOGE("[TestSchemaUpgrade] Remove test directory error.");
163 }
164 }
165
SetUp(void)166 void DistributedDBInterfacesSchemaDatabaseUpgradeTest::SetUp(void)
167 {
168 DistributedDBToolsUnitTest::PrintTestCaseInfo();
169 g_kvDelegateStatus = INVALID_ARGS;
170 g_kvDelegatePtr = nullptr;
171 }
172
TearDown(void)173 void DistributedDBInterfacesSchemaDatabaseUpgradeTest::TearDown(void)
174 {
175 if (g_kvDelegatePtr != nullptr) {
176 ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK);
177 g_kvDelegatePtr = nullptr;
178 }
179 }
180
181 /**
182 * @tc.name: UpgradeFromKv001
183 * @tc.desc: Schema database upgrade from kv database, exist value match compatible schema(mismatch strict schema)
184 * @tc.type: FUNC
185 * @tc.require: AR000F3OPD
186 * @tc.author: xiaozhenjian
187 */
188 HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromKv001, TestSize.Level1)
189 {
190 /**
191 * @tc.steps: step1. Prepare kv database with value match compatible schema(mismatch strict schema) then close
192 * @tc.expected: step1. E_OK
193 */
194 std::string storeId = "UpgradeFromKv001";
195 KvStoreNbDelegate::Option option;
196 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
197 ASSERT_TRUE(g_kvDelegatePtr != nullptr);
198 ASSERT_EQ(g_kvDelegateStatus, OK);
199
200 InsertPresetEntry(*g_kvDelegatePtr, std::vector<std::string>{"LACK", "BASE", "LACK", "BASE", "FULL"});
201 DBStatus ret = g_kvDelegatePtr->Delete(ToVec("KEY_4"));
202 ASSERT_EQ(ret, OK);
203 ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK);
204 g_kvDelegatePtr = nullptr;
205
206 /**
207 * @tc.steps: step2. Upgrade to schema(strict) database
208 * @tc.expected: step2. SCHEMA_VIOLATE_VALUE
209 */
210 option.schema = SchemaSwitchMode(SCHEMA_BASE);
211 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
212 ASSERT_TRUE(g_kvDelegatePtr == nullptr);
213 ASSERT_EQ(g_kvDelegateStatus, SCHEMA_VIOLATE_VALUE);
214
215 /**
216 * @tc.steps: step3. Upgrade to schema(compatible) database
217 * @tc.expected: step3. E_OK
218 */
219 option.schema = SCHEMA_BASE;
220 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
221 ASSERT_TRUE(g_kvDelegatePtr != nullptr);
222 ASSERT_EQ(g_kvDelegateStatus, OK);
223
224 /**
225 * @tc.steps: step4. Query field_2.field_3 equal OpenHarmony
226 * @tc.expected: step4. E_OK, KEY_1
227 */
228 Query query = Query::Select().EqualTo("field_2.field_3", "OpenHarmony");
229 std::vector<Entry> entries;
230 EXPECT_EQ(g_kvDelegatePtr->GetEntries(query, entries), OK);
231 ASSERT_EQ(entries.size(), 2ul);
232 EXPECT_EQ(ToStr(entries[0].key), std::string("KEY_1"));
233 EXPECT_EQ(ToStr(entries[1].key), std::string("KEY_3"));
234 std::string valStr = ToStr(entries[0].value);
235 std::string defaultVal = "OpenHarmony";
236 auto iter = std::search(valStr.begin(), valStr.end(), defaultVal.begin(), defaultVal.end());
237 EXPECT_TRUE(iter != valStr.end());
238 }
239
240 /**
241 * @tc.name: UpgradeFromKv002
242 * @tc.desc: Schema database upgrade from kv database, exist value mismatch compatible schema
243 * @tc.type: FUNC
244 * @tc.require: AR000F3OPD
245 * @tc.author: xiaozhenjian
246 */
247 HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromKv002, TestSize.Level1)
248 {
249 /**
250 * @tc.steps: step1. Prepare kv database with value mismatch compatible schema then close
251 * @tc.expected: step1. E_OK
252 */
253 std::string storeId = "UpgradeFromKv002";
254 KvStoreNbDelegate::Option option;
255 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
256 ASSERT_TRUE(g_kvDelegatePtr != nullptr);
257 ASSERT_EQ(g_kvDelegateStatus, OK);
258
259 InsertPresetEntry(*g_kvDelegatePtr, std::vector<std::string>{"BASE_WRONG_TYPE"});
260 ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK);
261 g_kvDelegatePtr = nullptr;
262
263 /**
264 * @tc.steps: step2. Upgrade to schema(compatible) database
265 * @tc.expected: step2. SCHEMA_VIOLATE_VALUE
266 */
267 option.schema = SCHEMA_BASE;
268 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
269 ASSERT_TRUE(g_kvDelegatePtr == nullptr);
270 ASSERT_EQ(g_kvDelegateStatus, SCHEMA_VIOLATE_VALUE);
271 }
272
273 namespace {
TestUpgradeFromSchema(const std::string & storeId,const std::vector<std::string> & selection,const std::string & newSchema,bool expectMatch,uint32_t expectCount)274 void TestUpgradeFromSchema(const std::string &storeId, const std::vector<std::string> &selection,
275 const std::string &newSchema, bool expectMatch, uint32_t expectCount)
276 {
277 LOGI("[TestUpgradeFromSchema] StoreId=%s", storeId.c_str());
278 /**
279 * @tc.steps: step1. Prepare kv database with value then close
280 * @tc.expected: step1. E_OK
281 */
282 KvStoreNbDelegate::Option option;
283 option.schema = g_baseSchema;
284 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
285 ASSERT_TRUE(g_kvDelegatePtr != nullptr);
286 ASSERT_EQ(g_kvDelegateStatus, OK);
287
288 InsertPresetEntry(*g_kvDelegatePtr, selection);
289 ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK);
290 g_kvDelegatePtr = nullptr;
291
292 /**
293 * @tc.steps: step2. Upgrade to schema database
294 * @tc.expected: step2. OK or SCHEMA_VIOLATE_VALUE
295 */
296 option.schema = newSchema;
297 g_manager.GetKvStore(storeId, option, g_kvDelegateCallback);
298 if (expectMatch) {
299 ASSERT_TRUE(g_kvDelegatePtr != nullptr);
300 ASSERT_EQ(g_kvDelegateStatus, OK);
301 } else {
302 ASSERT_TRUE(g_kvDelegatePtr == nullptr);
303 ASSERT_EQ(g_kvDelegateStatus, g_expectError);
304 }
305
306 /**
307 * @tc.steps: step3. Query field_2.field_3
308 * @tc.expected: step3. E_OK
309 */
310 if (expectMatch) {
311 Query query = Query::Select().EqualTo("field_2.field_4", 88); // 88 is the default value in the testcase.
312 std::vector<Entry> entries;
313 if (expectCount == 0) {
314 EXPECT_EQ(g_kvDelegatePtr->GetEntries(query, entries), NOT_FOUND);
315 } else {
316 ASSERT_EQ(g_kvDelegatePtr->GetEntries(query, entries), OK);
317 EXPECT_EQ(entries.size(), expectCount);
318 }
319 ASSERT_EQ(g_manager.CloseKvStore(g_kvDelegatePtr), OK);
320 g_kvDelegatePtr = nullptr;
321 }
322 }
323 }
324
325 /**
326 * @tc.name: UpgradeFromSchema001
327 * @tc.desc: Schema database upgrade from kv database, exist value match new schema
328 * @tc.type: FUNC
329 * @tc.require: AR000F3OPD
330 * @tc.author: xiaozhenjian
331 */
332 HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromSchema001, TestSize.Level1)
333 {
334 g_baseSchema = SCHEMA_BASE;
335 g_expectError = SCHEMA_VIOLATE_VALUE;
336 TestUpgradeFromSchema("UpgradeFromSchema001_1", std::vector<std::string>{"LACK", "BASE", "FULL", "FULL_NULL"},
337 SCHEMA_INC_FIELD, true, 0);
338 TestUpgradeFromSchema("UpgradeFromSchema001_2", std::vector<std::string>{"LACK", "BASE", "FULL", "FULL_NULL"},
339 SCHEMA_INC_FIELD_DEFAULT, true, 2);
340 TestUpgradeFromSchema("UpgradeFromSchema001_3", std::vector<std::string>{"LACK", "BASE", "FULL"},
341 SCHEMA_INC_FIELD_NOTNULL_DEFAULT, true, 2);
342 }
343
344 /**
345 * @tc.name: UpgradeFromSchema002
346 * @tc.desc: Schema database upgrade from kv database, exist value mismatch new schema
347 * @tc.type: FUNC
348 * @tc.require: AR000F3OPD
349 * @tc.author: xiaozhenjian
350 */
351 HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromSchema002, TestSize.Level1)
352 {
353 g_baseSchema = SCHEMA_BASE;
354 g_expectError = SCHEMA_VIOLATE_VALUE;
355 TestUpgradeFromSchema("UpgradeFromSchema002_1", std::vector<std::string>{"LACK", "BASE", "FULL", "FULL_WRONG_TYPE"},
356 SCHEMA_INC_FIELD, false, 0);
357 TestUpgradeFromSchema("UpgradeFromSchema002_2", std::vector<std::string>{"LACK", "BASE", "FULL", "FULL_WRONG_TYPE"},
358 SCHEMA_INC_FIELD_DEFAULT, false, 0);
359 TestUpgradeFromSchema("UpgradeFromSchema002_3", std::vector<std::string>{"LACK", "BASE", "FULL", "FULL_NULL"},
360 SCHEMA_INC_FIELD_NOTNULL_DEFAULT, false, 0);
361 }
362
363 /**
364 * @tc.name: UpgradeFromSchema003
365 * @tc.desc: Schema database upgrade from kv database, new schema incompatible with old schema
366 * @tc.type: FUNC
367 * @tc.require: AR000F3OPD
368 * @tc.author: xiaozhenjian
369 */
370 HWTEST_F(DistributedDBInterfacesSchemaDatabaseUpgradeTest, UpgradeFromSchema003, TestSize.Level1)
371 {
372 // Compatible schema can increase field, but must not be null without default.
373 g_baseSchema = SCHEMA_BASE;
374 g_expectError = SCHEMA_MISMATCH;
375 TestUpgradeFromSchema("UpgradeFromSchema003_1", std::vector<std::string>{"LACK", "BASE", "FULL", "FULL_NULL"},
376 SCHEMA_INC_FIELD_NOTNULL, false, 0);
377 // Strict schema can not incrase field
378 g_baseSchema = SchemaSwitchMode(SCHEMA_BASE);
379 TestUpgradeFromSchema("UpgradeFromSchema003_2", std::vector<std::string>{"LACK", "BASE"},
380 SchemaSwitchMode(SCHEMA_INC_FIELD), false, 0);
381 }
382 #endif