1 #include "dbus_environment.hpp" 2 #include "helpers.hpp" 3 #include "mocks/json_storage_mock.hpp" 4 #include "mocks/trigger_factory_mock.hpp" 5 #include "mocks/trigger_mock.hpp" 6 #include "params/trigger_params.hpp" 7 #include "trigger.hpp" 8 #include "trigger_manager.hpp" 9 #include "utils/conversion_trigger.hpp" 10 #include "utils/transform.hpp" 11 12 using namespace testing; 13 14 class TestTriggerManager : public Test 15 { 16 public: 17 std::pair<boost::system::error_code, std::string> 18 addTrigger(const TriggerParams& params) 19 { 20 const auto sensorInfos = 21 utils::fromLabeledSensorsInfo(params.sensors()); 22 23 std::promise<std::pair<boost::system::error_code, std::string>> 24 addTriggerPromise; 25 DbusEnvironment::getBus()->async_method_call( 26 [&addTriggerPromise](boost::system::error_code ec, 27 const std::string& path) { 28 addTriggerPromise.set_value({ec, path}); 29 }, 30 DbusEnvironment::serviceName(), TriggerManager::triggerManagerPath, 31 TriggerManager::triggerManagerIfaceName, "AddTrigger", params.id(), 32 params.name(), 33 utils::transform( 34 params.triggerActions(), 35 [](const auto& action) { return actionToString(action); }), 36 sensorInfos, params.reportIds(), 37 std::visit(utils::FromLabeledThresholdParamConversion(), 38 params.thresholdParams())); 39 return DbusEnvironment::waitForFuture(addTriggerPromise.get_future()); 40 } 41 42 std::unique_ptr<TriggerManager> makeTriggerManager() 43 { 44 return std::make_unique<TriggerManager>( 45 std::move(triggerFactoryMockPtr), std::move(storageMockPtr), 46 DbusEnvironment::getObjServer()); 47 } 48 49 void SetUp() override 50 { 51 sut = makeTriggerManager(); 52 } 53 54 std::unique_ptr<StorageMock> storageMockPtr = 55 std::make_unique<NiceMock<StorageMock>>(); 56 StorageMock& storageMock = *storageMockPtr; 57 std::unique_ptr<TriggerFactoryMock> triggerFactoryMockPtr = 58 std::make_unique<NiceMock<TriggerFactoryMock>>(); 59 TriggerFactoryMock& triggerFactoryMock = *triggerFactoryMockPtr; 60 std::unique_ptr<TriggerMock> triggerMockPtr = 61 std::make_unique<NiceMock<TriggerMock>>(TriggerParams().id()); 62 TriggerMock& triggerMock = *triggerMockPtr; 63 std::unique_ptr<TriggerManager> sut; 64 MockFunction<void(std::string)> checkPoint; 65 }; 66 67 TEST_F(TestTriggerManager, addTrigger) 68 { 69 triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 70 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 71 72 auto [ec, path] = addTrigger(TriggerParams()); 73 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 74 EXPECT_THAT(path, Eq(triggerMock.getPath())); 75 } 76 77 TEST_F(TestTriggerManager, addTriggerWithDiscreteThresholds) 78 { 79 TriggerParams triggerParamsDiscrete; 80 auto thresholds = std::vector<discrete::LabeledThresholdParam>{ 81 {"discrete_threshold1", discrete::Severity::ok, 10, "11.0"}, 82 {"discrete_threshold2", discrete::Severity::warning, 10, "12.0"}, 83 {"discrete_threshold3", discrete::Severity::critical, 10, "13.0"}}; 84 85 triggerParamsDiscrete.thresholdParams(thresholds); 86 87 auto [ec, path] = addTrigger(triggerParamsDiscrete); 88 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 89 EXPECT_THAT(path, Eq(triggerMock.getPath())); 90 } 91 92 TEST_F(TestTriggerManager, addDiscreteTriggerWithoutThresholds) 93 { 94 TriggerParams triggerParamsDiscrete; 95 auto thresholds = std::vector<discrete::LabeledThresholdParam>(); 96 97 triggerParamsDiscrete.thresholdParams(thresholds); 98 99 auto [ec, path] = addTrigger(triggerParamsDiscrete); 100 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 101 EXPECT_THAT(path, Eq(triggerMock.getPath())); 102 } 103 104 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerTwice) 105 { 106 triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 107 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 108 109 addTrigger(TriggerParams()); 110 111 auto [ec, path] = addTrigger(TriggerParams()); 112 EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists)); 113 EXPECT_THAT(path, Eq(std::string())); 114 } 115 116 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithInvalidId) 117 { 118 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 119 .Times(0); 120 121 auto [ec, path] = addTrigger(TriggerParams().id("not valid?")); 122 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 123 EXPECT_THAT(path, Eq(std::string())); 124 } 125 126 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithDuplicatesInReportsIds) 127 { 128 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 129 .Times(0); 130 131 auto [ec, path] = addTrigger( 132 TriggerParams().reportIds({"trigger1", "trigger2", "trigger1"})); 133 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 134 EXPECT_THAT(path, Eq(std::string())); 135 } 136 137 TEST_F(TestTriggerManager, addTriggerWithoutIdAndName) 138 { 139 triggerFactoryMock 140 .expectMake(TriggerParams() 141 .id(TriggerManager::triggerNameDefault) 142 .name(TriggerManager::triggerNameDefault), 143 Ref(*sut), Ref(storageMock)) 144 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 145 146 auto [ec, path] = addTrigger(TriggerParams().id("").name("")); 147 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 148 EXPECT_THAT(path, Not(Eq(""))); 149 } 150 151 TEST_F(TestTriggerManager, addTriggerWithPrefixId) 152 { 153 triggerFactoryMock 154 .expectMake(TriggerParams() 155 .id("TelemetryService/HackyName") 156 .name("Hacky/Name!@#$"), 157 Ref(*sut), Ref(storageMock)) 158 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 159 160 auto [ec, path] = addTrigger( 161 TriggerParams().id("TelemetryService/").name("Hacky/Name!@#$")); 162 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 163 EXPECT_THAT(path, Not(Eq(""))); 164 } 165 166 TEST_F(TestTriggerManager, addTriggerWithoutIdTwice) 167 { 168 addTrigger(TriggerParams().id("")); 169 170 auto [ec, path] = addTrigger(TriggerParams().id("")); 171 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 172 EXPECT_THAT(path, Not(Eq(""))); 173 } 174 175 TEST_F(TestTriggerManager, addTriggerWithoutIdAndWithLongNameTwice) 176 { 177 addTrigger(TriggerParams().id("").name( 178 std::string(2 * TriggerManager::maxTriggerIdLength, 'z'))); 179 180 auto [ec, path] = addTrigger(TriggerParams().id("").name( 181 std::string(2 * TriggerManager::maxTriggerIdLength, 'z'))); 182 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 183 EXPECT_THAT(path, Not(Eq(""))); 184 } 185 186 TEST_F(TestTriggerManager, addTriggerWithMaxLengthId) 187 { 188 auto triggerId = std::string(TriggerManager::maxTriggerIdLength, 'z'); 189 auto triggerParams = TriggerParams().id(triggerId); 190 191 triggerFactoryMock.expectMake(triggerParams, Ref(*sut), Ref(storageMock)) 192 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 193 194 auto [ec, path] = addTrigger(triggerParams); 195 196 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 197 EXPECT_THAT(path, Eq(triggerMock.getPath())); 198 } 199 200 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooLongId) 201 { 202 auto triggerId = std::string(TriggerManager::maxTriggerIdLength + 1, 'z'); 203 auto triggerParams = TriggerParams().id(triggerId); 204 205 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 206 .Times(0); 207 208 auto [ec, path] = addTrigger(triggerParams); 209 210 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 211 EXPECT_THAT(path, Eq(std::string())); 212 } 213 214 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWhenMaxTriggerIsReached) 215 { 216 auto triggerParams = TriggerParams(); 217 218 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 219 .Times(TriggerManager::maxTriggers); 220 221 for (size_t i = 0; i < TriggerManager::maxTriggers; i++) 222 { 223 triggerParams.id(TriggerParams().id() + std::to_string(i)); 224 225 auto [ec, path] = addTrigger(triggerParams); 226 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 227 } 228 229 triggerParams.id(TriggerParams().id() + 230 std::to_string(TriggerManager::maxTriggers)); 231 auto [ec, path] = addTrigger(triggerParams); 232 EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open)); 233 EXPECT_THAT(path, Eq(std::string())); 234 } 235 236 TEST_F(TestTriggerManager, removeTrigger) 237 { 238 { 239 InSequence seq; 240 triggerFactoryMock 241 .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 242 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 243 EXPECT_CALL(triggerMock, Die()); 244 EXPECT_CALL(checkPoint, Call("end")); 245 } 246 247 addTrigger(TriggerParams()); 248 sut->removeTrigger(&triggerMock); 249 checkPoint.Call("end"); 250 } 251 252 TEST_F(TestTriggerManager, removingTriggerThatIsNotInContainerHasNoEffect) 253 { 254 { 255 InSequence seq; 256 EXPECT_CALL(checkPoint, Call("end")); 257 EXPECT_CALL(triggerMock, Die()); 258 } 259 260 sut->removeTrigger(&triggerMock); 261 checkPoint.Call("end"); 262 } 263 264 TEST_F(TestTriggerManager, removingSameTriggerTwiceHasNoSideEffect) 265 { 266 { 267 InSequence seq; 268 triggerFactoryMock 269 .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 270 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 271 EXPECT_CALL(triggerMock, Die()); 272 EXPECT_CALL(checkPoint, Call("end")); 273 } 274 275 addTrigger(TriggerParams()); 276 sut->removeTrigger(&triggerMock); 277 sut->removeTrigger(&triggerMock); 278 checkPoint.Call("end"); 279 } 280 class TestTriggerManagerStorage : public TestTriggerManager 281 { 282 public: 283 using FilePath = interfaces::JsonStorage::FilePath; 284 using DirectoryPath = interfaces::JsonStorage::DirectoryPath; 285 286 void SetUp() override 287 { 288 ON_CALL(storageMock, list()) 289 .WillByDefault(Return(std::vector<FilePath>{ 290 {FilePath("trigger1")}, {FilePath("trigger2")}})); 291 292 ON_CALL(storageMock, load(FilePath("trigger1"))) 293 .WillByDefault(InvokeWithoutArgs([this] { return data1; })); 294 295 data2["Id"] = "Trigger2"; 296 data2["Name"] = "Second Trigger"; 297 ON_CALL(storageMock, load(FilePath("trigger2"))) 298 .WillByDefault(InvokeWithoutArgs([this] { return data2; })); 299 } 300 301 nlohmann::json data1 = nlohmann::json{ 302 {"Version", Trigger::triggerVersion}, 303 {"Id", TriggerParams().id()}, 304 {"Name", TriggerParams().name()}, 305 {"ThresholdParamsDiscriminator", 306 TriggerParams().thresholdParams().index()}, 307 {"TriggerActions", utils::transform(TriggerParams().triggerActions(), 308 [](const auto& action) { 309 return actionToString(action); 310 })}, 311 {"ThresholdParams", utils::labeledThresholdParamsToJson( 312 TriggerParams().thresholdParams())}, 313 {"ReportIds", TriggerParams().reportIds()}, 314 {"Sensors", TriggerParams().sensors()}}; 315 316 nlohmann::json data2 = data1; 317 }; 318 319 TEST_F(TestTriggerManagerStorage, triggerManagerCtorAddTriggerFromStorage) 320 { 321 triggerFactoryMock.expectMake(TriggerParams(), _, Ref(storageMock)); 322 triggerFactoryMock.expectMake( 323 TriggerParams().id("Trigger2").name("Second Trigger"), _, 324 Ref(storageMock)); 325 EXPECT_CALL(storageMock, remove(_)).Times(0); 326 327 sut = makeTriggerManager(); 328 } 329 330 TEST_F(TestTriggerManagerStorage, 331 triggerManagerCtorRemoveDiscreteTriggerFromStorage) 332 { 333 LabeledTriggerThresholdParams thresholdParams = 334 std::vector<discrete::LabeledThresholdParam>{ 335 {"userId1", discrete::Severity::warning, 15, "10.0"}, 336 {"userId2", discrete::Severity::critical, 5, "20.0"}}; 337 338 data1["ThresholdParamsDiscriminator"] = thresholdParams.index(); 339 340 data1["ThresholdParams"] = 341 utils::labeledThresholdParamsToJson(thresholdParams); 342 343 EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0); 344 345 sut = makeTriggerManager(); 346 } 347 348 TEST_F(TestTriggerManagerStorage, 349 triggerManagerCtorRemoveDiscreteTriggerFromStorage2) 350 { 351 data1["IsDiscrete"] = true; 352 353 EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0); 354 355 sut = makeTriggerManager(); 356 } 357 358 TEST_F(TestTriggerManagerStorage, 359 triggerManagerCtorAddProperRemoveInvalidTriggerFromStorage) 360 { 361 data1["Version"] = Trigger::triggerVersion - 1; 362 363 triggerFactoryMock.expectMake( 364 TriggerParams().id("Trigger2").name("Second Trigger"), _, 365 Ref(storageMock)); 366 EXPECT_CALL(storageMock, remove(FilePath("trigger1"))); 367 368 sut = makeTriggerManager(); 369 } 370