Skip to content

Commit

Permalink
chore: Coverage increase (#2478)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomaspoignant authored Oct 7, 2024
1 parent afb2bef commit 1bae773
Show file tree
Hide file tree
Showing 12 changed files with 249 additions and 92 deletions.
84 changes: 84 additions & 0 deletions cmd/relayproxy/api/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api_test

import (
"net/http"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -133,3 +134,86 @@ func Test_Starting_RelayProxy_with_monitoring_on_different_port(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, responseI1.StatusCode)
}

func Test_CheckOFREPAPIExists(t *testing.T) {
proxyConf := &config.Config{
Retriever: &config.RetrieverConf{
Kind: "file",
Path: "../../../testdata/flag-config.yaml",
},
ListenPort: 11024,
AuthorizedKeys: config.APIKeys{
Admin: nil,
Evaluation: []string{"test"},
},
}
log := log.InitLogger()
defer func() { _ = log.ZapLogger.Sync() }()

metricsV2, err := metric.NewMetrics()
if err != nil {
log.ZapLogger.Error("impossible to initialize prometheus metrics", zap.Error(err))
}
wsService := service.NewWebsocketService()
defer wsService.Close() // close all the open connections
prometheusNotifier := metric.NewPrometheusNotifier(metricsV2)
proxyNotifier := service.NewNotifierWebsocket(wsService)
goff, err := service.NewGoFeatureFlagClient(proxyConf, log.ZapLogger, []notifier.Notifier{
prometheusNotifier,
proxyNotifier,
})
if err != nil {
panic(err)
}

services := service.Services{
MonitoringService: service.NewMonitoring(goff),
WebsocketService: wsService,
GOFeatureFlagService: goff,
Metrics: metricsV2,
}

s := api.New(proxyConf, services, log.ZapLogger)
go func() { s.Start() }()
defer s.Stop()

time.Sleep(10 * time.Millisecond)

req, err := http.NewRequest("GET",
"http://localhost:11024/ofrep/v1/configuration", nil)
assert.NoError(t, err)
req.Header.Add("Authorization", "Bearer test")
response, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, response.StatusCode)

req, err = http.NewRequest("POST",
"http://localhost:11024/ofrep/v1/evaluate/flags",
strings.NewReader(`{ "context":{"targetingKey":"some-key"}}`))
assert.NoError(t, err)
req.Header.Add("Authorization", "Bearer test")
req.Header.Add("Content-Type", "application/json")
response, err = http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, response.StatusCode)

req, err = http.NewRequest("POST",
"http://localhost:11024/ofrep/v1/evaluate/flags/some-key",
strings.NewReader(`{ "context":{"targetingKey":"some-key"}}`))
assert.NoError(t, err)
req.Header.Add("Authorization", "Bearer test")
req.Header.Add("Content-Type", "application/json")
response, err = http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusNotFound, response.StatusCode)

req, err = http.NewRequest("POST",
"http://localhost:11024/ofrep/v1/evaluate/flags/test-flag",
strings.NewReader(`{ "context":{"targetingKey":"some-key"}}`))
assert.NoError(t, err)
req.Header.Add("Authorization", "Bearer test")
req.Header.Add("Content-Type", "application/json")
response, err = http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, response.StatusCode)
}
1 change: 1 addition & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ ignore:
- "cmd/relayproxy/docs/"
- "cmd/jsonschema-generator/"
- "cmd/relayproxy/modeldocs/"
- "model/dto/converter_v0.go"
9 changes: 9 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,12 @@ func TestConfig_GetRetrievers(t *testing.T) {
})
}
}

