1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2/* 3 * Copyright 2017 Couchbase, Inc 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18#include "collections/filter.h" 19#include "collections/vbucket_filter.h" 20#include "collections/vbucket_manifest.h" 21#include "ep_vb.h" 22#include "failover-table.h" 23 24#include <gtest/gtest.h> 25 26#include <limits> 27 28class CollectionsFilterTest : public ::testing::Test { 29public: 30 /// Dummy callback to replace the flusher callback so we can create VBuckets 31 class DummyCB : public Callback<uint16_t> { 32 public: 33 DummyCB() { 34 } 35 36 void callback(uint16_t& dummy) { 37 } 38 }; 39 40 CollectionsFilterTest() 41 : vb(0, 42 vbucket_state_active, 43 global_stats, 44 checkpoint_config, 45 /*kvshard*/ nullptr, 46 /*lastSeqno*/ 0, 47 /*lastSnapStart*/ 0, 48 /*lastSnapEnd*/ 0, 49 /*table*/ nullptr, 50 std::make_shared<DummyCB>(), 51 /*newSeqnoCb*/ nullptr, 52 config, 53 VALUE_ONLY) { 54 } 55 56 EPStats global_stats; 57 CheckpointConfig checkpoint_config; 58 Configuration config; 59 EPVBucket vb; 60}; 61 62/** 63 * Test invalid JSON formats as an input 64 */ 65TEST_F(CollectionsFilterTest, junk_in) { 66 Collections::Manifest m( 67 R"({"separator":":","uid":"0",)" 68 R"("collections":[{"name":"$default","uid":"0"}, 69 {"name":"vegetable","uid":"1"}]})"); 70 71 std::vector<std::string> inputs = {"{}", 72 R"({"collections":1})", 73 R"({"collections:"this"})", 74 R"({"collections:{"a":1})", 75 R"({"collection:["a"])", 76 R"({"collections:[a])"}; 77 78 for (const auto& s : inputs) { 79 boost::optional<const std::string&> json = s; 80 try { 81 Collections::Filter f(json, &m); 82 FAIL() << "Should of thrown an exception"; 83 } catch (const cb::engine_error& e) { 84 EXPECT_EQ(cb::engine_errc::invalid_arguments, e.code()); 85 } catch (...) { 86 FAIL() << "Should of thrown cb::engine_error"; 87 } 88 } 89} 90 91/** 92 * Test valid inputs to the filter. 93 */ 94TEST_F(CollectionsFilterTest, validation1) { 95 Collections::Manifest m( 96 R"({"separator":":","uid":"0",)" 97 R"("collections":[{"name":"$default","uid":"0"}, 98 {"name":"vegetable","uid":"1"}, 99 {"name":"meat","uid":"3"}, 100 {"name":"fruit", "uid":"4"}, 101 {"name":"dairy","uid":"5"}]})"); 102 103 std::vector<std::string> inputs = { 104 R"({"collections":["$default"]})", 105 R"({"collections":["vegetable"]})", 106 R"({"collections":["fruit", "meat"]})", 107 R"({"collections":[{"name":"vegetable","uid":"1"}]})", 108 R"({"collections":[{"name":"dairy","uid":"5"},{"name":"meat","uid":"3"}]})", 109 R"({"collections":[{"name":"$default","uid":"0"}]})"}; 110 111 for (const auto& s : inputs) { 112 boost::optional<const std::string&> json = s; 113 114 try { 115 Collections::Filter f(json, &m); 116 } catch (...) { 117 FAIL() << "Exception thrown with input " << s; 118 } 119 } 120} 121 122/** 123 * Test valid JSON formats to the filter, but they are contain invalid content 124 * such as unknown collections 125 */ 126TEST_F(CollectionsFilterTest, validation2) { 127 Collections::Manifest m( 128 R"({"revision":0,"separator":":","uid":"0",)" 129 R"("collections":[{"name":"$default","uid":"0"}, 130 {"name":"vegetable","uid":"1"}, 131 {"name":"meat","uid":"3"}, 132 {"name":"fruit", "uid":"4"}, 133 {"name":"dairy","uid":"5"}]})"); 134 135 std::vector<std::string> inputs = { 136 // wrong name inputs 137 R"({"collections":["cheese"]})", 138 R"({"collections":["fruit","beer"]})", 139 R"({"collections":["$dufault"]})", 140 // wrong UID inputs 141 R"({"collections":[{"name":"vegetable","uid":"2"}]})", 142 R"({"collections":[{"name":"meat","uid":"1"}]})", 143 R"({"collections":[{"name":"vegetable","uid":"1"},{"name":"dairy","uid":"9"}]})", 144 // wrong name and UID inputs 145 R"({"collections":[{"name":"vugetable","uid":"2"}]})", 146 R"({"collections":[{"name":"getable","uid":"1"}]})", 147 R"({"collections":[{"name":"vegetable","uid":"1"},{"name":"fairy","uid":"5"}]})", 148 149 // cannot mix name/uid and name 150 R"({"collections":[{"name":"vegetable","uid":"1"}, "cheese"]})", 151 }; 152 153 for (const auto& s : inputs) { 154 boost::optional<const std::string&> json = s; 155 try { 156 Collections::Filter f(json, &m); 157 FAIL() << "Should of thrown an exception"; 158 } catch (const cb::engine_error& e) { 159 EXPECT_EQ(cb::engine_errc::unknown_collection, e.code()); 160 } catch (...) { 161 FAIL() << "Should of thrown cb::engine_error"; 162 } 163 } 164} 165 166/** 167 * Test that we cannot create default collection filter when no default 168 * collection exists 169 */ 170TEST_F(CollectionsFilterTest, validation_no_default) { 171 // m does not include $default 172 Collections::Manifest m( 173 R"({"separator":":","uid":"0",)" 174 R"("collections":[{"name":"vegetable","uid":"1"}, 175 {"name":"meat","uid":"3"}, 176 {"name":"fruit", "uid":"4"}, 177 {"name":"dairy","uid":"5"}]})"); 178 boost::optional<const std::string&> json; 179 try { 180 Collections::Filter f(json, &m); 181 FAIL() << "Should of thrown an exception"; 182 } catch (const cb::engine_error& e) { 183 EXPECT_EQ(cb::engine_errc::unknown_collection, e.code()); 184 } catch (...) { 185 FAIL() << "Should of thrown cb::engine_error"; 186 } 187} 188 189/** 190 * Test that we cannot create a true filter without a manifest 191 */ 192TEST_F(CollectionsFilterTest, no_manifest) { 193 std::string jsonFilter = R"({"collections":["$default", "fruit", "meat"]})"; 194 boost::optional<const std::string&> json(jsonFilter); 195 196 try { 197 Collections::Filter f(json, nullptr); 198 FAIL() << "Should of thrown an exception"; 199 } catch (const cb::engine_error& e) { 200 EXPECT_EQ(cb::engine_errc::no_collections_manifest, e.code()); 201 } catch (...) { 202 FAIL() << "Should of thrown cb::engine_error"; 203 } 204} 205 206/** 207 * Construct a valid Collections::Filter and check its public methods 208 * This creates a filter which contains a set of collections 209 */ 210TEST_F(CollectionsFilterTest, filter_basic1) { 211 Collections::Manifest m( 212 R"({"separator":"$","uid":"0",)" 213 R"("collections":[{"name":"$default","uid":"0"}, 214 {"name":"vegetable","uid":"1"}, 215 {"name":"meat","uid":"3"}, 216 {"name":"fruit", "uid":"4"}, 217 {"name":"dairy","uid":"5"}]})"); 218 219 std::string jsonFilter = R"({"collections":["$default", "fruit", "meat"]})"; 220 boost::optional<const std::string&> json(jsonFilter); 221 Collections::Filter f(json, &m); 222 223 // This is not a passthrough filter 224 EXPECT_FALSE(f.isPassthrough()); 225 226 // But this filter would send the default 227 EXPECT_TRUE(f.allowDefaultCollection()); 228 // and allow system events 229 EXPECT_TRUE(f.allowSystemEvents()); 230 231 // The actual filter "list" only stores fruit and meat though, default is 232 // special cased via doesDefaultCollectionExist 233 EXPECT_EQ(2, f.getFilter().size()); 234 235 auto list = f.getFilter(); 236 EXPECT_TRUE(std::find_if( 237 std::begin(list), 238 std::end(list), 239 [](const std::pair<std::string, 240 boost::optional<Collections::uid_t>>& 241 item) { return item.first == "fruit"; }) != 242 list.end()); 243 EXPECT_TRUE(std::find_if( 244 std::begin(list), 245 std::end(list), 246 [](const std::pair<std::string, 247 boost::optional<Collections::uid_t>>& 248 item) { return item.first == "meat"; }) != 249 list.end()); 250} 251 252/** 253 * Construct a valid Collections::Filter and check its public methods 254 * This creates a filter which is passthrough 255 */ 256TEST_F(CollectionsFilterTest, filter_basic2) { 257 Collections::Manifest m( 258 R"({"separator":"$","uid":"0",)" 259 R"("collections":[{"name":"$default","uid":"0"}, 260 {"name":"vegetable","uid":"1"}, 261 {"name":"meat","uid":"3"}, 262 {"name":"fruit", "uid":"4"}, 263 {"name":"dairy","uid":"5"}]})"); 264 265 std::string jsonFilter; // empty string creates a pass through 266 boost::optional<const std::string&> json(jsonFilter); 267 Collections::Filter f(json, &m); 268 269 // This is a passthrough filter 270 EXPECT_TRUE(f.isPassthrough()); 271 272 // So this filter would send the default 273 EXPECT_TRUE(f.allowDefaultCollection()); 274 275 // and still allow system events 276 EXPECT_TRUE(f.allowSystemEvents()); 277 278 // The actual filter "list" stores nothing 279 EXPECT_EQ(0, f.getFilter().size()); 280} 281 282class CollectionsVBFilterTest : public CollectionsFilterTest {}; 283 284/** 285 * Try and create filter for collections which exist by name but not with the 286 * UID. This represents what could happen if a filtered producer was created 287 * successfully, but later when a stream request occurs, the VB's view of 288 * collections has shifted. 289 */ 290TEST_F(CollectionsVBFilterTest, uid_mismatch) { 291 Collections::Manifest m1( 292 R"({"separator":"$","uid":"0",)" 293 R"("collections":[{"name":"$default","uid":"0"}, 294 {"name":"vegetable","uid":"1"}, 295 {"name":"meat","uid":"3"}, 296 {"name":"fruit", "uid":"4"}, 297 {"name":"dairy","uid":"5"}]})"); 298 Collections::Manifest m2( 299 R"({"separator":"$","uid":"0",)" 300 R"("collections":[{"name":"$default","uid":"0"}, 301 {"name":"vegetable","uid":"8"}, 302 {"name":"meat","uid":"99"}, 303 {"name":"fruit", "uid":"4"}, 304 {"name":"dairy","uid":"5"}]})"); 305 306 // Create the "producer" level filter so that we in theory produce at least 307 // these collections 308 std::string jsonFilter = 309 R"({"collections":[{"name":"meat","uid":"3"}, {"name":"vegetable","uid":"1"}]})"; 310 boost::optional<const std::string&> json(jsonFilter); 311 // At this point the requested collections are valid for m1 312 Collections::Filter f(json, &m1); 313 314 Collections::VB::Manifest vbm({}); 315 // push creates 316 vbm.wlock().update(vb, m1); 317 // push deletes, removing both filtered collections 318 vbm.wlock().update(vb, m2); 319 320 // Construction will now fail as the newly calculated filter doesn't match 321 // the collections of vbm 322 Collections::VB::Filter vbf(f, vbm); 323 EXPECT_TRUE(vbf.empty()); 324} 325 326/** 327 * Try and create filter for collections which exist, but have been deleted 328 * i.e. they aren't writable so should never feature in a new VB::Filter 329 */ 330TEST_F(CollectionsVBFilterTest, deleted_collection) { 331 Collections::Manifest m1( 332 R"({"separator":"$","uid":"0",)" 333 R"("collections":[{"name":"$default","uid":"0"}, 334 {"name":"vegetable","uid":"1"}, 335 {"name":"meat","uid":"3"}, 336 {"name":"fruit", "uid":"4"}, 337 {"name":"dairy","uid":"5"}]})"); 338 Collections::Manifest m2( 339 R"({"separator":"$","uid":"0",)" 340 R"("collections":[{"name":"$default","uid":"0"}, 341 {"name":"meat","uid":"3"}, 342 {"name":"dairy","uid":"5"}]})"); 343 344 // Create the "producer" level filter so that we in theory produce at least 345 // these collections 346 std::string jsonFilter = R"({"collections":["vegetable", "fruit"]})"; 347 boost::optional<const std::string&> json(jsonFilter); 348 Collections::Filter f(json, &m1); 349 350 Collections::VB::Manifest vbm({}); 351 // push creates 352 vbm.wlock().update(vb, m1); 353 // push deletes, removing both filtered collections 354 vbm.wlock().update(vb, m2); 355 356 Collections::VB::Filter vbf(f, vbm); 357 EXPECT_TRUE(vbf.empty()); 358} 359 360/** 361 * Create a filter with collections and check we allow what should be allowed. 362 */ 363TEST_F(CollectionsVBFilterTest, basic_allow) { 364 Collections::Manifest m( 365 R"({"separator":"$","uid":"0",)" 366 R"("collections":[{"name":"$default","uid":"0"}, 367 {"name":"vegetable","uid":"1"}, 368 {"name":"meat","uid":"3"}, 369 {"name":"fruit", "uid":"4"}, 370 {"name":"dairy","uid":"5"}]})"); 371 372 std::string jsonFilter = R"({"collections":["$default", "fruit", "meat"]})"; 373 boost::optional<const std::string&> json(jsonFilter); 374 Collections::Filter f(json, &m); 375 376 Collections::VB::Manifest vbm({}); 377 vbm.wlock().update(vb, m); 378 379 Collections::VB::Filter vbf(f, vbm); 380 381 // Yes to these guys 382 EXPECT_TRUE(vbf.checkAndUpdate( 383 {{"anykey", DocNamespace::DefaultCollection}, 0, 0, nullptr, 0})); 384 EXPECT_TRUE(vbf.checkAndUpdate( 385 {{"fruit$apple", DocNamespace::Collections}, 0, 0, nullptr, 0})); 386 EXPECT_TRUE(vbf.checkAndUpdate( 387 {{"meat$bacon", DocNamespace::Collections}, 0, 0, nullptr, 0})); 388 389 // No to these keys 390 EXPECT_FALSE(vbf.checkAndUpdate( 391 {{"dairy$milk", DocNamespace::Collections}, 0, 0, nullptr, 0})); 392 EXPECT_FALSE(vbf.checkAndUpdate( 393 {{"vegetable$cabbage", DocNamespace::Collections}, 394 0, 395 0, 396 nullptr, 397 0})); 398} 399 400/** 401 * Create a filter as if a legacy DCP connection would, i.e. the optional 402 * JSON filter is not initialised (because DCP open does not send a value). 403 */ 404TEST_F(CollectionsVBFilterTest, legacy_filter) { 405 Collections::Manifest m( 406 R"({"separator":"$","uid":"0",)" 407 R"("collections":[{"name":"$default","uid":"0"}, 408 {"name":"meat","uid":"3"}]})"); 409 410 boost::optional<const std::string&> json; 411 Collections::Filter f(json, &m); 412 413 Collections::VB::Manifest vbm({}); 414 vbm.wlock().update(vb, m); 415 416 Collections::VB::Filter vbf(f, vbm); 417 // Legacy would only allow default 418 EXPECT_TRUE(vbf.checkAndUpdate( 419 {{"anykey", DocNamespace::DefaultCollection}, 0, 0, nullptr, 0})); 420 EXPECT_FALSE(vbf.checkAndUpdate( 421 {{"fruit$apple", DocNamespace::Collections}, 0, 0, nullptr, 0})); 422} 423 424/** 425 * Create a passthrough filter and check it allows anything 426 */ 427TEST_F(CollectionsVBFilterTest, passthrough) { 428 Collections::Manifest m( 429 R"({"separator":"$","uid":"0",)" 430 R"("collections":[{"name":"meat","uid":"3"}]})"); 431 std::string filterJson; // empty string 432 boost::optional<const std::string&> json(filterJson); 433 Collections::Filter f(json, &m); 434 435 Collections::VB::Manifest vbm({}); 436 vbm.wlock().update(vb, m); 437 438 // Everything is allowed (even junk, which isn't the filter's job to police) 439 Collections::VB::Filter vbf(f, vbm); 440 EXPECT_TRUE(vbf.checkAndUpdate( 441 {{"anykey", DocNamespace::DefaultCollection}, 0, 0, nullptr, 0})); 442 EXPECT_TRUE(vbf.checkAndUpdate( 443 {{"fruit$apple", DocNamespace::Collections}, 0, 0, nullptr, 0})); 444 EXPECT_TRUE(vbf.checkAndUpdate( 445 {{"meat$steak", DocNamespace::Collections}, 0, 0, nullptr, 0})); 446 EXPECT_TRUE(vbf.checkAndUpdate( 447 {{"dairy$milk", DocNamespace::Collections}, 0, 0, nullptr, 0})); 448 EXPECT_TRUE(vbf.checkAndUpdate( 449 {{"JUNK!!", DocNamespace::Collections}, 0, 0, nullptr, 0})); 450} 451 452/** 453 * Create a filter which blocks the default collection 454 */ 455TEST_F(CollectionsVBFilterTest, no_default) { 456 Collections::Manifest m( 457 R"({"separator":"$","uid":"0",)" 458 R"("collections":[{"name":"$default","uid":"0"}, 459 {"name":"vegetable","uid":"1"}, 460 {"name":"meat","uid":"3"}, 461 {"name":"fruit", "uid":"4"}, 462 {"name":"dairy","uid":"5"}]})"); 463 Collections::VB::Manifest vbm({}); 464 vbm.wlock().update(vb, m); 465 466 std::string jsonFilter = R"({"collections":["fruit", "meat"]})"; 467 boost::optional<const std::string&> json(jsonFilter); 468 Collections::Filter f(json, &m); 469 470 // Now filter! 471 Collections::VB::Filter vbf(f, vbm); 472 EXPECT_FALSE(vbf.checkAndUpdate( 473 {{"anykey", DocNamespace::DefaultCollection}, 0, 0, nullptr, 0})); 474 EXPECT_TRUE(vbf.checkAndUpdate( 475 {{"fruit$apple", DocNamespace::Collections}, 0, 0, nullptr, 0})); 476 EXPECT_TRUE(vbf.checkAndUpdate( 477 {{"meat$steak", DocNamespace::Collections}, 0, 0, nullptr, 0})); 478 EXPECT_FALSE(vbf.checkAndUpdate( 479 {{"dairy$milk", DocNamespace::Collections}, 0, 0, nullptr, 0})); 480 EXPECT_FALSE(vbf.checkAndUpdate( 481 {{"JUNK!!", DocNamespace::Collections}, 0, 0, nullptr, 0})); 482} 483 484/** 485 * Check we can remove collections from the filter (which live DCP may do) and 486 * check ::checkAndUpdate works as expected 487 */ 488TEST_F(CollectionsVBFilterTest, remove1) { 489 Collections::Manifest m( 490 R"({"separator":"$","uid":"0",)" 491 R"("collections":[{"name":"vegetable","uid":"1"}, 492 {"name":"meat","uid":"3"}, 493 {"name":"fruit", "uid":"4"}, 494 {"name":"dairy","uid":"5"}]})"); 495 Collections::VB::Manifest vbm({}); 496 vbm.wlock().update(vb, m); 497 498 std::string jsonFilter = R"({"collections":["fruit", "meat"]})"; 499 boost::optional<const std::string&> json(jsonFilter); 500 501 Collections::Filter f(json, &m); 502 Collections::VB::Filter vbf(f, vbm); 503 EXPECT_TRUE(vbf.checkAndUpdate( 504 {{"fruit$apple", DocNamespace::Collections}, 0, 0, nullptr, 0})); 505 506 // Process a deletion of fruit 507 Item deleteFruit{ 508 {"$collections:fruit", DocNamespace::System}, 0, 0, nullptr, 0}; 509 deleteFruit.setDeleted(); 510 EXPECT_TRUE(vbf.checkAndUpdate(deleteFruit)); 511 512 EXPECT_FALSE(vbf.checkAndUpdate( 513 {{"fruit$apple", DocNamespace::Collections}, 0, 0, nullptr, 0})); 514 515 EXPECT_TRUE(vbf.checkAndUpdate( 516 {{"meat$steak", DocNamespace::Collections}, 0, 0, nullptr, 0})); 517 518 // Process a deletion of meat 519 Item deleteMeat{ 520 {"$collections:meat", DocNamespace::System}, 0, 0, nullptr, 0}; 521 deleteMeat.setDeleted(); 522 EXPECT_TRUE(vbf.checkAndUpdate(deleteMeat)); 523 EXPECT_TRUE(vbf.empty()); // now empty 524 EXPECT_FALSE(vbf.checkAndUpdate(deleteMeat)); // no more meat for you 525} 526 527/** 528 * Check we can remove collections from the filter (which live DCP may do) and 529 * check ::allow works as expected 530 * This test includes checking we can remove $default 531 */ 532TEST_F(CollectionsVBFilterTest, remove2) { 533 Collections::Manifest m( 534 R"({"separator":"$","uid":"0",)" 535 R"("collections":[{"name":"$default","uid":"0"}, 536 {"name":"meat","uid":"3"}, 537 {"name":"fruit", "uid":"4"}, 538 {"name":"dairy","uid":"5"}]})"); 539 Collections::VB::Manifest vbm({}); 540 vbm.wlock().update(vb, m); 541 542 std::string jsonFilter = R"({"collections":["$default", "meat"]})"; 543 boost::optional<const std::string&> json(jsonFilter); 544 545 Collections::Filter f(json, &m); 546 Collections::VB::Filter vbf(f, vbm); 547 EXPECT_TRUE(vbf.checkAndUpdate( 548 {{"anykey", DocNamespace::DefaultCollection}, 0, 0, nullptr, 0})); 549 // Process a deletion of $default 550 Item deleteDefault{ 551 {"$collections:$default", DocNamespace::System}, 0, 0, nullptr, 0}; 552 deleteDefault.setDeleted(); 553 EXPECT_TRUE(vbf.checkAndUpdate(deleteDefault)); 554 EXPECT_FALSE(vbf.checkAndUpdate( 555 {{"anykey", DocNamespace::DefaultCollection}, 0, 0, nullptr, 0})); 556 557 EXPECT_TRUE(vbf.checkAndUpdate( 558 {{"meat$steak", DocNamespace::Collections}, 0, 0, nullptr, 0})); 559 // Process a deletion of meat 560 Item deleteMeat{ 561 {"$collections:meat", DocNamespace::System}, 0, 0, nullptr, 0}; 562 deleteMeat.setDeleted(); 563 EXPECT_TRUE(vbf.checkAndUpdate(deleteMeat)); 564 EXPECT_FALSE(vbf.checkAndUpdate( 565 {{"meat$apple", DocNamespace::Collections}, 0, 0, nullptr, 0})); 566 EXPECT_TRUE(vbf.empty()); // now empty 567 EXPECT_FALSE(vbf.checkAndUpdate(deleteMeat)); // no more meat for you 568 EXPECT_FALSE(vbf.checkAndUpdate( 569 {{"meat$steak", DocNamespace::Collections}, 0, 0, nullptr, 0})); 570} 571 572/** 573 * Test that a filter allows the right system events. 574 * This test creates a passthrough filter so everything is allowed. 575 */ 576TEST_F(CollectionsVBFilterTest, system_events1) { 577 Collections::Manifest m( 578 R"({"separator":"$","uid":"0",)" 579 R"("collections":[{"name":"$default","uid":"0"}, 580 {"name":"meat","uid":"3"}, 581 {"name":"fruit", "uid":"4"}]})"); 582 Collections::VB::Manifest vbm({}); 583 vbm.wlock().update(vb, m); 584 585 std::string jsonFilter; 586 boost::optional<const std::string&> json(jsonFilter); 587 588 Collections::Filter f(json, &m); 589 Collections::VB::Filter vbf(f, vbm); 590 591 // meat system event is allowed by the meat filter 592 EXPECT_TRUE(vbf.checkAndUpdate( 593 *SystemEventFactory::make(SystemEvent::Collection, "meat", 0, {}))); 594 595 // $default system event is allowed by the filter 596 EXPECT_TRUE(vbf.checkAndUpdate(*SystemEventFactory::make( 597 SystemEvent::Collection, "$default", 0, {}))); 598 599 // dairy system event is allowed even though dairy doesn't exist in the 600 // manifest, we wouldn't actually create this event as dairy isn't present 601 // but this just shows the passthrough interface at work. 602 EXPECT_TRUE(vbf.checkAndUpdate(*SystemEventFactory::make( 603 SystemEvent::Collection, "dairy", 0, {}))); 604 605 // A change of separator is also allowed 606 EXPECT_TRUE(vbf.checkAndUpdate(*SystemEventFactory::make( 607 SystemEvent::CollectionsSeparatorChanged, "::", 0, {}))); 608} 609 610/** 611 * Test that a filter allows the right system events. 612 * This test creates a filter where only matching events are allowed 613 */ 614TEST_F(CollectionsVBFilterTest, system_events2) { 615 Collections::Manifest m( 616 R"({"separator":"$","uid":"0",)" 617 R"("collections":[{"name":"$default","uid":"0"}, 618 {"name":"meat","uid":"3"}, 619 {"name":"fruit", "uid":"4"}, 620 {"name":"dairy","uid":"5"}]})"); 621 Collections::VB::Manifest vbm({}); 622 vbm.wlock().update(vb, m); 623 624 // only events for default and meat are allowed 625 std::string jsonFilter = R"({"collections":["$default", "meat"]})"; 626 boost::optional<const std::string&> json(jsonFilter); 627 628 Collections::Filter f(json, &m); 629 Collections::VB::Filter vbf(f, vbm); 630 631 // meat system event is allowed by the meat filter 632 EXPECT_TRUE(vbf.checkAndUpdate( 633 *SystemEventFactory::make(SystemEvent::Collection, "meat", 0, {}))); 634 635 // $default system event is allowed by the filter 636 EXPECT_TRUE(vbf.checkAndUpdate(*SystemEventFactory::make( 637 SystemEvent::Collection, "$default", 0, {}))); 638 639 // dairy system event is not allowed by the filter 640 EXPECT_FALSE(vbf.checkAndUpdate(*SystemEventFactory::make( 641 SystemEvent::Collection, "dairy", 0, {}))); 642 643 // A change of separator is also allowed 644 EXPECT_TRUE(vbf.checkAndUpdate(*SystemEventFactory::make( 645 SystemEvent::CollectionsSeparatorChanged, "::", 0, {}))); 646} 647 648/** 649 * Test that a filter allows the right system events. 650 * This test creates a 'legacy' filter, one which an old DCP client would be 651 * attached to - no system events at all are allowed. 652 */ 653TEST_F(CollectionsVBFilterTest, system_events3) { 654 Collections::Manifest m( 655 R"({"separator":"$","uid":"0",)" 656 R"("collections":[{"name":"$default","uid":"0"}, 657 {"name":"meat","uid":"3"}, 658 {"name":"fruit", "uid":"4"}, 659 {"name":"dairy","uid":"5"}]})"); 660 Collections::VB::Manifest vbm({}); 661 vbm.wlock().update(vb, m); 662 663 boost::optional<const std::string&> json; 664 665 Collections::Filter f(json, &m); 666 Collections::VB::Filter vbf(f, vbm); 667 668 // All system events dropped by this empty/legacy filter 669 EXPECT_FALSE(vbf.checkAndUpdate( 670 *SystemEventFactory::make(SystemEvent::Collection, "meat", 0, {}))); 671 EXPECT_FALSE(vbf.checkAndUpdate(*SystemEventFactory::make( 672 SystemEvent::Collection, "$default", 0, {}))); 673 EXPECT_FALSE(vbf.checkAndUpdate(*SystemEventFactory::make( 674 SystemEvent::Collection, "dairy", 0, {}))); 675 EXPECT_FALSE(vbf.checkAndUpdate(*SystemEventFactory::make( 676 SystemEvent::CollectionsSeparatorChanged, "::", 0, {}))); 677}