github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/development/resolver_test.go (about)

     1  package development
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/require"
     7  
     8  	"github.com/authzed/spicedb/pkg/schemadsl/compiler"
     9  	"github.com/authzed/spicedb/pkg/schemadsl/input"
    10  )
    11  
    12  func TestResolver(t *testing.T) {
    13  	testSource := input.Source("test")
    14  
    15  	tcs := []struct {
    16  		name              string
    17  		schema            string
    18  		line              int
    19  		column            int
    20  		expectedReference *SchemaReference
    21  	}{
    22  		{
    23  			name: "basic relation",
    24  			schema: `definition user {}
    25  
    26  			definition resource {
    27  				relation viewer: user
    28  				permission view = viewer
    29  			}
    30  			`,
    31  			line:   4,
    32  			column: 24,
    33  			expectedReference: &SchemaReference{
    34  				Source:                   input.Source("test"),
    35  				Position:                 input.Position{LineNumber: 4, ColumnPosition: 24},
    36  				Text:                     "viewer",
    37  				ReferenceType:            ReferenceTypeRelation,
    38  				ReferenceMarkdown:        "relation viewer",
    39  				TargetSource:             &testSource,
    40  				TargetPosition:           &input.Position{LineNumber: 3, ColumnPosition: 4},
    41  				TargetSourceCode:         "relation viewer: user\n",
    42  				TargetNamePositionOffset: 9,
    43  			},
    44  		},
    45  		{
    46  			name: "basic permission",
    47  			schema: `definition user {}
    48  
    49  			definition resource {
    50  				relation viewer: user
    51  				relation editor: user
    52  				permission edit = editor
    53  				permission view = viewer + edit
    54  			}
    55  			`,
    56  			line:   6,
    57  			column: 33,
    58  			expectedReference: &SchemaReference{
    59  				Source:                   input.Source("test"),
    60  				Position:                 input.Position{LineNumber: 6, ColumnPosition: 33},
    61  				Text:                     "edit",
    62  				ReferenceType:            ReferenceTypePermission,
    63  				ReferenceMarkdown:        "permission edit",
    64  				TargetSource:             &testSource,
    65  				TargetPosition:           &input.Position{LineNumber: 5, ColumnPosition: 4},
    66  				TargetSourceCode:         "permission edit = editor\n",
    67  				TargetNamePositionOffset: 11,
    68  			},
    69  		},
    70  		{
    71  			name: "basic type",
    72  			schema: `definition user {}
    73  
    74  			definition resource {
    75  				relation viewer: user
    76  				permission view = viewer
    77  			}
    78  			`,
    79  			line:   3,
    80  			column: 24,
    81  			expectedReference: &SchemaReference{
    82  				Source:                   input.Source("test"),
    83  				Position:                 input.Position{LineNumber: 3, ColumnPosition: 24},
    84  				Text:                     "user",
    85  				ReferenceType:            ReferenceTypeDefinition,
    86  				ReferenceMarkdown:        "definition user",
    87  				TargetSource:             &testSource,
    88  				TargetPosition:           &input.Position{LineNumber: 0, ColumnPosition: 0},
    89  				TargetSourceCode:         "definition user {}",
    90  				TargetNamePositionOffset: 11,
    91  			},
    92  		},
    93  		{
    94  			name: "subject relation type",
    95  			schema: `definition user {}
    96  
    97  			definition group {
    98  				relation member: user
    99  			}
   100  
   101  			definition resource {
   102  				relation viewer: group#member
   103  				permission view = viewer
   104  			}
   105  			`,
   106  			line:   7,
   107  			column: 24,
   108  			expectedReference: &SchemaReference{
   109  				Source:                   input.Source("test"),
   110  				Position:                 input.Position{LineNumber: 7, ColumnPosition: 24},
   111  				Text:                     "group",
   112  				ReferenceType:            ReferenceTypeDefinition,
   113  				ReferenceMarkdown:        "definition group",
   114  				TargetSource:             &testSource,
   115  				TargetPosition:           &input.Position{LineNumber: 2, ColumnPosition: 3},
   116  				TargetSourceCode:         "definition group {\n\t// ...\n}",
   117  				TargetNamePositionOffset: 11,
   118  			},
   119  		},
   120  		{
   121  			name: "subject relation relation",
   122  			schema: `definition user {}
   123  
   124  			definition group {
   125  				relation member: user
   126  			}
   127  
   128  			definition resource {
   129  				relation viewer: group#member
   130  				permission view = viewer
   131  			}
   132  			`,
   133  			line:   7,
   134  			column: 32,
   135  			expectedReference: &SchemaReference{
   136  				Source:                   input.Source("test"),
   137  				Position:                 input.Position{LineNumber: 7, ColumnPosition: 32},
   138  				Text:                     "member",
   139  				ReferenceType:            ReferenceTypeRelation,
   140  				ReferenceMarkdown:        "relation member",
   141  				TargetSource:             &testSource,
   142  				TargetPosition:           &input.Position{LineNumber: 3, ColumnPosition: 4},
   143  				TargetSourceCode:         "relation member: user\n",
   144  				TargetNamePositionOffset: 9,
   145  			},
   146  		},
   147  		{
   148  			name: "filled in type",
   149  			schema: `definition user {}
   150  
   151  			definition resource {
   152  				relation viewer: user | resource
   153  				permission view = viewer
   154  			}
   155  			`,
   156  			line:   3,
   157  			column: 29,
   158  			expectedReference: &SchemaReference{
   159  				Source:                   input.Source("test"),
   160  				Position:                 input.Position{LineNumber: 3, ColumnPosition: 29},
   161  				Text:                     "resource",
   162  				ReferenceType:            ReferenceTypeDefinition,
   163  				ReferenceMarkdown:        "definition resource",
   164  				TargetSource:             &testSource,
   165  				TargetPosition:           &input.Position{LineNumber: 2, ColumnPosition: 3},
   166  				TargetSourceCode:         "definition resource {\n\t// ...\n}",
   167  				TargetNamePositionOffset: 11,
   168  			},
   169  		},
   170  		{
   171  			name: "caveat reference",
   172  			schema: `definition user {}
   173  
   174  			caveat somecaveat(someparam int) {
   175  				someparam < 42
   176  			}
   177  
   178  			definition resource {
   179  				relation viewer: user with somecaveat
   180  				permission view = viewer
   181  			}
   182  			`,
   183  			line:   7,
   184  			column: 35,
   185  			expectedReference: &SchemaReference{
   186  				Source:                   input.Source("test"),
   187  				Position:                 input.Position{LineNumber: 7, ColumnPosition: 35},
   188  				Text:                     "somecaveat",
   189  				ReferenceType:            ReferenceTypeCaveat,
   190  				ReferenceMarkdown:        "caveat somecaveat",
   191  				TargetSource:             &testSource,
   192  				TargetPosition:           &input.Position{LineNumber: 2, ColumnPosition: 3},
   193  				TargetSourceCode:         "caveat somecaveat(someparam int) {\n\t// ...\n}",
   194  				TargetNamePositionOffset: 7,
   195  			},
   196  		},
   197  		{
   198  			name: "arrow relation reference",
   199  			schema: `definition user {}
   200  
   201  			definition resource {
   202  				relation viewer: user
   203  				permission view = viewer->foo
   204  			}
   205  			`,
   206  			line:   4,
   207  			column: 23,
   208  			expectedReference: &SchemaReference{
   209  				Source:                   input.Source("test"),
   210  				Position:                 input.Position{LineNumber: 4, ColumnPosition: 23},
   211  				Text:                     "viewer",
   212  				ReferenceType:            ReferenceTypeRelation,
   213  				ReferenceMarkdown:        "relation viewer",
   214  				TargetSource:             &testSource,
   215  				TargetPosition:           &input.Position{LineNumber: 3, ColumnPosition: 4},
   216  				TargetSourceCode:         "relation viewer: user\n",
   217  				TargetNamePositionOffset: 9,
   218  			},
   219  		},
   220  		{
   221  			name: "arrow permission reference",
   222  			schema: `definition user {}
   223  
   224  			definition resource {
   225  				relation viewer: user
   226  				permission view = viewer->viewer
   227  			}
   228  			`,
   229  			line:              4,
   230  			column:            31,
   231  			expectedReference: nil,
   232  		},
   233  		{
   234  			name: "caveat parameter reference",
   235  			schema: `definition user {}
   236  
   237  			caveat somecaveat(someparam int) {
   238  				someparam < 42
   239  			}
   240  
   241  			definition resource {
   242  				relation viewer: user with somecaveat
   243  				permission view = viewer
   244  			}
   245  			`,
   246  			line:   3,
   247  			column: 6,
   248  			expectedReference: &SchemaReference{
   249  				Source:                   input.Source("test"),
   250  				Position:                 input.Position{LineNumber: 3, ColumnPosition: 6},
   251  				Text:                     "someparam",
   252  				ReferenceType:            ReferenceTypeCaveatParameter,
   253  				ReferenceMarkdown:        "someparam int",
   254  				TargetSource:             &testSource,
   255  				TargetSourceCode:         "someparam int",
   256  				TargetNamePositionOffset: 0,
   257  			},
   258  		},
   259  		{
   260  			name: "longer test",
   261  			schema: `definition user {}
   262  
   263  definition document {
   264  	relation viewer: user
   265  	relation editor: user
   266  	relation third: user
   267  	permission another = viewer
   268  	permission view = third + editor + another
   269  }`,
   270  			line:   7,
   271  			column: 19,
   272  			expectedReference: &SchemaReference{
   273  				Source:                   input.Source("test"),
   274  				Position:                 input.Position{LineNumber: 7, ColumnPosition: 19},
   275  				Text:                     "third",
   276  				ReferenceType:            ReferenceTypeRelation,
   277  				ReferenceMarkdown:        "relation third",
   278  				TargetSource:             &testSource,
   279  				TargetPosition:           &input.Position{LineNumber: 5, ColumnPosition: 1},
   280  				TargetSourceCode:         "relation third: user\n",
   281  				TargetNamePositionOffset: 9,
   282  			},
   283  		},
   284  		{
   285  			name: "reference to comment",
   286  			schema: `
   287  				definition user {}
   288  
   289  				definition resource {
   290  					// viewer is some sort of relation
   291  					relation viewer: user
   292  				}
   293  			`,
   294  			line:              4,
   295  			column:            10,
   296  			expectedReference: nil,
   297  		},
   298  		{
   299  			name: "reference to on commented",
   300  			schema: `
   301  				definition user {}
   302  
   303  				definition resource {
   304  					// viewer is some sort of relation
   305  					relation viewer: user
   306  				}
   307  			`,
   308  			line:   5,
   309  			column: 22,
   310  			expectedReference: &SchemaReference{
   311  				Source:                   "test",
   312  				Position:                 input.Position{LineNumber: 5, ColumnPosition: 22},
   313  				Text:                     "user",
   314  				ReferenceType:            1,
   315  				ReferenceMarkdown:        "definition user",
   316  				TargetSource:             &testSource,
   317  				TargetPosition:           &input.Position{LineNumber: 1, ColumnPosition: 4},
   318  				TargetSourceCode:         "definition user {}",
   319  				TargetNamePositionOffset: 11,
   320  			},
   321  		},
   322  		{
   323  			name: "reference to commented",
   324  			schema: `
   325  				definition user {}
   326  
   327  				definition resource {
   328  					// viewer is some sort of relation
   329  					relation viewer: user
   330  				}
   331  			`,
   332  			line:              5,
   333  			column:            10,
   334  			expectedReference: nil,
   335  		},
   336  		{
   337  			name: "reference to doc comment",
   338  			schema: `
   339  				definition user {}
   340  
   341  				/**
   342  				 * This is a comment
   343  				 */
   344  				definition resource {
   345  					relation viewer: user
   346  				}
   347  			`,
   348  			line:              4,
   349  			column:            5,
   350  			expectedReference: nil,
   351  		},
   352  		{
   353  			name: "reference to commented type",
   354  			schema: `
   355  				/** user is a user */
   356  				definition user {}
   357  
   358  				definition resource {
   359  					relation viewer: user
   360  				}
   361  			`,
   362  			line:   5,
   363  			column: 22,
   364  			expectedReference: &SchemaReference{
   365  				Source:                   "test",
   366  				Position:                 input.Position{LineNumber: 5, ColumnPosition: 22},
   367  				Text:                     "user",
   368  				ReferenceType:            1,
   369  				ReferenceMarkdown:        "definition user",
   370  				TargetSource:             &testSource,
   371  				TargetPosition:           &input.Position{LineNumber: 2, ColumnPosition: 4},
   372  				TargetSourceCode:         "/** user is a user */\ndefinition user {}",
   373  				TargetNamePositionOffset: 11,
   374  			},
   375  		},
   376  		{
   377  			name: "reference to commented relation",
   378  			schema: `
   379  				definition user {}
   380  
   381  				definition resource {
   382  					// viewer is a relation for viewing
   383  					relation viewer: user
   384  
   385  					permission view = viewer
   386  				}
   387  			`,
   388  			line:   7,
   389  			column: 25,
   390  			expectedReference: &SchemaReference{
   391  				Source:                   "test",
   392  				Position:                 input.Position{LineNumber: 7, ColumnPosition: 25},
   393  				Text:                     "viewer",
   394  				ReferenceType:            ReferenceTypeRelation,
   395  				ReferenceMarkdown:        "relation viewer",
   396  				TargetSource:             &testSource,
   397  				TargetPosition:           &input.Position{LineNumber: 5, ColumnPosition: 5},
   398  				TargetSourceCode:         "// viewer is a relation for viewing\nrelation viewer: user\n",
   399  				TargetNamePositionOffset: 9,
   400  			},
   401  		},
   402  	}
   403  
   404  	for _, tc := range tcs {
   405  		tc := tc
   406  		t.Run(tc.name, func(t *testing.T) {
   407  			compiled, err := compiler.Compile(compiler.InputSchema{
   408  				Source:       input.Source("test"),
   409  				SchemaString: tc.schema,
   410  			}, compiler.AllowUnprefixedObjectType())
   411  			require.NoError(t, err)
   412  
   413  			resolver, err := NewResolver(compiled)
   414  			require.NoError(t, err)
   415  
   416  			ref, err := resolver.ReferenceAtPosition(input.Source("test"), input.Position{
   417  				LineNumber:     tc.line,
   418  				ColumnPosition: tc.column,
   419  			})
   420  
   421  			require.NoError(t, err)
   422  			require.Equal(t, tc.expectedReference, ref)
   423  		})
   424  	}
   425  }