func TestOfflineConfig(t *testing.T) {
c := ffClient.Config{
Offline: true,
}
assert.True(t, c.IsOffline())
c.SetOffline(false)
assert.False(t, c.IsOffline())
}
4 changes: 2 additions & 2 deletions exporter/gcstorageexporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func (f *Exporter) Export(ctx context.Context, logger *fflog.FFLogger, featureEv
for _, file := range files {
of, err := os.Open(outputDir + "/" + file.Name())
if err != nil {
logger.Error("[GCP DeprecatedExporter] impossible to open the file",
logger.Error("[GCP Exporter] impossible to open the file",
slog.String("path", outputDir+"/"+file.Name()))
continue
}
Expand All @@ -111,7 +111,7 @@ func (f *Exporter) Export(ctx context.Context, logger *fflog.FFLogger, featureEv
_, err = io.Copy(wc, of)
_ = wc.Close()
if err != nil {
return fmt.Errorf("error: [DeprecatedExporter] impossible to copy the file from %s to bucket %s: %v",
return fmt.Errorf("error: [GCP Exporter] impossible to copy the file from %s to bucket %s: %v",
source, f.Bucket, err)
}
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ require (
github.com/swaggo/echo-swagger v1.4.1
github.com/swaggo/swag v1.16.3
github.com/testcontainers/testcontainers-go v0.33.0
github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0
github.com/testcontainers/testcontainers-go/modules/redis v0.33.0
github.com/thejerf/slogassert v0.3.4
github.com/xitongsys/parquet-go v1.6.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,8 @@ github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw=
github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8=
github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0 h1:iXVA84s5hKMS5gn01GWOYHE3ymy/2b+0YkpFeTxB2XY=
github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0/go.mod h1:R6tMjTojRiaoo89fh/hf7tOmfzohdqSU17R9DwSVSog=
github.com/testcontainers/testcontainers-go/modules/redis v0.33.0 h1:S/QvMOwpr00MM2aWH+krzP73Erlp/Ug0dr2rkgZYI5s=
github.com/testcontainers/testcontainers-go/modules/redis v0.33.0/go.mod h1:gudb3+6uZ9SsAysOVoLs7nazbjGlkHegBW8nqPXvDMI=
github.com/thejerf/slogassert v0.3.4 h1:VoTsXixRbXMrRSSxDjYTiEDCM4VWbsYPW5rB/hX24kM=
Expand Down
47 changes: 47 additions & 0 deletions internal/flagstate/all_flags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package flagstate_test

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/thomaspoignant/go-feature-flag/internal/flag"
"github.com/thomaspoignant/go-feature-flag/internal/flagstate"
)

func TestAllFlags(t *testing.T) {
afs := flagstate.NewAllFlags()
assert.NotNil(t, afs.GetFlags())
assert.Equal(t, 0, len(afs.GetFlags()))
assert.True(t, afs.IsValid())

fs := flagstate.FlagState{
Value: 20,
Timestamp: time.Date(2022, 8, 1, 0, 0, 10, 0, time.UTC).Unix(),
VariationType: "var_a",
TrackEvents: false,
Failed: false,
Reason: flag.ReasonStatic,
}
afs.AddFlag("my-key", fs)
assert.Equal(t, 1, len(afs.GetFlags()))
assert.True(t, afs.IsValid())
fs2 := flagstate.FlagState{
Value: 20,
Timestamp: time.Date(2022, 8, 1, 0, 0, 10, 0, time.UTC).Unix(),
VariationType: "var_a",
TrackEvents: false,
Failed: true,
ErrorCode: flag.ErrorCodeTargetingKeyMissing,
ErrorDetails: "The targeting key is missing",
Reason: flag.ReasonError,
}
afs.AddFlag("my-key-2", fs2)
assert.Equal(t, 2, len(afs.GetFlags()))
assert.False(t, afs.IsValid())

want := "{\"flags\":{\"my-key\":{\"value\":20,\"timestamp\":1659312010,\"variationType\":\"var_a\",\"trackEvents\":false,\"errorCode\":\"\",\"reason\":\"STATIC\"},\"my-key-2\":{\"value\":20,\"timestamp\":1659312010,\"variationType\":\"var_a\",\"trackEvents\":false,\"errorCode\":\"TARGETING_KEY_MISSING\",\"errorDetails\":\"The targeting key is missing\",\"reason\":\"ERROR\"}},\"valid\":false}\n"
got, err := afs.MarshalJSON()
assert.NoError(t, err)
assert.JSONEq(t, want, string(got))
}
2 changes: 1 addition & 1 deletion model/dto/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func TestConvertV1DtoToInternalFlag(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := dto.ConvertV1DtoToInternalFlag(tt.input)
result := tt.input.Convert(nil, "random-flag-name")
assert.Equal(t, tt.expected, result)
})
}
Expand Down
3 changes: 3 additions & 0 deletions retriever/mongodbretriever/retriever.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ func (r *Retriever) Init(ctx context.Context, logger *fflog.FFLogger) error {

// Status returns the current status of the retriever
func (r *Retriever) Status() retriever.Status {
if r == nil || r.status == "" {
return retriever.RetrieverNotReady
}
return r.status
}

Expand Down
122 changes: 94 additions & 28 deletions retriever/mongodbretriever/retriever_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
package mongodbretriever
//go:build docker
// +build docker

package mongodbretriever_test

import (
"context"
"encoding/json"
"github.com/stretchr/testify/require"
"github.com/thomaspoignant/go-feature-flag/retriever"
"github.com/thomaspoignant/go-feature-flag/retriever/mongodbretriever"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"testing"

"github.com/stretchr/testify/assert"
"github.com/testcontainers/testcontainers-go/modules/mongodb"
"github.com/thomaspoignant/go-feature-flag/testutils"
"github.com/thomaspoignant/go-feature-flag/utils/fflog"
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
Expand All @@ -17,64 +27,120 @@ func Test_MongoDBRetriever_Retrieve(t *testing.T) {
tests := []struct {
name string
want []byte
mocker *func(t *mtest.T)
data string
wantErr bool
}{
{
name: "Returns well formed flag definition document",
mocker: &testutils.MockSuccessFind,
data: testutils.MongoFindResultString,
want: []byte(testutils.QueryResult),
wantErr: false,
},
{
name: "One of the Flag definition document does not have 'flag' key/value (ignore this document)",
mocker: &testutils.MockNoFlagKeyResult,
data: testutils.MongoMissingFlagKey,
want: []byte(testutils.MissingFlagKeyResult),
wantErr: false,
},
{
name: "Flag definition document 'flag' key does not have 'string' value (ignore this document)",
mocker: &testutils.MockFlagNotStrResult,
data: testutils.MongoFindResultFlagNoStr,
want: []byte(testutils.FlagKeyNotStringResult),
wantErr: false,
},
{
name: "No flags found on DB",
mocker: &testutils.MockNoFlags,
want: nil,
want: []byte("{}"),
wantErr: true,
},
}
for _, tt := range tests {
mtDB.Run(tt.name, func(t *mtest.T) {
mdb := Retriever{
URI: "mongouri",
Collection: "collection",
Database: "database",
dbConnection: t.DB,
}
mongodbContainer, err := mongodb.Run(context.TODO(), "mongo:6")
require.NoError(t, err)
defer func() {
err := mongodbContainer.Terminate(context.TODO())
require.NoError(t, err)
}()

if tt.mocker != nil {
(*tt.mocker)(t)
}
uri, err := mongodbContainer.ConnectionString(context.Background())

_ = mdb.Init(context.TODO(), &fflog.FFLogger{})
got, err := mdb.Retrieve(context.Background())
if tt.data != "" {
// insert data
client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(uri))
coll := client.Database("database").Collection("collection")
var documents []bson.M
err = json.Unmarshal([]byte(tt.data), &documents)
require.NoError(t, err)

if tt.wantErr {
assert.Error(t, err, "Retrieve() error = %v, wantErr %v", err, tt.wantErr)
return
for _, doc := range documents {
_, err := coll.InsertOne(context.TODO(), doc)
require.NoError(t, err)
}
}

var gotUnm, wantUn interface{}
if err := json.Unmarshal(tt.want, &wantUn); err != nil {
assert.Fail(t, "could not json unmarshall wanted flag structure")
}
if err := json.Unmarshal(got, &gotUnm); err != nil {
assert.Fail(t, "could not json unmarshall got flag structure")
// retriever
mdb := mongodbretriever.Retriever{
URI: uri,
Collection: "collection",
Database: "database",
}
assert.Equal(t, retriever.RetrieverNotReady, mdb.Status())
err = mdb.Init(context.TODO(), &fflog.FFLogger{})
assert.NoError(t, err)
defer func() { _ = mdb.Shutdown(context.TODO()) }()
assert.Equal(t, retriever.RetrieverReady, mdb.Status())

assert.Equal(t, wantUn, gotUnm)
got, err := mdb.Retrieve(context.Background())
if tt.want == nil {
assert.Nil(t, got)
} else {
modifiedGot, err := removeIDFromJSON(string(got))
require.NoError(t, err)
assert.JSONEq(t, string(tt.want), modifiedGot)
}
})
}
}

func Test_MongoDBRetriever_InvalidURI(t *testing.T) {
mdb := mongodbretriever.Retriever{
URI: "invalidURI",
Collection: "collection",
Database: "database",
}
assert.Equal(t, retriever.RetrieverNotReady, mdb.Status())
err := mdb.Init(context.TODO(), &fflog.FFLogger{})
assert.Error(t, err)
assert.Equal(t, retriever.RetrieverError, mdb.Status())
}

func removeIDFromJSON(jsonStr string) (string, error) {
var data interface{}
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
return "", err
}

removeIDFields(data)

modifiedJSON, err := json.Marshal(data)
if err != nil {
return "", err
}

return string(modifiedJSON), nil
}

func removeIDFields(data interface{}) {
switch v := data.(type) {
case map[string]interface{}:
delete(v, "_id")
for _, value := range v {
removeIDFields(value)
}
case []interface{}:
for _, item := range v {
removeIDFields(item)
}
}
}
Loading

0 comments on commit 1bae773

Please sign in to comment.