Skip to content

Commit 7f041fe

Browse files
authored
test: Enable filter test with cancelled context (#7189)
* test: Enable filter test with cancelled context * fixup! test: Enable filter test with cancelled context
1 parent b26826e commit 7f041fe

File tree

1 file changed

+83
-15
lines changed

1 file changed

+83
-15
lines changed

coderd/rbac/authz_internal_test.go

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"sync"
78
"testing"
89

910
"github.com/google/uuid"
@@ -12,6 +13,7 @@ import (
1213
"github.com/stretchr/testify/require"
1314
"golang.org/x/xerrors"
1415

16+
"github.com/coder/coder/coderd/rbac/regosql"
1517
"github.com/coder/coder/testutil"
1618
)
1719

@@ -63,11 +65,14 @@ func TestFilterError(t *testing.T) {
6365

6466
t.Run("CancelledContext", func(t *testing.T) {
6567
t.Parallel()
66-
t.Skipf("This test is racy as rego eval checks the ctx canceled in a go routine. " +
67-
"It is a coin flip if the query finishes before the 'cancel' is checked. " +
68-
"So sometimes the 'Authorize' call succeeds even if ctx is canceled.")
6968

70-
auth := NewAuthorizer(prometheus.NewRegistry())
69+
auth := &MockAuthorizer{
70+
AuthorizeFunc: func(ctx context.Context, subject Subject, action Action, object Object) error {
71+
// Authorize func always returns nil, unless the context is cancelled.
72+
return ctx.Err()
73+
},
74+
}
75+
7176
subject := Subject{
7277
ID: uuid.NewString(),
7378
Roles: RoleNames{
@@ -77,20 +82,44 @@ func TestFilterError(t *testing.T) {
7782
Scope: ScopeAll,
7883
}
7984

80-
ctx, cancel := context.WithCancel(context.Background())
81-
defer cancel()
82-
objects := []Objecter{
83-
ResourceUser,
84-
ResourceUser,
85-
&objectBomb{
85+
t.Run("SmallSet", func(t *testing.T) {
86+
t.Parallel()
87+
88+
ctx, cancel := context.WithCancel(context.Background())
89+
defer cancel()
90+
objects := []Objecter{
91+
ResourceUser,
92+
ResourceUser,
93+
&objectBomb{
94+
Objecter: ResourceUser,
95+
bomb: cancel,
96+
},
97+
ResourceUser,
98+
}
99+
100+
_, err := Filter(ctx, auth, subject, ActionRead, objects)
101+
require.ErrorIs(t, err, context.Canceled)
102+
})
103+
104+
// Triggers Prepared Authorize
105+
t.Run("LargeSet", func(t *testing.T) {
106+
t.Parallel()
107+
108+
ctx, cancel := context.WithCancel(context.Background())
109+
defer cancel()
110+
111+
objects := make([]Objecter, 100)
112+
for i := 0; i < 100; i++ {
113+
objects[i] = ResourceUser
114+
}
115+
objects[20] = &objectBomb{
86116
Objecter: ResourceUser,
87117
bomb: cancel,
88-
},
89-
ResourceUser,
90-
}
118+
}
91119

92-
_, err := Filter(ctx, auth, subject, ActionRead, objects)
93-
require.ErrorIs(t, err, context.Canceled)
120+
_, err := Filter(ctx, auth, subject, ActionRead, objects)
121+
require.ErrorIs(t, err, context.Canceled)
122+
})
94123
})
95124
}
96125

@@ -1095,3 +1124,42 @@ func must[T any](value T, err error) T {
10951124
}
10961125
return value
10971126
}
1127+
1128+
type MockAuthorizer struct {
1129+
AuthorizeFunc func(context.Context, Subject, Action, Object) error
1130+
}
1131+
1132+
var _ Authorizer = (*MockAuthorizer)(nil)
1133+
1134+
func (d *MockAuthorizer) Authorize(ctx context.Context, s Subject, a Action, o Object) error {
1135+
return d.AuthorizeFunc(ctx, s, a, o)
1136+
}
1137+
1138+
func (d *MockAuthorizer) Prepare(_ context.Context, subject Subject, action Action, _ string) (PreparedAuthorized, error) {
1139+
return &mockPreparedAuthorizer{
1140+
Original: d,
1141+
Subject: subject,
1142+
Action: action,
1143+
}, nil
1144+
}
1145+
1146+
var _ PreparedAuthorized = (*mockPreparedAuthorizer)(nil)
1147+
1148+
// fakePreparedAuthorizer is the prepared version of a FakeAuthorizer. It will
1149+
// return the same error as the original FakeAuthorizer.
1150+
type mockPreparedAuthorizer struct {
1151+
sync.RWMutex
1152+
Original *MockAuthorizer
1153+
Subject Subject
1154+
Action Action
1155+
}
1156+
1157+
func (f *mockPreparedAuthorizer) Authorize(ctx context.Context, object Object) error {
1158+
return f.Original.Authorize(ctx, f.Subject, f.Action, object)
1159+
}
1160+
1161+
// CompileToSQL returns a compiled version of the authorizer that will work for
1162+
// in memory databases. This fake version will not work against a SQL database.
1163+
func (*mockPreparedAuthorizer) CompileToSQL(_ context.Context, _ regosql.ConvertConfig) (string, error) {
1164+
return "not a valid sql string", nil
1165+
}

0 commit comments

Comments
 (0)