github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/metrics/rules/namespace_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 rules
    22  
    23  import (
    24  	"testing"
    25  
    26  	"github.com/m3db/m3/src/metrics/generated/proto/rulepb"
    27  	"github.com/m3db/m3/src/metrics/rules/view"
    28  
    29  	"github.com/stretchr/testify/require"
    30  )
    31  
    32  func TestNewNamespaceSnapshotFromNilProto(t *testing.T) {
    33  	_, err := newNamespaceSnapshot(nil)
    34  	require.Equal(t, err, errNilNamespaceSnapshotProto)
    35  }
    36  
    37  func TestNewNamespaceSnapshotFromValidProto(t *testing.T) {
    38  	snapshot, err := newNamespaceSnapshot(&rulepb.NamespaceSnapshot{
    39  		ForRulesetVersion:  123,
    40  		Tombstoned:         true,
    41  		LastUpdatedAtNanos: 456,
    42  		LastUpdatedBy:      "someone",
    43  	})
    44  	require.NoError(t, err)
    45  	require.Equal(t, 123, snapshot.ForRuleSetVersion())
    46  	require.Equal(t, true, snapshot.Tombstoned())
    47  	require.Equal(t, int64(456), snapshot.LastUpdatedAtNanos())
    48  	require.Equal(t, "someone", snapshot.LastUpdatedBy())
    49  }
    50  
    51  func TestNamespaceSnapshotToProto(t *testing.T) {
    52  	snapshot := NamespaceSnapshot{
    53  		forRuleSetVersion:  123,
    54  		tombstoned:         true,
    55  		lastUpdatedAtNanos: 456,
    56  		lastUpdatedBy:      "someone",
    57  	}
    58  	proto := snapshot.Proto()
    59  	require.Equal(t, int32(123), proto.ForRulesetVersion)
    60  	require.Equal(t, true, proto.Tombstoned)
    61  	require.Equal(t, int64(456), proto.LastUpdatedAtNanos)
    62  	require.Equal(t, "someone", proto.LastUpdatedBy)
    63  }
    64  
    65  func TestNamespaceSnapshotRoundTrip(t *testing.T) {
    66  	proto := &rulepb.NamespaceSnapshot{
    67  		ForRulesetVersion:  123,
    68  		Tombstoned:         true,
    69  		LastUpdatedAtNanos: 456,
    70  		LastUpdatedBy:      "someone",
    71  	}
    72  	snapshot, err := newNamespaceSnapshot(proto)
    73  	require.NoError(t, err)
    74  	res := snapshot.Proto()
    75  	require.Equal(t, proto, res)
    76  }
    77  
    78  func TestNamespaceView(t *testing.T) {
    79  	ns := Namespace{
    80  		name: b("foo"),
    81  		snapshots: []NamespaceSnapshot{
    82  			{
    83  				forRuleSetVersion:  123,
    84  				tombstoned:         false,
    85  				lastUpdatedAtNanos: 456000000,
    86  				lastUpdatedBy:      "someone",
    87  			},
    88  			{
    89  				forRuleSetVersion:  456,
    90  				tombstoned:         true,
    91  				lastUpdatedAtNanos: 7890000000,
    92  				lastUpdatedBy:      "someone else",
    93  			},
    94  		},
    95  	}
    96  
    97  	expected := []view.Namespace{
    98  		{
    99  			ID:                  "foo",
   100  			ForRuleSetVersion:   123,
   101  			Tombstoned:          false,
   102  			LastUpdatedAtMillis: 456,
   103  			LastUpdatedBy:       "someone",
   104  		},
   105  		{
   106  			ID:                  "foo",
   107  			ForRuleSetVersion:   456,
   108  			Tombstoned:          true,
   109  			LastUpdatedAtMillis: 7890,
   110  			LastUpdatedBy:       "someone else",
   111  		},
   112  	}
   113  	for i := range ns.snapshots {
   114  		res, err := ns.NamespaceView(i)
   115  		require.NoError(t, err)
   116  		require.Equal(t, expected[i], res)
   117  	}
   118  }
   119  
   120  func TestNamespaceViewError(t *testing.T) {
   121  	n := Namespace{
   122  		name: b("test"),
   123  		snapshots: []NamespaceSnapshot{
   124  			NamespaceSnapshot{forRuleSetVersion: 3, tombstoned: false},
   125  			NamespaceSnapshot{forRuleSetVersion: 4, tombstoned: true},
   126  		},
   127  	}
   128  
   129  	badIdx := []int{-2, 2, 30}
   130  	for _, i := range badIdx {
   131  		_, err := n.NamespaceView(i)
   132  		require.Equal(t, errNamespaceSnapshotIndexOutOfRange, err)
   133  	}
   134  }
   135  
   136  func TestNamespaceClone(t *testing.T) {
   137  	ns := Namespace{
   138  		name: b("foo"),
   139  		snapshots: []NamespaceSnapshot{
   140  			{
   141  				forRuleSetVersion:  123,
   142  				tombstoned:         false,
   143  				lastUpdatedAtNanos: 456,
   144  				lastUpdatedBy:      "someone",
   145  			},
   146  			{
   147  				forRuleSetVersion:  456,
   148  				tombstoned:         true,
   149  				lastUpdatedAtNanos: 7890,
   150  				lastUpdatedBy:      "someone else",
   151  			},
   152  		},
   153  	}
   154  
   155  	// Assert a clone looks the same as the original.
   156  	nsClone := ns.clone()
   157  	require.Equal(t, ns, nsClone)
   158  
   159  	// Assert changing the clone does not change the original.
   160  	nsClone.snapshots[0].forRuleSetVersion = 2934
   161  	require.NotEqual(t, ns, nsClone)
   162  }
   163  
   164  func TestNewNamespaceFromNilProto(t *testing.T) {
   165  	_, err := newNamespace(nil)
   166  	require.Equal(t, err, errNilNamespaceProto)
   167  }
   168  
   169  func TestNewNamespaceFromValidProto(t *testing.T) {
   170  	ns, err := newNamespace(&rulepb.Namespace{
   171  		Name: "foo",
   172  		Snapshots: []*rulepb.NamespaceSnapshot{
   173  			&rulepb.NamespaceSnapshot{
   174  				ForRulesetVersion:  123,
   175  				Tombstoned:         false,
   176  				LastUpdatedAtNanos: 456,
   177  				LastUpdatedBy:      "someone",
   178  			},
   179  			&rulepb.NamespaceSnapshot{
   180  				ForRulesetVersion:  456,
   181  				Tombstoned:         true,
   182  				LastUpdatedAtNanos: 7890,
   183  				LastUpdatedBy:      "someone else",
   184  			},
   185  		},
   186  	})
   187  	expected := []NamespaceSnapshot{
   188  		{
   189  			forRuleSetVersion:  123,
   190  			tombstoned:         false,
   191  			lastUpdatedAtNanos: 456,
   192  			lastUpdatedBy:      "someone",
   193  		},
   194  		{
   195  			forRuleSetVersion:  456,
   196  			tombstoned:         true,
   197  			lastUpdatedAtNanos: 7890,
   198  			lastUpdatedBy:      "someone else",
   199  		},
   200  	}
   201  	require.NoError(t, err)
   202  	require.Equal(t, []byte("foo"), ns.Name())
   203  	require.Equal(t, expected, ns.Snapshots())
   204  }
   205  
   206  func TestNamespaceToProto(t *testing.T) {
   207  	ns := Namespace{
   208  		name: b("foo"),
   209  		snapshots: []NamespaceSnapshot{
   210  			{
   211  				forRuleSetVersion:  123,
   212  				tombstoned:         false,
   213  				lastUpdatedAtNanos: 456,
   214  				lastUpdatedBy:      "someone",
   215  			},
   216  			{
   217  				forRuleSetVersion:  456,
   218  				tombstoned:         true,
   219  				lastUpdatedAtNanos: 7890,
   220  				lastUpdatedBy:      "someone else",
   221  			},
   222  		},
   223  	}
   224  	res, err := ns.Proto()
   225  	require.NoError(t, err)
   226  
   227  	expected := &rulepb.Namespace{
   228  		Name: "foo",
   229  		Snapshots: []*rulepb.NamespaceSnapshot{
   230  			&rulepb.NamespaceSnapshot{
   231  				ForRulesetVersion:  123,
   232  				Tombstoned:         false,
   233  				LastUpdatedAtNanos: 456,
   234  				LastUpdatedBy:      "someone",
   235  			},
   236  			&rulepb.NamespaceSnapshot{
   237  				ForRulesetVersion:  456,
   238  				Tombstoned:         true,
   239  				LastUpdatedAtNanos: 7890,
   240  				LastUpdatedBy:      "someone else",
   241  			},
   242  		},
   243  	}
   244  	require.Equal(t, expected, res)
   245  }
   246  
   247  func TestNamespaceToProtoNoSnapshots(t *testing.T) {
   248  	badNs := Namespace{
   249  		name: []byte("foo"),
   250  	}
   251  	res, err := badNs.Proto()
   252  	require.Equal(t, errNilNamespaceSnapshot, err)
   253  	require.Nil(t, res)
   254  }
   255  
   256  func TestNamespaceRoundTrip(t *testing.T) {
   257  	testNs := &rulepb.Namespace{
   258  		Name: "foo",
   259  		Snapshots: []*rulepb.NamespaceSnapshot{
   260  			&rulepb.NamespaceSnapshot{
   261  				ForRulesetVersion:  123,
   262  				Tombstoned:         false,
   263  				LastUpdatedAtNanos: 456,
   264  				LastUpdatedBy:      "someone",
   265  			},
   266  			&rulepb.NamespaceSnapshot{
   267  				ForRulesetVersion:  456,
   268  				Tombstoned:         true,
   269  				LastUpdatedAtNanos: 7890,
   270  				LastUpdatedBy:      "someone else",
   271  			},
   272  		},
   273  	}
   274  
   275  	ns, err := newNamespace(testNs)
   276  	require.NoError(t, err)
   277  
   278  	res, err := ns.Proto()
   279  	require.NoError(t, err)
   280  
   281  	require.Equal(t, testNs, res)
   282  }
   283  
   284  func TestNamespaceMarkTombstoned(t *testing.T) {
   285  	ns := Namespace{
   286  		name: b("foo"),
   287  		snapshots: []NamespaceSnapshot{
   288  			{
   289  				forRuleSetVersion:  1,
   290  				tombstoned:         false,
   291  				lastUpdatedAtNanos: 456,
   292  				lastUpdatedBy:      "someone",
   293  			},
   294  		},
   295  	}
   296  	meta := UpdateMetadata{updatedAtNanos: 789, updatedBy: "someone else"}
   297  	require.NoError(t, ns.markTombstoned(4, meta))
   298  	require.True(t, ns.Tombstoned())
   299  
   300  	expected := Namespace{
   301  		name: b("foo"),
   302  		snapshots: []NamespaceSnapshot{
   303  			{
   304  				forRuleSetVersion:  1,
   305  				tombstoned:         false,
   306  				lastUpdatedAtNanos: 456,
   307  				lastUpdatedBy:      "someone",
   308  			},
   309  			{
   310  				forRuleSetVersion:  4,
   311  				tombstoned:         true,
   312  				lastUpdatedAtNanos: 789,
   313  				lastUpdatedBy:      "someone else",
   314  			},
   315  		},
   316  	}
   317  	require.Equal(t, expected, ns)
   318  }
   319  
   320  func TestNamespaceMarkTombstonedAlreadyTombstoned(t *testing.T) {
   321  	ns := Namespace{
   322  		name: b("foo"),
   323  		snapshots: []NamespaceSnapshot{
   324  			{
   325  				forRuleSetVersion:  1,
   326  				tombstoned:         false,
   327  				lastUpdatedAtNanos: 456,
   328  				lastUpdatedBy:      "someone",
   329  			},
   330  			{
   331  				forRuleSetVersion:  4,
   332  				tombstoned:         true,
   333  				lastUpdatedAtNanos: 789,
   334  				lastUpdatedBy:      "someone else",
   335  			},
   336  		},
   337  	}
   338  	require.Equal(t, errNamespaceAlreadyTombstoned, ns.markTombstoned(4, UpdateMetadata{}))
   339  }
   340  
   341  func TestNamespaceRevive(t *testing.T) {
   342  	ns := Namespace{
   343  		name: b("foo"),
   344  		snapshots: []NamespaceSnapshot{
   345  			{
   346  				forRuleSetVersion:  1,
   347  				tombstoned:         false,
   348  				lastUpdatedAtNanos: 456,
   349  				lastUpdatedBy:      "someone",
   350  			},
   351  			{
   352  				forRuleSetVersion:  4,
   353  				tombstoned:         true,
   354  				lastUpdatedAtNanos: 789,
   355  				lastUpdatedBy:      "someone else",
   356  			},
   357  		},
   358  	}
   359  	meta := UpdateMetadata{updatedAtNanos: 2378, updatedBy: "john"}
   360  	require.NoError(t, ns.revive(meta))
   361  
   362  	expected := Namespace{
   363  		name: b("foo"),
   364  		snapshots: []NamespaceSnapshot{
   365  			{
   366  				forRuleSetVersion:  1,
   367  				tombstoned:         false,
   368  				lastUpdatedAtNanos: 456,
   369  				lastUpdatedBy:      "someone",
   370  			},
   371  			{
   372  				forRuleSetVersion:  4,
   373  				tombstoned:         true,
   374  				lastUpdatedAtNanos: 789,
   375  				lastUpdatedBy:      "someone else",
   376  			},
   377  			{
   378  				forRuleSetVersion:  5,
   379  				tombstoned:         false,
   380  				lastUpdatedAtNanos: 2378,
   381  				lastUpdatedBy:      "john",
   382  			},
   383  		},
   384  	}
   385  	require.Equal(t, expected, ns)
   386  }
   387  
   388  func TestNamespaceReviveNotTombstoned(t *testing.T) {
   389  	ns := Namespace{
   390  		name: b("foo"),
   391  		snapshots: []NamespaceSnapshot{
   392  			{
   393  				forRuleSetVersion:  1,
   394  				tombstoned:         false,
   395  				lastUpdatedAtNanos: 456,
   396  				lastUpdatedBy:      "someone",
   397  			},
   398  		},
   399  	}
   400  	require.Equal(t, errNamespaceNotTombstoned, ns.revive(UpdateMetadata{}))
   401  }
   402  
   403  func TestNamespaceReviveNoSnapshots(t *testing.T) {
   404  	ns := Namespace{
   405  		name:      b("foo"),
   406  		snapshots: []NamespaceSnapshot{},
   407  	}
   408  	require.Equal(t, errNoNamespaceSnapshots, ns.revive(UpdateMetadata{}))
   409  }
   410  
   411  func TestNamespaceTombstoned(t *testing.T) {
   412  	inputs := []struct {
   413  		ns       Namespace
   414  		expected bool
   415  	}{
   416  		{
   417  			ns:       Namespace{name: b("foo")},
   418  			expected: true,
   419  		},
   420  		{
   421  			ns: Namespace{
   422  				name: b("foo"),
   423  				snapshots: []NamespaceSnapshot{
   424  					{
   425  						forRuleSetVersion:  1,
   426  						tombstoned:         false,
   427  						lastUpdatedAtNanos: 456,
   428  						lastUpdatedBy:      "someone",
   429  					},
   430  				},
   431  			},
   432  			expected: false,
   433  		},
   434  		{
   435  			ns: Namespace{
   436  				name: b("foo"),
   437  				snapshots: []NamespaceSnapshot{
   438  					{
   439  						forRuleSetVersion:  1,
   440  						tombstoned:         false,
   441  						lastUpdatedAtNanos: 456,
   442  						lastUpdatedBy:      "someone",
   443  					},
   444  					{
   445  						forRuleSetVersion:  4,
   446  						tombstoned:         true,
   447  						lastUpdatedAtNanos: 789,
   448  						lastUpdatedBy:      "someone else",
   449  					},
   450  				},
   451  			},
   452  			expected: true,
   453  		},
   454  	}
   455  
   456  	for _, input := range inputs {
   457  		require.Equal(t, input.expected, input.ns.Tombstoned())
   458  	}
   459  }
   460  
   461  func TestNamespacesView(t *testing.T) {
   462  	nss := Namespaces{
   463  		version: 1,
   464  		namespaces: []Namespace{
   465  			{
   466  				name: b("foo"),
   467  				snapshots: []NamespaceSnapshot{
   468  					{
   469  						forRuleSetVersion:  123,
   470  						tombstoned:         false,
   471  						lastUpdatedAtNanos: 456000000,
   472  						lastUpdatedBy:      "someone",
   473  					},
   474  					{
   475  						forRuleSetVersion:  456,
   476  						tombstoned:         true,
   477  						lastUpdatedAtNanos: 7890000000,
   478  						lastUpdatedBy:      "someone else",
   479  					},
   480  				},
   481  			},
   482  			{
   483  				name: b("bar"),
   484  				snapshots: []NamespaceSnapshot{
   485  					{
   486  						forRuleSetVersion:  789,
   487  						tombstoned:         false,
   488  						lastUpdatedAtNanos: 12345000000,
   489  						lastUpdatedBy:      "john",
   490  					},
   491  				},
   492  			},
   493  		},
   494  	}
   495  
   496  	expected := view.Namespaces{
   497  		Version: 1,
   498  		Namespaces: []view.Namespace{
   499  			{
   500  				ID:                  "foo",
   501  				ForRuleSetVersion:   456,
   502  				Tombstoned:          true,
   503  				LastUpdatedAtMillis: 7890,
   504  				LastUpdatedBy:       "someone else",
   505  			},
   506  			{
   507  				ID:                  "bar",
   508  				ForRuleSetVersion:   789,
   509  				Tombstoned:          false,
   510  				LastUpdatedAtMillis: 12345,
   511  				LastUpdatedBy:       "john",
   512  			},
   513  		},
   514  	}
   515  
   516  	actual, err := nss.NamespacesView()
   517  	require.NoError(t, err)
   518  	require.Equal(t, expected, actual)
   519  }
   520  
   521  func TestNamespacesClone(t *testing.T) {
   522  	nss := Namespaces{
   523  		version: 1,
   524  		namespaces: []Namespace{
   525  			{
   526  				name: b("foo"),
   527  				snapshots: []NamespaceSnapshot{
   528  					{
   529  						forRuleSetVersion:  123,
   530  						tombstoned:         false,
   531  						lastUpdatedAtNanos: 456,
   532  						lastUpdatedBy:      "someone",
   533  					},
   534  					{
   535  						forRuleSetVersion:  456,
   536  						tombstoned:         true,
   537  						lastUpdatedAtNanos: 7890,
   538  						lastUpdatedBy:      "someone else",
   539  					},
   540  				},
   541  			},
   542  			{
   543  				name: b("bar"),
   544  				snapshots: []NamespaceSnapshot{
   545  					{
   546  						forRuleSetVersion:  789,
   547  						tombstoned:         false,
   548  						lastUpdatedAtNanos: 12345,
   549  						lastUpdatedBy:      "john",
   550  					},
   551  				},
   552  			},
   553  		},
   554  	}
   555  
   556  	// Assert clone looks the same as the original.
   557  	nssClone := nss.Clone()
   558  	require.Equal(t, nss, nssClone)
   559  	require.False(t, &nss.namespaces[0] == &nssClone.namespaces[0])
   560  
   561  	// Assert changing the clone does not affect the original.
   562  	nssClone.Namespaces()[0].Snapshots()[0].forRuleSetVersion = 384
   563  	require.NotEqual(t, nss, nssClone)
   564  }
   565  
   566  func TestNewNamespacesFromNilProto(t *testing.T) {
   567  	_, err := NewNamespaces(1, nil)
   568  	require.Equal(t, errNilNamespacesProto, err)
   569  }
   570  
   571  func TestNewNamespacesFromValidProto(t *testing.T) {
   572  	ns, err := NewNamespaces(1, &rulepb.Namespaces{
   573  		Namespaces: []*rulepb.Namespace{
   574  			&rulepb.Namespace{
   575  				Name: "foo",
   576  				Snapshots: []*rulepb.NamespaceSnapshot{
   577  					&rulepb.NamespaceSnapshot{
   578  						ForRulesetVersion:  123,
   579  						Tombstoned:         false,
   580  						LastUpdatedAtNanos: 456,
   581  						LastUpdatedBy:      "someone",
   582  					},
   583  					&rulepb.NamespaceSnapshot{
   584  						ForRulesetVersion:  456,
   585  						Tombstoned:         true,
   586  						LastUpdatedAtNanos: 7890,
   587  						LastUpdatedBy:      "someone else",
   588  					},
   589  				},
   590  			},
   591  			&rulepb.Namespace{
   592  				Name: "bar",
   593  				Snapshots: []*rulepb.NamespaceSnapshot{
   594  					&rulepb.NamespaceSnapshot{
   595  						ForRulesetVersion:  789,
   596  						Tombstoned:         false,
   597  						LastUpdatedAtNanos: 12345,
   598  						LastUpdatedBy:      "john",
   599  					},
   600  					&rulepb.NamespaceSnapshot{
   601  						ForRulesetVersion:  1000,
   602  						Tombstoned:         true,
   603  						LastUpdatedAtNanos: 67890,
   604  						LastUpdatedBy:      "joe",
   605  					},
   606  				},
   607  			},
   608  		},
   609  	})
   610  	require.NoError(t, err)
   611  	require.Equal(t, 1, ns.Version())
   612  	expected := []Namespace{
   613  		{
   614  			name: b("foo"),
   615  			snapshots: []NamespaceSnapshot{
   616  				{
   617  					forRuleSetVersion:  123,
   618  					tombstoned:         false,
   619  					lastUpdatedAtNanos: 456,
   620  					lastUpdatedBy:      "someone",
   621  				},
   622  				{
   623  					forRuleSetVersion:  456,
   624  					tombstoned:         true,
   625  					lastUpdatedAtNanos: 7890,
   626  					lastUpdatedBy:      "someone else",
   627  				},
   628  			},
   629  		},
   630  		{
   631  			name: b("bar"),
   632  			snapshots: []NamespaceSnapshot{
   633  				{
   634  					forRuleSetVersion:  789,
   635  					tombstoned:         false,
   636  					lastUpdatedAtNanos: 12345,
   637  					lastUpdatedBy:      "john",
   638  				},
   639  				{
   640  					forRuleSetVersion:  1000,
   641  					tombstoned:         true,
   642  					lastUpdatedAtNanos: 67890,
   643  					lastUpdatedBy:      "joe",
   644  				},
   645  			},
   646  		},
   647  	}
   648  	require.Equal(t, expected, ns.Namespaces())
   649  }
   650  
   651  func TestNamespacesRoundTrip(t *testing.T) {
   652  	testNss := &rulepb.Namespaces{
   653  		Namespaces: []*rulepb.Namespace{
   654  			&rulepb.Namespace{
   655  				Name: "foo",
   656  				Snapshots: []*rulepb.NamespaceSnapshot{
   657  					&rulepb.NamespaceSnapshot{
   658  						ForRulesetVersion:  123,
   659  						Tombstoned:         false,
   660  						LastUpdatedAtNanos: 456,
   661  						LastUpdatedBy:      "someone",
   662  					},
   663  					&rulepb.NamespaceSnapshot{
   664  						ForRulesetVersion:  456,
   665  						Tombstoned:         true,
   666  						LastUpdatedAtNanos: 7890,
   667  						LastUpdatedBy:      "someone else",
   668  					},
   669  				},
   670  			},
   671  			&rulepb.Namespace{
   672  				Name: "foo2",
   673  				Snapshots: []*rulepb.NamespaceSnapshot{
   674  					&rulepb.NamespaceSnapshot{
   675  						ForRulesetVersion:  789,
   676  						Tombstoned:         false,
   677  						LastUpdatedAtNanos: 12345,
   678  						LastUpdatedBy:      "john",
   679  					},
   680  					&rulepb.NamespaceSnapshot{
   681  						ForRulesetVersion:  1000,
   682  						Tombstoned:         true,
   683  						LastUpdatedAtNanos: 67890,
   684  						LastUpdatedBy:      "joe",
   685  					},
   686  				},
   687  			},
   688  		},
   689  	}
   690  
   691  	nss, err := NewNamespaces(1, testNss)
   692  	require.NoError(t, err)
   693  
   694  	res, err := nss.Proto()
   695  	require.NoError(t, err)
   696  	require.Equal(t, testNss, res)
   697  }
   698  
   699  func TestNamespacesNamespace(t *testing.T) {
   700  	nss := Namespaces{
   701  		version: 1,
   702  		namespaces: []Namespace{
   703  			{
   704  				name: b("foo"),
   705  				snapshots: []NamespaceSnapshot{
   706  					{
   707  						forRuleSetVersion:  123,
   708  						tombstoned:         false,
   709  						lastUpdatedAtNanos: 456,
   710  						lastUpdatedBy:      "someone",
   711  					},
   712  					{
   713  						forRuleSetVersion:  456,
   714  						tombstoned:         true,
   715  						lastUpdatedAtNanos: 7890,
   716  						lastUpdatedBy:      "someone else",
   717  					},
   718  				},
   719  			},
   720  			{
   721  				name: b("bar"),
   722  				snapshots: []NamespaceSnapshot{
   723  					{
   724  						forRuleSetVersion:  789,
   725  						tombstoned:         false,
   726  						lastUpdatedAtNanos: 12345,
   727  						lastUpdatedBy:      "john",
   728  					},
   729  				},
   730  			},
   731  		},
   732  	}
   733  
   734  	inputs := []string{"foo", "bar"}
   735  	for _, input := range inputs {
   736  		ns, err := nss.Namespace(input)
   737  		require.NoError(t, err)
   738  		require.Equal(t, string(ns.Name()), input)
   739  	}
   740  }
   741  
   742  func TestNamespacesNamespaceNotFound(t *testing.T) {
   743  	nss := Namespaces{
   744  		version: 1,
   745  		namespaces: []Namespace{
   746  			{
   747  				name: b("bar"),
   748  				snapshots: []NamespaceSnapshot{
   749  					{
   750  						forRuleSetVersion:  789,
   751  						tombstoned:         false,
   752  						lastUpdatedAtNanos: 12345,
   753  						lastUpdatedBy:      "john",
   754  					},
   755  				},
   756  			},
   757  		},
   758  	}
   759  	_, err := nss.Namespace("foo")
   760  	require.Equal(t, errNamespaceNotFound, err)
   761  }
   762  
   763  func TestNamespacesNamespaceMultipleMatches(t *testing.T) {
   764  	nss := Namespaces{
   765  		version: 1,
   766  		namespaces: []Namespace{
   767  			{
   768  				name: b("bar"),
   769  				snapshots: []NamespaceSnapshot{
   770  					{
   771  						forRuleSetVersion:  789,
   772  						tombstoned:         false,
   773  						lastUpdatedAtNanos: 12345,
   774  						lastUpdatedBy:      "john",
   775  					},
   776  				},
   777  			},
   778  			{
   779  				name: b("bar"),
   780  				snapshots: []NamespaceSnapshot{
   781  					{
   782  						forRuleSetVersion:  789,
   783  						tombstoned:         false,
   784  						lastUpdatedAtNanos: 12345,
   785  						lastUpdatedBy:      "john",
   786  					},
   787  				},
   788  			},
   789  		},
   790  	}
   791  	_, err := nss.Namespace("bar")
   792  	require.Equal(t, errMultipleNamespaceMatches, err)
   793  }
   794  
   795  func TestNamespacesAddNewNamespace(t *testing.T) {
   796  	nss := Namespaces{
   797  		version: 1,
   798  		namespaces: []Namespace{
   799  			{
   800  				name: b("foo"),
   801  				snapshots: []NamespaceSnapshot{
   802  					{
   803  						forRuleSetVersion:  123,
   804  						tombstoned:         false,
   805  						lastUpdatedAtNanos: 456,
   806  						lastUpdatedBy:      "someone",
   807  					},
   808  					{
   809  						forRuleSetVersion:  456,
   810  						tombstoned:         true,
   811  						lastUpdatedAtNanos: 7890,
   812  						lastUpdatedBy:      "someone else",
   813  					},
   814  				},
   815  			},
   816  		},
   817  	}
   818  	meta := UpdateMetadata{updatedAtNanos: 12345, updatedBy: "john"}
   819  	revived, err := nss.AddNamespace("bar", meta)
   820  	require.NoError(t, err)
   821  	require.False(t, revived)
   822  
   823  	expected := Namespaces{
   824  		version: 1,
   825  		namespaces: []Namespace{
   826  			{
   827  				name: b("foo"),
   828  				snapshots: []NamespaceSnapshot{
   829  					{
   830  						forRuleSetVersion:  123,
   831  						tombstoned:         false,
   832  						lastUpdatedAtNanos: 456,
   833  						lastUpdatedBy:      "someone",
   834  					},
   835  					{
   836  						forRuleSetVersion:  456,
   837  						tombstoned:         true,
   838  						lastUpdatedAtNanos: 7890,
   839  						lastUpdatedBy:      "someone else",
   840  					},
   841  				},
   842  			},
   843  			{
   844  				name: b("bar"),
   845  				snapshots: []NamespaceSnapshot{
   846  					{
   847  						forRuleSetVersion:  1,
   848  						tombstoned:         false,
   849  						lastUpdatedAtNanos: 12345,
   850  						lastUpdatedBy:      "john",
   851  					},
   852  				},
   853  			},
   854  		},
   855  	}
   856  	require.Equal(t, expected, nss)
   857  }
   858  
   859  func TestNamespacesAddTombstonedNamespace(t *testing.T) {
   860  	nss := Namespaces{
   861  		version: 1,
   862  		namespaces: []Namespace{
   863  			{
   864  				name: b("foo"),
   865  				snapshots: []NamespaceSnapshot{
   866  					{
   867  						forRuleSetVersion:  123,
   868  						tombstoned:         false,
   869  						lastUpdatedAtNanos: 456,
   870  						lastUpdatedBy:      "someone",
   871  					},
   872  					{
   873  						forRuleSetVersion:  456,
   874  						tombstoned:         true,
   875  						lastUpdatedAtNanos: 7890,
   876  						lastUpdatedBy:      "someone else",
   877  					},
   878  				},
   879  			},
   880  		},
   881  	}
   882  
   883  	meta := UpdateMetadata{updatedAtNanos: 12345, updatedBy: "john"}
   884  	revived, err := nss.AddNamespace("foo", meta)
   885  	require.NoError(t, err)
   886  	require.True(t, revived)
   887  
   888  	expected := Namespaces{
   889  		version: 1,
   890  		namespaces: []Namespace{
   891  			{
   892  				name: b("foo"),
   893  				snapshots: []NamespaceSnapshot{
   894  					{
   895  						forRuleSetVersion:  123,
   896  						tombstoned:         false,
   897  						lastUpdatedAtNanos: 456,
   898  						lastUpdatedBy:      "someone",
   899  					},
   900  					{
   901  						forRuleSetVersion:  456,
   902  						tombstoned:         true,
   903  						lastUpdatedAtNanos: 7890,
   904  						lastUpdatedBy:      "someone else",
   905  					},
   906  					{
   907  						forRuleSetVersion:  457,
   908  						tombstoned:         false,
   909  						lastUpdatedAtNanos: 12345,
   910  						lastUpdatedBy:      "john",
   911  					},
   912  				},
   913  			},
   914  		},
   915  	}
   916  	require.Equal(t, expected, nss)
   917  }
   918  
   919  func TestNamespacesAddLiveNamespace(t *testing.T) {
   920  	nss := Namespaces{
   921  		version: 1,
   922  		namespaces: []Namespace{
   923  			{
   924  				name: b("foo"),
   925  				snapshots: []NamespaceSnapshot{
   926  					{
   927  						forRuleSetVersion:  123,
   928  						tombstoned:         false,
   929  						lastUpdatedAtNanos: 456,
   930  						lastUpdatedBy:      "someone",
   931  					},
   932  				},
   933  			},
   934  		},
   935  	}
   936  	_, err := nss.AddNamespace("foo", UpdateMetadata{})
   937  	require.Error(t, err)
   938  }
   939  
   940  func TestNamespacesDeleteNamespace(t *testing.T) {
   941  	nss := Namespaces{
   942  		version: 1,
   943  		namespaces: []Namespace{
   944  			{
   945  				name: b("foo"),
   946  				snapshots: []NamespaceSnapshot{
   947  					{
   948  						forRuleSetVersion:  123,
   949  						tombstoned:         false,
   950  						lastUpdatedAtNanos: 456,
   951  						lastUpdatedBy:      "someone",
   952  					},
   953  				},
   954  			},
   955  			{
   956  				name: b("bar"),
   957  				snapshots: []NamespaceSnapshot{
   958  					{
   959  						forRuleSetVersion:  789,
   960  						tombstoned:         false,
   961  						lastUpdatedAtNanos: 12345,
   962  						lastUpdatedBy:      "john",
   963  					},
   964  				},
   965  			},
   966  		},
   967  	}
   968  
   969  	meta := UpdateMetadata{updatedAtNanos: 1000, updatedBy: "someone else"}
   970  	require.NoError(t, nss.DeleteNamespace("foo", 200, meta))
   971  
   972  	expected := Namespaces{
   973  		version: 1,
   974  		namespaces: []Namespace{
   975  			{
   976  				name: b("foo"),
   977  				snapshots: []NamespaceSnapshot{
   978  					{
   979  						forRuleSetVersion:  123,
   980  						tombstoned:         false,
   981  						lastUpdatedAtNanos: 456,
   982  						lastUpdatedBy:      "someone",
   983  					},
   984  					{
   985  						forRuleSetVersion:  201,
   986  						tombstoned:         true,
   987  						lastUpdatedAtNanos: 1000,
   988  						lastUpdatedBy:      "someone else",
   989  					},
   990  				},
   991  			},
   992  			{
   993  				name: b("bar"),
   994  				snapshots: []NamespaceSnapshot{
   995  					{
   996  						forRuleSetVersion:  789,
   997  						tombstoned:         false,
   998  						lastUpdatedAtNanos: 12345,
   999  						lastUpdatedBy:      "john",
  1000  					},
  1001  				},
  1002  			},
  1003  		},
  1004  	}
  1005  	require.Equal(t, expected, nss)
  1006  }
  1007  
  1008  func TestNamespacesDeleteMissingNamespace(t *testing.T) {
  1009  	nss := Namespaces{
  1010  		version: 1,
  1011  		namespaces: []Namespace{
  1012  			{
  1013  				name: b("foo"),
  1014  				snapshots: []NamespaceSnapshot{
  1015  					{
  1016  						forRuleSetVersion:  123,
  1017  						tombstoned:         false,
  1018  						lastUpdatedAtNanos: 456,
  1019  						lastUpdatedBy:      "someone",
  1020  					},
  1021  				},
  1022  			},
  1023  		},
  1024  	}
  1025  	require.Error(t, nss.DeleteNamespace("bar", 300, UpdateMetadata{}))
  1026  }
  1027  
  1028  func TestNamespacesDeleteTombstonedNamespace(t *testing.T) {
  1029  	nss := Namespaces{
  1030  		version: 1,
  1031  		namespaces: []Namespace{
  1032  			{
  1033  				name: b("foo"),
  1034  				snapshots: []NamespaceSnapshot{
  1035  					{
  1036  						forRuleSetVersion:  123,
  1037  						tombstoned:         false,
  1038  						lastUpdatedAtNanos: 456,
  1039  						lastUpdatedBy:      "someone",
  1040  					},
  1041  					{
  1042  						forRuleSetVersion:  201,
  1043  						tombstoned:         true,
  1044  						lastUpdatedAtNanos: 1000,
  1045  						lastUpdatedBy:      "someone else",
  1046  					},
  1047  				},
  1048  			},
  1049  		},
  1050  	}
  1051  	require.Error(t, nss.DeleteNamespace("foo", 300, UpdateMetadata{}))
  1052  }
  1053  
  1054  func TestNamespacesDeleteAndReviveNamespace(t *testing.T) {
  1055  	nss := Namespaces{
  1056  		version: 1,
  1057  		namespaces: []Namespace{
  1058  			{
  1059  				name: b("foo"),
  1060  				snapshots: []NamespaceSnapshot{
  1061  					{
  1062  						forRuleSetVersion:  1,
  1063  						tombstoned:         false,
  1064  						lastUpdatedAtNanos: 456,
  1065  						lastUpdatedBy:      "someone",
  1066  					},
  1067  				},
  1068  			},
  1069  		},
  1070  	}
  1071  
  1072  	ns, err := nss.Namespace("foo")
  1073  	require.NoError(t, err)
  1074  	require.False(t, ns.Tombstoned())
  1075  	require.Equal(t, len(ns.Snapshots()), 1)
  1076  	lastSnapshot := ns.Snapshots()[0]
  1077  	require.Equal(t, 1, lastSnapshot.ForRuleSetVersion())
  1078  	require.False(t, lastSnapshot.Tombstoned())
  1079  	require.Equal(t, int64(456), lastSnapshot.LastUpdatedAtNanos())
  1080  	require.Equal(t, "someone", lastSnapshot.LastUpdatedBy())
  1081  
  1082  	meta := UpdateMetadata{updatedAtNanos: 1000, updatedBy: "someone else"}
  1083  	err = nss.DeleteNamespace("foo", 4, meta)
  1084  	require.NoError(t, err)
  1085  	ns, err = nss.Namespace("foo")
  1086  	require.NoError(t, err)
  1087  	require.Equal(t, len(ns.snapshots), 2)
  1088  	require.Equal(t, 1, ns.Snapshots()[0].ForRuleSetVersion())
  1089  	lastSnapshot = ns.Snapshots()[1]
  1090  	require.Equal(t, 5, lastSnapshot.ForRuleSetVersion())
  1091  	require.True(t, lastSnapshot.Tombstoned())
  1092  	require.Equal(t, int64(1000), lastSnapshot.LastUpdatedAtNanos())
  1093  	require.Equal(t, "someone else", lastSnapshot.LastUpdatedBy())
  1094  
  1095  	meta = UpdateMetadata{updatedAtNanos: 2000, updatedBy: "john"}
  1096  	revived, err := nss.AddNamespace("foo", meta)
  1097  	require.NoError(t, err)
  1098  	require.True(t, revived)
  1099  	ns, err = nss.Namespace("foo")
  1100  	require.NoError(t, err)
  1101  	require.False(t, ns.Tombstoned())
  1102  	require.Equal(t, len(ns.snapshots), 3)
  1103  	require.Equal(t, 1, ns.Snapshots()[0].ForRuleSetVersion())
  1104  	lastSnapshot = ns.Snapshots()[2]
  1105  	require.Equal(t, 6, lastSnapshot.ForRuleSetVersion())
  1106  	require.False(t, lastSnapshot.Tombstoned())
  1107  	require.Equal(t, int64(2000), lastSnapshot.LastUpdatedAtNanos())
  1108  	require.Equal(t, "john", lastSnapshot.LastUpdatedBy())
  1109  }