// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include #include #include #include using namespace std; using namespace isc; using namespace isc::data; using namespace isc::yang; namespace { // Test that get context works as expected i.e. moves a comment into // the user context creating it if not exists. TEST(AdaptorTest, getContext) { // Empty. string config = "{\n" "}\n"; ElementPtr json = Element::fromJSON(config); ConstElementPtr context; ASSERT_NO_THROW_LOG(context = Adaptor::getContext(json)); EXPECT_FALSE(context); // No relevant. config = "{\n" " \"foo\": 1\n" "}\n"; json = Element::fromJSON(config); ASSERT_NO_THROW_LOG(context = Adaptor::getContext(json)); EXPECT_FALSE(context); // User context. config = "{\n" " \"foo\": 1,\n" " \"user-context\": { \"bar\": 2 }\n" "}\n"; json = Element::fromJSON(config); ASSERT_NO_THROW_LOG(context = Adaptor::getContext(json)); ASSERT_TRUE(context); EXPECT_EQ("{ \"bar\": 2 }", context->str()); // Comment. config = "{\n" " \"foo\": 1,\n" " \"comment\": \"a comment\"\n" "}\n"; json = Element::fromJSON(config); ASSERT_NO_THROW_LOG(context = Adaptor::getContext(json)); ASSERT_TRUE(context); EXPECT_EQ("{ \"comment\": \"a comment\" }", context->str()); // User context and comment. config = "{\n" " \"foo\": 1,\n" " \"user-context\": { \"bar\": 2 },\n" " \"comment\": \"a comment\"\n" "}\n"; json = Element::fromJSON(config); ASSERT_NO_THROW_LOG(context = Adaptor::getContext(json)); ASSERT_TRUE(context); EXPECT_EQ("{ \"bar\": 2, \"comment\": \"a comment\" }", context->str()); // User context with conflicting comment and comment. config = "{\n" " \"foo\": 1,\n" " \"user-context\": {\n" " \"bar\": 2,\n" " \"comment\": \"conflicting\"\n" " },\n" " \"comment\": \"a comment\"\n" "}\n"; json = Element::fromJSON(config); ASSERT_NO_THROW_LOG(context = Adaptor::getContext(json)); ASSERT_TRUE(context); EXPECT_EQ("{ \"bar\": 2, \"comment\": \"a comment\" }", context->str()); } // Test that fromParent works as expected i.e. moves parameters from the // parent to children and not overwrite them. TEST(AdaptorTest, fromParent) { string config = "{\n" " \"param1\": 123,\n" " \"param2\": \"foo\",\n" " \"list\": [\n" " {\n" " \"param1\": 234\n" " },{\n" " \"another\": \"entry\"\n" " }\n" " ]\n" "}\n"; ElementPtr json = Element::fromJSON(config); EXPECT_NO_THROW_LOG(Adaptor::fromParent("param1", json, json->get("list"))); EXPECT_NO_THROW_LOG(Adaptor::fromParent("param2", json, json->get("list"))); EXPECT_NO_THROW_LOG(Adaptor::fromParent("param3", json, json->get("list"))); string expected = "{\n" " \"param1\": 123,\n" " \"param2\": \"foo\",\n" " \"list\": [\n" " {\n" " \"param1\": 234,\n" " \"param2\": \"foo\"\n" " },{\n" " \"another\": \"entry\",\n" " \"param1\": 123,\n" " \"param2\": \"foo\"\n" " }\n" " ]\n" "}\n"; EXPECT_TRUE(json->equals(*Element::fromJSON(expected))); } // Test that toParent works as expected i.e. moves parameters from children // to the parent throwing when a value differs between two children. TEST(AdaptorTest, toParent) { string config = "{\n" " \"list\": [\n" " {\n" " \"param2\": \"foo\",\n" " \"param3\": 234,\n" " \"param4\": true\n" " },{\n" " \"another\": \"entry\",\n" " \"param2\": \"foo\",\n" " \"param3\": 123,\n" " \"param5\": false\n" " }\n" " ]\n" "}\n"; ElementPtr json = Element::fromJSON(config); EXPECT_NO_THROW_LOG(Adaptor::toParent("param1", json, json->get("list"))); EXPECT_TRUE(json->equals(*Element::fromJSON(config))); string expected = "{\n" " \"param2\": \"foo\",\n" " \"list\": [\n" " {\n" " \"param3\": 234,\n" " \"param4\": true\n" " },{\n" " \"another\": \"entry\",\n" " \"param3\": 123,\n" " \"param5\": false\n" " }\n" " ]\n" "}\n"; EXPECT_NO_THROW_LOG(Adaptor::toParent("param2",json, json->get("list"))); EXPECT_TRUE(json->equals(*Element::fromJSON(expected))); // param[345] have different values so it should throw. EXPECT_THROW_MSG(Adaptor::toParent("param3", json, json->get("list")), BadValue, "inconsistent value of param3 in [ { \"param3\": 234, \"param4\": true }, { " "\"another\": \"entry\", \"param3\": 123, \"param5\": false } ]"); EXPECT_THROW_MSG(Adaptor::toParent("param4", json, json->get("list")), BadValue, "inconsistent value of param4 in [ { \"param3\": 234, \"param4\": true }, { " "\"another\": \"entry\", \"param3\": 123, \"param5\": false } ]"); EXPECT_THROW_MSG(Adaptor::toParent("param5", json, json->get("list")), BadValue, "inconsistent value of param5 in [ { \"param3\": 234, \"param4\": true }, { " "\"another\": \"entry\", \"param3\": 123, \"param5\": false } ]"); // And not modify the value. EXPECT_TRUE(json->equals(*Element::fromJSON(expected))); } // Test for modify (maps & insert). TEST(AdaptorTest, modifyMapInsert) { string config = "{\n" " \"foo\": {\n" " \"bar\": {\n" "}}}\n"; ElementPtr json; ASSERT_NO_THROW_LOG(json = Element::fromJSON(config)); string spath = "[ \"foo\", \"bar\" ]"; ConstElementPtr path; ASSERT_NO_THROW_LOG(path = Element::fromJSON(spath)); string sactions = "[\n" "{\n" " \"action\": \"insert\",\n" " \"key\": \"test\",\n" " \"value\": 1234\n" "}]\n"; ConstElementPtr actions; ASSERT_NO_THROW_LOG(actions = Element::fromJSON(sactions)); string result = "{\n" " \"foo\": {\n" " \"bar\": {\n" " \"test\": 1234\n" "}}}\n"; ConstElementPtr expected; ASSERT_NO_THROW_LOG(expected = Element::fromJSON(result)); ASSERT_NO_THROW_LOG(Adaptor::modify(path, actions, json)); EXPECT_TRUE(expected->equals(*json)); } // Test for modify (maps & replace). TEST(AdaptorTest, modifyMapReplace) { string config = "{\n" " \"foo\": {\n" " \"bar\": {\n" " \"test1\": 1234,\n" " \"test2\": 1234\n" "}}}\n"; ElementPtr json; ASSERT_NO_THROW_LOG(json = Element::fromJSON(config)); string spath = "[ \"foo\", \"bar\" ]"; ConstElementPtr path; ASSERT_NO_THROW_LOG(path = Element::fromJSON(spath)); string sactions = "[\n" "{\n" " \"action\": \"insert\",\n" " \"key\": \"test1\",\n" " \"value\": 5678\n" "},{\n" " \"action\": \"replace\",\n" " \"key\": \"test2\",\n" " \"value\": 5678\n" "}]\n"; ConstElementPtr actions; ASSERT_NO_THROW_LOG(actions = Element::fromJSON(sactions)); string result = "{\n" " \"foo\": {\n" " \"bar\": {\n" " \"test1\": 1234,\n" " \"test2\": 5678\n" "}}}\n"; ConstElementPtr expected; ASSERT_NO_THROW_LOG(expected = Element::fromJSON(result)); ASSERT_NO_THROW_LOG(Adaptor::modify(path, actions, json)); EXPECT_TRUE(expected->equals(*json)); } // Test for modify (maps & delete). TEST(AdaptorTest, modifyMapDelete) { string config = "{\n" " \"foo\": {\n" " \"bar\": {\n" " \"test\": 1234\n" "}}}\n"; ElementPtr json; ASSERT_NO_THROW_LOG(json = Element::fromJSON(config)); string spath = "[ \"foo\", \"bar\" ]"; ConstElementPtr path; ASSERT_NO_THROW_LOG(path = Element::fromJSON(spath)); string sactions = "[\n" "{\n" " \"action\": \"delete\",\n" " \"key\": \"test\"\n" "}]\n"; ConstElementPtr actions; ASSERT_NO_THROW_LOG(actions = Element::fromJSON(sactions)); string result = "{\n" " \"foo\": {\n" " \"bar\": {\n" "}}}\n"; ConstElementPtr expected; ASSERT_NO_THROW_LOG(expected = Element::fromJSON(result)); ASSERT_NO_THROW_LOG(Adaptor::modify(path, actions, json)); EXPECT_TRUE(expected->equals(*json)); } // Test for modify (lists & insert). TEST(AdaptorTest, modifyListInsert) { string config = "[\n" "[{\n" " \"foo\": \"bar\"\n" "}]]\n"; ElementPtr json; ASSERT_NO_THROW_LOG(json = Element::fromJSON(config)); string spath = "[ 0, { \"key\": \"foo\", \"value\": \"bar\" }]"; ConstElementPtr path; ASSERT_NO_THROW_LOG(path = Element::fromJSON(spath)); string sactions = "[\n" "{\n" " \"action\": \"insert\",\n" " \"key\": \"test\",\n" " \"value\": 1234\n" "}]\n"; ConstElementPtr actions; ASSERT_NO_THROW_LOG(actions = Element::fromJSON(sactions)); string result = "[\n" "[{\n" " \"foo\": \"bar\",\n" " \"test\": 1234\n" "}]]\n"; ConstElementPtr expected; ASSERT_NO_THROW_LOG(expected = Element::fromJSON(result)); ASSERT_NO_THROW_LOG(Adaptor::modify(path, actions, json)); EXPECT_TRUE(expected->equals(*json)); } // Test for modify (list all & insert). TEST(AdaptorTest, modifyListAllInsert) { string config = "[\n" "{},\n" "{},\n" "{ \"test\": 1234 },\n" "]\n"; ElementPtr json; ASSERT_NO_THROW_LOG(json = Element::fromJSON(config)); string spath = "[ -1 ]"; ConstElementPtr path; ASSERT_NO_THROW_LOG(path = Element::fromJSON(spath)); string sactions = "[\n" "{\n" " \"action\": \"insert\",\n" " \"key\": \"test\",\n" " \"value\": 5678\n" "}]\n"; ConstElementPtr actions; ASSERT_NO_THROW_LOG(actions = Element::fromJSON(sactions)); string result = "[\n" "{ \"test\": 5678 },\n" "{ \"test\": 5678 },\n" "{ \"test\": 1234 }\n" "]\n"; ConstElementPtr expected; ASSERT_NO_THROW_LOG(expected = Element::fromJSON(result)); ASSERT_NO_THROW_LOG(Adaptor::modify(path, actions, json)); EXPECT_TRUE(expected->equals(*json)); } TEST(AdaptorTest, modifyListDelete) { string config = "[[\n" "{\n" " \"foo\": \"bar\"\n" "},{\n" "},[\n" "0, 1, 2, 3\n" "]]]\n"; ElementPtr json; ASSERT_NO_THROW_LOG(json = Element::fromJSON(config)); string spath = "[ 0 ]"; ConstElementPtr path; ASSERT_NO_THROW_LOG(path = Element::fromJSON(spath)); // Put the positional first as it applies after previous actions... string sactions = "[\n" "{\n" " \"action\": \"delete\",\n" " \"key\": 2\n" "},{\n" " \"action\": \"delete\",\n" " \"key\": { \"key\": \"foo\", \"value\": \"bar\" }\n" "}]\n"; ConstElementPtr actions; ASSERT_NO_THROW_LOG(actions = Element::fromJSON(sactions)); string result = "[[{}]]\n"; ConstElementPtr expected; ASSERT_NO_THROW_LOG(expected = Element::fromJSON(result)); ASSERT_NO_THROW_LOG(Adaptor::modify(path, actions, json)); EXPECT_TRUE(expected->equals(*json)); } TEST(AdaptorTest, modifyListAllDelete) { string config = "[[\n" "{\n" " \"foo\": \"bar\"\n" "},{\n" "},[\n" "0, 1, 2, 3\n" "]]]\n"; ElementPtr json; ASSERT_NO_THROW_LOG(json = Element::fromJSON(config)); // The main change from the previous unit test is key 0 -> -1 so // modify() applies the delete to all elements vs only the first one. string spath = "[ -1 ]"; ConstElementPtr path; ASSERT_NO_THROW_LOG(path = Element::fromJSON(spath)); // Put the positional first as it applies after previous actions... string sactions = "[\n" "{\n" " \"action\": \"delete\",\n" " \"key\": 2\n" "},{\n" " \"action\": \"delete\",\n" " \"key\": { \"key\": \"foo\", \"value\": \"bar\" }\n" "}]\n"; ConstElementPtr actions; ASSERT_NO_THROW_LOG(actions = Element::fromJSON(sactions)); string result = "[[{}]]\n"; ConstElementPtr expected; ASSERT_NO_THROW_LOG(expected = Element::fromJSON(result)); ASSERT_NO_THROW_LOG(Adaptor::modify(path, actions, json)); EXPECT_TRUE(expected->equals(*json)); } } // anonymous namespace