github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/services/v1/metadata_test.go (about) 1 package v1_test 2 3 import ( 4 "context" 5 "errors" 6 "io" 7 "reflect" 8 "testing" 9 10 "github.com/authzed/spicedb/pkg/tuple" 11 12 "github.com/authzed/authzed-go/pkg/responsemeta" 13 v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" 14 "github.com/stretchr/testify/require" 15 "google.golang.org/grpc" 16 "google.golang.org/grpc/metadata" 17 18 "github.com/authzed/spicedb/internal/datastore/memdb" 19 tf "github.com/authzed/spicedb/internal/testfixtures" 20 "github.com/authzed/spicedb/internal/testserver" 21 "github.com/authzed/spicedb/pkg/zedtoken" 22 ) 23 24 func TestAllMethodsReturnMetadata(t *testing.T) { 25 req := require.New(t) 26 conn, cleanup, _, revision := testserver.NewTestServer(req, 0, memdb.DisableGC, true, tf.StandardDatastoreWithData) 27 t.Cleanup(cleanup) 28 29 ctx := context.Background() 30 31 // PermissionsService 32 checkServiceMethods( 33 t, 34 v1.NewPermissionsServiceClient(conn), 35 map[string]func(t *testing.T, client v1.PermissionsServiceClient) metadata.MD{ 36 "CheckPermission": func(t *testing.T, client v1.PermissionsServiceClient) metadata.MD { 37 var trailer metadata.MD 38 _, err := client.CheckPermission(ctx, &v1.CheckPermissionRequest{ 39 Consistency: &v1.Consistency{ 40 Requirement: &v1.Consistency_AtLeastAsFresh{ 41 AtLeastAsFresh: zedtoken.MustNewFromRevision(revision), 42 }, 43 }, 44 Resource: obj("document", "masterplan"), 45 Permission: "view", 46 Subject: sub("user", "eng_lead", ""), 47 }, grpc.Trailer(&trailer)) 48 require.NoError(t, err) 49 return trailer 50 }, 51 "CheckBulkPermissions": func(t *testing.T, client v1.PermissionsServiceClient) metadata.MD { 52 var trailer metadata.MD 53 _, err := client.CheckBulkPermissions(ctx, &v1.CheckBulkPermissionsRequest{ 54 Consistency: &v1.Consistency{ 55 Requirement: &v1.Consistency_AtLeastAsFresh{ 56 AtLeastAsFresh: zedtoken.MustNewFromRevision(revision), 57 }, 58 }, 59 Items: []*v1.CheckBulkPermissionsRequestItem{ 60 { 61 Resource: obj("document", "masterplan"), 62 Permission: "view", 63 Subject: sub("user", "eng_lead", ""), 64 }, 65 }, 66 }, grpc.Trailer(&trailer)) 67 require.NoError(t, err) 68 return trailer 69 }, 70 "DeleteRelationships": func(t *testing.T, client v1.PermissionsServiceClient) metadata.MD { 71 var trailer metadata.MD 72 _, err := client.DeleteRelationships(ctx, &v1.DeleteRelationshipsRequest{ 73 RelationshipFilter: &v1.RelationshipFilter{ 74 ResourceType: "folder", 75 OptionalResourceId: "somefolder", 76 }, 77 }, grpc.Trailer(&trailer)) 78 require.NoError(t, err) 79 return trailer 80 }, 81 "WriteRelationships": func(t *testing.T, client v1.PermissionsServiceClient) metadata.MD { 82 var trailer metadata.MD 83 _, err := client.WriteRelationships(ctx, &v1.WriteRelationshipsRequest{ 84 Updates: []*v1.RelationshipUpdate{ 85 { 86 Operation: v1.RelationshipUpdate_OPERATION_TOUCH, 87 Relationship: tuple.MustToRelationship(tuple.MustParse("document:anotherdoc#viewer@user:tom")), 88 }, 89 }, 90 }, grpc.Trailer(&trailer)) 91 require.NoError(t, err) 92 return trailer 93 }, 94 "ExpandPermissionTree": func(t *testing.T, client v1.PermissionsServiceClient) metadata.MD { 95 var trailer metadata.MD 96 _, err := client.ExpandPermissionTree(ctx, &v1.ExpandPermissionTreeRequest{ 97 Consistency: &v1.Consistency{ 98 Requirement: &v1.Consistency_AtLeastAsFresh{ 99 AtLeastAsFresh: zedtoken.MustNewFromRevision(revision), 100 }, 101 }, 102 Resource: obj("document", "masterplan"), 103 Permission: "view", 104 }, grpc.Trailer(&trailer)) 105 require.NoError(t, err) 106 return trailer 107 }, 108 "ReadRelationships": func(t *testing.T, client v1.PermissionsServiceClient) metadata.MD { 109 var trailer metadata.MD 110 stream, err := client.ReadRelationships(ctx, &v1.ReadRelationshipsRequest{ 111 RelationshipFilter: &v1.RelationshipFilter{ 112 ResourceType: "folder", 113 }, 114 }, grpc.Trailer(&trailer)) 115 require.NoError(t, err) 116 117 for { 118 _, err := stream.Recv() 119 if errors.Is(err, io.EOF) { 120 break 121 } 122 123 require.NoError(t, err) 124 } 125 126 return trailer 127 }, 128 "LookupResources": func(t *testing.T, client v1.PermissionsServiceClient) metadata.MD { 129 var trailer metadata.MD 130 stream, err := client.LookupResources(ctx, &v1.LookupResourcesRequest{ 131 Consistency: &v1.Consistency{ 132 Requirement: &v1.Consistency_AtLeastAsFresh{ 133 AtLeastAsFresh: zedtoken.MustNewFromRevision(revision), 134 }, 135 }, 136 ResourceObjectType: "document", 137 Permission: "view", 138 Subject: sub("user", "tom", ""), 139 }, grpc.Trailer(&trailer)) 140 require.NoError(t, err) 141 142 for { 143 _, err := stream.Recv() 144 if errors.Is(err, io.EOF) { 145 break 146 } 147 148 require.NoError(t, err) 149 } 150 151 return trailer 152 }, 153 "LookupSubjects": func(t *testing.T, client v1.PermissionsServiceClient) metadata.MD { 154 var trailer metadata.MD 155 stream, err := client.LookupSubjects(ctx, &v1.LookupSubjectsRequest{ 156 Consistency: &v1.Consistency{ 157 Requirement: &v1.Consistency_AtLeastAsFresh{ 158 AtLeastAsFresh: zedtoken.MustNewFromRevision(revision), 159 }, 160 }, 161 Resource: obj("document", "masterplan"), 162 Permission: "view", 163 SubjectObjectType: "user", 164 }, grpc.Trailer(&trailer)) 165 require.NoError(t, err) 166 167 for { 168 _, err := stream.Recv() 169 if errors.Is(err, io.EOF) { 170 break 171 } 172 173 require.NoError(t, err) 174 } 175 176 return trailer 177 }, 178 }, 179 ) 180 181 // SchemaService 182 checkServiceMethods( 183 t, 184 v1.NewSchemaServiceClient(conn), 185 map[string]func(t *testing.T, client v1.SchemaServiceClient) metadata.MD{ 186 "ReadSchema": func(t *testing.T, client v1.SchemaServiceClient) metadata.MD { 187 var trailer metadata.MD 188 _, err := client.ReadSchema(ctx, &v1.ReadSchemaRequest{}, grpc.Trailer(&trailer)) 189 require.NoError(t, err) 190 return trailer 191 }, 192 "WriteSchema": func(t *testing.T, client v1.SchemaServiceClient) metadata.MD { 193 resp, err := client.ReadSchema(ctx, &v1.ReadSchemaRequest{}) 194 require.NoError(t, err) 195 196 var trailer metadata.MD 197 _, err = client.WriteSchema(ctx, &v1.WriteSchemaRequest{ 198 Schema: resp.SchemaText + "\ndefinition foo {}", 199 }, grpc.Trailer(&trailer)) 200 require.NoError(t, err) 201 return trailer 202 }, 203 }, 204 ) 205 } 206 207 func checkServiceMethods[T any]( 208 t *testing.T, 209 client T, 210 handlers map[string]func(t *testing.T, client T) metadata.MD, 211 ) { 212 et := reflect.TypeOf(new(T)).Elem() 213 for i := 0; i < et.NumMethod(); i++ { 214 methodName := et.Method(i).Name 215 t.Run(methodName, func(t *testing.T) { 216 handler, ok := handlers[methodName] 217 require.True(t, ok, "missing handler for method %s under %T", methodName, new(T)) 218 219 trailer := handler(t, client) 220 require.Greater(t, trailer.Len(), 0) 221 222 dispatchCount, err := responsemeta.GetIntResponseTrailerMetadata(trailer, responsemeta.DispatchedOperationsCount) 223 require.NoError(t, err) 224 require.Greater(t, dispatchCount, 0) 225 }) 226 } 227 }