• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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