github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/caveats/run_test.go (about) 1 package caveats_test 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "testing" 8 9 "github.com/stretchr/testify/require" 10 11 "github.com/authzed/spicedb/internal/caveats" 12 "github.com/authzed/spicedb/internal/datastore/memdb" 13 "github.com/authzed/spicedb/internal/testfixtures" 14 core "github.com/authzed/spicedb/pkg/proto/core/v1" 15 ) 16 17 var ( 18 caveatexpr = caveats.CaveatExprForTesting 19 caveatAnd = caveats.And 20 caveatOr = caveats.Or 21 caveatInvert = caveats.Invert 22 ) 23 24 func TestRunCaveatExpressions(t *testing.T) { 25 tcs := []struct { 26 name string 27 expression *core.CaveatExpression 28 context map[string]any 29 expectedValue bool 30 }{ 31 { 32 "basic", 33 caveatexpr("firstCaveat"), 34 map[string]any{ 35 "first": "42", 36 }, 37 true, 38 }, 39 { 40 "another basic", 41 caveatexpr("firstCaveat"), 42 map[string]any{ 43 "first": "12", 44 }, 45 false, 46 }, 47 { 48 "short circuited or", 49 caveatOr( 50 caveatexpr("firstCaveat"), 51 caveatexpr("secondCaveat"), 52 ), 53 map[string]any{ 54 "first": "42", 55 }, 56 true, 57 }, 58 { 59 "non-short circuited or", 60 caveatOr( 61 caveatexpr("firstCaveat"), 62 caveatexpr("secondCaveat"), 63 ), 64 map[string]any{ 65 "first": "12", 66 "second": "hello", 67 }, 68 true, 69 }, 70 { 71 "false or", 72 caveatOr( 73 caveatexpr("firstCaveat"), 74 caveatexpr("secondCaveat"), 75 ), 76 map[string]any{ 77 "first": "12", 78 "second": "hi", 79 }, 80 false, 81 }, 82 { 83 "short circuited and", 84 caveatAnd( 85 caveatexpr("firstCaveat"), 86 caveatexpr("secondCaveat"), 87 ), 88 map[string]any{ 89 "first": "12", 90 }, 91 false, 92 }, 93 { 94 "non-short circuited and", 95 caveatAnd( 96 caveatexpr("firstCaveat"), 97 caveatexpr("secondCaveat"), 98 ), 99 map[string]any{ 100 "first": "12", 101 "second": "hello", 102 }, 103 false, 104 }, 105 { 106 "false or", 107 caveatAnd( 108 caveatexpr("firstCaveat"), 109 caveatexpr("secondCaveat"), 110 ), 111 map[string]any{ 112 "first": "42", 113 "second": "hi", 114 }, 115 false, 116 }, 117 { 118 "inversion", 119 caveatInvert( 120 caveatexpr("firstCaveat"), 121 ), 122 map[string]any{ 123 "first": "12", 124 }, 125 true, 126 }, 127 { 128 "nested", 129 caveatAnd( 130 caveatOr( 131 caveatexpr("firstCaveat"), 132 caveatexpr("secondCaveat"), 133 ), 134 caveatInvert( 135 caveatexpr("thirdCaveat"), 136 ), 137 ), 138 map[string]any{ 139 "first": "12", 140 "second": "hi", 141 "third": false, 142 }, 143 false, 144 }, 145 { 146 "nested true", 147 caveatAnd( 148 caveatOr( 149 caveatexpr("firstCaveat"), 150 caveatexpr("secondCaveat"), 151 ), 152 caveatInvert( 153 caveatexpr("thirdCaveat"), 154 ), 155 ), 156 map[string]any{ 157 "first": "42", 158 "second": "hi", 159 "third": false, 160 }, 161 true, 162 }, 163 } 164 165 for _, tc := range tcs { 166 tc := tc 167 t.Run(tc.name, func(t *testing.T) { 168 req := require.New(t) 169 170 rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) 171 req.NoError(err) 172 173 ds, _ := testfixtures.DatastoreFromSchemaAndTestRelationships(rawDS, ` 174 caveat firstCaveat(first int) { 175 first == 42 176 } 177 178 caveat secondCaveat(second string) { 179 second == 'hello' 180 } 181 182 caveat thirdCaveat(third bool) { 183 third 184 } 185 `, nil, req) 186 headRevision, err := ds.HeadRevision(context.Background()) 187 req.NoError(err) 188 189 reader := ds.SnapshotReader(headRevision) 190 191 for _, debugOption := range []caveats.RunCaveatExpressionDebugOption{ 192 caveats.RunCaveatExpressionNoDebugging, 193 caveats.RunCaveatExpressionWithDebugInformation, 194 } { 195 debugOption := debugOption 196 t.Run(fmt.Sprintf("%v", debugOption), func(t *testing.T) { 197 req := require.New(t) 198 199 result, err := caveats.RunCaveatExpression(context.Background(), tc.expression, tc.context, reader, debugOption) 200 req.NoError(err) 201 req.Equal(tc.expectedValue, result.Value()) 202 }) 203 } 204 }) 205 } 206 } 207 208 func TestRunCaveatWithMissingMap(t *testing.T) { 209 req := require.New(t) 210 211 rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) 212 req.NoError(err) 213 214 ds, _ := testfixtures.DatastoreFromSchemaAndTestRelationships(rawDS, ` 215 caveat some_caveat(themap map<any>) { 216 themap.first == 42 217 } 218 `, nil, req) 219 220 headRevision, err := ds.HeadRevision(context.Background()) 221 req.NoError(err) 222 223 reader := ds.SnapshotReader(headRevision) 224 225 result, err := caveats.RunCaveatExpression( 226 context.Background(), 227 caveatexpr("some_caveat"), 228 map[string]any{}, 229 reader, 230 caveats.RunCaveatExpressionNoDebugging, 231 ) 232 req.NoError(err) 233 req.True(result.IsPartial()) 234 req.False(result.Value()) 235 } 236 237 func TestRunCaveatWithEmptyMap(t *testing.T) { 238 req := require.New(t) 239 240 rawDS, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC) 241 req.NoError(err) 242 243 ds, _ := testfixtures.DatastoreFromSchemaAndTestRelationships(rawDS, ` 244 caveat some_caveat(themap map<any>) { 245 themap.first == 42 246 } 247 `, nil, req) 248 249 headRevision, err := ds.HeadRevision(context.Background()) 250 req.NoError(err) 251 252 reader := ds.SnapshotReader(headRevision) 253 254 _, err = caveats.RunCaveatExpression( 255 context.Background(), 256 caveatexpr("some_caveat"), 257 map[string]any{ 258 "themap": map[string]any{}, 259 }, 260 reader, 261 caveats.RunCaveatExpressionNoDebugging, 262 ) 263 req.Error(err) 264 req.True(errors.As(err, &caveats.EvaluationErr{})) 265 }