github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datasets/subjectset_test.go (about)

     1  package datasets
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/authzed/spicedb/internal/caveats"
    11  	"github.com/authzed/spicedb/internal/testutil"
    12  	v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1"
    13  )
    14  
    15  var (
    16  	caveatexpr = caveats.CaveatExprForTesting
    17  	sub        = testutil.FoundSubject
    18  	wc         = testutil.Wildcard
    19  	csub       = testutil.CaveatedFoundSubject
    20  	cwc        = testutil.CaveatedWildcard
    21  	wrap       = testutil.WrapFoundSubject
    22  )
    23  
    24  func TestSubjectSetAdd(t *testing.T) {
    25  	tcs := []struct {
    26  		name        string
    27  		existing    []*v1.FoundSubject
    28  		toAdd       *v1.FoundSubject
    29  		expectedSet []*v1.FoundSubject
    30  	}{
    31  		{
    32  			"basic add",
    33  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
    34  			sub("baz"),
    35  			[]*v1.FoundSubject{sub("foo"), sub("bar"), sub("baz")},
    36  		},
    37  		{
    38  			"basic repeated add",
    39  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
    40  			sub("bar"),
    41  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
    42  		},
    43  		{
    44  			"add of an empty wildcard",
    45  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
    46  			wc(),
    47  			[]*v1.FoundSubject{sub("foo"), sub("bar"), wc()},
    48  		},
    49  		{
    50  			"add of a wildcard with exclusions",
    51  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
    52  			wc("1", "2"),
    53  			[]*v1.FoundSubject{sub("foo"), sub("bar"), wc("1", "2")},
    54  		},
    55  		{
    56  			"add of a wildcard to a wildcard",
    57  			[]*v1.FoundSubject{sub("foo"), sub("bar"), wc()},
    58  			wc(),
    59  			[]*v1.FoundSubject{sub("foo"), sub("bar"), wc()},
    60  		},
    61  		{
    62  			"add of a wildcard with exclusions to a bare wildcard",
    63  			[]*v1.FoundSubject{sub("foo"), sub("bar"), wc()},
    64  			wc("1", "2"),
    65  			[]*v1.FoundSubject{sub("foo"), sub("bar"), wc()},
    66  		},
    67  		{
    68  			"add of a bare wildcard to a wildcard with exclusions",
    69  			[]*v1.FoundSubject{sub("foo"), sub("bar"), wc("1", "2")},
    70  			wc(),
    71  			[]*v1.FoundSubject{sub("foo"), sub("bar"), wc()},
    72  		},
    73  		{
    74  			"add of a wildcard with exclusions to a wildcard with exclusions",
    75  			[]*v1.FoundSubject{sub("foo"), sub("bar"), wc("1", "2")},
    76  			wc("2", "3"),
    77  			[]*v1.FoundSubject{sub("foo"), sub("bar"), wc("2")},
    78  		},
    79  		{
    80  			"add of a subject to a wildcard with exclusions that does not have that subject",
    81  			[]*v1.FoundSubject{wc("1", "2")},
    82  			sub("3"),
    83  			[]*v1.FoundSubject{wc("1", "2"), sub("3")},
    84  		},
    85  		{
    86  			"add of a subject to a wildcard with exclusions that has that subject",
    87  			[]*v1.FoundSubject{wc("1", "2")},
    88  			sub("2"),
    89  			[]*v1.FoundSubject{wc("1"), sub("2")},
    90  		},
    91  		{
    92  			"add of a subject to a wildcard with exclusions that has that subject",
    93  			[]*v1.FoundSubject{sub("2")},
    94  			wc("1", "2"),
    95  			[]*v1.FoundSubject{wc("1"), sub("2")},
    96  		},
    97  		{
    98  			"add of a subject to a bare wildcard",
    99  			[]*v1.FoundSubject{wc()},
   100  			sub("1"),
   101  			[]*v1.FoundSubject{wc(), sub("1")},
   102  		},
   103  		{
   104  			"add of two wildcards",
   105  			[]*v1.FoundSubject{wc("1")},
   106  			wc("2"),
   107  			[]*v1.FoundSubject{wc()},
   108  		},
   109  		{
   110  			"add of two wildcards with same restrictions",
   111  			[]*v1.FoundSubject{wc("1")},
   112  			wc("1", "2"),
   113  			[]*v1.FoundSubject{wc("1")},
   114  		},
   115  
   116  		// Tests with caveats.
   117  		{
   118  			"basic add of caveated to non-caveated",
   119  			[]*v1.FoundSubject{
   120  				csub("foo", caveatexpr("testcaveat")),
   121  				sub("bar"),
   122  			},
   123  			sub("foo"),
   124  			[]*v1.FoundSubject{sub("bar"), sub("foo")},
   125  		},
   126  		{
   127  			"basic add of non-caveated to caveated",
   128  			[]*v1.FoundSubject{
   129  				sub("foo"),
   130  				sub("bar"),
   131  			},
   132  			csub("foo", caveatexpr("testcaveat")),
   133  			[]*v1.FoundSubject{sub("bar"), sub("foo")},
   134  		},
   135  		{
   136  			"basic add of caveated to caveated",
   137  			[]*v1.FoundSubject{
   138  				csub("foo", caveatexpr("testcaveat")),
   139  				sub("bar"),
   140  			},
   141  			csub("foo", caveatexpr("anothercaveat")),
   142  			[]*v1.FoundSubject{
   143  				sub("bar"),
   144  				csub("foo", caveatOr(caveatexpr("testcaveat"), caveatexpr("anothercaveat"))),
   145  			},
   146  		},
   147  		{
   148  			"add of caveated wildcard to non-caveated",
   149  			[]*v1.FoundSubject{
   150  				cwc(caveatexpr("testcaveat")),
   151  				sub("bar"),
   152  			},
   153  			wc(),
   154  			[]*v1.FoundSubject{sub("bar"), wc()},
   155  		},
   156  		{
   157  			"add of caveated wildcard to caveated",
   158  			[]*v1.FoundSubject{
   159  				cwc(caveatexpr("testcaveat")),
   160  				sub("bar"),
   161  			},
   162  			cwc(caveatexpr("anothercaveat")),
   163  			[]*v1.FoundSubject{
   164  				sub("bar"),
   165  				cwc(caveatOr(caveatexpr("testcaveat"), caveatexpr("anothercaveat"))),
   166  			},
   167  		},
   168  		{
   169  			"add of wildcard to a caveated sub",
   170  			[]*v1.FoundSubject{
   171  				csub("bar", caveatexpr("testcaveat")),
   172  			},
   173  			wc(),
   174  			[]*v1.FoundSubject{
   175  				csub("bar", caveatexpr("testcaveat")),
   176  				wc(),
   177  			},
   178  		},
   179  		{
   180  			"add caveated sub to wildcard with non-matching caveated exclusion",
   181  			[]*v1.FoundSubject{
   182  				cwc(nil, csub("bar", caveatexpr("testcaveat"))),
   183  			},
   184  			csub("foo", caveatexpr("anothercaveat")),
   185  			[]*v1.FoundSubject{
   186  				cwc(nil, csub("bar", caveatexpr("testcaveat"))),
   187  				csub("foo", caveatexpr("anothercaveat")),
   188  			},
   189  		},
   190  		{
   191  			"add caveated sub to wildcard with matching caveated exclusion",
   192  			[]*v1.FoundSubject{
   193  				cwc(nil, csub("foo", caveatexpr("testcaveat"))),
   194  			},
   195  			csub("foo", caveatexpr("anothercaveat")),
   196  			[]*v1.FoundSubject{
   197  				// The caveat of the exclusion is now the combination of the caveats from the original
   198  				// wildcard and the concrete which was added, as the exclusion applies when the exclusion
   199  				// caveat is true and the concrete's caveat is false.
   200  				cwc(nil, csub("foo",
   201  					caveatAnd(
   202  						caveatexpr("testcaveat"),
   203  						caveatInvert(caveatexpr("anothercaveat")),
   204  					),
   205  				)),
   206  				csub("foo", caveatexpr("anothercaveat")),
   207  			},
   208  		},
   209  		{
   210  			"add caveated sub to caveated wildcard with matching caveated exclusion",
   211  			[]*v1.FoundSubject{
   212  				cwc(caveatexpr("wildcardcaveat"), csub("foo", caveatexpr("testcaveat"))),
   213  			},
   214  			csub("foo", caveatexpr("anothercaveat")),
   215  			[]*v1.FoundSubject{
   216  				// The caveat of the exclusion is now the combination of the caveats from the original
   217  				// wildcard and the concrete which was added, as the exclusion applies when the exclusion
   218  				// caveat is true and the concrete's caveat is false.
   219  				cwc(caveatexpr("wildcardcaveat"), csub("foo",
   220  					caveatAnd(
   221  						caveatexpr("testcaveat"),
   222  						caveatInvert(caveatexpr("anothercaveat")),
   223  					),
   224  				)),
   225  				csub("foo", caveatexpr("anothercaveat")),
   226  			},
   227  		},
   228  	}
   229  
   230  	for _, tc := range tcs {
   231  		tc := tc
   232  
   233  		t.Run(tc.name, func(t *testing.T) {
   234  			existingSet := NewSubjectSet()
   235  			for _, existing := range tc.existing {
   236  				err := existingSet.Add(existing)
   237  				require.NoError(t, err)
   238  			}
   239  
   240  			err := existingSet.Add(tc.toAdd)
   241  			require.NoError(t, err)
   242  
   243  			expectedSet := tc.expectedSet
   244  			computedSet := existingSet.AsSlice()
   245  			testutil.RequireEquivalentSets(t, expectedSet, computedSet)
   246  
   247  			require.Equal(t, len(expectedSet), existingSet.SubjectCount())
   248  			if existingSet.HasWildcard() {
   249  				require.Equal(t, len(expectedSet), existingSet.ConcreteSubjectCount()+1)
   250  			} else {
   251  				require.Equal(t, len(expectedSet), existingSet.ConcreteSubjectCount())
   252  			}
   253  		})
   254  	}
   255  }
   256  
   257  func TestSubjectSetSubtract(t *testing.T) {
   258  	tcs := []struct {
   259  		name        string
   260  		existing    []*v1.FoundSubject
   261  		toSubtract  *v1.FoundSubject
   262  		expectedSet []*v1.FoundSubject
   263  	}{
   264  		{
   265  			"basic subtract, no overlap",
   266  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
   267  			sub("baz"),
   268  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
   269  		},
   270  		{
   271  			"basic subtract, with overlap",
   272  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
   273  			sub("bar"),
   274  			[]*v1.FoundSubject{sub("foo")},
   275  		},
   276  		{
   277  			"basic subtract from bare wildcard",
   278  			[]*v1.FoundSubject{sub("foo"), wc()},
   279  			sub("bar"),
   280  			[]*v1.FoundSubject{sub("foo"), wc("bar")},
   281  		},
   282  		{
   283  			"subtract from bare wildcard and set",
   284  			[]*v1.FoundSubject{sub("bar"), wc()},
   285  			sub("bar"),
   286  			[]*v1.FoundSubject{wc("bar")},
   287  		},
   288  		{
   289  			"subtract from wildcard",
   290  			[]*v1.FoundSubject{sub("bar"), wc("bar")},
   291  			sub("bar"),
   292  			[]*v1.FoundSubject{wc("bar")},
   293  		},
   294  		{
   295  			"subtract from wildcard with existing exclusions",
   296  			[]*v1.FoundSubject{sub("bar"), wc("hiya")},
   297  			sub("bar"),
   298  			[]*v1.FoundSubject{wc("hiya", "bar")},
   299  		},
   300  		{
   301  			"subtract bare wildcard from set",
   302  			[]*v1.FoundSubject{sub("bar"), sub("foo")},
   303  			wc(),
   304  			[]*v1.FoundSubject{},
   305  		},
   306  		{
   307  			"subtract wildcard with no matching exclusions from set",
   308  			[]*v1.FoundSubject{sub("bar"), sub("foo")},
   309  			wc("baz"),
   310  			[]*v1.FoundSubject{},
   311  		},
   312  		{
   313  			"subtract wildcard with matching exclusions from set",
   314  			[]*v1.FoundSubject{sub("bar"), sub("foo")},
   315  			wc("bar"),
   316  			[]*v1.FoundSubject{sub("bar")},
   317  		},
   318  		{
   319  			"subtract wildcard from another wildcard, both with the same exclusions",
   320  			[]*v1.FoundSubject{wc("sarah")},
   321  			wc("sarah"),
   322  			[]*v1.FoundSubject{},
   323  		},
   324  		{
   325  			"subtract wildcard from another wildcard, with different exclusions",
   326  			[]*v1.FoundSubject{wc("tom"), sub("foo"), sub("bar")},
   327  			wc("sarah"),
   328  			[]*v1.FoundSubject{sub("sarah")},
   329  		},
   330  		{
   331  			"subtract wildcard from another wildcard, with more exclusions",
   332  			[]*v1.FoundSubject{wc("sarah")},
   333  			wc("sarah", "tom"),
   334  			[]*v1.FoundSubject{sub("tom")},
   335  		},
   336  
   337  		// Tests with caveats.
   338  		{
   339  			"subtract non-caveated from caveated",
   340  			[]*v1.FoundSubject{csub("foo", caveatexpr("somecaveat"))},
   341  			sub("foo"),
   342  			[]*v1.FoundSubject{},
   343  		},
   344  		{
   345  			"subtract caveated from non-caveated",
   346  			[]*v1.FoundSubject{sub("foo")},
   347  			csub("foo", caveatexpr("somecaveat")),
   348  			[]*v1.FoundSubject{
   349  				// The subject should only appear now when the caveat is false.
   350  				csub("foo", caveatInvert(caveatexpr("somecaveat"))),
   351  			},
   352  		},
   353  		{
   354  			"subtract caveated from caveated",
   355  			[]*v1.FoundSubject{
   356  				csub("foo", caveatexpr("startingcaveat")),
   357  			},
   358  			csub("foo", caveatexpr("somecaveat")),
   359  			[]*v1.FoundSubject{
   360  				// The subject should only appear when its caveat is true, and the subtracted caveat
   361  				// is false.
   362  				csub("foo", caveatAnd(caveatexpr("startingcaveat"), caveatInvert(caveatexpr("somecaveat")))),
   363  			},
   364  		},
   365  		{
   366  			"subtract caveated bare wildcard from non-caveated",
   367  			[]*v1.FoundSubject{sub("foo")},
   368  			cwc(caveatexpr("somecaveat")),
   369  			[]*v1.FoundSubject{
   370  				// The subject should only appear now when the caveat is false.
   371  				csub("foo", caveatInvert(caveatexpr("somecaveat"))),
   372  			},
   373  		},
   374  		{
   375  			"subtract bare wildcard from caveated",
   376  			[]*v1.FoundSubject{csub("foo", caveatexpr("something"))},
   377  			wc(),
   378  			[]*v1.FoundSubject{},
   379  		},
   380  		{
   381  			"subtract caveated bare wildcard from caveated",
   382  			[]*v1.FoundSubject{csub("foo", caveatexpr("something"))},
   383  			cwc(caveatexpr("somecaveat")),
   384  			[]*v1.FoundSubject{
   385  				// The subject should only appear now when its caveat is true and the wildcard's caveat
   386  				// is false.
   387  				csub("foo", caveatAnd(caveatexpr("something"), caveatInvert(caveatexpr("somecaveat")))),
   388  			},
   389  		},
   390  		{
   391  			"subtract wildcard with caveated exception from non-caveated",
   392  			[]*v1.FoundSubject{sub("foo")},
   393  			cwc(nil, csub("foo", caveatexpr("somecaveat"))),
   394  			[]*v1.FoundSubject{
   395  				// The subject should only appear now when somecaveat is true, making the exclusion present
   396  				// on the wildcard, and thus preventing the wildcard from removing the concrete subject.
   397  				csub("foo", caveatexpr("somecaveat")),
   398  			},
   399  		},
   400  		{
   401  			"subtract wildcard with caveated exception from caveated",
   402  			[]*v1.FoundSubject{csub("foo", caveatexpr("somecaveat"))},
   403  			cwc(nil, csub("foo", caveatexpr("anothercaveat"))),
   404  			[]*v1.FoundSubject{
   405  				// The subject should only appear now when both caveats are true.
   406  				csub("foo", caveatAnd(caveatexpr("somecaveat"), caveatexpr("anothercaveat"))),
   407  			},
   408  		},
   409  		{
   410  			"subtract caveated wildcard with caveated exception from caveated",
   411  			[]*v1.FoundSubject{csub("foo", caveatexpr("somecaveat"))},
   412  			cwc(caveatexpr("wildcardcaveat"), csub("foo", caveatexpr("anothercaveat"))),
   413  			[]*v1.FoundSubject{
   414  				// The subject should appear when:
   415  				// somecaveat && (!wildcardcaveat || anothercaveat)
   416  				csub("foo", caveatAnd(caveatexpr("somecaveat"), caveatOr(caveatInvert(caveatexpr("wildcardcaveat")), caveatexpr("anothercaveat")))),
   417  			},
   418  		},
   419  		{
   420  			"subtract caveated concrete from non-caveated wildcard",
   421  			[]*v1.FoundSubject{wc()},
   422  			csub("foo", caveatexpr("somecaveat")),
   423  			[]*v1.FoundSubject{
   424  				// The subject should be a caveated exception on the wildcard.
   425  				cwc(nil, csub("foo", caveatexpr("somecaveat"))),
   426  			},
   427  		},
   428  		{
   429  			"subtract caveated concrete from caveated wildcard",
   430  			[]*v1.FoundSubject{cwc(caveatexpr("wildcardcaveat"))},
   431  			csub("foo", caveatexpr("somecaveat")),
   432  			[]*v1.FoundSubject{
   433  				// The subject should be a caveated exception on the wildcard.
   434  				cwc(caveatexpr("wildcardcaveat"), csub("foo", caveatexpr("somecaveat"))),
   435  			},
   436  		},
   437  		{
   438  			"subtract caveated concrete from wildcard with exclusion",
   439  			[]*v1.FoundSubject{
   440  				wc("foo"),
   441  			},
   442  			csub("foo", caveatexpr("somecaveat")),
   443  			[]*v1.FoundSubject{
   444  				// No change, as `foo` is always removed.
   445  				wc("foo"),
   446  			},
   447  		},
   448  		{
   449  			"subtract caveated concrete from caveated wildcard with exclusion",
   450  			[]*v1.FoundSubject{
   451  				cwc(caveatexpr("wildcardcaveat"), sub("foo")),
   452  			},
   453  			csub("foo", caveatexpr("somecaveat")),
   454  			[]*v1.FoundSubject{
   455  				// No change, as `foo` is always removed.
   456  				cwc(caveatexpr("wildcardcaveat"), sub("foo")),
   457  			},
   458  		},
   459  		{
   460  			"subtract caveated concrete from caveated wildcard with caveated exclusion",
   461  			[]*v1.FoundSubject{
   462  				cwc(caveatexpr("wildcardcaveat"), csub("foo", caveatexpr("exclusioncaveat"))),
   463  			},
   464  			csub("foo", caveatexpr("somecaveat")),
   465  			[]*v1.FoundSubject{
   466  				// The subject should be excluded now if *either* caveat is true.
   467  				cwc(caveatexpr("wildcardcaveat"), csub("foo", caveatOr(caveatexpr("exclusioncaveat"), caveatexpr("somecaveat")))),
   468  			},
   469  		},
   470  		{
   471  			"subtract non-caveated bare wildcard from caveated wildcard",
   472  			[]*v1.FoundSubject{
   473  				cwc(caveatexpr("wildcardcaveat")),
   474  			},
   475  			wc(),
   476  			[]*v1.FoundSubject{},
   477  		},
   478  		{
   479  			"subtract caveated bare wildcard from non-caveated wildcard",
   480  			[]*v1.FoundSubject{
   481  				wc(),
   482  			},
   483  			cwc(caveatexpr("wildcardcaveat")),
   484  			[]*v1.FoundSubject{
   485  				// The new wildcard is caveated on the inversion of the caveat.
   486  				cwc(caveatInvert(caveatexpr("wildcardcaveat"))),
   487  			},
   488  		},
   489  		{
   490  			"subtract non-caveated bare wildcard from caveated wildcard with exclusions",
   491  			[]*v1.FoundSubject{
   492  				cwc(caveatexpr("wildcardcaveat"), sub("foo")),
   493  			},
   494  			wc(),
   495  			[]*v1.FoundSubject{},
   496  		},
   497  		{
   498  			"subtract caveated bare wildcard from non-caveated wildcard with exclusions",
   499  			[]*v1.FoundSubject{
   500  				wc("foo"),
   501  			},
   502  			cwc(caveatexpr("wildcardcaveat")),
   503  			[]*v1.FoundSubject{
   504  				// The new wildcard is caveated on the inversion of the caveat.
   505  				cwc(caveatInvert(caveatexpr("wildcardcaveat")), sub("foo")),
   506  			},
   507  		},
   508  		{
   509  			"subtract caveated wildcard with caveated exclusion from caveated wildcard with caveated exclusion",
   510  			[]*v1.FoundSubject{
   511  				cwc(caveatexpr("wildcardcaveat1"), csub("foo", caveatexpr("exclusion1"))),
   512  			},
   513  			cwc(caveatexpr("wildcardcaveat2"), csub("foo", caveatexpr("exclusion2"))),
   514  			[]*v1.FoundSubject{
   515  				cwc(
   516  					// The wildcard itself only appears if caveat1 is true and caveat2 is *false*.
   517  					caveatAnd(caveatexpr("wildcardcaveat1"), caveatInvert(caveatexpr("wildcardcaveat2"))),
   518  
   519  					// The exclusion still only applies if exclusion1 is true, because if it is false,
   520  					// it doesn't matter that the subject was excluded from the subtraction.
   521  					csub("foo", caveatexpr("exclusion1")),
   522  				),
   523  
   524  				// A concrete of foo is produced when all of these are true:
   525  				// 1) the first wildcard is present
   526  				// 2) the second wildcard is present
   527  				// 3) the exclusion on the first wildcard is false, meaning the concrete is present within
   528  				//    the wildcard
   529  				// 4) the exclusion within the second is true, meaning that the concrete was not present
   530  				//
   531  				// thus causing the expression to become `{*} - {* - {foo}}`, and therefore producing
   532  				// `foo` as a concrete.
   533  				csub("foo",
   534  					caveatAnd(
   535  						caveatAnd(
   536  							caveatAnd(
   537  								caveatexpr("wildcardcaveat1"),
   538  								caveatexpr("wildcardcaveat2"),
   539  							),
   540  							caveatInvert(caveatexpr("exclusion1")),
   541  						),
   542  						caveatexpr("exclusion2"),
   543  					),
   544  				),
   545  			},
   546  		},
   547  		{
   548  			"subtract caveated wildcard with caveated exclusion from caveated wildcard with different caveated exclusion",
   549  			[]*v1.FoundSubject{
   550  				cwc(caveatexpr("wildcardcaveat1"), csub("foo", caveatexpr("exclusion1"))),
   551  			},
   552  			cwc(caveatexpr("wildcardcaveat2"), csub("bar", caveatexpr("exclusion2"))),
   553  			[]*v1.FoundSubject{
   554  				cwc(
   555  					// The wildcard itself only appears if caveat1 is true and caveat2 is *false*.
   556  					caveatAnd(caveatexpr("wildcardcaveat1"), caveatInvert(caveatexpr("wildcardcaveat2"))),
   557  
   558  					// The foo exclusion remains the same.
   559  					csub("foo", caveatexpr("exclusion1")),
   560  				),
   561  
   562  				// Because `bar` is excluded in the *subtraction*, it can appear as a *concrete* subject
   563  				// in the scenario where the first wildcard is applied, the second wildcard is applied
   564  				// and its own exclusion is true.
   565  				// Therefore, bar must be *concretely* in the set if:
   566  				// wildcard1 && wildcard2 && exclusion2
   567  				csub("bar",
   568  					caveatAnd(
   569  						caveatAnd(
   570  							caveatexpr("wildcardcaveat1"),
   571  							caveatexpr("wildcardcaveat2"),
   572  						),
   573  						caveatexpr("exclusion2"),
   574  					),
   575  				),
   576  			},
   577  		},
   578  		{
   579  			"concretes with wildcard with partially matching exclusions subtracted",
   580  			[]*v1.FoundSubject{
   581  				sub("foo"),
   582  				sub("bar"),
   583  			},
   584  			cwc(caveatexpr("caveat"), sub("foo")),
   585  			[]*v1.FoundSubject{
   586  				sub("foo"),
   587  				csub("bar", caveatInvert(caveatexpr("caveat"))),
   588  			},
   589  		},
   590  		{
   591  			"subtract caveated from caveated (same caveat)",
   592  			[]*v1.FoundSubject{
   593  				csub("foo", caveatexpr("somecaveat")),
   594  			},
   595  			csub("foo", caveatexpr("somecaveat")),
   596  			[]*v1.FoundSubject{
   597  				// Since no expression simplification is occurring, subtracting a caveated concrete
   598  				// subject from another with the *same* caveat, results in an expression that &&'s
   599  				// together the expression and its inversion. In practice, this will simplify down to
   600  				// nothing, but that happens at a different layer.
   601  				csub("foo", caveatAnd(caveatexpr("somecaveat"), caveatInvert(caveatexpr("somecaveat")))),
   602  			},
   603  		},
   604  	}
   605  
   606  	for _, tc := range tcs {
   607  		tc := tc
   608  
   609  		t.Run(tc.name, func(t *testing.T) {
   610  			existingSet := NewSubjectSet()
   611  			for _, existing := range tc.existing {
   612  				existingSet.MustAdd(existing)
   613  			}
   614  
   615  			existingSet.Subtract(tc.toSubtract)
   616  
   617  			expectedSet := tc.expectedSet
   618  			computedSet := existingSet.AsSlice()
   619  			testutil.RequireEquivalentSets(t, expectedSet, computedSet)
   620  		})
   621  	}
   622  }
   623  
   624  func TestSubjectSetIntersection(t *testing.T) {
   625  	tcs := []struct {
   626  		name                string
   627  		existing            []*v1.FoundSubject
   628  		toIntersect         []*v1.FoundSubject
   629  		expectedSet         []*v1.FoundSubject
   630  		expectedInvertedSet []*v1.FoundSubject
   631  	}{
   632  		{
   633  			"basic intersection, full overlap",
   634  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
   635  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
   636  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
   637  			nil,
   638  		},
   639  		{
   640  			"basic intersection, partial overlap",
   641  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
   642  			[]*v1.FoundSubject{sub("foo")},
   643  			[]*v1.FoundSubject{sub("foo")},
   644  			nil,
   645  		},
   646  		{
   647  			"basic intersection, no overlap",
   648  			[]*v1.FoundSubject{sub("foo"), sub("bar")},
   649  			[]*v1.FoundSubject{sub("baz")},
   650  			[]*v1.FoundSubject{},
   651  			nil,
   652  		},
   653  		{
   654  			"intersection between bare wildcard and concrete",
   655  			[]*v1.FoundSubject{sub("foo")},
   656  			[]*v1.FoundSubject{wc()},
   657  			[]*v1.FoundSubject{sub("foo")},
   658  			nil,
   659  		},
   660  		{
   661  			"intersection between wildcard with exclusions and concrete",
   662  			[]*v1.FoundSubject{sub("foo")},
   663  			[]*v1.FoundSubject{wc("tom")},
   664  			[]*v1.FoundSubject{sub("foo")},
   665  			nil,
   666  		},
   667  		{
   668  			"intersection between wildcard with matching exclusions and concrete",
   669  			[]*v1.FoundSubject{sub("foo")},
   670  			[]*v1.FoundSubject{wc("foo")},
   671  			[]*v1.FoundSubject{},
   672  			nil,
   673  		},
   674  		{
   675  			"intersection between bare wildcards",
   676  			[]*v1.FoundSubject{wc()},
   677  			[]*v1.FoundSubject{wc()},
   678  			[]*v1.FoundSubject{wc()},
   679  			nil,
   680  		},
   681  		{
   682  			"intersection between bare wildcard and one with exclusions",
   683  			[]*v1.FoundSubject{wc()},
   684  			[]*v1.FoundSubject{wc("1", "2")},
   685  			[]*v1.FoundSubject{wc("1", "2")},
   686  			nil,
   687  		},
   688  		{
   689  			"intersection between wildcards",
   690  			[]*v1.FoundSubject{wc("2", "3")},
   691  			[]*v1.FoundSubject{wc("1", "2")},
   692  			[]*v1.FoundSubject{wc("1", "2", "3")},
   693  			nil,
   694  		},
   695  		{
   696  			"intersection wildcard with exclusions and concrete",
   697  			[]*v1.FoundSubject{wc("2", "3")},
   698  			[]*v1.FoundSubject{sub("4")},
   699  			[]*v1.FoundSubject{sub("4")},
   700  			nil,
   701  		},
   702  		{
   703  			"intersection wildcard with matching exclusions and concrete",
   704  			[]*v1.FoundSubject{wc("2", "3", "4")},
   705  			[]*v1.FoundSubject{sub("4")},
   706  			[]*v1.FoundSubject{},
   707  			nil,
   708  		},
   709  		{
   710  			"intersection of wildcards and two concrete types",
   711  			[]*v1.FoundSubject{wc(), sub("1")},
   712  			[]*v1.FoundSubject{wc(), sub("2")},
   713  			[]*v1.FoundSubject{wc(), sub("1"), sub("2")},
   714  			nil,
   715  		},
   716  
   717  		// Tests with caveats.
   718  		{
   719  			"intersection of non-caveated concrete and caveated concrete",
   720  			[]*v1.FoundSubject{sub("1")},
   721  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat"))},
   722  
   723  			// Resulting subject is caveated on the caveat.
   724  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat"))},
   725  			nil,
   726  		},
   727  		{
   728  			"intersection of caveated concrete and non-caveated concrete",
   729  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat"))},
   730  			[]*v1.FoundSubject{sub("1")},
   731  
   732  			// Resulting subject is caveated on the caveat.
   733  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat"))},
   734  			nil,
   735  		},
   736  		{
   737  			"intersection of caveated concrete and caveated concrete",
   738  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   739  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat2"))},
   740  
   741  			// Resulting subject is caveated both caveats.
   742  			[]*v1.FoundSubject{csub("1", caveatAnd(caveatexpr("caveat1"), caveatexpr("caveat2")))},
   743  
   744  			// Inverted result has the caveats reversed in the `&&` expression.
   745  			[]*v1.FoundSubject{csub("1", caveatAnd(caveatexpr("caveat2"), caveatexpr("caveat1")))},
   746  		},
   747  		{
   748  			"intersection of caveated concrete and non-caveated bare wildcard",
   749  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   750  			[]*v1.FoundSubject{wc()},
   751  
   752  			// Resulting subject is caveated and concrete.
   753  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   754  			nil,
   755  		},
   756  		{
   757  			"intersection of non-caveated bare wildcard and caveated concrete",
   758  			[]*v1.FoundSubject{wc()},
   759  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   760  
   761  			// Resulting subject is caveated and concrete.
   762  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   763  			nil,
   764  		},
   765  		{
   766  			"intersection of caveated concrete and caveated bare wildcard",
   767  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   768  			[]*v1.FoundSubject{cwc(caveatexpr("caveat2"))},
   769  
   770  			// Resulting subject is caveated from both and concrete.
   771  			[]*v1.FoundSubject{csub("1", caveatAnd(caveatexpr("caveat1"), caveatexpr("caveat2")))},
   772  			nil,
   773  		},
   774  		{
   775  			"intersection of caveated bare wildcard and caveated concrete",
   776  			[]*v1.FoundSubject{cwc(caveatexpr("caveat2"))},
   777  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   778  
   779  			// Resulting subject is caveated from both and concrete.
   780  			[]*v1.FoundSubject{csub("1", caveatAnd(caveatexpr("caveat1"), caveatexpr("caveat2")))},
   781  			nil,
   782  		},
   783  		{
   784  			"intersection of caveated concrete and non-caveated wildcard with non-matching exclusion",
   785  			[]*v1.FoundSubject{wc("2")},
   786  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   787  
   788  			// Resulting subject is caveated and concrete.
   789  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   790  			nil,
   791  		},
   792  		{
   793  			"intersection of caveated concrete and non-caveated wildcard with matching exclusion",
   794  			[]*v1.FoundSubject{wc("1")},
   795  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   796  
   797  			// Empty since the subject was in the exclusion set.
   798  			[]*v1.FoundSubject{},
   799  			nil,
   800  		},
   801  		{
   802  			"intersection of caveated concrete and caveated wildcard with non-matching exclusion",
   803  			[]*v1.FoundSubject{cwc(caveatexpr("wcaveat"), sub("2"))},
   804  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   805  
   806  			// Since the wildcard is caveated and has a non-matching exclusion, the caveat is added to
   807  			// the subject.
   808  			[]*v1.FoundSubject{csub("1", caveatAnd(caveatexpr("caveat1"), caveatexpr("wcaveat")))},
   809  			nil,
   810  		},
   811  		{
   812  			"intersection of caveated concrete and caveated wildcard with matching exclusion",
   813  			[]*v1.FoundSubject{cwc(caveatexpr("wcaveat"), sub("1"))},
   814  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   815  
   816  			// Since the wildcard is caveated but has a matching exclusion, the result is empty.
   817  			[]*v1.FoundSubject{},
   818  			nil,
   819  		},
   820  		{
   821  			"intersection of caveated concrete and caveated wildcard with matching caveated exclusion",
   822  			[]*v1.FoundSubject{cwc(caveatexpr("wcaveat"), csub("1", caveatexpr("ecaveat")))},
   823  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1"))},
   824  
   825  			[]*v1.FoundSubject{
   826  				// The concrete is included if its own caveat is true, the wildcard's caveat is true
   827  				// and the exclusion's caveat is false.
   828  				csub("1",
   829  					caveatAnd(
   830  						caveatexpr("caveat1"),
   831  						caveatAnd(
   832  							caveatexpr("wcaveat"),
   833  							caveatInvert(caveatexpr("ecaveat")),
   834  						),
   835  					),
   836  				),
   837  			},
   838  			nil,
   839  		},
   840  		{
   841  			"intersection of caveated concrete and wildcard",
   842  			[]*v1.FoundSubject{csub("1", caveatexpr("first"))},
   843  			[]*v1.FoundSubject{wc()},
   844  
   845  			[]*v1.FoundSubject{
   846  				csub("1",
   847  					caveatexpr("first"),
   848  				),
   849  			},
   850  			nil,
   851  		},
   852  		{
   853  			"intersection of caveated concrete and wildcards",
   854  			[]*v1.FoundSubject{wc(), csub("1", caveatexpr("first"))},
   855  			[]*v1.FoundSubject{wc(), csub("1", caveatexpr("second"))},
   856  
   857  			[]*v1.FoundSubject{
   858  				// Wildcard is included because it is in both sets.
   859  				wc(),
   860  
   861  				// The subject is caveated to appear if either first or second is true, or both are true.
   862  				// This is because of the interaction with the wildcards in each set:
   863  				//
   864  				// - If first is true and second is false, we get {*, 1} ∩ {*} => {*, 1}
   865  				// - If first is false and second is true, we get {*} ∩ {*, 1} => {*, 1}
   866  				// - If both are true, then the subject appears, but that is just a remnant of the join
   867  				//   on the concrete subject itself (since the set does not simplify expressions)
   868  				csub("1",
   869  					caveatOr(
   870  						caveatOr(
   871  							caveatAnd(
   872  								caveatexpr("first"),
   873  								caveatexpr("second"),
   874  							),
   875  							caveatexpr("first"),
   876  						),
   877  						caveatexpr("second"),
   878  					),
   879  				),
   880  			},
   881  			[]*v1.FoundSubject{
   882  				wc(),
   883  				csub("1",
   884  					caveatOr(
   885  						caveatOr(
   886  							caveatAnd(
   887  								caveatexpr("second"),
   888  								caveatexpr("first"),
   889  							),
   890  							caveatexpr("second"),
   891  						),
   892  						caveatexpr("first"),
   893  					),
   894  				),
   895  			},
   896  		},
   897  		{
   898  			"intersection of caveated concrete and caveated wildcard",
   899  			[]*v1.FoundSubject{csub("1", caveatexpr("first"))},
   900  			[]*v1.FoundSubject{
   901  				cwc(caveatexpr("wcaveat")),
   902  				csub("1", caveatexpr("second")),
   903  			},
   904  			[]*v1.FoundSubject{
   905  				csub("1",
   906  					caveatOr(
   907  						caveatAnd(
   908  							caveatexpr("first"),
   909  							caveatexpr("second"),
   910  						),
   911  						caveatAnd(
   912  							caveatexpr("first"),
   913  							caveatexpr("wcaveat"),
   914  						),
   915  					),
   916  				),
   917  			},
   918  			[]*v1.FoundSubject{
   919  				csub("1",
   920  					caveatOr(
   921  						caveatAnd(
   922  							caveatexpr("second"),
   923  							caveatexpr("first"),
   924  						),
   925  						caveatAnd(
   926  							caveatexpr("first"),
   927  							caveatexpr("wcaveat"),
   928  						),
   929  					),
   930  				),
   931  			},
   932  		},
   933  		{
   934  			"intersection of caveated concrete and wildcard with wildcard",
   935  			[]*v1.FoundSubject{csub("1", caveatexpr("caveat1")), wc()},
   936  			[]*v1.FoundSubject{wc()},
   937  			[]*v1.FoundSubject{wc(), csub("1", caveatexpr("caveat1"))},
   938  			nil,
   939  		},
   940  	}
   941  
   942  	for _, tc := range tcs {
   943  		tc := tc
   944  
   945  		t.Run(tc.name, func(t *testing.T) {
   946  			existingSet := NewSubjectSet()
   947  			for _, existing := range tc.existing {
   948  				existingSet.MustAdd(existing)
   949  			}
   950  
   951  			toIntersect := NewSubjectSet()
   952  			for _, toAdd := range tc.toIntersect {
   953  				toIntersect.MustAdd(toAdd)
   954  			}
   955  
   956  			existingSet.MustIntersectionDifference(toIntersect)
   957  
   958  			expectedSet := tc.expectedSet
   959  			computedSet := existingSet.AsSlice()
   960  			testutil.RequireEquivalentSets(t, expectedSet, computedSet)
   961  
   962  			// Run the intersection inverted, which should always result in the same results.
   963  			t.Run("inverted", func(t *testing.T) {
   964  				existingSet := NewSubjectSet()
   965  				for _, existing := range tc.existing {
   966  					existingSet.MustAdd(existing)
   967  				}
   968  
   969  				toIntersect := NewSubjectSet()
   970  				for _, toAdd := range tc.toIntersect {
   971  					toIntersect.MustAdd(toAdd)
   972  				}
   973  
   974  				toIntersect.MustIntersectionDifference(existingSet)
   975  
   976  				// The inverted set is necessary for some caveated sets, because their expressions may
   977  				// have references in reversed locations.
   978  				expectedSet := tc.expectedSet
   979  				if tc.expectedInvertedSet != nil {
   980  					expectedSet = tc.expectedInvertedSet
   981  				}
   982  
   983  				computedSet := toIntersect.AsSlice()
   984  				testutil.RequireEquivalentSets(t, expectedSet, computedSet)
   985  			})
   986  		})
   987  	}
   988  }
   989  
   990  func TestMultipleOperations(t *testing.T) {
   991  	tcs := []struct {
   992  		name        string
   993  		runOps      func(set SubjectSet)
   994  		expectedSet []*v1.FoundSubject
   995  	}{
   996  		{
   997  			"basic adds",
   998  			func(set SubjectSet) {
   999  				set.MustAdd(sub("1"))
  1000  				set.MustAdd(sub("2"))
  1001  				set.MustAdd(sub("3"))
  1002  
  1003  				set.MustAdd(sub("1"))
  1004  				set.MustAdd(sub("2"))
  1005  				set.MustAdd(sub("3"))
  1006  			},
  1007  			[]*v1.FoundSubject{sub("1"), sub("2"), sub("3")},
  1008  		},
  1009  		{
  1010  			"add and remove",
  1011  			func(set SubjectSet) {
  1012  				set.MustAdd(sub("1"))
  1013  				set.MustAdd(sub("2"))
  1014  				set.MustAdd(sub("3"))
  1015  
  1016  				set.Subtract(sub("1"))
  1017  			},
  1018  			[]*v1.FoundSubject{sub("2"), sub("3")},
  1019  		},
  1020  		{
  1021  			"add and intersect",
  1022  			func(set SubjectSet) {
  1023  				set.MustAdd(sub("1"))
  1024  				set.MustAdd(sub("2"))
  1025  				set.MustAdd(sub("3"))
  1026  
  1027  				other := NewSubjectSet()
  1028  				other.MustAdd(sub("2"))
  1029  
  1030  				set.MustIntersectionDifference(other)
  1031  			},
  1032  			[]*v1.FoundSubject{sub("2")},
  1033  		},
  1034  		{
  1035  			"caveated adds",
  1036  			func(set SubjectSet) {
  1037  				set.MustAdd(sub("1"))
  1038  				set.MustAdd(sub("2"))
  1039  				set.MustAdd(sub("3"))
  1040  
  1041  				set.MustAdd(csub("1", caveatexpr("first")))
  1042  				set.MustAdd(csub("1", caveatexpr("second")))
  1043  			},
  1044  			[]*v1.FoundSubject{sub("1"), sub("2"), sub("3")},
  1045  		},
  1046  		{
  1047  			"all caveated adds",
  1048  			func(set SubjectSet) {
  1049  				set.MustAdd(csub("1", caveatexpr("first")))
  1050  				set.MustAdd(csub("1", caveatexpr("second")))
  1051  			},
  1052  			[]*v1.FoundSubject{csub("1", caveatOr(caveatexpr("first"), caveatexpr("second")))},
  1053  		},
  1054  		{
  1055  			"caveated adds and caveated sub",
  1056  			func(set SubjectSet) {
  1057  				set.MustAdd(csub("1", caveatexpr("first")))
  1058  				set.MustAdd(csub("1", caveatexpr("second")))
  1059  				set.Subtract(csub("1", caveatexpr("third")))
  1060  			},
  1061  			[]*v1.FoundSubject{
  1062  				csub("1",
  1063  					caveatAnd(
  1064  						caveatOr(caveatexpr("first"), caveatexpr("second")),
  1065  						caveatInvert(caveatexpr("third")),
  1066  					),
  1067  				),
  1068  			},
  1069  		},
  1070  		{
  1071  			"caveated adds, sub and add",
  1072  			func(set SubjectSet) {
  1073  				set.MustAdd(csub("1", caveatexpr("first")))
  1074  				set.MustAdd(csub("1", caveatexpr("second")))
  1075  				set.Subtract(csub("1", caveatexpr("third")))
  1076  				set.MustAdd(csub("1", caveatexpr("fourth")))
  1077  			},
  1078  			[]*v1.FoundSubject{
  1079  				csub("1",
  1080  					caveatOr(
  1081  						caveatAnd(
  1082  							caveatOr(caveatexpr("first"), caveatexpr("second")),
  1083  							caveatInvert(caveatexpr("third")),
  1084  						),
  1085  						caveatexpr("fourth"),
  1086  					),
  1087  				),
  1088  			},
  1089  		},
  1090  		{
  1091  			"caveated adds, sub and concrete add",
  1092  			func(set SubjectSet) {
  1093  				set.MustAdd(csub("1", caveatexpr("first")))
  1094  				set.MustAdd(csub("1", caveatexpr("second")))
  1095  				set.Subtract(csub("1", caveatexpr("third")))
  1096  				set.MustAdd(sub("1"))
  1097  			},
  1098  			[]*v1.FoundSubject{
  1099  				sub("1"),
  1100  			},
  1101  		},
  1102  		{
  1103  			"add concrete, add wildcard, sub concrete",
  1104  			func(set SubjectSet) {
  1105  				set.MustAdd(sub("1"))
  1106  				set.MustAdd(wc())
  1107  				set.Subtract(sub("1"))
  1108  				set.Subtract(sub("1"))
  1109  			},
  1110  			[]*v1.FoundSubject{wc("1")},
  1111  		},
  1112  		{
  1113  			"add concrete, add wildcard, sub concrete, add concrete",
  1114  			func(set SubjectSet) {
  1115  				set.MustAdd(sub("1"))
  1116  				set.MustAdd(wc())
  1117  				set.Subtract(sub("1"))
  1118  				set.MustAdd(sub("1"))
  1119  			},
  1120  			[]*v1.FoundSubject{wc(), sub("1")},
  1121  		},
  1122  		{
  1123  			"caveated concrete subtracted from wildcard and then concrete added back",
  1124  			func(set SubjectSet) {
  1125  				set.MustAdd(sub("1"))
  1126  				set.MustAdd(wc())
  1127  				set.Subtract(csub("1", caveatexpr("first")))
  1128  				set.MustAdd(sub("1"))
  1129  			},
  1130  			[]*v1.FoundSubject{wc(), sub("1")},
  1131  		},
  1132  		{
  1133  			"concrete subtracted from wildcard and then caveated added back",
  1134  			func(set SubjectSet) {
  1135  				set.MustAdd(sub("1"))
  1136  				set.MustAdd(wc())
  1137  				set.Subtract(sub("1"))
  1138  				set.MustAdd(csub("1", caveatexpr("first")))
  1139  			},
  1140  			[]*v1.FoundSubject{
  1141  				cwc(nil, csub("1", caveatInvert(caveatexpr("first")))),
  1142  				csub("1", caveatexpr("first")),
  1143  			},
  1144  		},
  1145  		{
  1146  			"multiple concrete operations",
  1147  			func(set SubjectSet) {
  1148  				// Start with two sets of concretes and a wildcard.
  1149  				// Example: permission view = viewer + editor
  1150  				//														^ {*}    ^ {1,2,3,4}
  1151  				set.MustAdd(sub("1"))
  1152  				set.MustAdd(sub("2"))
  1153  				set.MustAdd(sub("3"))
  1154  				set.MustAdd(sub("4"))
  1155  				set.MustAdd(wc())
  1156  
  1157  				// Subtract out banned users.
  1158  				// Example: permission view_but_not_banned = view - banned
  1159  				//																								  ^ {3,6,7}
  1160  				set.Subtract(sub("3"))
  1161  				set.Subtract(sub("6"))
  1162  				set.Subtract(sub("7"))
  1163  
  1164  				// Intersect with another set.
  1165  				// Example: permission result = view_but_not_banned & another_set
  1166  				//																										^ {1,3,*}
  1167  				other := NewSubjectSet()
  1168  				other.MustAdd(sub("1"))
  1169  				other.MustAdd(sub("3"))
  1170  				other.MustAdd(wc())
  1171  
  1172  				set.MustIntersectionDifference(other)
  1173  
  1174  				// Remaining
  1175  				// `1` from `view` and `another_set`
  1176  				// `2` from `view` and via * in `another_set`
  1177  				// `4` from `view` and via * in `another_set`
  1178  				// `*` with the `banned`` exclusions
  1179  			},
  1180  			[]*v1.FoundSubject{
  1181  				sub("1"),
  1182  				sub("2"),
  1183  				sub("4"),
  1184  				wc("3", "6", "7"),
  1185  			},
  1186  		},
  1187  		{
  1188  			"multiple operations with caveats",
  1189  			func(set SubjectSet) {
  1190  				// Start with two sets of concretes and a wildcard.
  1191  				// Example: permission view = viewer + editor
  1192  				//														^ {1}    ^ {2,3,4}
  1193  				set.MustAdd(csub("1", caveatexpr("first")))
  1194  				set.MustAdd(sub("2"))
  1195  				set.MustAdd(sub("3"))
  1196  				set.MustAdd(sub("4"))
  1197  
  1198  				// Subtract out banned users.
  1199  				// Example: permission view_but_not_banned = view - banned
  1200  				//																								  ^ {3,6,7}
  1201  				set.Subtract(csub("3", caveatexpr("banned")))
  1202  				set.Subtract(sub("6"))
  1203  				set.Subtract(sub("7"))
  1204  
  1205  				// Intersect with another set.
  1206  				// Example: permission result = view_but_not_banned & another_set
  1207  				//																										^ {1,3,*}
  1208  				other := NewSubjectSet()
  1209  				other.MustAdd(sub("1"))
  1210  				other.MustAdd(csub("3", caveatexpr("second")))
  1211  
  1212  				set.MustIntersectionDifference(other)
  1213  			},
  1214  			[]*v1.FoundSubject{
  1215  				csub("1", caveatexpr("first")),
  1216  				csub("3", caveatAnd(
  1217  					caveatInvert(caveatexpr("banned")),
  1218  					caveatexpr("second"),
  1219  				)),
  1220  			},
  1221  		},
  1222  		{
  1223  			"multiple operations with caveats and wildcards",
  1224  			func(set SubjectSet) {
  1225  				// Start with two sets of concretes and a wildcard.
  1226  				// Example: permission view = viewer + editor
  1227  				//														^ {*}    ^ {1,2,3,4}
  1228  				set.MustAdd(csub("1", caveatexpr("first")))
  1229  				set.MustAdd(sub("2"))
  1230  				set.MustAdd(sub("3"))
  1231  				set.MustAdd(sub("4"))
  1232  				set.MustAdd(wc())
  1233  
  1234  				// Subtract out banned users.
  1235  				// Example: permission view_but_not_banned = view - banned
  1236  				//																								  ^ {3,6,7}
  1237  				set.Subtract(csub("3", caveatexpr("banned")))
  1238  				set.Subtract(sub("6"))
  1239  				set.Subtract(sub("7"))
  1240  
  1241  				// Intersect with another set.
  1242  				// Example: permission result = view_but_not_banned & another_set
  1243  				//																										^ {1,3,*}
  1244  				other := NewSubjectSet()
  1245  				other.MustAdd(sub("1"))
  1246  				other.MustAdd(csub("3", caveatexpr("second")))
  1247  				other.MustAdd(wc())
  1248  
  1249  				set.MustIntersectionDifference(other)
  1250  			},
  1251  			[]*v1.FoundSubject{
  1252  				// `1` is included without caveat because it is non-caveated in other and there is a
  1253  				// wildcard in the original set.
  1254  				sub("1"),
  1255  
  1256  				// `3` inclusion expression:
  1257  				//   ({*, 3} - {3[banned]}) ∩ ({*, 3[second]})
  1258  				//   therefore:
  1259  				//		 `3` is included if banned is false (otherwise it gets removed in the first set).
  1260  				//
  1261  				//   NOTE: the remaining expressions are cruft generated to cover other cases, but because
  1262  				//   there is no expression simplification, it is not collapsing due to just `!banned`
  1263  				csub("3",
  1264  					caveatOr(
  1265  						caveatOr(
  1266  							caveatAnd(
  1267  								caveatInvert(caveatexpr("banned")),
  1268  								caveatexpr("second"),
  1269  							),
  1270  							caveatInvert(caveatexpr("banned")),
  1271  						),
  1272  						caveatAnd(
  1273  							caveatexpr("second"),
  1274  							caveatInvert(caveatexpr("banned")),
  1275  						),
  1276  					),
  1277  				),
  1278  				sub("2"),
  1279  				sub("4"),
  1280  				cwc(nil, csub("3", caveatexpr("banned")), sub("6"), sub("7")),
  1281  			},
  1282  		},
  1283  		{
  1284  			"subtraction followed by intersection without wildcard",
  1285  			func(set SubjectSet) {
  1286  				set.MustAdd(sub("3"))
  1287  				set.MustAdd(wc())
  1288  
  1289  				set.Subtract(csub("3", caveatexpr("banned")))
  1290  
  1291  				other := NewSubjectSet()
  1292  				other.MustAdd(csub("3", caveatexpr("second")))
  1293  
  1294  				set.MustIntersectionDifference(other)
  1295  			},
  1296  			[]*v1.FoundSubject{
  1297  				// `3` inclusion expression:
  1298  				//   ({*, 3} - {3[banned]}) ∩ ({3[second]})
  1299  				//   therefore:
  1300  				//		 `3` is included if banned is false and second is true.
  1301  				csub("3",
  1302  					caveatOr(
  1303  						caveatAnd(
  1304  							caveatInvert(caveatexpr("banned")),
  1305  							caveatexpr("second"),
  1306  						),
  1307  						caveatAnd(
  1308  							caveatexpr("second"),
  1309  							caveatInvert(caveatexpr("banned")),
  1310  						),
  1311  					),
  1312  				),
  1313  			},
  1314  		},
  1315  	}
  1316  
  1317  	for _, tc := range tcs {
  1318  		tc := tc
  1319  
  1320  		t.Run(tc.name, func(t *testing.T) {
  1321  			set := NewSubjectSet()
  1322  			tc.runOps(set)
  1323  
  1324  			expectedSet := tc.expectedSet
  1325  			computedSet := set.AsSlice()
  1326  			testutil.RequireEquivalentSets(t, expectedSet, computedSet)
  1327  		})
  1328  	}
  1329  }
  1330  
  1331  func TestSubtractAll(t *testing.T) {
  1332  	tcs := []struct {
  1333  		name             string
  1334  		startingSubjects []*v1.FoundSubject
  1335  		toSubtract       []*v1.FoundSubject
  1336  		expected         []*v1.FoundSubject
  1337  	}{
  1338  		{
  1339  			"basic mult-subtraction",
  1340  			[]*v1.FoundSubject{
  1341  				sub("1"), sub("2"), sub("3"),
  1342  			},
  1343  			[]*v1.FoundSubject{sub("1"), sub("3")},
  1344  			[]*v1.FoundSubject{sub("2")},
  1345  		},
  1346  		{
  1347  			"wildcad subtraction",
  1348  			[]*v1.FoundSubject{
  1349  				sub("1"), sub("2"), sub("3"),
  1350  			},
  1351  			[]*v1.FoundSubject{wc()},
  1352  			[]*v1.FoundSubject{},
  1353  		},
  1354  		{
  1355  			"wildcad with exclusions subtraction",
  1356  			[]*v1.FoundSubject{
  1357  				sub("1"), sub("2"), sub("3"),
  1358  			},
  1359  			[]*v1.FoundSubject{wc("1"), sub("3")},
  1360  			[]*v1.FoundSubject{sub("1")},
  1361  		},
  1362  	}
  1363  
  1364  	for _, tc := range tcs {
  1365  		tc := tc
  1366  
  1367  		t.Run(tc.name, func(t *testing.T) {
  1368  			set := NewSubjectSet()
  1369  
  1370  			for _, starting := range tc.startingSubjects {
  1371  				set.MustAdd(starting)
  1372  			}
  1373  
  1374  			toRemove := NewSubjectSet()
  1375  			for _, toSubtract := range tc.toSubtract {
  1376  				toRemove.MustAdd(toSubtract)
  1377  			}
  1378  
  1379  			set.SubtractAll(toRemove)
  1380  
  1381  			expectedSet := tc.expected
  1382  			computedSet := set.AsSlice()
  1383  			testutil.RequireEquivalentSets(t, expectedSet, computedSet)
  1384  		})
  1385  	}
  1386  }
  1387  
  1388  func TestSubjectSetClone(t *testing.T) {
  1389  	ss := NewSubjectSet()
  1390  	require.True(t, ss.IsEmpty())
  1391  
  1392  	ss.MustAdd(sub("first"))
  1393  	ss.MustAdd(sub("second"))
  1394  	ss.MustAdd(csub("third", caveatexpr("somecaveat")))
  1395  	ss.MustAdd(wc("one", "two"))
  1396  	require.False(t, ss.IsEmpty())
  1397  
  1398  	existingSubjects := ss.AsSlice()
  1399  
  1400  	// Clone the set.
  1401  	cloned := ss.Clone()
  1402  	require.False(t, cloned.IsEmpty())
  1403  
  1404  	clonedSubjects := cloned.AsSlice()
  1405  	testutil.RequireEquivalentSets(t, existingSubjects, clonedSubjects)
  1406  
  1407  	// Modify the existing set and ensure the cloned is not changed.
  1408  	ss.Subtract(sub("first"))
  1409  	ss.Subtract(wc())
  1410  
  1411  	clonedSubjects = cloned.AsSlice()
  1412  	updatedExistingSubjects := ss.AsSlice()
  1413  
  1414  	testutil.RequireEquivalentSets(t, existingSubjects, clonedSubjects)
  1415  	require.NotEqual(t, len(updatedExistingSubjects), len(clonedSubjects))
  1416  }
  1417  
  1418  func TestSubjectSetGet(t *testing.T) {
  1419  	ss := NewSubjectSet()
  1420  	require.True(t, ss.IsEmpty())
  1421  
  1422  	ss.MustAdd(sub("first"))
  1423  	ss.MustAdd(sub("second"))
  1424  	ss.MustAdd(csub("third", caveatexpr("somecaveat")))
  1425  	ss.MustAdd(wc("one", "two"))
  1426  	require.False(t, ss.IsEmpty())
  1427  
  1428  	found, ok := ss.Get("first")
  1429  	require.True(t, ok)
  1430  	require.Equal(t, "first", found.SubjectId)
  1431  	require.Nil(t, found.CaveatExpression)
  1432  
  1433  	found, ok = ss.Get("second")
  1434  	require.True(t, ok)
  1435  	require.Equal(t, "second", found.SubjectId)
  1436  	require.Nil(t, found.CaveatExpression)
  1437  
  1438  	found, ok = ss.Get("third")
  1439  	require.True(t, ok)
  1440  	require.Equal(t, "third", found.SubjectId)
  1441  	require.NotNil(t, found.CaveatExpression)
  1442  
  1443  	_, ok = ss.Get("fourth")
  1444  	require.False(t, ok)
  1445  
  1446  	found, ok = ss.Get("*")
  1447  	require.True(t, ok)
  1448  	require.Equal(t, "*", found.SubjectId)
  1449  	require.Nil(t, found.CaveatExpression)
  1450  	require.Equal(t, 2, len(found.ExcludedSubjects))
  1451  }
  1452  
  1453  var testSets = [][]*v1.FoundSubject{
  1454  	{sub("foo"), sub("bar")},
  1455  	{sub("foo")},
  1456  	{sub("baz")},
  1457  	{wc()},
  1458  	{wc("tom")},
  1459  	{wc("1", "2")},
  1460  	{wc("1", "2", "3")},
  1461  	{wc("2", "3")},
  1462  	{sub("1")},
  1463  	{wc(), sub("1"), sub("2")},
  1464  	{wc(), sub("2")},
  1465  	{wc(), sub("1")},
  1466  	{csub("1", caveatexpr("caveat"))},
  1467  	{csub("1", caveatexpr("caveat2"))},
  1468  	{cwc(caveatexpr("caveat2"))},
  1469  	{cwc(caveatexpr("wcaveat"), sub("1"))},
  1470  }
  1471  
  1472  func TestUnionCommutativity(t *testing.T) {
  1473  	for _, pair := range allSubsets(testSets, 2) {
  1474  		pair := pair
  1475  		t.Run(fmt.Sprintf("%v", pair), func(t *testing.T) {
  1476  			left1, left2 := NewSubjectSet(), NewSubjectSet()
  1477  			for _, l := range pair[0] {
  1478  				left1.MustAdd(l)
  1479  				left2.MustAdd(l)
  1480  			}
  1481  			right1, right2 := NewSubjectSet(), NewSubjectSet()
  1482  			for _, r := range pair[1] {
  1483  				right1.MustAdd(r)
  1484  				right2.MustAdd(r)
  1485  			}
  1486  			// left union right
  1487  			left1.MustUnionWithSet(right1)
  1488  
  1489  			// right union left
  1490  			right2.MustUnionWithSet(left2)
  1491  
  1492  			mergedLeft := left1.AsSlice()
  1493  			mergedRight := right2.AsSlice()
  1494  			testutil.RequireEquivalentSets(t, mergedLeft, mergedRight)
  1495  		})
  1496  	}
  1497  }
  1498  
  1499  func TestUnionAssociativity(t *testing.T) {
  1500  	for _, triple := range allSubsets(testSets, 3) {
  1501  		triple := triple
  1502  		t.Run(fmt.Sprintf("%s U %s U %s", testutil.FormatSubjects(triple[0]), testutil.FormatSubjects(triple[1]), testutil.FormatSubjects(triple[2])), func(t *testing.T) {
  1503  			// A U (B U C) == (A U B) U C
  1504  
  1505  			A1, A2 := NewSubjectSet(), NewSubjectSet()
  1506  			for _, l := range triple[0] {
  1507  				A1.MustAdd(l)
  1508  				A2.MustAdd(l)
  1509  			}
  1510  			B1, B2 := NewSubjectSet(), NewSubjectSet()
  1511  			for _, l := range triple[1] {
  1512  				B1.MustAdd(l)
  1513  				B2.MustAdd(l)
  1514  			}
  1515  			C1, C2 := NewSubjectSet(), NewSubjectSet()
  1516  			for _, l := range triple[2] {
  1517  				C1.MustAdd(l)
  1518  				C2.MustAdd(l)
  1519  			}
  1520  
  1521  			// A U (B U C)
  1522  			B1.MustUnionWithSet(C1)
  1523  			A1.MustUnionWithSet(B1)
  1524  
  1525  			// (A U B) U C
  1526  			A2.MustUnionWithSet(B2)
  1527  			A2.MustUnionWithSet(C2)
  1528  
  1529  			mergedLeft := A1.AsSlice()
  1530  			mergedRight := A2.AsSlice()
  1531  			testutil.RequireEquivalentSets(t, mergedLeft, mergedRight)
  1532  		})
  1533  	}
  1534  }
  1535  
  1536  func TestIntersectionCommutativity(t *testing.T) {
  1537  	for _, pair := range allSubsets(testSets, 2) {
  1538  		pair := pair
  1539  		t.Run(fmt.Sprintf("%v", pair), func(t *testing.T) {
  1540  			left1, left2 := NewSubjectSet(), NewSubjectSet()
  1541  			for _, l := range pair[0] {
  1542  				left1.MustAdd(l)
  1543  				left2.MustAdd(l)
  1544  			}
  1545  			right1, right2 := NewSubjectSet(), NewSubjectSet()
  1546  			for _, r := range pair[1] {
  1547  				right1.MustAdd(r)
  1548  				right2.MustAdd(r)
  1549  			}
  1550  			// left intersect right
  1551  			left1.MustIntersectionDifference(right1)
  1552  			// right intersects left
  1553  			right2.MustIntersectionDifference(left2)
  1554  
  1555  			mergedLeft := left1.AsSlice()
  1556  			mergedRight := right2.AsSlice()
  1557  			testutil.RequireEquivalentSets(t, mergedLeft, mergedRight)
  1558  		})
  1559  	}
  1560  }
  1561  
  1562  func TestIntersectionAssociativity(t *testing.T) {
  1563  	for _, triple := range allSubsets(testSets, 3) {
  1564  		triple := triple
  1565  		t.Run(fmt.Sprintf("%s ∩ %s ∩ %s", testutil.FormatSubjects(triple[0]), testutil.FormatSubjects(triple[1]), testutil.FormatSubjects(triple[2])), func(t *testing.T) {
  1566  			// A ∩ (B ∩ C) == (A ∩ B) ∩ C
  1567  
  1568  			A1, A2 := NewSubjectSet(), NewSubjectSet()
  1569  			for _, l := range triple[0] {
  1570  				A1.MustAdd(l)
  1571  				A2.MustAdd(l)
  1572  			}
  1573  			B1, B2 := NewSubjectSet(), NewSubjectSet()
  1574  			for _, l := range triple[1] {
  1575  				B1.MustAdd(l)
  1576  				B2.MustAdd(l)
  1577  			}
  1578  			C1, C2 := NewSubjectSet(), NewSubjectSet()
  1579  			for _, l := range triple[2] {
  1580  				C1.MustAdd(l)
  1581  				C2.MustAdd(l)
  1582  			}
  1583  
  1584  			// A ∩ (B ∩ C)
  1585  			B1.MustIntersectionDifference(C1)
  1586  			A1.MustIntersectionDifference(B1)
  1587  
  1588  			// (A ∩ B) ∩ C
  1589  			A2.MustIntersectionDifference(B2)
  1590  			A2.MustIntersectionDifference(C2)
  1591  
  1592  			mergedLeft := A1.AsSlice()
  1593  			mergedRight := A2.AsSlice()
  1594  			testutil.RequireEquivalentSets(t, mergedLeft, mergedRight)
  1595  		})
  1596  	}
  1597  }
  1598  
  1599  func TestIdempotentUnion(t *testing.T) {
  1600  	for _, set := range testSets {
  1601  		set := set
  1602  		t.Run(fmt.Sprintf("%v", set), func(t *testing.T) {
  1603  			// A U A == A
  1604  			A1, A2 := NewSubjectSet(), NewSubjectSet()
  1605  			for _, l := range set {
  1606  				A1.MustAdd(l)
  1607  				A2.MustAdd(l)
  1608  			}
  1609  			A1.MustUnionWithSet(A2)
  1610  
  1611  			mergedLeft := A1.AsSlice()
  1612  			mergedRight := A2.AsSlice()
  1613  			testutil.RequireEquivalentSets(t, mergedLeft, mergedRight)
  1614  		})
  1615  	}
  1616  }
  1617  
  1618  func TestIdempotentIntersection(t *testing.T) {
  1619  	for _, set := range testSets {
  1620  		set := set
  1621  		t.Run(fmt.Sprintf("%v", set), func(t *testing.T) {
  1622  			// A ∩ A == A
  1623  			A1, A2 := NewSubjectSet(), NewSubjectSet()
  1624  			for _, l := range set {
  1625  				A1.MustAdd(l)
  1626  				A2.MustAdd(l)
  1627  			}
  1628  			A1.MustIntersectionDifference(A2)
  1629  
  1630  			mergedLeft := A1.AsSlice()
  1631  			mergedRight := A2.AsSlice()
  1632  			testutil.RequireEquivalentSets(t, mergedLeft, mergedRight)
  1633  		})
  1634  	}
  1635  }
  1636  
  1637  func TestUnionWildcardWithWildcard(t *testing.T) {
  1638  	tcs := []struct {
  1639  		existing        *v1.FoundSubject
  1640  		toUnion         *v1.FoundSubject
  1641  		expected        *v1.FoundSubject
  1642  		expectedInverse *v1.FoundSubject
  1643  	}{
  1644  		{
  1645  			nil,
  1646  			wc(),
  1647  
  1648  			// nil U {*} => {*}
  1649  			wc(),
  1650  			wc(),
  1651  		},
  1652  		{
  1653  			wc(),
  1654  			wc(),
  1655  
  1656  			// {*} U {*} => {*}
  1657  			wc(),
  1658  			wc(),
  1659  		},
  1660  		{
  1661  			wc("1"),
  1662  			wc(),
  1663  
  1664  			// {* - {1}} U {*} => {*}
  1665  			wc(),
  1666  			wc(),
  1667  		},
  1668  		{
  1669  			wc("1"),
  1670  			wc("1"),
  1671  
  1672  			// {* - {1}} U {* - {1}} => {* - {1}}
  1673  			wc("1"),
  1674  			wc("1"),
  1675  		},
  1676  		{
  1677  			wc("1", "2"),
  1678  			wc("1"),
  1679  
  1680  			// {* - {1, 2}} U {* - {1}} => {* - {1}}
  1681  			wc("1"),
  1682  			wc("1"),
  1683  		},
  1684  		{
  1685  			cwc(caveatexpr("first")),
  1686  			wc(),
  1687  
  1688  			// {*[first]} U {*} => {*}
  1689  			wc(),
  1690  			wc(),
  1691  		},
  1692  		{
  1693  			cwc(caveatexpr("first")),
  1694  			cwc(caveatexpr("second")),
  1695  
  1696  			// {*[first]} U {*[second]} => {*[first || second]}
  1697  			cwc(caveatOr(caveatexpr("first"), caveatexpr("second"))),
  1698  			cwc(caveatOr(caveatexpr("second"), caveatexpr("first"))),
  1699  		},
  1700  		{
  1701  			wc("1"),
  1702  			cwc(nil, csub("1", caveatexpr("first"))),
  1703  
  1704  			// Expected
  1705  			// The subject is only excluded if the caveat is true
  1706  			cwc(nil, csub("1", caveatexpr("first"))),
  1707  			cwc(nil, csub("1", caveatexpr("first"))),
  1708  		},
  1709  		{
  1710  			cwc(nil, csub("1", caveatexpr("second"))),
  1711  			cwc(nil, csub("1", caveatexpr("first"))),
  1712  
  1713  			// Expected
  1714  			// The subject is excluded if both its caveats are true
  1715  			cwc(nil,
  1716  				csub("1",
  1717  					caveatAnd(
  1718  						caveatexpr("second"),
  1719  						caveatexpr("first"),
  1720  					),
  1721  				),
  1722  			),
  1723  			cwc(nil,
  1724  				csub("1",
  1725  					caveatAnd(
  1726  						caveatexpr("first"),
  1727  						caveatexpr("second"),
  1728  					),
  1729  				),
  1730  			),
  1731  		},
  1732  	}
  1733  
  1734  	for _, tc := range tcs {
  1735  		tc := tc
  1736  
  1737  		t.Run(fmt.Sprintf("%s U %s", testutil.FormatSubject(tc.existing), testutil.FormatSubject(tc.toUnion)), func(t *testing.T) {
  1738  			existing := wrap(tc.existing)
  1739  			produced, err := unionWildcardWithWildcard(existing, tc.toUnion, subjectSetConstructor)
  1740  			require.NoError(t, err)
  1741  			testutil.RequireExpectedSubject(t, tc.expected, produced)
  1742  
  1743  			toUnion := wrap(tc.toUnion)
  1744  			produced2, err := unionWildcardWithWildcard(toUnion, tc.existing, subjectSetConstructor)
  1745  			require.NoError(t, err)
  1746  			testutil.RequireExpectedSubject(t, tc.expectedInverse, produced2)
  1747  		})
  1748  	}
  1749  }
  1750  
  1751  func TestUnionWildcardWithConcrete(t *testing.T) {
  1752  	tcs := []struct {
  1753  		existing *v1.FoundSubject
  1754  		toUnion  *v1.FoundSubject
  1755  		expected *v1.FoundSubject
  1756  	}{
  1757  		{
  1758  			nil,
  1759  			sub("1"),
  1760  
  1761  			// nil U {1} => nil
  1762  			nil,
  1763  		},
  1764  		{
  1765  			wc(),
  1766  			sub("1"),
  1767  
  1768  			// {*} U {1} => {*}
  1769  			wc(),
  1770  		},
  1771  		{
  1772  			wc("1"),
  1773  			sub("1"),
  1774  
  1775  			// {* - {1}} U {1} => {*}
  1776  			wc(),
  1777  		},
  1778  		{
  1779  			wc("1", "2"),
  1780  			sub("1"),
  1781  
  1782  			// {* - {1, 2}} U {1} => {* - {2}}
  1783  			wc("2"),
  1784  		},
  1785  		{
  1786  			cwc(nil, csub("1", caveatexpr("first"))),
  1787  			sub("1"),
  1788  
  1789  			// {* - {1[first]}} U {1} => {*}
  1790  			wc(),
  1791  		},
  1792  		{
  1793  			cwc(nil, csub("2", caveatexpr("first"))),
  1794  			sub("1"),
  1795  
  1796  			// {* - {2[first]}} U {1} => {* - {2[first]}}
  1797  			cwc(nil, csub("2", caveatexpr("first"))),
  1798  		},
  1799  		{
  1800  			cwc(nil,
  1801  				csub("1", caveatexpr("first")),
  1802  				csub("2", caveatexpr("second")),
  1803  			),
  1804  			sub("1"),
  1805  
  1806  			// {* - {1[first], 2[second]}} U {1} => {* - {2[second]}}
  1807  			cwc(nil, csub("2", caveatexpr("second"))),
  1808  		},
  1809  		{
  1810  			cwc(nil,
  1811  				csub("1", caveatexpr("first")),
  1812  				csub("2", caveatexpr("second")),
  1813  			),
  1814  			csub("1", caveatexpr("third")),
  1815  
  1816  			// {* - {1[first], 2[second]}} U {1[third]} => {* - {1[first && !third], 2[second]}}
  1817  			cwc(nil,
  1818  				csub("1",
  1819  					caveatAnd(
  1820  						caveatexpr("first"),
  1821  						caveatInvert(caveatexpr("third")),
  1822  					),
  1823  				),
  1824  				csub("2", caveatexpr("second")),
  1825  			),
  1826  		},
  1827  		{
  1828  			cwc(caveatexpr("first")),
  1829  			sub("1"),
  1830  
  1831  			// {*}[first] U {1} => {*}[first]
  1832  			cwc(caveatexpr("first")),
  1833  		},
  1834  		{
  1835  			cwc(caveatexpr("wcaveat"),
  1836  				csub("1", caveatexpr("first")),
  1837  				csub("2", caveatexpr("second")),
  1838  			),
  1839  			csub("1", caveatexpr("third")),
  1840  
  1841  			// {* - {1[first], 2[second]}}[wcaveat] U {1[third]} => {* - {1[first && !third], 2[second]}}[wcaveat]
  1842  			cwc(caveatexpr("wcaveat"),
  1843  				csub("1",
  1844  					caveatAnd(
  1845  						caveatexpr("first"),
  1846  						caveatInvert(caveatexpr("third")),
  1847  					),
  1848  				),
  1849  				csub("2", caveatexpr("second")),
  1850  			),
  1851  		},
  1852  	}
  1853  
  1854  	for _, tc := range tcs {
  1855  		tc := tc
  1856  
  1857  		t.Run(fmt.Sprintf("%s U %s", testutil.FormatSubject(tc.existing), testutil.FormatSubject(tc.toUnion)), func(t *testing.T) {
  1858  			existing := wrap(tc.existing)
  1859  			produced := unionWildcardWithConcrete(existing, tc.toUnion, subjectSetConstructor)
  1860  			testutil.RequireExpectedSubject(t, tc.expected, produced)
  1861  		})
  1862  	}
  1863  }
  1864  
  1865  func TestUnionConcreteWithConcrete(t *testing.T) {
  1866  	tcs := []struct {
  1867  		existing         *v1.FoundSubject
  1868  		toUnion          *v1.FoundSubject
  1869  		expected         *v1.FoundSubject
  1870  		expectedInverted *v1.FoundSubject
  1871  	}{
  1872  		{
  1873  			nil,
  1874  			nil,
  1875  
  1876  			// nil U nil => nil
  1877  			nil,
  1878  			nil,
  1879  		},
  1880  		{
  1881  			sub("1"),
  1882  			nil,
  1883  
  1884  			// {1} U nil => {1}
  1885  			sub("1"),
  1886  			sub("1"),
  1887  		},
  1888  		{
  1889  			sub("1"),
  1890  			sub("1"),
  1891  
  1892  			// {1} U {1} => {1}
  1893  			sub("1"),
  1894  			sub("1"),
  1895  		},
  1896  		{
  1897  			csub("1", caveatexpr("first")),
  1898  			sub("1"),
  1899  
  1900  			// {1}[first] U {1} => {1}
  1901  			sub("1"),
  1902  			sub("1"),
  1903  		},
  1904  		{
  1905  			csub("1", caveatexpr("first")),
  1906  			csub("1", caveatexpr("second")),
  1907  
  1908  			// {1}[first] U {1}[second] => {1}[first || second]
  1909  			csub("1", caveatOr(caveatexpr("first"), caveatexpr("second"))),
  1910  			csub("1", caveatOr(caveatexpr("second"), caveatexpr("first"))),
  1911  		},
  1912  	}
  1913  
  1914  	for _, tc := range tcs {
  1915  		tc := tc
  1916  
  1917  		t.Run(fmt.Sprintf("%s U %s", testutil.FormatSubject(tc.existing), testutil.FormatSubject(tc.toUnion)), func(t *testing.T) {
  1918  			existing := wrap(tc.existing)
  1919  			toUnion := wrap(tc.toUnion)
  1920  
  1921  			produced := unionConcreteWithConcrete(existing, toUnion, subjectSetConstructor)
  1922  			testutil.RequireExpectedSubject(t, tc.expected, produced)
  1923  
  1924  			produced2 := unionConcreteWithConcrete(toUnion, existing, subjectSetConstructor)
  1925  			testutil.RequireExpectedSubject(t, tc.expectedInverted, produced2)
  1926  		})
  1927  	}
  1928  }
  1929  
  1930  func TestSubtractWildcardFromWildcard(t *testing.T) {
  1931  	tcs := []struct {
  1932  		existing          *v1.FoundSubject
  1933  		toSubtract        *v1.FoundSubject
  1934  		expected          *v1.FoundSubject
  1935  		expectedConcretes []*v1.FoundSubject
  1936  	}{
  1937  		{
  1938  			nil,
  1939  			wc(),
  1940  
  1941  			// nil - {*} => nil
  1942  			nil,
  1943  			nil,
  1944  		},
  1945  		{
  1946  			wc(),
  1947  			wc(),
  1948  
  1949  			// {*} - {*} => nil
  1950  			nil,
  1951  			nil,
  1952  		},
  1953  		{
  1954  			wc("1", "2"),
  1955  			wc(),
  1956  
  1957  			// {* - {1, 2}} - {*} => nil
  1958  			nil,
  1959  			nil,
  1960  		},
  1961  		{
  1962  			wc("1", "2"),
  1963  			wc("2", "3"),
  1964  
  1965  			// {* - {1, 2}} - {* - {2, 3}} => {3}
  1966  			nil,
  1967  			[]*v1.FoundSubject{sub("3")},
  1968  		},
  1969  		{
  1970  			wc(),
  1971  			wc("1", "2"),
  1972  
  1973  			// {*} - {* - {1, 2}} => {1, 2}
  1974  			nil,
  1975  			[]*v1.FoundSubject{sub("1"), sub("2")},
  1976  		},
  1977  		{
  1978  			cwc(caveatexpr("first")),
  1979  			wc(),
  1980  
  1981  			// {*}[first] - {*} => nil
  1982  			nil,
  1983  			nil,
  1984  		},
  1985  		{
  1986  			wc(),
  1987  			cwc(caveatexpr("first")),
  1988  
  1989  			// {*} - {*}[first] => {*}[!first]
  1990  			cwc(caveatInvert(caveatexpr("first"))),
  1991  			[]*v1.FoundSubject{},
  1992  		},
  1993  		{
  1994  			wc(),
  1995  			cwc(nil, csub("1", caveatexpr("first"))),
  1996  
  1997  			// {*} - {* - {1}[first]} => {1}[first]
  1998  			nil,
  1999  			[]*v1.FoundSubject{csub("1", caveatexpr("first"))},
  2000  		},
  2001  		{
  2002  			cwc(nil, csub("1", caveatexpr("first"))),
  2003  			cwc(nil, csub("1", caveatexpr("second"))),
  2004  
  2005  			// {* - {1}[first]} - {* - {1}[second]} => {1}[!first && second]
  2006  			nil,
  2007  			[]*v1.FoundSubject{
  2008  				csub("1",
  2009  					caveatAnd(
  2010  						caveatInvert(caveatexpr("first")),
  2011  						caveatexpr("second"),
  2012  					),
  2013  				),
  2014  			},
  2015  		},
  2016  		{
  2017  			cwc(caveatexpr("wcaveat1"), csub("1", caveatexpr("first"))),
  2018  			cwc(caveatexpr("wcaveat2"), csub("1", caveatexpr("second"))),
  2019  
  2020  			// {* - {1}[first]}[wcaveat1] -
  2021  			// {* - {1}[second]}[wcaveat2] =>
  2022  			//
  2023  			//		The wildcard itself exists if its caveat is true and the caveat on the second wildcard
  2024  			//		is false:
  2025  			//	  	{* - {1}[first]}[wcaveat1 && !wcaveat2]
  2026  			//
  2027  			//		The concrete is only produced when the first wildcard is present, the exclusion is
  2028  			//		not, the second wildcard is present, and its exclusion is true:
  2029  			//	  	{1}[wcaveat1 && !first && wcaveat2 && second]
  2030  			cwc(
  2031  				caveatAnd(
  2032  					caveatexpr("wcaveat1"),
  2033  					caveatInvert(caveatexpr("wcaveat2")),
  2034  				),
  2035  
  2036  				// Note that the exclusion does not rely on the second caveat, because if the first caveat
  2037  				// is true, then the value is excluded regardless of the second caveat's value.
  2038  				csub("1", caveatexpr("first")),
  2039  			),
  2040  			[]*v1.FoundSubject{
  2041  				csub("1",
  2042  					caveatAnd(
  2043  						caveatAnd(
  2044  							caveatAnd(
  2045  								caveatexpr("wcaveat1"),
  2046  								caveatexpr("wcaveat2"),
  2047  							),
  2048  							caveatInvert(caveatexpr("first")),
  2049  						),
  2050  						caveatexpr("second"),
  2051  					),
  2052  				),
  2053  			},
  2054  		},
  2055  	}
  2056  
  2057  	for _, tc := range tcs {
  2058  		tc := tc
  2059  
  2060  		t.Run(fmt.Sprintf("%s - %s", testutil.FormatSubject(tc.existing), testutil.FormatSubject(tc.toSubtract)), func(t *testing.T) {
  2061  			existing := wrap(tc.existing)
  2062  
  2063  			produced, concrete := subtractWildcardFromWildcard(existing, tc.toSubtract, subjectSetConstructor)
  2064  			testutil.RequireExpectedSubject(t, tc.expected, produced)
  2065  			testutil.RequireEquivalentSets(t, tc.expectedConcretes, concrete)
  2066  		})
  2067  	}
  2068  }
  2069  
  2070  func TestSubtractWildcardFromConcrete(t *testing.T) {
  2071  	tcs := []struct {
  2072  		existing   *v1.FoundSubject
  2073  		toSubtract *v1.FoundSubject
  2074  		expected   *v1.FoundSubject
  2075  	}{
  2076  		{
  2077  			sub("1"),
  2078  			wc(),
  2079  
  2080  			// {1} - {*} => nil
  2081  			nil,
  2082  		},
  2083  		{
  2084  			sub("1"),
  2085  			wc("2"),
  2086  
  2087  			// {1} - {* - {2}} => nil
  2088  			nil,
  2089  		},
  2090  		{
  2091  			sub("1"),
  2092  			wc("1", "2", "3"),
  2093  
  2094  			// {1} - {* - {1, 2, 3}} => {1}
  2095  			sub("1"),
  2096  		},
  2097  		{
  2098  			csub("1", caveatexpr("first")),
  2099  			wc(),
  2100  
  2101  			// {1}[first] - {*} => nil
  2102  			nil,
  2103  		},
  2104  		{
  2105  			csub("1", caveatexpr("first")),
  2106  			cwc(caveatexpr("second")),
  2107  
  2108  			// {1}[first] - {*}[second] => {1[first && !second]}
  2109  			csub("1",
  2110  				caveatAnd(
  2111  					caveatexpr("first"),
  2112  					caveatInvert(caveatexpr("second")),
  2113  				),
  2114  			),
  2115  		},
  2116  		{
  2117  			sub("1"),
  2118  			cwc(nil, csub("1", caveatexpr("first"))),
  2119  
  2120  			// {1} - {* - {1[first]}} => {1}[first]
  2121  			csub("1",
  2122  				caveatexpr("first"),
  2123  			),
  2124  		},
  2125  		{
  2126  			csub("1", caveatexpr("previous")),
  2127  			cwc(nil, csub("1", caveatexpr("exclusion"))),
  2128  
  2129  			// {1}[previous] - {* - {1[exclusion]}} => {1}[previous && exclusion]
  2130  			csub("1",
  2131  				caveatAnd(
  2132  					caveatexpr("previous"),
  2133  					caveatexpr("exclusion"),
  2134  				),
  2135  			),
  2136  		},
  2137  		{
  2138  			csub("1", caveatexpr("previous")),
  2139  			cwc(caveatexpr("wcaveat"), csub("1", caveatexpr("exclusion"))),
  2140  
  2141  			// {1}[previous] - {* - {1[exclusion]}}[wcaveat] => {1}[previous && (!wcaveat || exclusion)]]
  2142  			csub("1",
  2143  				caveatAnd(
  2144  					caveatexpr("previous"),
  2145  					caveatOr(
  2146  						caveatInvert(caveatexpr("wcaveat")),
  2147  						caveatexpr("exclusion"),
  2148  					),
  2149  				),
  2150  			),
  2151  		},
  2152  		{
  2153  			csub("1", caveatexpr("previous")),
  2154  			cwc(caveatexpr("wcaveat"), csub("2", caveatexpr("exclusion"))),
  2155  
  2156  			// {1}[previous] - {* - {2[exclusion]}}[wcaveat] => {1}[previous && !wcaveat)]]
  2157  			csub("1",
  2158  				caveatAnd(
  2159  					caveatexpr("previous"),
  2160  					caveatInvert(caveatexpr("wcaveat")),
  2161  				),
  2162  			),
  2163  		},
  2164  	}
  2165  
  2166  	for _, tc := range tcs {
  2167  		tc := tc
  2168  
  2169  		t.Run(fmt.Sprintf("%v - %v", testutil.FormatSubject(tc.existing), testutil.FormatSubject(tc.toSubtract)), func(t *testing.T) {
  2170  			produced := subtractWildcardFromConcrete(tc.existing, tc.toSubtract, subjectSetConstructor)
  2171  			testutil.RequireExpectedSubject(t, tc.expected, produced)
  2172  		})
  2173  	}
  2174  }
  2175  
  2176  func TestSubtractConcreteFromConcrete(t *testing.T) {
  2177  	tcs := []struct {
  2178  		existing   *v1.FoundSubject
  2179  		toSubtract *v1.FoundSubject
  2180  		expected   *v1.FoundSubject
  2181  	}{
  2182  		{
  2183  			sub("1"),
  2184  			sub("1"),
  2185  
  2186  			// {1} - {1} => nil
  2187  			nil,
  2188  		},
  2189  		{
  2190  			csub("1", caveatexpr("first")),
  2191  			sub("1"),
  2192  
  2193  			// {1[first]} - {1} => nil
  2194  			nil,
  2195  		},
  2196  		{
  2197  			sub("1"),
  2198  			csub("1", caveatexpr("first")),
  2199  
  2200  			// {1} - {1[first]} => {1[!first]}
  2201  			csub("1", caveatInvert(caveatexpr("first"))),
  2202  		},
  2203  		{
  2204  			csub("1", caveatexpr("first")),
  2205  			csub("1", caveatexpr("second")),
  2206  
  2207  			// {1[first]} - {1[second]} => {1[first && !second]}
  2208  			csub("1",
  2209  				caveatAnd(
  2210  					caveatexpr("first"),
  2211  					caveatInvert(caveatexpr("second")),
  2212  				),
  2213  			),
  2214  		},
  2215  	}
  2216  
  2217  	for _, tc := range tcs {
  2218  		tc := tc
  2219  
  2220  		t.Run(fmt.Sprintf("%s - %s", testutil.FormatSubject(tc.existing), testutil.FormatSubject(tc.toSubtract)), func(t *testing.T) {
  2221  			produced := subtractConcreteFromConcrete(tc.existing, tc.toSubtract, subjectSetConstructor)
  2222  			testutil.RequireExpectedSubject(t, tc.expected, produced)
  2223  		})
  2224  	}
  2225  }
  2226  
  2227  func TestSubtractConcreteFromWildcard(t *testing.T) {
  2228  	tcs := []struct {
  2229  		existing   *v1.FoundSubject
  2230  		toSubtract *v1.FoundSubject
  2231  		expected   *v1.FoundSubject
  2232  	}{
  2233  		{
  2234  			wc(),
  2235  			sub("1"),
  2236  
  2237  			// {*} - {1} => {* - {1}}
  2238  			wc("1"),
  2239  		},
  2240  		{
  2241  			wc("1"),
  2242  			sub("1"),
  2243  
  2244  			// {* - {1}} - {1} => {* - {1}}
  2245  			wc("1"),
  2246  		},
  2247  		{
  2248  			wc("1", "2"),
  2249  			sub("1"),
  2250  
  2251  			// {* - {1, 2}} - {1} => {* - {1, 2}}
  2252  			wc("1", "2"),
  2253  		},
  2254  		{
  2255  			cwc(caveatexpr("wcaveat"), sub("1"), sub("2")),
  2256  			sub("1"),
  2257  
  2258  			// {* - {1, 2}}[wcaveat] - {1} => {* - {1, 2}}[wcaveat]
  2259  			cwc(caveatexpr("wcaveat"), sub("1"), sub("2")),
  2260  		},
  2261  		{
  2262  			cwc(caveatexpr("wcaveat"), csub("1", caveatexpr("first")), sub("2")),
  2263  			sub("1"),
  2264  
  2265  			// {* - {1[first], 2}}[wcaveat] - {1} => {* - {1, 2}}[wcaveat]
  2266  			cwc(caveatexpr("wcaveat"), sub("1"), sub("2")),
  2267  		},
  2268  		{
  2269  			cwc(caveatexpr("wcaveat"), sub("1"), sub("2")),
  2270  			csub("1", caveatexpr("second")),
  2271  
  2272  			// {* - {1, 2}}[wcaveat] - {1}[first] => {* - {1, 2}}[wcaveat]
  2273  			cwc(caveatexpr("wcaveat"), sub("1"), sub("2")),
  2274  		},
  2275  		{
  2276  			cwc(caveatexpr("wcaveat"), csub("1", caveatexpr("first")), sub("2")),
  2277  			csub("1", caveatexpr("second")),
  2278  
  2279  			// {* - {1[first], 2}}[wcaveat] - {1}[second] => {* - {1[first || second], 2}}[wcaveat]
  2280  			cwc(
  2281  				caveatexpr("wcaveat"),
  2282  				csub("1",
  2283  					caveatOr(
  2284  						caveatexpr("first"),
  2285  						caveatexpr("second"),
  2286  					),
  2287  				),
  2288  				sub("2")),
  2289  		},
  2290  	}
  2291  
  2292  	for _, tc := range tcs {
  2293  		tc := tc
  2294  
  2295  		t.Run(fmt.Sprintf("%s - %s", testutil.FormatSubject(tc.existing), testutil.FormatSubject(tc.toSubtract)), func(t *testing.T) {
  2296  			produced := subtractConcreteFromWildcard(tc.existing, tc.toSubtract, subjectSetConstructor)
  2297  			testutil.RequireExpectedSubject(t, tc.expected, produced)
  2298  		})
  2299  	}
  2300  }
  2301  
  2302  func TestIntersectConcreteWithConcrete(t *testing.T) {
  2303  	tcs := []struct {
  2304  		first    *v1.FoundSubject
  2305  		second   *v1.FoundSubject
  2306  		expected *v1.FoundSubject
  2307  	}{
  2308  		{
  2309  			sub("1"),
  2310  			nil,
  2311  
  2312  			// {1} ∩ {} => nil
  2313  			nil,
  2314  		},
  2315  		{
  2316  			sub("1"),
  2317  			sub("1"),
  2318  
  2319  			// {1} ∩ {1} => {1}
  2320  			sub("1"),
  2321  		},
  2322  		{
  2323  			csub("1", caveatexpr("first")),
  2324  			sub("1"),
  2325  
  2326  			// {1[first]} ∩ {1} => {1[first]}
  2327  			csub("1", caveatexpr("first")),
  2328  		},
  2329  		{
  2330  			sub("1"),
  2331  			csub("1", caveatexpr("first")),
  2332  
  2333  			// {1} ∩ {1[first]} => {1[first]}
  2334  			csub("1", caveatexpr("first")),
  2335  		},
  2336  		{
  2337  			csub("1", caveatexpr("first")),
  2338  			csub("1", caveatexpr("second")),
  2339  
  2340  			// {1[first]} ∩ {1[second]} => {1[first && second]}
  2341  			csub("1",
  2342  				caveatAnd(
  2343  					caveatexpr("first"),
  2344  					caveatexpr("second"),
  2345  				),
  2346  			),
  2347  		},
  2348  	}
  2349  
  2350  	for _, tc := range tcs {
  2351  		tc := tc
  2352  
  2353  		t.Run(fmt.Sprintf("%s ∩ %s", testutil.FormatSubject(tc.first), testutil.FormatSubject(tc.second)), func(t *testing.T) {
  2354  			second := wrap(tc.second)
  2355  
  2356  			produced := intersectConcreteWithConcrete(tc.first, second, subjectSetConstructor)
  2357  			testutil.RequireExpectedSubject(t, tc.expected, produced)
  2358  		})
  2359  	}
  2360  }
  2361  
  2362  func TestIntersectWildcardWithWildcard(t *testing.T) {
  2363  	tcs := []struct {
  2364  		first  *v1.FoundSubject
  2365  		second *v1.FoundSubject
  2366  
  2367  		expected         *v1.FoundSubject
  2368  		expectedInverted *v1.FoundSubject
  2369  	}{
  2370  		{
  2371  			nil,
  2372  			nil,
  2373  
  2374  			// nil ∩ nil => nil
  2375  			nil,
  2376  			nil,
  2377  		},
  2378  		{
  2379  			wc(),
  2380  			nil,
  2381  
  2382  			// {*} ∩ nil => nil
  2383  			nil,
  2384  			nil,
  2385  		},
  2386  		{
  2387  			wc(),
  2388  			wc(),
  2389  
  2390  			// {*} ∩ {*} => {*}
  2391  			wc(),
  2392  			wc(),
  2393  		},
  2394  		{
  2395  			wc("1"),
  2396  			wc(),
  2397  
  2398  			// {* - {1}} ∩ {*} => {* - {1}}
  2399  			wc("1"),
  2400  			wc("1"),
  2401  		},
  2402  		{
  2403  			wc("1", "2"),
  2404  			wc("2", "3"),
  2405  
  2406  			// {* - {1,2}} ∩ {* - {2,3}} => {* - {1,2,3}}
  2407  			wc("1", "2", "3"),
  2408  			wc("1", "2", "3"),
  2409  		},
  2410  		{
  2411  			cwc(caveatexpr("first")),
  2412  			cwc(caveatexpr("second")),
  2413  
  2414  			// {*}[first] ∩ {*}[second] => {*}[first && second]
  2415  			cwc(
  2416  				caveatAnd(
  2417  					caveatexpr("first"),
  2418  					caveatexpr("second"),
  2419  				),
  2420  			),
  2421  			cwc(
  2422  				caveatAnd(
  2423  					caveatexpr("second"),
  2424  					caveatexpr("first"),
  2425  				),
  2426  			),
  2427  		},
  2428  		{
  2429  			cwc(
  2430  				caveatexpr("first"),
  2431  				csub("1", caveatexpr("ex1cav")),
  2432  				csub("2", caveatexpr("ex2cav")),
  2433  			),
  2434  			cwc(
  2435  				caveatexpr("second"),
  2436  				csub("2", caveatexpr("ex3cav")),
  2437  				csub("3", caveatexpr("ex4cav")),
  2438  			),
  2439  
  2440  			// {* - {1[ex1cav], 2[ex2cav]}}[first] ∩ {* - {2[ex3cav], 3[ex4cav]}}[second] => {* - {1[ex1cav], 2[ex2cav || ex3cav], 3[ex4cav]}}[first && second]
  2441  			cwc(
  2442  				caveatAnd(
  2443  					caveatexpr("first"),
  2444  					caveatexpr("second"),
  2445  				),
  2446  				csub("1", caveatexpr("ex1cav")),
  2447  				csub("2", caveatOr(caveatexpr("ex2cav"), caveatexpr("ex3cav"))),
  2448  				csub("3", caveatexpr("ex4cav")),
  2449  			),
  2450  			cwc(
  2451  				caveatAnd(
  2452  					caveatexpr("second"),
  2453  					caveatexpr("first"),
  2454  				),
  2455  				csub("1", caveatexpr("ex1cav")),
  2456  				csub("2", caveatOr(caveatexpr("ex3cav"), caveatexpr("ex2cav"))),
  2457  				csub("3", caveatexpr("ex4cav")),
  2458  			),
  2459  		},
  2460  	}
  2461  
  2462  	for _, tc := range tcs {
  2463  		tc := tc
  2464  
  2465  		t.Run(fmt.Sprintf("%s ∩ %s", testutil.FormatSubject(tc.first), testutil.FormatSubject(tc.second)), func(t *testing.T) {
  2466  			first := wrap(tc.first)
  2467  			second := wrap(tc.second)
  2468  
  2469  			produced, err := intersectWildcardWithWildcard(first, second, subjectSetConstructor)
  2470  			require.NoError(t, err)
  2471  			testutil.RequireExpectedSubject(t, tc.expected, produced)
  2472  
  2473  			produced2, err := intersectWildcardWithWildcard(second, first, subjectSetConstructor)
  2474  			require.NoError(t, err)
  2475  			testutil.RequireExpectedSubject(t, tc.expectedInverted, produced2)
  2476  		})
  2477  	}
  2478  }
  2479  
  2480  func TestIntersectConcreteWithWildcard(t *testing.T) {
  2481  	tcs := []struct {
  2482  		concrete *v1.FoundSubject
  2483  		wildcard *v1.FoundSubject
  2484  
  2485  		expected *v1.FoundSubject
  2486  	}{
  2487  		{
  2488  			sub("1"),
  2489  			nil,
  2490  
  2491  			// 1 ∩ nil => nil
  2492  			nil,
  2493  		},
  2494  		{
  2495  			sub("1"),
  2496  			wc(),
  2497  
  2498  			// 1 ∩ {*} => {1}
  2499  			sub("1"),
  2500  		},
  2501  		{
  2502  			sub("1"),
  2503  			wc("1"),
  2504  
  2505  			// 1 ∩ {* - {1}} => nil
  2506  			nil,
  2507  		},
  2508  		{
  2509  			sub("1"),
  2510  			wc("2"),
  2511  
  2512  			// 1 ∩ {* - {2}} => {1}
  2513  			sub("1"),
  2514  		},
  2515  		{
  2516  			sub("1"),
  2517  			cwc(caveatexpr("wcaveat")),
  2518  
  2519  			// 1 ∩ {*}[wcaveat] => {1}[wcaveat]
  2520  			csub("1", caveatexpr("wcaveat")),
  2521  		},
  2522  		{
  2523  			sub("42"),
  2524  			cwc(nil, csub("42", caveatexpr("first"))),
  2525  
  2526  			// 42 ∩ {* - 42[first]} => {42}[!first]
  2527  			csub("42",
  2528  				caveatInvert(caveatexpr("first")),
  2529  			),
  2530  		},
  2531  		{
  2532  			sub("1"),
  2533  			cwc(caveatexpr("wcaveat"), csub("1", caveatexpr("first"))),
  2534  
  2535  			// 1 ∩ {* - 1[first]}[wcaveat] => {1}[wcaveat && !first]
  2536  			csub("1",
  2537  				caveatAnd(
  2538  					caveatexpr("wcaveat"),
  2539  					caveatInvert(caveatexpr("first")),
  2540  				),
  2541  			),
  2542  		},
  2543  		{
  2544  			csub("1", caveatexpr("first")),
  2545  			cwc(nil, csub("1", caveatexpr("second"))),
  2546  
  2547  			// 1[first] ∩ {* - 1[second]} => {1}[first && !second]
  2548  			csub("1",
  2549  				caveatAnd(
  2550  					caveatexpr("first"),
  2551  					caveatInvert(caveatexpr("second")),
  2552  				),
  2553  			),
  2554  		},
  2555  		{
  2556  			csub("1", caveatexpr("first")),
  2557  			cwc(caveatexpr("wcaveat"), csub("1", caveatexpr("second"))),
  2558  
  2559  			// 1[first] ∩ {* - 1[second]}[wcaveat] => {1}[first && !second && wcaveat]
  2560  			csub("1",
  2561  				caveatAnd(
  2562  					caveatexpr("first"),
  2563  					caveatAnd(
  2564  						caveatexpr("wcaveat"),
  2565  						caveatInvert(caveatexpr("second")),
  2566  					),
  2567  				),
  2568  			),
  2569  		},
  2570  		{
  2571  			csub("1", caveatexpr("first")),
  2572  			cwc(
  2573  				caveatexpr("wcaveat"),
  2574  				csub("1", caveatexpr("second")),
  2575  				csub("2", caveatexpr("third")),
  2576  			),
  2577  
  2578  			// 1[first] ∩ {* - {1[second], 2[third]}}[wcaveat] => {1}[first && !second && wcaveat]
  2579  			csub("1",
  2580  				caveatAnd(
  2581  					caveatexpr("first"),
  2582  					caveatAnd(
  2583  						caveatexpr("wcaveat"),
  2584  						caveatInvert(caveatexpr("second")),
  2585  					),
  2586  				),
  2587  			),
  2588  		},
  2589  	}
  2590  
  2591  	for _, tc := range tcs {
  2592  		tc := tc
  2593  
  2594  		t.Run(fmt.Sprintf("%s ∩ %s", testutil.FormatSubject(tc.concrete), testutil.FormatSubject(tc.wildcard)), func(t *testing.T) {
  2595  			wildcard := wrap(tc.wildcard)
  2596  
  2597  			produced, err := intersectConcreteWithWildcard(tc.concrete, wildcard, subjectSetConstructor)
  2598  			require.NoError(t, err)
  2599  			testutil.RequireExpectedSubject(t, tc.expected, produced)
  2600  		})
  2601  	}
  2602  }
  2603  
  2604  // allSubsets returns a list of all subsets of length n
  2605  // it counts in binary and "activates" input funcs that match 1s in the binary representation
  2606  // it doesn't check for overflow so don't go crazy
  2607  func allSubsets[T any](objs []T, n int) [][]T {
  2608  	maxInt := uint64(math.Exp2(float64(len(objs)))) - 1
  2609  	all := make([][]T, 0)
  2610  
  2611  	for i := uint64(0); i < maxInt; i++ {
  2612  		set := make([]T, 0, n)
  2613  		for digit := uint64(0); digit < uint64(len(objs)); digit++ {
  2614  			mask := uint64(1) << digit
  2615  			if mask&i != 0 {
  2616  				set = append(set, objs[digit])
  2617  			}
  2618  			if len(set) > n {
  2619  				break
  2620  			}
  2621  		}
  2622  		if len(set) == n {
  2623  			all = append(all, set)
  2624  		}
  2625  	}
  2626  	return all
  2627  }