1 // Copyright (C) 2009-2020 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <gtest/gtest.h>
10 
11 #include <cc/command_interpreter.h>
12 #include <config/tests/data_def_unittests_config.h>
13 #include <log/logger_name.h>
14 
15 #include <boost/scoped_ptr.hpp>
16 
17 #include <fstream>
18 
19 using namespace isc::data;
20 using namespace isc::config;
21 using namespace std;
22 
23 namespace {
24 
25 
26 /// @brief Convenience method for creating elements from JSON string
27 ///
28 /// @param str string to be converted
29 /// @return Element structure
30 ElementPtr
el(const std::string & str)31 el(const std::string& str) {
32     return (Element::fromJSON(str));
33 }
34 
35 // This test verifies that that createAnswer method is able to generate
36 // various answers.
TEST(CommandInterpreterTest,createAnswer)37 TEST(CommandInterpreterTest, createAnswer) {
38     ConstElementPtr answer;
39 
40     // By default the answer is a successful one.
41     answer = createAnswer();
42     EXPECT_EQ("{ \"result\": 0 }", answer->str());
43 
44     // Let's check if we can generate an error.
45     answer = createAnswer(1, "error");
46     EXPECT_EQ("{ \"result\": 1, \"text\": \"error\" }", answer->str());
47 
48     // This is expected to throw. When status code is non-zero (indicating error),
49     // textual explanation is mandatory.
50     EXPECT_THROW(createAnswer(1, ElementPtr()), CtrlChannelError);
51     EXPECT_THROW(createAnswer(1, Element::create(1)), CtrlChannelError);
52 
53     // Let's check if answer can be generate with some data in it.
54     ConstElementPtr arg = el("[ \"just\", \"some\", \"data\" ]");
55     answer = createAnswer(0, arg);
56     EXPECT_EQ("{ \"arguments\": [ \"just\", \"some\", \"data\" ], \"result\": 0 }",
57               answer->str());
58 }
59 
60 // This test checks whether parseAnswer is able to handle good and malformed
61 // answers.
TEST(CommandInterpreterTest,parseAnswer)62 TEST(CommandInterpreterTest, parseAnswer) {
63     ConstElementPtr answer;
64     ConstElementPtr arg;
65     int rcode;
66 
67     EXPECT_THROW(parseAnswer(rcode, ElementPtr()), CtrlChannelError);
68     EXPECT_THROW(parseAnswer(rcode, el("1")), CtrlChannelError);
69     EXPECT_THROW(parseAnswer(rcode, el("[]")), CtrlChannelError);
70     EXPECT_THROW(parseAnswer(rcode, el("{  }")), CtrlChannelError);
71     EXPECT_THROW(parseAnswer(rcode, el("{ \"something\": 1 }")), CtrlChannelError);
72     EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 0 ] }")), CtrlChannelError);
73     EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 1 ] }")), CtrlChannelError);
74     EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 1, 1 ] }")), CtrlChannelError);
75 
76     answer = el("{ \"result\": 0 }");
77     arg = parseAnswer(rcode, answer);
78     EXPECT_EQ(0, rcode);
79     EXPECT_TRUE(isNull(arg));
80 
81     answer = el("{ \"result\": 1, \"text\": \"error\" }");
82     arg = parseAnswer(rcode, answer);
83     EXPECT_EQ(1, rcode);
84     EXPECT_EQ("error", arg->stringValue());
85 
86     answer = el("{ \"result\": 0, \"arguments\": [ \"just\", \"some\", \"data\" ] }");
87     arg = parseAnswer(rcode, answer);
88     EXPECT_EQ(0, rcode);
89     EXPECT_EQ("[ \"just\", \"some\", \"data\" ]", arg->str());
90 }
91 
92 // This checks whether we can convert an answer to easily printable form.
TEST(CommandInterpreterTest,answerToText)93 TEST(CommandInterpreterTest, answerToText) {
94     ConstElementPtr answer;
95 
96     // Doing jolly good here.
97     answer = el("{ \"result\": 0 }");
98     EXPECT_EQ("success(0)", answerToText(answer));
99 
100     // Sometimes things don't go according to plan.
101     answer = el("{ \"result\": 1, \"text\": \"ho lee fuk sum ting wong\" }");
102     EXPECT_EQ("failure(1), text=ho lee fuk sum ting wong", answerToText(answer));
103 }
104 
105 // This test checks whether createCommand function is able to create commands
106 // with and without parameters.
TEST(CommandInterpreterTest,createCommand)107 TEST(CommandInterpreterTest, createCommand) {
108     ConstElementPtr command;
109     ConstElementPtr arg;
110     string service;
111 
112     command = createCommand("my_command");
113     ASSERT_EQ("{ \"command\": \"my_command\" }", command->str());
114 
115     arg = el("1");
116     command = createCommand("my_command", arg);
117     ASSERT_EQ("{ \"arguments\": 1, \"command\": \"my_command\" }",
118               command->str());
119 
120     arg = el("[ \"a\", \"b\" ]");
121     command = createCommand("my_cmd", arg);
122     ASSERT_EQ("{ \"arguments\": [ \"a\", \"b\" ], \"command\": \"my_cmd\" }",
123               command->str());
124 
125     arg = el("{ \"a\": \"map\" }");
126     command = createCommand("foo", arg);
127     ASSERT_EQ("{ \"arguments\": { \"a\": \"map\" }, \"command\": \"foo\" }",
128               command->str());
129 
130     command = createCommand("my_command", "my_service");
131     ASSERT_EQ("{ \"command\": \"my_command\", "
132               "\"service\": [ \"my_service\" ] }",
133               command->str());
134 
135     arg = el("1");
136     command = createCommand("my_command", arg, "my_service");
137     ASSERT_EQ("{ \"arguments\": 1, \"command\": \"my_command\", "
138               "\"service\": [ \"my_service\" ] }",
139               command->str());
140 
141     arg = el("[ \"a\", \"b\" ]");
142     command = createCommand("my_cmd", arg, "my_server");
143     ASSERT_EQ("{ \"arguments\": [ \"a\", \"b\" ], "
144               "\"command\": \"my_cmd\", "
145               "\"service\": [ \"my_server\" ] }",
146               command->str());
147 
148     arg = el("{ \"a\": \"map\" }");
149     command = createCommand("foo", arg, "bar");
150     ASSERT_EQ("{ \"arguments\": { \"a\": \"map\" }, "
151               "\"command\": \"foo\", "
152               "\"service\": [ \"bar\" ] }",
153               command->str());
154 }
155 
156 // This test checks whether parseCommand function is able to parse various valid
157 // and malformed commands.
TEST(CommandInterpreterTest,parseCommand)158 TEST(CommandInterpreterTest, parseCommand) {
159     ConstElementPtr arg;
160     std::string cmd;
161 
162     // should throw
163     EXPECT_THROW(parseCommand(arg, ElementPtr()), CtrlChannelError);
164     EXPECT_THROW(parseCommand(arg, el("1")), CtrlChannelError);
165     EXPECT_THROW(parseCommand(arg, el("{  }")), CtrlChannelError);
166     EXPECT_THROW(parseCommand(arg, el("{ \"not a command\": 1 }")), CtrlChannelError);
167     EXPECT_THROW(parseCommand(arg, el("{ \"command\": 1 }")), CtrlChannelError);
168     EXPECT_THROW(parseCommand(arg, el("{ \"command\": [] }")), CtrlChannelError);
169     EXPECT_THROW(parseCommand(arg, el("{ \"command\": [ 1 ] }")), CtrlChannelError);
170     EXPECT_THROW(parseCommand(arg, el("{ \"command\": \"my_command\", "
171                                       "\"unknown\": \"xyz\" }")), CtrlChannelError);
172 
173     cmd = parseCommand(arg, el("{ \"command\": \"my_command\" }"));
174     EXPECT_EQ("my_command", cmd);
175     EXPECT_FALSE(arg);
176 
177     // Include "service" to verify that it is not rejected.
178     cmd = parseCommand(arg, el("{ \"command\": \"my_command\", \"arguments\": 1, "
179                                "  \"service\": [ \"dhcp4\" ] }"));
180     ASSERT_TRUE(arg);
181     EXPECT_EQ("my_command", cmd);
182     EXPECT_EQ("1", arg->str());
183 
184     parseCommand(arg, el("{ \"command\": \"my_command\", \"arguments\": "
185                          "[ \"some\", \"argument\", \"list\" ] }"));
186     EXPECT_EQ("my_command", cmd);
187     ASSERT_TRUE(arg);
188     EXPECT_EQ("[ \"some\", \"argument\", \"list\" ]", arg->str());
189 
190 }
191 
192 // This test checks whether parseCommandWithArgs function is able to parse
193 // various valid and malformed commands.
TEST(CommandInterpreterTest,parseCommandWithArgs)194 TEST(CommandInterpreterTest, parseCommandWithArgs) {
195     ConstElementPtr arg;
196     std::string cmd;
197 
198     // Arguments are required.
199     EXPECT_THROW(parseCommandWithArgs(arg, el("{ \"command\": \"my_command\" }")),
200                  CtrlChannelError);
201 
202     // Arguments must be a map.
203     EXPECT_THROW(parseCommandWithArgs(arg, el("{ \"command\": \"my_command\", "
204                                               "\"arguments\": [ 1, 2, 3 ] }")),
205                  CtrlChannelError);
206 
207     // Arguments must not be empty.
208     EXPECT_THROW(parseCommandWithArgs(arg, el("{ \"command\": \"my_command\", "
209                                               "\"arguments\": { } }")),
210                  CtrlChannelError);
211 
212     // Command with unsupported parameter is rejected.
213     EXPECT_THROW(parseCommandWithArgs(arg, el("{ \"command\": \"my_command\", "
214                                               "  \"arguments\": { \"arg1\": \"value1\" }, "
215                                               "  \"unsupported\": 1 }")),
216                  CtrlChannelError);
217 
218 
219     // Specifying arguments in non empty map should be successful.
220     EXPECT_NO_THROW(
221         cmd = parseCommandWithArgs(arg, el("{ \"command\": \"my_command\", "
222                                            "  \"arguments\": { \"arg1\": \"value1\" } }"))
223     );
224     ASSERT_TRUE(arg);
225     ASSERT_EQ(Element::map, arg->getType());
226     auto arg1 = arg->get("arg1");
227     ASSERT_TRUE(arg1);
228     ASSERT_EQ(Element::string, arg1->getType());
229     EXPECT_EQ("value1", arg1->stringValue());
230     EXPECT_EQ("my_command", cmd);
231 
232     // The "service" parameter should be allowed.
233     EXPECT_NO_THROW(parseCommandWithArgs(arg, el("{ \"command\": \"my_command\", "
234                                                  "  \"service\": [ \"dhcp4\" ], "
235                                                  "  \"arguments\": { \"arg1\": \"value1\" } }"))
236     );
237 
238 }
239 
240 }
241