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