Files
grafana/pkg/services/featuremgmt/openfeature_test.go
T
Tania 016301c304 OpenFeature: Rename provider from goff to features-service (#115895)
* OpenFeature: Rename provider

* Update other references to goff

* Update pkg/services/featuremgmt/openfeature.go

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>

* Update pkg/services/featuremgmt/openfeature.go

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>

* Update pkg/services/featuremgmt/openfeature.go

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>

* Update pkg/services/featuremgmt/openfeature.go

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>

---------

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>
2026-01-07 12:04:30 +01:00

151 lines
4.8 KiB
Go

package featuremgmt
import (
"context"
"errors"
"net/url"
"testing"
gofeatureflag "github.com/open-feature/go-sdk-contrib/providers/go-feature-flag/pkg"
ofrep "github.com/open-feature/go-sdk-contrib/providers/ofrep"
"github.com/open-feature/go-sdk/openfeature"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
authlib "github.com/grafana/authlib/authn"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/clientauth/middleware"
"github.com/grafana/grafana/pkg/setting"
)
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: "features-service provider",
cfg: setting.OpenFeatureSettings{
ProviderType: setting.FeaturesServiceProviderType,
URL: u,
TargetingKey: "grafana",
},
expectExchangeRequest: &authlib.TokenExchangeRequest{
Namespace: "*",
Audiences: []string{"features.grafana.app"},
},
expectedProvider: setting.FeaturesServiceProviderType,
},
{
name: "features-service provider with failing token exchange",
cfg: setting.OpenFeatureSettings{
ProviderType: setting.FeaturesServiceProviderType,
URL: u,
TargetingKey: "grafana",
},
expectExchangeRequest: &authlib.TokenExchangeRequest{
Namespace: "*",
Audiences: []string{"features.grafana.app"},
},
expectedProvider: setting.FeaturesServiceProviderType,
failSigning: true,
},
{
name: "ofrep provider",
cfg: setting.OpenFeatureSettings{
ProviderType: setting.OFREPProviderType,
URL: u,
TargetingKey: "grafana",
},
expectedProvider: setting.OFREPProviderType,
},
{
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)
httpClient, err := createHTTPClient(tokenExchangeMiddleware)
require.NoError(t, err, "failed to create features-service http client")
provider, err := createProvider(tc.cfg.ProviderType, tc.cfg.URL, nil, httpClient)
require.NoError(t, err)
err = openfeature.SetProviderAndWait(provider)
require.NoError(t, err, "failed to set provider")
switch tc.expectedProvider {
case setting.FeaturesServiceProviderType:
_, ok := provider.(*gofeatureflag.Provider)
assert.True(t, ok, "expected provider to be of type goff.Provider")
testGoFFProvider(t, tc.failSigning)
case setting.OFREPProviderType:
_, ok := provider.(*ofrep.Provider)
assert.True(t, ok, "expected provider to be of type ofrep.Provider")
default:
_, 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 features-service 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 features-service url is not set")
}
}