Skip to content

Commit 2ca7968

Browse files
committed
Add users list support in store check tests
1 parent 296eca9 commit 2ca7968

6 files changed

Lines changed: 156 additions & 76 deletions

File tree

README.md

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,13 @@ tests:
303303
assertions:
304304
member: true
305305
moderator: false
306+
# checks can also be defined for multiple users sharing the same expectation
307+
- object: group:employees
308+
users:
309+
- user: user:3
310+
- user: user:4
311+
assertions:
312+
member: true
306313
```
307314
308315
If using `output-file`, the response will be written to the specified file on disk. If the desired file already exists, you will be prompted to overwrite the file.
@@ -558,19 +565,26 @@ tests: # required
558565
- name: test-1
559566
description: testing that the model works # optional
560567
# tuple_file: ./tuples.json # tuples that would apply per test
561-
tuples:
562-
- user: user:anne
563-
relation: owner
564-
object: folder:1
565-
check: # a set of checks to run
566-
- user: user:anne
567-
object: folder:1
568-
assertions:
569-
# a set of expected results for each relation
570-
can_view: true
571-
can_write: true
572-
can_share: false
573-
list_objects: # a set of list objects to run
568+
tuples:
569+
- user: user:anne
570+
relation: owner
571+
object: folder:1
572+
check: # a set of checks to run
573+
- user: user:anne
574+
object: folder:1
575+
assertions:
576+
# a set of expected results for each relation
577+
can_view: true
578+
can_write: true
579+
can_share: false
580+
# checks can group multiple users that share the same expected results
581+
- object: folder:2
582+
users:
583+
- user:beth
584+
- user:carl
585+
assertions:
586+
can_view: false
587+
list_objects: # a set of list objects to run
574588
- user: user:anne
575589
type: folder
576590
assertions:

cmd/store/import.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,13 +232,20 @@ func getCheckAssertions(checkTests []storetest.ModelTestCheck) []client.ClientAs
232232
var assertions []client.ClientAssertion
233233

234234
for _, checkTest := range checkTests {
235-
for relation, expectation := range checkTest.Assertions {
236-
assertions = append(assertions, client.ClientAssertion{
237-
User: checkTest.User,
238-
Relation: relation,
239-
Object: checkTest.Object,
240-
Expectation: expectation,
241-
})
235+
users := []string{checkTest.User}
236+
if len(checkTest.Users) > 0 {
237+
users = checkTest.Users
238+
}
239+
240+
for _, user := range users {
241+
for relation, expectation := range checkTest.Assertions {
242+
assertions = append(assertions, client.ClientAssertion{
243+
User: user,
244+
Relation: relation,
245+
Object: checkTest.Object,
246+
Expectation: expectation,
247+
})
248+
}
242249
}
243250
}
244251

cmd/store/import_test.go

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@ func TestImportStore(t *testing.T) {
2121
Object: "document:doc1",
2222
Expectation: true,
2323
}}
24+
25+
multiUserAssertions := []client.ClientAssertion{
26+
{
27+
User: "user:anne",
28+
Relation: "reader",
29+
Object: "document:doc1",
30+
Expectation: true,
31+
},
32+
{
33+
User: "user:peter",
34+
Relation: "reader",
35+
Object: "document:doc1",
36+
Expectation: true,
37+
},
38+
}
2439
modelID, storeID := "model-1", "store-1"
2540
expectedOptions := client.ClientWriteAssertionsOptions{AuthorizationModelId: &modelID, StoreId: &storeID}
2641

@@ -38,9 +53,9 @@ func TestImportStore(t *testing.T) {
3853
mockCreateStore: true,
3954
testStore: storetest.StoreData{
4055
Model: `type user
41-
type document
42-
relations
43-
define reader: [user]`,
56+
type document
57+
relations
58+
define reader: [user]`,
4459
Tests: []storetest.ModelTest{
4560
{
4661
Name: "Test",
@@ -55,6 +70,30 @@ func TestImportStore(t *testing.T) {
5570
},
5671
},
5772
},
73+
{
74+
name: "import store with multi user assertions",
75+
mockWriteAssertions: true,
76+
mockWriteModel: true,
77+
mockCreateStore: true,
78+
testStore: storetest.StoreData{
79+
Model: `type user
80+
type document
81+
relations
82+
define reader: [user]`,
83+
Tests: []storetest.ModelTest{
84+
{
85+
Name: "Test",
86+
Check: []storetest.ModelTestCheck{
87+
{
88+
Users: []string{"user:anne", "user:peter"},
89+
Object: "document:doc1",
90+
Assertions: map[string]bool{"reader": true},
91+
},
92+
},
93+
},
94+
},
95+
},
96+
},
5897
{
5998
name: "create new store without assertions",
6099
mockWriteAssertions: false,
@@ -107,7 +146,11 @@ func TestImportStore(t *testing.T) {
107146
defer mockCtrl.Finish()
108147

109148
if test.mockWriteAssertions {
110-
setupWriteAssertionsMock(mockCtrl, mockFgaClient, expectedAssertions, expectedOptions)
149+
expected := expectedAssertions
150+
if test.name == "import store with multi user assertions" {
151+
expected = multiUserAssertions
152+
}
153+
setupWriteAssertionsMock(mockCtrl, mockFgaClient, expected, expectedOptions)
111154
} else {
112155
mockFgaClient.EXPECT().WriteAssertions(gomock.Any()).Times(0)
113156
}

internal/storetest/localtest.go

Lines changed: 47 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -24,55 +24,63 @@ func RunLocalCheckTest(
2424
tuples []client.ClientContextualTupleKey,
2525
options ModelTestOptions,
2626
) []ModelTestCheckSingleResult {
27-
results := []ModelTestCheckSingleResult{}
2827

29-
for relation, expectation := range checkTest.Assertions {
30-
result := ModelTestCheckSingleResult{
31-
Request: client.ClientCheckRequest{
32-
User: checkTest.User,
33-
Relation: relation,
34-
Object: checkTest.Object,
35-
ContextualTuples: tuples,
36-
Context: checkTest.Context,
37-
},
38-
Expected: expectation,
39-
}
40-
41-
var (
42-
ctx *structpb.Struct
43-
err error
44-
)
28+
results := []ModelTestCheckSingleResult{}
4529

46-
if checkTest.Context != nil {
47-
ctx, err = structpb.NewStruct(*checkTest.Context)
48-
}
30+
users := []string{checkTest.User}
31+
if len(checkTest.Users) > 0 {
32+
users = checkTest.Users
33+
}
4934

50-
if err != nil {
51-
result.Error = err
52-
} else {
53-
response, err := RunSingleLocalCheckTest(fgaServer,
54-
&pb.CheckRequest{
55-
StoreId: *options.StoreID,
56-
AuthorizationModelId: *options.ModelID,
57-
TupleKey: &pb.CheckRequestTupleKey{
58-
User: checkTest.User,
59-
Relation: relation,
60-
Object: checkTest.Object,
61-
},
62-
Context: ctx,
35+
for _, user := range users {
36+
for relation, expectation := range checkTest.Assertions {
37+
result := ModelTestCheckSingleResult{
38+
Request: client.ClientCheckRequest{
39+
User: user,
40+
Relation: relation,
41+
Object: checkTest.Object,
42+
ContextualTuples: tuples,
43+
Context: checkTest.Context,
6344
},
45+
Expected: expectation,
46+
}
47+
48+
var (
49+
ctx *structpb.Struct
50+
err error
6451
)
52+
53+
if checkTest.Context != nil {
54+
ctx, err = structpb.NewStruct(*checkTest.Context)
55+
}
56+
6557
if err != nil {
6658
result.Error = err
67-
}
59+
} else {
60+
response, err := RunSingleLocalCheckTest(fgaServer,
61+
&pb.CheckRequest{
62+
StoreId: *options.StoreID,
63+
AuthorizationModelId: *options.ModelID,
64+
TupleKey: &pb.CheckRequestTupleKey{
65+
User: user,
66+
Relation: relation,
67+
Object: checkTest.Object,
68+
},
69+
Context: ctx,
70+
},
71+
)
72+
if err != nil {
73+
result.Error = err
74+
}
6875

69-
if response != nil {
70-
result.Got = &response.Allowed
71-
result.TestResult = result.IsPassing()
76+
if response != nil {
77+
result.Got = &response.Allowed
78+
result.TestResult = result.IsPassing()
79+
}
7280
}
73-
}
7481

75-
results = append(results, result)
82+
results = append(results, result)
83+
}
7684
}
7785

7886
return results

internal/storetest/remotetest.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,26 @@ func RunRemoteCheckTest(
3434
) []ModelTestCheckSingleResult {
3535
results := []ModelTestCheckSingleResult{}
3636

37-
for relation, expectation := range checkTest.Assertions {
38-
result := RunSingleRemoteCheckTest(
39-
fgaClient,
40-
client.ClientCheckRequest{
41-
User: checkTest.User,
42-
Relation: relation,
43-
Object: checkTest.Object,
44-
Context: checkTest.Context,
45-
ContextualTuples: tuples,
46-
},
47-
expectation,
48-
)
49-
results = append(results, result)
37+
users := []string{checkTest.User}
38+
if len(checkTest.Users) > 0 {
39+
users = checkTest.Users
40+
}
41+
42+
for _, user := range users {
43+
for relation, expectation := range checkTest.Assertions {
44+
result := RunSingleRemoteCheckTest(
45+
fgaClient,
46+
client.ClientCheckRequest{
47+
User: user,
48+
Relation: relation,
49+
Object: checkTest.Object,
50+
Context: checkTest.Context,
51+
ContextualTuples: tuples,
52+
},
53+
expectation,
54+
)
55+
results = append(results, result)
56+
}
5057
}
5158

5259
return results

internal/storetest/storedata.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131

3232
type ModelTestCheck struct {
3333
User string `json:"user" yaml:"user"`
34+
Users []string `json:"users" yaml:"users"`
3435
Object string `json:"object" yaml:"object"`
3536
Context *map[string]interface{} `json:"context" yaml:"context,omitempty"`
3637
Assertions map[string]bool `json:"assertions" yaml:"assertions"`

0 commit comments

Comments
 (0)