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  }