blob: 531a56125f3ca13e235a5d5f180c808879333b55 [file] [log] [blame]
// Copyright 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/commands/command_dictionary.h"
#include <gtest/gtest.h>
#include "src/commands/unittest_utils.h"
namespace weave {
using test::CreateDictionaryValue;
using test::IsEqualValue;
TEST(CommandDictionary, Empty) {
CommandDictionary dict;
EXPECT_TRUE(dict.IsEmpty());
EXPECT_EQ(nullptr, dict.FindCommand("robot.jump"));
}
TEST(CommandDictionary, LoadCommands) {
auto json = CreateDictionaryValue(R"({
'robot': {
'jump': {
'parameters': {
'height': 'integer',
'_jumpType': ['_withAirFlip', '_withSpin', '_withKick']
},
'progress': {
'progress': 'integer'
},
'results': {}
}
}
})");
CommandDictionary dict;
EXPECT_TRUE(dict.LoadCommands(*json, nullptr, nullptr));
EXPECT_EQ(1, dict.GetSize());
EXPECT_NE(nullptr, dict.FindCommand("robot.jump"));
json = CreateDictionaryValue(R"({
'base': {
'reboot': {
'parameters': {'delay': 'integer'}
},
'shutdown': {
}
}
})");
EXPECT_TRUE(dict.LoadCommands(*json, nullptr, nullptr));
EXPECT_EQ(3, dict.GetSize());
EXPECT_NE(nullptr, dict.FindCommand("robot.jump"));
EXPECT_NE(nullptr, dict.FindCommand("base.reboot"));
EXPECT_NE(nullptr, dict.FindCommand("base.shutdown"));
EXPECT_EQ(nullptr, dict.FindCommand("foo.bar"));
}
TEST(CommandDictionary, LoadWithInheritance) {
auto json = CreateDictionaryValue(R"({
'robot': {
'jump': {
'minimalRole': 'viewer',
'visibility':'local',
'parameters': {
'height': 'integer'
},
'progress': {
'progress': 'integer'
},
'results': {
'success': 'boolean'
}
}
}
})");
CommandDictionary base_dict;
EXPECT_TRUE(base_dict.LoadCommands(*json, nullptr, nullptr));
EXPECT_EQ(1, base_dict.GetSize());
json = CreateDictionaryValue(R"({'robot': {'jump': {}}})");
CommandDictionary dict;
EXPECT_TRUE(dict.LoadCommands(*json, &base_dict, nullptr));
EXPECT_EQ(1, dict.GetSize());
auto cmd = dict.FindCommand("robot.jump");
EXPECT_NE(nullptr, cmd);
EXPECT_EQ("local", cmd->GetVisibility().ToString());
EXPECT_EQ(UserRole::kViewer, cmd->GetMinimalRole());
EXPECT_JSON_EQ("{'height': {'type': 'integer'}}",
*cmd->GetParameters()->ToJson(true, true));
EXPECT_JSON_EQ("{'progress': {'type': 'integer'}}",
*cmd->GetProgress()->ToJson(true, false));
EXPECT_JSON_EQ("{'success': {'type': 'boolean'}}",
*cmd->GetResults()->ToJson(true, false));
}
TEST(CommandDictionary, LoadCommands_Failures) {
CommandDictionary dict;
ErrorPtr error;
// Command definition is not an object.
auto json = CreateDictionaryValue("{'robot':{'jump':0}}");
EXPECT_FALSE(dict.LoadCommands(*json, nullptr, &error));
EXPECT_EQ("type_mismatch", error->GetCode());
error.reset();
// Package definition is not an object.
json = CreateDictionaryValue("{'robot':'blah'}");
EXPECT_FALSE(dict.LoadCommands(*json, nullptr, &error));
EXPECT_EQ("type_mismatch", error->GetCode());
error.reset();
// Invalid command definition is not an object.
json = CreateDictionaryValue(
"{'robot':{'jump':{'parameters':{'flip':0},'results':{}}}}");
EXPECT_FALSE(dict.LoadCommands(*json, nullptr, &error));
EXPECT_EQ("invalid_object_schema", error->GetCode());
EXPECT_NE(nullptr, error->GetInnerError()); // Must have additional info.
error.reset();
// Empty command name.
json = CreateDictionaryValue("{'robot':{'':{'parameters':{},'results':{}}}}");
EXPECT_FALSE(dict.LoadCommands(*json, nullptr, &error));
EXPECT_EQ("invalid_command_name", error->GetCode());
error.reset();
}
TEST(CommandDictionaryDeathTest, LoadCommands_RedefineInDifferentCategory) {
// Redefine commands in different category.
CommandDictionary dict;
ErrorPtr error;
auto json = CreateDictionaryValue("{'robot':{'jump':{}}}");
dict.LoadCommands(*json, nullptr, &error);
ASSERT_DEATH(dict.LoadCommands(*json, nullptr, &error),
".*Definition for command 'robot.jump' overrides an "
"earlier definition");
}
TEST(CommandDictionary, LoadCommands_CustomCommandNaming) {
// Custom command must start with '_'.
CommandDictionary base_dict;
CommandDictionary dict;
ErrorPtr error;
auto json = CreateDictionaryValue(R"({
'base': {
'reboot': {
'parameters': {'delay': 'integer'},
'results': {}
}
}
})");
base_dict.LoadCommands(*json, nullptr, &error);
EXPECT_TRUE(dict.LoadCommands(*json, &base_dict, &error));
auto json2 =
CreateDictionaryValue("{'base':{'jump':{'parameters':{},'results':{}}}}");
EXPECT_FALSE(dict.LoadCommands(*json2, &base_dict, &error));
EXPECT_EQ("invalid_command_name", error->GetCode());
error.reset();
// If the command starts with "_", then it's Ok.
json2 = CreateDictionaryValue(
"{'base':{'_jump':{'parameters':{},'results':{}}}}");
EXPECT_TRUE(dict.LoadCommands(*json2, &base_dict, nullptr));
}
TEST(CommandDictionary, LoadCommands_RedefineStdCommand) {
// Redefine commands parameter type.
CommandDictionary base_dict;
CommandDictionary dict;
ErrorPtr error;
auto json = CreateDictionaryValue(R"({
'base': {
'reboot': {
'parameters': {'delay': 'integer'},
'results': {'version': 'integer'}
}
}
})");
base_dict.LoadCommands(*json, nullptr, &error);
auto json2 = CreateDictionaryValue(R"({
'base': {
'reboot': {
'parameters': {'delay': 'string'},
'results': {'version': 'integer'}
}
}
})");
EXPECT_FALSE(dict.LoadCommands(*json2, &base_dict, &error));
EXPECT_EQ("invalid_object_schema", error->GetCode());
EXPECT_EQ("invalid_parameter_definition", error->GetInnerError()->GetCode());
EXPECT_EQ("param_type_changed", error->GetFirstError()->GetCode());
error.reset();
auto json3 = CreateDictionaryValue(R"({
'base': {
'reboot': {
'parameters': {'delay': 'integer'},
'results': {'version': 'string'}
}
}
})");
EXPECT_FALSE(dict.LoadCommands(*json3, &base_dict, &error));
EXPECT_EQ("invalid_object_schema", error->GetCode());
// TODO(antonm): remove parameter from error below and use some generic.
EXPECT_EQ("invalid_parameter_definition", error->GetInnerError()->GetCode());
EXPECT_EQ("param_type_changed", error->GetFirstError()->GetCode());
error.reset();
}
TEST(CommandDictionary, GetCommandsAsJson) {
auto json_base = CreateDictionaryValue(R"({
'base': {
'reboot': {
'parameters': {'delay': {'maximum': 100}},
'results': {}
},
'shutdown': {
'parameters': {},
'results': {}
}
}
})");
CommandDictionary base_dict;
base_dict.LoadCommands(*json_base, nullptr, nullptr);
auto json = CreateDictionaryValue(R"({
'base': {
'reboot': {
'parameters': {'delay': {'minimum': 10}},
'results': {}
}
},
'robot': {
'_jump': {
'parameters': {'_height': 'integer'},
'results': {}
}
}
})");
CommandDictionary dict;
dict.LoadCommands(*json, &base_dict, nullptr);
json = dict.GetCommandsAsJson(
[](const CommandDefinition* def) { return true; }, false, nullptr);
ASSERT_NE(nullptr, json.get());
auto expected = R"({
'base': {
'reboot': {
'parameters': {'delay': {'minimum': 10}},
'minimalRole': 'user'
}
},
'robot': {
'_jump': {
'parameters': {'_height': 'integer'},
'minimalRole': 'user'
}
}
})";
EXPECT_JSON_EQ(expected, *json);
json = dict.GetCommandsAsJson(
[](const CommandDefinition* def) { return true; }, true, nullptr);
ASSERT_NE(nullptr, json.get());
expected = R"({
'base': {
'reboot': {
'parameters': {
'delay': {
'maximum': 100,
'minimum': 10,
'type': 'integer'
}
},
'minimalRole': 'user'
}
},
'robot': {
'_jump': {
'parameters': {
'_height': {
'type': 'integer'
}
},
'minimalRole': 'user'
}
}
})";
EXPECT_JSON_EQ(expected, *json);
}
TEST(CommandDictionary, GetCommandsAsJsonWithVisibility) {
auto json = CreateDictionaryValue(R"({
'test': {
'command1': {
'parameters': {},
'results': {},
'visibility': 'none'
},
'command2': {
'parameters': {},
'results': {},
'visibility': 'local'
},
'command3': {
'parameters': {},
'results': {},
'visibility': 'cloud'
},
'command4': {
'parameters': {},
'results': {},
'visibility': 'all'
},
'command5': {
'parameters': {},
'results': {},
'visibility': 'none'
},
'command6': {
'parameters': {},
'results': {},
'visibility': 'local'
},
'command7': {
'parameters': {},
'results': {},
'visibility': 'cloud'
},
'command8': {
'parameters': {},
'results': {},
'visibility': 'all'
}
}
})");
CommandDictionary dict;
ASSERT_TRUE(dict.LoadCommands(*json, nullptr, nullptr));
json = dict.GetCommandsAsJson(
[](const CommandDefinition* def) { return true; }, false, nullptr);
ASSERT_NE(nullptr, json.get());
auto expected = R"({
'test': {
'command1': {'parameters': {}, 'minimalRole': 'user'},
'command2': {'parameters': {}, 'minimalRole': 'user'},
'command3': {'parameters': {}, 'minimalRole': 'user'},
'command4': {'parameters': {}, 'minimalRole': 'user'},
'command5': {'parameters': {}, 'minimalRole': 'user'},
'command6': {'parameters': {}, 'minimalRole': 'user'},
'command7': {'parameters': {}, 'minimalRole': 'user'},
'command8': {'parameters': {}, 'minimalRole': 'user'}
}
})";
EXPECT_JSON_EQ(expected, *json);
json = dict.GetCommandsAsJson(
[](const CommandDefinition* def) { return def->GetVisibility().local; },
false, nullptr);
ASSERT_NE(nullptr, json.get());
expected = R"({
'test': {
'command2': {'parameters': {}, 'minimalRole': 'user'},
'command4': {'parameters': {}, 'minimalRole': 'user'},
'command6': {'parameters': {}, 'minimalRole': 'user'},
'command8': {'parameters': {}, 'minimalRole': 'user'}
}
})";
EXPECT_JSON_EQ(expected, *json);
json = dict.GetCommandsAsJson(
[](const CommandDefinition* def) { return def->GetVisibility().cloud; },
false, nullptr);
ASSERT_NE(nullptr, json.get());
expected = R"({
'test': {
'command3': {'parameters': {}, 'minimalRole': 'user'},
'command4': {'parameters': {}, 'minimalRole': 'user'},
'command7': {'parameters': {}, 'minimalRole': 'user'},
'command8': {'parameters': {}, 'minimalRole': 'user'}
}
})";
EXPECT_JSON_EQ(expected, *json);
json = dict.GetCommandsAsJson(
[](const CommandDefinition* def) {
return def->GetVisibility().local && def->GetVisibility().cloud;
},
false, nullptr);
ASSERT_NE(nullptr, json.get());
expected = R"({
'test': {
'command4': {'parameters': {}, 'minimalRole': 'user'},
'command8': {'parameters': {}, 'minimalRole': 'user'}
}
})";
EXPECT_JSON_EQ(expected, *json);
}
TEST(CommandDictionary, LoadWithPermissions) {
CommandDictionary base_dict;
auto json = CreateDictionaryValue(R"({
'base': {
'command1': {
'parameters': {},
'results': {},
'visibility':'none'
},
'command2': {
'minimalRole': 'viewer',
'parameters': {},
'results': {},
'visibility':'local'
},
'command3': {
'minimalRole': 'user',
'parameters': {},
'results': {},
'visibility':'cloud'
},
'command4': {
'minimalRole': 'manager',
'parameters': {},
'results': {},
'visibility':'all'
},
'command5': {
'minimalRole': 'owner',
'parameters': {},
'results': {},
'visibility':'local,cloud'
}
}
})");
EXPECT_TRUE(base_dict.LoadCommands(*json, nullptr, nullptr));
auto cmd = base_dict.FindCommand("base.command1");
ASSERT_NE(nullptr, cmd);
EXPECT_EQ("none", cmd->GetVisibility().ToString());
EXPECT_EQ(UserRole::kUser, cmd->GetMinimalRole());
cmd = base_dict.FindCommand("base.command2");
ASSERT_NE(nullptr, cmd);
EXPECT_EQ("local", cmd->GetVisibility().ToString());
EXPECT_EQ(UserRole::kViewer, cmd->GetMinimalRole());
cmd = base_dict.FindCommand("base.command3");
ASSERT_NE(nullptr, cmd);
EXPECT_EQ("cloud", cmd->GetVisibility().ToString());
EXPECT_EQ(UserRole::kUser, cmd->GetMinimalRole());
cmd = base_dict.FindCommand("base.command4");
ASSERT_NE(nullptr, cmd);
EXPECT_EQ("all", cmd->GetVisibility().ToString());
EXPECT_EQ(UserRole::kManager, cmd->GetMinimalRole());
cmd = base_dict.FindCommand("base.command5");
ASSERT_NE(nullptr, cmd);
EXPECT_EQ("all", cmd->GetVisibility().ToString());
EXPECT_EQ(UserRole::kOwner, cmd->GetMinimalRole());
CommandDictionary dict;
json = CreateDictionaryValue(R"({
'base': {
'command1': {
'parameters': {},
'results': {}
},
'command2': {
'parameters': {},
'results': {}
},
'command3': {
'parameters': {},
'results': {}
},
'command4': {
'parameters': {},
'results': {}
},
'command5': {
'parameters': {},
'results': {}
},
'_command6': {
'parameters': {},
'results': {}
}
}
})");
EXPECT_TRUE(dict.LoadCommands(*json, &base_dict, nullptr));
cmd = dict.FindCommand("base.command1");
ASSERT_NE(nullptr, cmd);
EXPECT_EQ("none", cmd->GetVisibility().ToString());
EXPECT_EQ(UserRole::kUser, cmd->GetMinimalRole());
cmd = dict.FindCommand("base.command2");
ASSERT_NE(nullptr, cmd);
EXPECT_EQ("local", cmd->GetVisibility().ToString());
EXPECT_EQ(UserRole::kViewer, cmd->GetMinimalRole());
cmd = dict.FindCommand("base.command3");
ASSERT_NE(nullptr, cmd);
EXPECT_EQ("cloud", cmd->GetVisibility().ToString());
EXPECT_EQ(UserRole::kUser, cmd->GetMinimalRole());
cmd = dict.FindCommand("base.command4");
ASSERT_NE(nullptr, cmd);
EXPECT_EQ("all", cmd->GetVisibility().ToString());
EXPECT_EQ(UserRole::kManager, cmd->GetMinimalRole());
cmd = dict.FindCommand("base.command5");
ASSERT_NE(nullptr, cmd);
EXPECT_EQ("all", cmd->GetVisibility().ToString());
EXPECT_EQ(UserRole::kOwner, cmd->GetMinimalRole());
cmd = dict.FindCommand("base._command6");
ASSERT_NE(nullptr, cmd);
EXPECT_EQ("all", cmd->GetVisibility().ToString());
EXPECT_EQ(UserRole::kUser, cmd->GetMinimalRole());
}
TEST(CommandDictionary, LoadWithPermissions_InvalidVisibility) {
CommandDictionary dict;
ErrorPtr error;
auto json = CreateDictionaryValue(R"({
'base': {
'jump': {
'parameters': {},
'results': {},
'visibility':'foo'
}
}
})");
EXPECT_FALSE(dict.LoadCommands(*json, nullptr, &error));
EXPECT_EQ("invalid_command_visibility", error->GetCode());
EXPECT_EQ("invalid_parameter_value", error->GetInnerError()->GetCode());
error.reset();
}
TEST(CommandDictionary, LoadWithPermissions_InvalidRole) {
CommandDictionary dict;
ErrorPtr error;
auto json = CreateDictionaryValue(R"({
'base': {
'jump': {
'parameters': {},
'results': {},
'visibility':'local,cloud',
'minimalRole':'foo'
}
}
})");
EXPECT_FALSE(dict.LoadCommands(*json, nullptr, &error));
EXPECT_EQ("invalid_minimal_role", error->GetCode());
EXPECT_EQ("invalid_parameter_value", error->GetInnerError()->GetCode());
error.reset();
}
} // namespace weave