mirror of https://github.com/grafana/grafana.git
137 lines
4.3 KiB
Go
137 lines
4.3 KiB
Go
package featuremgmt
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
|
"github.com/grafana/grafana/pkg/clientauth/middleware"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
|
|
authlib "github.com/grafana/authlib/authn"
|
|
gofeatureflag "github.com/open-feature/go-sdk-contrib/providers/go-feature-flag/pkg"
|
|
"github.com/open-feature/go-sdk/openfeature"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestCreateProvider(t *testing.T) {
|
|
u, err := url.Parse("http://localhost:10333")
|
|
require.NoError(t, err)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
cfg setting.OpenFeatureSettings
|
|
expectedProvider string
|
|
expectExchangeRequest *authlib.TokenExchangeRequest
|
|
failSigning bool
|
|
}{
|
|
{
|
|
name: "static provider",
|
|
expectedProvider: setting.StaticProviderType,
|
|
},
|
|
{
|
|
name: "goff provider",
|
|
cfg: setting.OpenFeatureSettings{
|
|
ProviderType: setting.GOFFProviderType,
|
|
URL: u,
|
|
TargetingKey: "grafana",
|
|
},
|
|
expectExchangeRequest: &authlib.TokenExchangeRequest{
|
|
Namespace: "*",
|
|
Audiences: []string{"features.grafana.app"},
|
|
},
|
|
expectedProvider: setting.GOFFProviderType,
|
|
},
|
|
{
|
|
name: "goff provider with failing token exchange",
|
|
cfg: setting.OpenFeatureSettings{
|
|
ProviderType: setting.GOFFProviderType,
|
|
URL: u,
|
|
TargetingKey: "grafana",
|
|
},
|
|
expectExchangeRequest: &authlib.TokenExchangeRequest{
|
|
Namespace: "*",
|
|
Audiences: []string{"features.grafana.app"},
|
|
},
|
|
expectedProvider: setting.GOFFProviderType,
|
|
failSigning: true,
|
|
},
|
|
{
|
|
name: "invalid provider",
|
|
cfg: setting.OpenFeatureSettings{
|
|
ProviderType: "some_provider",
|
|
},
|
|
expectedProvider: setting.StaticProviderType,
|
|
},
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
cfg := setting.NewCfg()
|
|
cfg.OpenFeature = tc.cfg
|
|
|
|
var tokenExchangeClient *fakeTokenExchangeClient
|
|
|
|
if tc.expectExchangeRequest != nil {
|
|
tokenExchangeClient = &fakeTokenExchangeClient{
|
|
Mock: &mock.Mock{
|
|
ExpectedCalls: []*mock.Call{
|
|
{
|
|
Method: "Exchange",
|
|
Arguments: mock.Arguments{mock.Anything, *tc.expectExchangeRequest},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if tc.failSigning {
|
|
tokenExchangeClient.expectedErr = errors.New("failed signing access token")
|
|
}
|
|
}
|
|
|
|
tokenExchangeMiddleware := middleware.TestingTokenExchangeMiddleware(tokenExchangeClient)
|
|
goffClient, err := goffHTTPClient(tokenExchangeMiddleware)
|
|
require.NoError(t, err, "failed to create goff http client")
|
|
provider, err := createProvider(tc.cfg.ProviderType, tc.cfg.URL, nil, goffClient)
|
|
require.NoError(t, err)
|
|
|
|
err = openfeature.SetProviderAndWait(provider)
|
|
require.NoError(t, err, "failed to set provider")
|
|
|
|
if tc.expectedProvider == setting.GOFFProviderType {
|
|
_, ok := provider.(*gofeatureflag.Provider)
|
|
assert.True(t, ok, "expected provider to be of type goff.Provider")
|
|
|
|
testGoFFProvider(t, tc.failSigning)
|
|
} else {
|
|
_, ok := provider.(*inMemoryBulkProvider)
|
|
assert.True(t, ok, "expected provider to be of type memprovider.InMemoryProvider")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func testGoFFProvider(t *testing.T, failSigning bool) {
|
|
// this tests with a fake identity with * namespace access, but in any case, it proves what the requester
|
|
// is scoped to is what is used to sign the token with
|
|
ctx, _ := identity.WithServiceIdentity(context.Background(), 1)
|
|
|
|
// Test that the flag evaluation can be attempted (though it will fail due to non-existent service)
|
|
// The important thing is that the authentication middleware is properly integrated
|
|
_, err := openfeature.NewDefaultClient().BooleanValueDetails(ctx, "test", false, openfeature.NewEvaluationContext("test", map[string]interface{}{"test": "test"}))
|
|
|
|
// Error related to the token exchange should be returned if signing fails
|
|
// otherwise, it should return a connection refused error since the goff URL is not set
|
|
if failSigning {
|
|
assert.ErrorContains(t, err, "failed to exchange token: error signing token", "should return an error when signing fails")
|
|
} else {
|
|
assert.ErrorContains(t, err, "connect: connection refused", "should return an error when goff url is not set")
|
|
}
|
|
}
|