test: Enable filter test with cancelled context (#7189)

* test: Enable filter test with cancelled context

* fixup! test: Enable filter test with cancelled context
This commit is contained in:
Steven Masley 2023-04-18 11:06:10 -05:00 committed by GitHub
parent b26826ee3f
commit 7f041fecd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 83 additions and 15 deletions

View File

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"sync"
"testing"
"github.com/google/uuid"
@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/coder/coder/coderd/rbac/regosql"
"github.com/coder/coder/testutil"
)
@ -63,11 +65,14 @@ func TestFilterError(t *testing.T) {
t.Run("CancelledContext", func(t *testing.T) {
t.Parallel()
t.Skipf("This test is racy as rego eval checks the ctx canceled in a go routine. " +
"It is a coin flip if the query finishes before the 'cancel' is checked. " +
"So sometimes the 'Authorize' call succeeds even if ctx is canceled.")
auth := NewAuthorizer(prometheus.NewRegistry())
auth := &MockAuthorizer{
AuthorizeFunc: func(ctx context.Context, subject Subject, action Action, object Object) error {
// Authorize func always returns nil, unless the context is cancelled.
return ctx.Err()
},
}
subject := Subject{
ID: uuid.NewString(),
Roles: RoleNames{
@ -77,20 +82,44 @@ func TestFilterError(t *testing.T) {
Scope: ScopeAll,
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
objects := []Objecter{
ResourceUser,
ResourceUser,
&objectBomb{
t.Run("SmallSet", func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
objects := []Objecter{
ResourceUser,
ResourceUser,
&objectBomb{
Objecter: ResourceUser,
bomb: cancel,
},
ResourceUser,
}
_, err := Filter(ctx, auth, subject, ActionRead, objects)
require.ErrorIs(t, err, context.Canceled)
})
// Triggers Prepared Authorize
t.Run("LargeSet", func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
objects := make([]Objecter, 100)
for i := 0; i < 100; i++ {
objects[i] = ResourceUser
}
objects[20] = &objectBomb{
Objecter: ResourceUser,
bomb: cancel,
},
ResourceUser,
}
}
_, err := Filter(ctx, auth, subject, ActionRead, objects)
require.ErrorIs(t, err, context.Canceled)
_, err := Filter(ctx, auth, subject, ActionRead, objects)
require.ErrorIs(t, err, context.Canceled)
})
})
}
@ -1095,3 +1124,42 @@ func must[T any](value T, err error) T {
}
return value
}
type MockAuthorizer struct {
AuthorizeFunc func(context.Context, Subject, Action, Object) error
}
var _ Authorizer = (*MockAuthorizer)(nil)
func (d *MockAuthorizer) Authorize(ctx context.Context, s Subject, a Action, o Object) error {
return d.AuthorizeFunc(ctx, s, a, o)
}
func (d *MockAuthorizer) Prepare(_ context.Context, subject Subject, action Action, _ string) (PreparedAuthorized, error) {
return &mockPreparedAuthorizer{
Original: d,
Subject: subject,
Action: action,
}, nil
}
var _ PreparedAuthorized = (*mockPreparedAuthorizer)(nil)
// fakePreparedAuthorizer is the prepared version of a FakeAuthorizer. It will
// return the same error as the original FakeAuthorizer.
type mockPreparedAuthorizer struct {
sync.RWMutex
Original *MockAuthorizer
Subject Subject
Action Action
}
func (f *mockPreparedAuthorizer) Authorize(ctx context.Context, object Object) error {
return f.Original.Authorize(ctx, f.Subject, f.Action, object)
}
// CompileToSQL returns a compiled version of the authorizer that will work for
// in memory databases. This fake version will not work against a SQL database.
func (*mockPreparedAuthorizer) CompileToSQL(_ context.Context, _ regosql.ConvertConfig) (string, error) {
return "not a valid sql string", nil
}