github.com/m3db/m3@v1.5.0/src/metrics/rules/store/kv/store_test.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package kv 22 23 import ( 24 "errors" 25 "fmt" 26 "testing" 27 "time" 28 29 "github.com/m3db/m3/src/cluster/kv/mem" 30 merrors "github.com/m3db/m3/src/metrics/errors" 31 "github.com/m3db/m3/src/metrics/generated/proto/aggregationpb" 32 "github.com/m3db/m3/src/metrics/generated/proto/metricpb" 33 "github.com/m3db/m3/src/metrics/generated/proto/pipelinepb" 34 "github.com/m3db/m3/src/metrics/generated/proto/policypb" 35 "github.com/m3db/m3/src/metrics/generated/proto/rulepb" 36 "github.com/m3db/m3/src/metrics/rules" 37 "github.com/m3db/m3/src/metrics/rules/view" 38 39 "github.com/stretchr/testify/require" 40 ) 41 42 const ( 43 testNamespaceKey = "testKey" 44 testNamespace = "fooNs" 45 testRuleSetKeyFmt = "rules/%s" 46 ) 47 48 var ( 49 testNamespaces = &rulepb.Namespaces{ 50 Namespaces: []*rulepb.Namespace{ 51 &rulepb.Namespace{ 52 Name: "fooNs", 53 Snapshots: []*rulepb.NamespaceSnapshot{ 54 &rulepb.NamespaceSnapshot{ 55 ForRulesetVersion: 1, 56 Tombstoned: false, 57 }, 58 &rulepb.NamespaceSnapshot{ 59 ForRulesetVersion: 2, 60 Tombstoned: false, 61 }, 62 }, 63 }, 64 &rulepb.Namespace{ 65 Name: "barNs", 66 Snapshots: []*rulepb.NamespaceSnapshot{ 67 &rulepb.NamespaceSnapshot{ 68 ForRulesetVersion: 1, 69 Tombstoned: false, 70 }, 71 &rulepb.NamespaceSnapshot{ 72 ForRulesetVersion: 2, 73 Tombstoned: true, 74 }, 75 }, 76 }, 77 }, 78 } 79 80 testRuleSetKey = fmt.Sprintf(testRuleSetKeyFmt, testNamespace) 81 testRuleSet = &rulepb.RuleSet{ 82 Uuid: "ruleset", 83 Namespace: "fooNs", 84 CreatedAtNanos: 1234, 85 LastUpdatedAtNanos: 5678, 86 Tombstoned: false, 87 CutoverNanos: 34923, 88 MappingRules: []*rulepb.MappingRule{ 89 &rulepb.MappingRule{ 90 Uuid: "12669817-13ae-40e6-ba2f-33087b262c68", 91 Snapshots: []*rulepb.MappingRuleSnapshot{ 92 &rulepb.MappingRuleSnapshot{ 93 Name: "foo", 94 Tombstoned: false, 95 CutoverNanos: 12345, 96 Filter: "tag1:value1 tag2:value2", 97 StoragePolicies: []*policypb.StoragePolicy{ 98 &policypb.StoragePolicy{ 99 Resolution: policypb.Resolution{ 100 WindowSize: int64(10 * time.Second), 101 Precision: int64(time.Second), 102 }, 103 Retention: policypb.Retention{ 104 Period: int64(24 * time.Hour), 105 }, 106 }, 107 }, 108 Tags: []*metricpb.Tag{ 109 { 110 Name: []byte("name"), 111 Value: []byte("name"), 112 }, 113 }, 114 }, 115 &rulepb.MappingRuleSnapshot{ 116 Name: "foo", 117 Tombstoned: false, 118 CutoverNanos: 67890, 119 Filter: "tag3:value3 tag4:value4", 120 StoragePolicies: []*policypb.StoragePolicy{ 121 &policypb.StoragePolicy{ 122 Resolution: policypb.Resolution{ 123 WindowSize: int64(time.Minute), 124 Precision: int64(time.Minute), 125 }, 126 Retention: policypb.Retention{ 127 Period: int64(24 * time.Hour), 128 }, 129 }, 130 &policypb.StoragePolicy{ 131 Resolution: policypb.Resolution{ 132 WindowSize: int64(5 * time.Minute), 133 Precision: int64(time.Minute), 134 }, 135 Retention: policypb.Retention{ 136 Period: int64(48 * time.Hour), 137 }, 138 }, 139 }, 140 Tags: []*metricpb.Tag{ 141 { 142 Name: []byte("name"), 143 Value: []byte("name"), 144 }, 145 }, 146 }, 147 }, 148 }, 149 &rulepb.MappingRule{ 150 Uuid: "12669817-13ae-40e6-ba2f-33087b262c68", 151 Snapshots: []*rulepb.MappingRuleSnapshot{ 152 &rulepb.MappingRuleSnapshot{ 153 Name: "dup", 154 Tombstoned: false, 155 CutoverNanos: 12345, 156 Filter: "tag1:value1 tag2:value2", 157 AggregationTypes: []aggregationpb.AggregationType{ 158 aggregationpb.AggregationType_P999, 159 }, 160 StoragePolicies: []*policypb.StoragePolicy{ 161 &policypb.StoragePolicy{ 162 Resolution: policypb.Resolution{ 163 WindowSize: int64(10 * time.Second), 164 Precision: int64(time.Second), 165 }, 166 Retention: policypb.Retention{ 167 Period: int64(24 * time.Hour), 168 }, 169 }, 170 }, 171 Tags: []*metricpb.Tag{ 172 { 173 Name: []byte("name"), 174 Value: []byte("name"), 175 }, 176 }, 177 }, 178 }, 179 }, 180 }, 181 RollupRules: []*rulepb.RollupRule{ 182 &rulepb.RollupRule{ 183 Uuid: "12669817-13ae-40e6-ba2f-33087b262c68", 184 Snapshots: []*rulepb.RollupRuleSnapshot{ 185 &rulepb.RollupRuleSnapshot{ 186 Name: "foo2", 187 Tombstoned: false, 188 CutoverNanos: 12345, 189 Filter: "tag1:value1 tag2:value2", 190 Tags: []*metricpb.Tag{ 191 { 192 Name: []byte("name"), 193 Value: []byte("name"), 194 }, 195 }, 196 TargetsV2: []*rulepb.RollupTargetV2{ 197 &rulepb.RollupTargetV2{ 198 Pipeline: &pipelinepb.Pipeline{ 199 Ops: []pipelinepb.PipelineOp{ 200 { 201 Type: pipelinepb.PipelineOp_ROLLUP, 202 Rollup: &pipelinepb.RollupOp{ 203 NewName: "rName1", 204 Tags: []string{"rtagName1", "rtagName2"}, 205 }, 206 }, 207 }, 208 }, 209 StoragePolicies: []*policypb.StoragePolicy{ 210 &policypb.StoragePolicy{ 211 Resolution: policypb.Resolution{ 212 WindowSize: int64(10 * time.Second), 213 Precision: int64(time.Second), 214 }, 215 Retention: policypb.Retention{ 216 Period: int64(24 * time.Hour), 217 }, 218 }, 219 }, 220 }, 221 }, 222 }, 223 &rulepb.RollupRuleSnapshot{ 224 Name: "bar", 225 Tombstoned: true, 226 CutoverNanos: 67890, 227 Filter: "tag3:value3 tag4:value4", 228 Tags: []*metricpb.Tag{ 229 { 230 Name: []byte("name"), 231 Value: []byte("name"), 232 }, 233 }, 234 TargetsV2: []*rulepb.RollupTargetV2{ 235 &rulepb.RollupTargetV2{ 236 Pipeline: &pipelinepb.Pipeline{ 237 Ops: []pipelinepb.PipelineOp{ 238 { 239 Type: pipelinepb.PipelineOp_ROLLUP, 240 Rollup: &pipelinepb.RollupOp{ 241 NewName: "rName1", 242 Tags: []string{"rtagName1", "rtagName2"}, 243 AggregationTypes: []aggregationpb.AggregationType{ 244 aggregationpb.AggregationType_MEAN, 245 }, 246 }, 247 }, 248 }, 249 }, 250 StoragePolicies: []*policypb.StoragePolicy{ 251 &policypb.StoragePolicy{ 252 Resolution: policypb.Resolution{ 253 WindowSize: int64(10 * time.Second), 254 Precision: int64(time.Second), 255 }, 256 Retention: policypb.Retention{ 257 Period: int64(24 * time.Hour), 258 }, 259 }, 260 &policypb.StoragePolicy{ 261 Resolution: policypb.Resolution{ 262 WindowSize: int64(5 * time.Minute), 263 Precision: int64(time.Minute), 264 }, 265 Retention: policypb.Retention{ 266 Period: int64(48 * time.Hour), 267 }, 268 }, 269 }, 270 }, 271 }, 272 }, 273 }, 274 }, 275 &rulepb.RollupRule{ 276 Uuid: "12669817-13ae-40e6-ba2f-33087b262c68", 277 Snapshots: []*rulepb.RollupRuleSnapshot{ 278 &rulepb.RollupRuleSnapshot{ 279 Name: "foo", 280 Tombstoned: false, 281 CutoverNanos: 12345, 282 Filter: "tag1:value1 tag2:value2", 283 Tags: []*metricpb.Tag{ 284 { 285 Name: []byte("name"), 286 Value: []byte("name"), 287 }, 288 }, 289 TargetsV2: []*rulepb.RollupTargetV2{ 290 &rulepb.RollupTargetV2{ 291 Pipeline: &pipelinepb.Pipeline{ 292 Ops: []pipelinepb.PipelineOp{ 293 { 294 Type: pipelinepb.PipelineOp_ROLLUP, 295 Rollup: &pipelinepb.RollupOp{ 296 NewName: "rName1", 297 Tags: []string{"rtagName1", "rtagName2"}, 298 }, 299 }, 300 }, 301 }, 302 StoragePolicies: []*policypb.StoragePolicy{ 303 &policypb.StoragePolicy{ 304 Resolution: policypb.Resolution{ 305 WindowSize: int64(10 * time.Second), 306 Precision: int64(time.Second), 307 }, 308 Retention: policypb.Retention{ 309 Period: int64(24 * time.Hour), 310 }, 311 }, 312 }, 313 }, 314 }, 315 }, 316 &rulepb.RollupRuleSnapshot{ 317 Name: "baz", 318 Tombstoned: false, 319 CutoverNanos: 67890, 320 Filter: "tag3:value3 tag4:value4", 321 Tags: []*metricpb.Tag{ 322 { 323 Name: []byte("name"), 324 Value: []byte("name"), 325 }, 326 }, 327 TargetsV2: []*rulepb.RollupTargetV2{ 328 &rulepb.RollupTargetV2{ 329 Pipeline: &pipelinepb.Pipeline{ 330 Ops: []pipelinepb.PipelineOp{ 331 { 332 Type: pipelinepb.PipelineOp_ROLLUP, 333 Rollup: &pipelinepb.RollupOp{ 334 NewName: "rName1", 335 Tags: []string{"rtagName1", "rtagName2"}, 336 AggregationTypes: []aggregationpb.AggregationType{ 337 aggregationpb.AggregationType_MEAN, 338 }, 339 }, 340 }, 341 }, 342 }, 343 StoragePolicies: []*policypb.StoragePolicy{ 344 &policypb.StoragePolicy{ 345 Resolution: policypb.Resolution{ 346 WindowSize: int64(time.Minute), 347 Precision: int64(time.Minute), 348 }, 349 Retention: policypb.Retention{ 350 Period: int64(24 * time.Hour), 351 }, 352 }, 353 &policypb.StoragePolicy{ 354 Resolution: policypb.Resolution{ 355 WindowSize: int64(5 * time.Minute), 356 Precision: int64(time.Minute), 357 }, 358 Retention: policypb.Retention{ 359 Period: int64(48 * time.Hour), 360 }, 361 }, 362 }, 363 }, 364 }, 365 }, 366 }, 367 }, 368 &rulepb.RollupRule{ 369 Uuid: "12669817-13ae-40e6-ba2f-33087b262c68", 370 Snapshots: []*rulepb.RollupRuleSnapshot{ 371 &rulepb.RollupRuleSnapshot{ 372 Name: "dup", 373 Tombstoned: false, 374 CutoverNanos: 12345, 375 Filter: "tag1:value1 tag2:value2", 376 Tags: []*metricpb.Tag{ 377 { 378 Name: []byte("name"), 379 Value: []byte("name"), 380 }, 381 }, 382 TargetsV2: []*rulepb.RollupTargetV2{ 383 &rulepb.RollupTargetV2{ 384 Pipeline: &pipelinepb.Pipeline{ 385 Ops: []pipelinepb.PipelineOp{ 386 { 387 Type: pipelinepb.PipelineOp_ROLLUP, 388 Rollup: &pipelinepb.RollupOp{ 389 NewName: "rName1", 390 Tags: []string{"rtagName1", "rtagName2"}, 391 }, 392 }, 393 }, 394 }, 395 StoragePolicies: []*policypb.StoragePolicy{ 396 &policypb.StoragePolicy{ 397 Resolution: policypb.Resolution{ 398 WindowSize: int64(10 * time.Second), 399 Precision: int64(time.Second), 400 }, 401 Retention: policypb.Retention{ 402 Period: int64(24 * time.Hour), 403 }, 404 }, 405 }, 406 }, 407 }, 408 }, 409 }, 410 }, 411 }, 412 } 413 ) 414 415 func TestRuleSetKey(t *testing.T) { 416 s := testStore() 417 defer s.Close() 418 419 key := s.(*store).ruleSetKey(testNamespace) 420 require.Equal(t, "rules/fooNs", key) 421 } 422 423 func TestNewStore(t *testing.T) { 424 opts := NewStoreOptions(testNamespaceKey, testRuleSetKeyFmt, nil) 425 kvStore := mem.NewStore() 426 s := NewStore(kvStore, opts).(*store) 427 defer s.Close() 428 429 require.Equal(t, s.kvStore, kvStore) 430 require.Equal(t, s.opts, opts) 431 } 432 433 func TestReadNamespaces(t *testing.T) { 434 s := testStore() 435 defer s.Close() 436 437 _, e := s.(*store).kvStore.Set(testNamespaceKey, testNamespaces) 438 require.NoError(t, e) 439 nss, err := s.ReadNamespaces() 440 require.NoError(t, err) 441 require.NotNil(t, nss.Namespaces) 442 } 443 444 func TestReadNamespaceNotFound(t *testing.T) { 445 s := testStore() 446 defer s.Close() 447 448 _, err := s.ReadNamespaces() 449 require.IsType(t, merrors.NewNotFoundError(""), err) 450 } 451 452 func TestReadNamespacesError(t *testing.T) { 453 s := testStore() 454 defer s.Close() 455 456 _, e := s.(*store).kvStore.Set(testNamespaceKey, &rulepb.RollupRule{Uuid: "x"}) 457 require.NoError(t, e) 458 nss, err := s.ReadNamespaces() 459 require.Error(t, err) 460 require.Nil(t, nss) 461 } 462 463 func TestReadRuleSet(t *testing.T) { 464 s := testStore() 465 defer s.Close() 466 467 _, e := s.(*store).kvStore.Set(testRuleSetKey, testRuleSet) 468 require.NoError(t, e) 469 rs, err := s.ReadRuleSet(testNamespace) 470 require.NoError(t, err) 471 require.NotNil(t, rs) 472 } 473 474 func TestReadRuleSetNotFound(t *testing.T) { 475 s := testStore() 476 defer s.Close() 477 478 _, err := s.ReadRuleSet(testNamespace) 479 require.IsType(t, merrors.NewNotFoundError(""), err) 480 } 481 482 func TestReadRuleSetError(t *testing.T) { 483 s := testStore() 484 defer s.Close() 485 486 _, e := s.(*store).kvStore.Set(testRuleSetKey, &rulepb.Namespace{Name: "x"}) 487 require.NoError(t, e) 488 rs, err := s.ReadRuleSet("blah") 489 require.Error(t, err) 490 require.Nil(t, rs) 491 } 492 493 func TestWriteAll(t *testing.T) { 494 s := testStore() 495 defer s.Close() 496 497 rs, err := s.ReadRuleSet(testNamespaceKey) 498 require.Error(t, err) 499 require.Nil(t, rs) 500 501 nss, err := s.ReadNamespaces() 502 require.Error(t, err) 503 require.Nil(t, nss) 504 505 mutable := newMutableRuleSetFromProto(t, 0, testRuleSet) 506 namespaces, err := rules.NewNamespaces(0, testNamespaces) 507 require.NoError(t, err) 508 509 err = s.WriteAll(&namespaces, mutable) 510 require.NoError(t, err) 511 512 rs, err = s.ReadRuleSet(testNamespace) 513 require.NoError(t, err) 514 rsProto, err := rs.ToMutableRuleSet().Proto() 515 require.NoError(t, err) 516 require.Equal(t, rsProto, testRuleSet) 517 518 nss, err = s.ReadNamespaces() 519 require.NoError(t, err) 520 nssProto, err := nss.Proto() 521 require.NoError(t, err) 522 require.Equal(t, nssProto, testNamespaces) 523 } 524 525 func TestWriteAllValidationError(t *testing.T) { 526 errInvalidRuleSet := errors.New("invalid ruleset") 527 v := &mockValidator{ 528 validateFn: func(rules.RuleSet) error { return errInvalidRuleSet }, 529 } 530 s := testStoreWithValidator(v) 531 defer s.Close() 532 require.Equal(t, errInvalidRuleSet, s.WriteAll(nil, nil)) 533 } 534 535 func TestWriteAllError(t *testing.T) { 536 s := testStore() 537 defer s.Close() 538 539 rs, err := s.ReadRuleSet(testNamespaceKey) 540 require.Error(t, err) 541 require.Nil(t, rs) 542 543 nss, err := s.ReadNamespaces() 544 require.Error(t, err) 545 require.Nil(t, nss) 546 547 mutable := newMutableRuleSetFromProto(t, 1, testRuleSet) 548 namespaces, err := rules.NewNamespaces(0, testNamespaces) 549 require.NoError(t, err) 550 551 type dataPair struct { 552 nss *rules.Namespaces 553 rs rules.MutableRuleSet 554 } 555 556 otherNss, err := rules.NewNamespaces(1, testNamespaces) 557 require.NoError(t, err) 558 559 badPairs := []dataPair{ 560 dataPair{nil, nil}, 561 dataPair{nil, mutable}, 562 dataPair{&namespaces, nil}, 563 dataPair{&otherNss, mutable}, 564 } 565 566 for _, p := range badPairs { 567 err = s.WriteAll(p.nss, p.rs) 568 require.Error(t, err) 569 } 570 571 _, err = s.ReadRuleSet(testNamespace) 572 require.Error(t, err) 573 574 _, err = s.ReadNamespaces() 575 require.Error(t, err) 576 } 577 578 func TestWriteRuleSetValidationError(t *testing.T) { 579 errInvalidRuleSet := errors.New("invalid ruleset") 580 v := &mockValidator{ 581 validateFn: func(rules.RuleSet) error { return errInvalidRuleSet }, 582 } 583 s := testStoreWithValidator(v) 584 defer s.Close() 585 require.Equal(t, errInvalidRuleSet, s.WriteRuleSet(nil)) 586 } 587 588 func TestWriteRuleSetError(t *testing.T) { 589 s := testStore() 590 defer s.Close() 591 592 rs, err := s.ReadRuleSet(testNamespaceKey) 593 require.Error(t, err) 594 require.Nil(t, rs) 595 596 nss, err := s.ReadNamespaces() 597 require.Error(t, err) 598 require.Nil(t, nss) 599 600 mutable := newMutableRuleSetFromProto(t, 1, testRuleSet) 601 badRuleSets := []rules.MutableRuleSet{mutable, nil} 602 for _, rs := range badRuleSets { 603 err = s.WriteRuleSet(rs) 604 require.Error(t, err) 605 } 606 607 err = s.WriteRuleSet(nil) 608 require.Error(t, err) 609 610 _, err = s.ReadRuleSet(testNamespace) 611 require.Error(t, err) 612 } 613 614 func TestWriteRuleSetStaleDataError(t *testing.T) { 615 s := testStore() 616 defer s.Close() 617 618 mutable := newMutableRuleSetFromProto(t, 0, testRuleSet) 619 err := s.WriteRuleSet(mutable) 620 require.NoError(t, err) 621 622 jumpRuleSet := newMutableRuleSetFromProto(t, 5, testRuleSet) 623 err = s.WriteRuleSet(jumpRuleSet) 624 require.Error(t, err) 625 require.IsType(t, merrors.NewStaleDataError(""), err) 626 } 627 628 func TestWriteNamespace(t *testing.T) { 629 s := testStore() 630 defer s.Close() 631 632 nss, err := rules.NewNamespaces(0, testNamespaces) 633 require.NoError(t, err) 634 635 err = s.WriteNamespaces(&nss) 636 require.NoError(t, err) 637 638 existing, err := s.ReadNamespaces() 639 require.NoError(t, err) 640 641 revived, err := existing.AddNamespace( 642 "new", 643 rules.NewRuleSetUpdateHelper(0).NewUpdateMetadata(time.Now().UnixNano(), "test"), 644 ) 645 require.NoError(t, err) 646 require.False(t, revived) 647 648 // Update should succeed 649 err = s.WriteNamespaces(existing) 650 require.NoError(t, err) 651 } 652 653 func TestWriteNamespaceError(t *testing.T) { 654 s := testStore() 655 defer s.Close() 656 657 err := s.WriteNamespaces(nil) 658 require.Error(t, err) 659 } 660 661 func TestWriteNamespacesStaleDataError(t *testing.T) { 662 s := testStore() 663 defer s.Close() 664 665 nss, err := rules.NewNamespaces(0, testNamespaces) 666 require.NoError(t, err) 667 668 // First write should succeed 669 err = s.WriteNamespaces(&nss) 670 require.NoError(t, err) 671 672 // writing again will encounter stale version 673 err = s.WriteNamespaces(&nss) 674 require.Error(t, err) 675 require.IsType(t, merrors.NewStaleDataError(""), err) 676 } 677 678 func TestWriteAllNoNamespace(t *testing.T) { 679 s := testStore() 680 defer s.Close() 681 682 rs, err := s.ReadRuleSet(testNamespaceKey) 683 require.Error(t, err) 684 require.Nil(t, rs) 685 686 nss, err := s.ReadNamespaces() 687 require.Error(t, err) 688 require.Nil(t, nss) 689 690 mutable := newMutableRuleSetFromProto(t, 0, testRuleSet) 691 namespaces, err := rules.NewNamespaces(0, testNamespaces) 692 require.NoError(t, err) 693 694 err = s.WriteAll(&namespaces, mutable) 695 require.NoError(t, err) 696 697 rs, err = s.ReadRuleSet(testNamespace) 698 require.NoError(t, err) 699 700 _, err = s.ReadNamespaces() 701 require.NoError(t, err) 702 703 err = s.WriteRuleSet(rs.ToMutableRuleSet()) 704 require.NoError(t, err) 705 706 rs, err = s.ReadRuleSet(testNamespace) 707 require.NoError(t, err) 708 nss, err = s.ReadNamespaces() 709 require.NoError(t, err) 710 require.Equal(t, nss.Version(), 1) 711 require.Equal(t, rs.Version(), 2) 712 } 713 func TestWriteAllStaleDataError(t *testing.T) { 714 s := testStore() 715 defer s.Close() 716 717 mutable := newMutableRuleSetFromProto(t, 0, testRuleSet) 718 namespaces, err := rules.NewNamespaces(0, testNamespaces) 719 require.NoError(t, err) 720 721 err = s.WriteAll(&namespaces, mutable) 722 require.NoError(t, err) 723 724 jumpNamespaces, err := rules.NewNamespaces(5, testNamespaces) 725 require.NoError(t, err) 726 err = s.WriteAll(&jumpNamespaces, mutable) 727 require.Error(t, err) 728 require.IsType(t, merrors.NewStaleDataError(""), err) 729 } 730 731 func testStore() rules.Store { 732 return testStoreWithValidator(nil) 733 } 734 735 func testStoreWithValidator(validator rules.Validator) rules.Store { 736 opts := NewStoreOptions(testNamespaceKey, testRuleSetKeyFmt, validator) 737 kvStore := mem.NewStore() 738 return NewStore(kvStore, opts) 739 } 740 741 // newMutableRuleSetFromProto creates a new MutableRuleSet from a proto object. 742 func newMutableRuleSetFromProto( 743 t *testing.T, 744 version int, 745 rs *rulepb.RuleSet, 746 ) rules.MutableRuleSet { 747 // Takes a blank Options stuct because none of the mutation functions need the options. 748 roRuleSet, err := rules.NewRuleSetFromProto(version, rs, rules.NewOptions()) 749 require.NoError(t, err) 750 return roRuleSet.ToMutableRuleSet() 751 } 752 753 type validateFn func(rs rules.RuleSet) error 754 755 type mockValidator struct { 756 validateFn validateFn 757 } 758 759 func (v *mockValidator) Validate(rs rules.RuleSet) error { return v.validateFn(rs) } 760 func (v *mockValidator) ValidateSnapshot(snapshot view.RuleSet) error { return nil } 761 func (v *mockValidator) Close() {}