From c29611169cadf274fc1c77b0614adba4e7a94037 Mon Sep 17 00:00:00 2001 From: Prashansa Kulshrestha Date: Thu, 23 Jan 2025 19:30:57 +0530 Subject: [PATCH] chore: added tests for degraphql_routes entity (#155) * feat: added support for degraphql_routes * chore: fixed lint issues * chore: fixed code comments * chore: addressed PR comments * fix: renamed plugin_entities to custom_entities for clarity * fix: fixed comment strings * fix: fixed schema * chore: added tests for degraphql_routes entity * test: fixed tests based on PR suggestions * test: added integration test for degraphql routes * fix: fixed testcases based on renames * style: lint fix * fix: added PR suggestions --- pkg/file/builder.go | 8 + pkg/file/builder_test.go | 294 +++++++++++++++++- pkg/file/writer_test.go | 11 + pkg/state/degraphql_route_test.go | 290 +++++++++++++++++ pkg/state/types.go | 4 + tests/integration/sync_test.go | 87 ++++++ .../kong-complex-query.yaml | 35 +++ .../sync/036-degraphql-routes/kong.yaml | 26 ++ 8 files changed, 738 insertions(+), 17 deletions(-) create mode 100644 pkg/state/degraphql_route_test.go create mode 100644 tests/integration/testdata/sync/036-degraphql-routes/kong-complex-query.yaml create mode 100644 tests/integration/testdata/sync/036-degraphql-routes/kong.yaml diff --git a/pkg/file/builder.go b/pkg/file/builder.go index 52ba873..0b49dfe 100644 --- a/pkg/file/builder.go +++ b/pkg/file/builder.go @@ -1668,6 +1668,14 @@ func copyToDegraphqlRoute(fcEntity FCustomEntity) (DegraphqlRoute, error) { degraphqlRoute.Methods = kong.StringSlice("GET") } + if degraphqlRoute.Service == nil { + return DegraphqlRoute{}, fmt.Errorf("service is required for degraphql_routes") + } + + if degraphqlRoute.URI == nil || degraphqlRoute.Query == nil { + return DegraphqlRoute{}, fmt.Errorf("uri and query are required for degraphql_routes") + } + return degraphqlRoute, nil } diff --git a/pkg/file/builder_test.go b/pkg/file/builder_test.go index 9cc1504..8918ce4 100644 --- a/pkg/file/builder_test.go +++ b/pkg/file/builder_test.go @@ -14,6 +14,7 @@ import ( "github.com/kong/go-database-reconciler/pkg/utils" "github.com/kong/go-kong/kong" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( @@ -389,10 +390,32 @@ func existingFilterChainState() *state.KongState { return s } +func existingDegraphqlRouteState(t *testing.T) *state.KongState { + t.Helper() + s, err := state.NewKongState() + require.NoError(t, err, "error in getting new kongState") + + s.DegraphqlRoutes.Add( + state.DegraphqlRoute{ + DegraphqlRoute: kong.DegraphqlRoute{ + ID: kong.String("4bfcb11f-c962-4817-83e5-9433cf20b663"), + Service: &kong.Service{ + ID: kong.String("ba54b737-38aa-49d1-87c4-64e756b0c6f9"), + }, + Methods: kong.StringSlice("GET"), + URI: kong.String("/example"), + Query: kong.String("query{ example { foo } }"), + }, + }) + return s +} + +var testRand *rand.Rand + var deterministicUUID = func() *string { version := byte(4) uuid := make([]byte, 16) - rand.Read(uuid) + testRand.Read(uuid) // Set version uuid[6] = (uuid[6] & 0x0f) | (version << 4) @@ -421,7 +444,7 @@ func TestMain(m *testing.M) { } func Test_stateBuilder_services(t *testing.T) { - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { targetContent *Content currentState *state.KongState @@ -508,7 +531,7 @@ func Test_stateBuilder_services(t *testing.T) { } func Test_stateBuilder_ingestRoute(t *testing.T) { - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { currentState *state.KongState } @@ -621,7 +644,7 @@ func Test_stateBuilder_ingestRoute(t *testing.T) { } func Test_stateBuilder_ingestTargets(t *testing.T) { - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { currentState *state.KongState } @@ -870,7 +893,7 @@ func Test_stateBuilder_ingestTargets(t *testing.T) { } func Test_stateBuilder_ingestPlugins(t *testing.T) { - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { currentState *state.KongState } @@ -1060,7 +1083,7 @@ func Test_pluginRelations(t *testing.T) { } func Test_stateBuilder_ingestFilterChains(t *testing.T) { - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { currentState *state.KongState } @@ -1162,7 +1185,7 @@ func Test_stateBuilder_ingestFilterChains(t *testing.T) { } func Test_stateBuilder_consumers(t *testing.T) { - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { currentState *state.KongState targetContent *Content @@ -1641,7 +1664,7 @@ func Test_stateBuilder_consumers(t *testing.T) { } func Test_stateBuilder_certificates(t *testing.T) { - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { currentState *state.KongState targetContent *Content @@ -1808,7 +1831,7 @@ func Test_stateBuilder_certificates(t *testing.T) { } func Test_stateBuilder_caCertificates(t *testing.T) { - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { currentState *state.KongState targetContent *Content @@ -1881,7 +1904,7 @@ func Test_stateBuilder_caCertificates(t *testing.T) { } func Test_stateBuilder_upstream(t *testing.T) { - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { targetContent *Content currentState *state.KongState @@ -2233,7 +2256,7 @@ func Test_stateBuilder_upstream(t *testing.T) { } func Test_stateBuilder_documents(t *testing.T) { - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { targetContent *Content currentState *state.KongState @@ -2874,7 +2897,7 @@ func Test_stateBuilder_kong370(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) b := &stateBuilder{ targetContent: tt.fields.targetContent, currentState: tt.fields.currentState, @@ -3419,7 +3442,7 @@ func Test_stateBuilder_kong360(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) b := &stateBuilder{ targetContent: tt.fields.targetContent, currentState: tt.fields.currentState, @@ -3584,7 +3607,7 @@ func Test_getStripPathBasedOnProtocols(t *testing.T) { func Test_stateBuilder_ingestRouteKonnectTraditionalRoute(t *testing.T) { assert := assert.New(t) - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { currentState *state.KongState } @@ -3653,7 +3676,7 @@ func Test_stateBuilder_ingestRouteKonnectTraditionalRoute(t *testing.T) { func Test_stateBuilder_expressionRoutes_kong360(t *testing.T) { assert := assert.New(t) - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { targetContent *Content currentState *state.KongState @@ -3722,7 +3745,7 @@ func Test_stateBuilder_expressionRoutes_kong360(t *testing.T) { func Test_stateBuilder_expressionRoutes_kong370(t *testing.T) { assert := assert.New(t) - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { targetContent *Content currentState *state.KongState @@ -3789,7 +3812,7 @@ func Test_stateBuilder_expressionRoutes_kong370(t *testing.T) { func Test_stateBuilder_expressionRoutes_kong370_withKonnect(t *testing.T) { assert := assert.New(t) - rand.Seed(42) + testRand = rand.New(rand.NewSource(42)) type fields struct { targetContent *Content currentState *state.KongState @@ -3853,3 +3876,240 @@ func Test_stateBuilder_expressionRoutes_kong370_withKonnect(t *testing.T) { }) } } + +func Test_stateBuilder_ingestCustomEntities(t *testing.T) { + testRand = rand.New(rand.NewSource(42)) + type fields struct { + currentState *state.KongState + targetContent *Content + } + tests := []struct { + name string + fields fields + want *utils.KongRawState + wantErr bool + }{ + { + name: "generates a new degraphql route from valid config passed", + fields: fields{ + targetContent: &Content{ + CustomEntities: []FCustomEntity{ + { + Type: kong.String("degraphql_routes"), + Fields: CustomEntityConfiguration{ + "uri": kong.String("/foo"), + "query": kong.String("query { foo { bar }}"), + "service": kong.String("fdfd14cc-cd69-49a0-9e23-cd3375b6c0cd"), + }, + }, + }, + }, + currentState: emptyState(), + }, + want: &utils.KongRawState{ + DegraphqlRoutes: []*kong.DegraphqlRoute{ + { + ID: kong.String("538c7f96-b164-4f1b-97bb-9f4bb472e89f"), + URI: kong.String("/foo"), + Query: kong.String("query { foo { bar }}"), + Service: &kong.Service{ + ID: kong.String("fdfd14cc-cd69-49a0-9e23-cd3375b6c0cd"), + }, + Methods: kong.StringSlice("GET"), + }, + }, + }, + }, + { + name: "matches ID for an existing degraphql route", + fields: fields{ + targetContent: &Content{ + CustomEntities: []FCustomEntity{ + { + Type: kong.String("degraphql_routes"), + Fields: CustomEntityConfiguration{ + "uri": kong.String("/example"), + "query": kong.String("query{ example { foo } }"), + "service": kong.String("ba54b737-38aa-49d1-87c4-64e756b0c6f9"), + }, + }, + }, + }, + currentState: existingDegraphqlRouteState(t), + }, + want: &utils.KongRawState{ + DegraphqlRoutes: []*kong.DegraphqlRoute{ + { + ID: kong.String("4bfcb11f-c962-4817-83e5-9433cf20b663"), + Service: &kong.Service{ + ID: kong.String("ba54b737-38aa-49d1-87c4-64e756b0c6f9"), + }, + Methods: kong.StringSlice("GET"), + URI: kong.String("/example"), + Query: kong.String("query{ example { foo } }"), + }, + }, + }, + }, + { + name: "accepts multi line query input and service name", + fields: fields{ + targetContent: &Content{ + Services: []FService{ + { + Service: kong.Service{ + Name: kong.String("foo"), + }, + }, + }, + CustomEntities: []FCustomEntity{ + { + Type: kong.String("degraphql_routes"), + Fields: CustomEntityConfiguration{ + "uri": kong.String("/foo"), + "query": kong.String(`query SearchPosts($filters: PostsFilters) { + posts(filter: $filters) { + id + title + author + } + }`), + "service": kong.String("foo"), + "methods": kong.StringSlice("GET", "POST"), + }, + }, + }, + }, + currentState: emptyState(), + }, + want: &utils.KongRawState{ + DegraphqlRoutes: []*kong.DegraphqlRoute{ + { + ID: kong.String("dfd79b4d-7642-4b61-ba0c-9f9f0d3ba55b"), + Service: &kong.Service{ + ID: kong.String("foo"), + }, + Methods: kong.StringSlice("GET", "POST"), + URI: kong.String("/foo"), + Query: kong.String(`query SearchPosts($filters: PostsFilters) { + posts(filter: $filters) { + id + title + author + } + }`), + }, + }, + Services: []*kong.Service{ + { + ID: kong.String("5b1484f2-5209-49d9-b43e-92ba09dd9d52"), + Name: kong.String("foo"), + Protocol: kong.String("http"), + ConnectTimeout: kong.Int(60000), + WriteTimeout: kong.Int(60000), + ReadTimeout: kong.Int(60000), + }, + }, + }, + }, + { + name: "handles empty plugin entities", + fields: fields{ + targetContent: &Content{ + CustomEntities: []FCustomEntity{}, + }, + currentState: emptyState(), + }, + want: &utils.KongRawState{ + DegraphqlRoutes: nil, + }, + }, + { + name: "handles multiple degraphql routes", + fields: fields{ + targetContent: &Content{ + CustomEntities: []FCustomEntity{ + { + Type: kong.String("degraphql_routes"), + Fields: CustomEntityConfiguration{ + "uri": kong.String("/foo"), + "query": kong.String("query { foo }"), + "service": kong.String("service1"), + }, + }, + { + Type: kong.String("degraphql_routes"), + Fields: CustomEntityConfiguration{ + "uri": kong.String("/bar"), + "query": kong.String("query { bar }"), + "service": kong.String("service2"), + "methods": kong.StringSlice("POST", "PUT"), + }, + }, + }, + }, + currentState: emptyState(), + }, + want: &utils.KongRawState{ + DegraphqlRoutes: []*kong.DegraphqlRoute{ + { + ID: kong.String("0cc0d614-4c88-4535-841a-cbe0709b0758"), + URI: kong.String("/foo"), + Query: kong.String("query { foo }"), + Service: &kong.Service{ + ID: kong.String("service1"), + }, + Methods: kong.StringSlice("GET"), + }, + { + ID: kong.String("083f61d3-75bc-42b4-9df4-f91929e18fda"), + URI: kong.String("/bar"), + Query: kong.String("query { bar }"), + Service: &kong.Service{ + ID: kong.String("service2"), + }, + Methods: kong.StringSlice("POST", "PUT"), + }, + }, + }, + }, + { + name: "handles missing required fields", + fields: fields{ + targetContent: &Content{ + CustomEntities: []FCustomEntity{ + { + Type: kong.String("degraphql_routes"), + Fields: CustomEntityConfiguration{ + "uri": kong.String("/foo"), + }, + }, + }, + }, + currentState: emptyState(), + }, + want: &utils.KongRawState{ + DegraphqlRoutes: nil, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := &stateBuilder{ + targetContent: tt.fields.targetContent, + currentState: tt.fields.currentState, + } + _, _, err := b.build() + if tt.wantErr { + require.Error(t, err, "build error was expected") + assert.Equal(t, tt.want, b.rawState) + return + } + + require.NoError(t, err, "build error is not nil") + assert.Equal(t, tt.want, b.rawState) + }) + } +} diff --git a/pkg/file/writer_test.go b/pkg/file/writer_test.go index 991a284..4bca518 100644 --- a/pkg/file/writer_test.go +++ b/pkg/file/writer_test.go @@ -220,6 +220,17 @@ func Test_compareOrder(t *testing.T) { }, expected: true, }, + { + sortable1: FCustomEntity{ + ID: kong.String("degraphql-route-1"), + Type: kong.String("degraphql_routes"), + }, + sortable2: FCustomEntity{ + ID: kong.String("degraphql-route-2"), + Type: kong.String("degraphql_routes"), + }, + expected: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/state/degraphql_route_test.go b/pkg/state/degraphql_route_test.go new file mode 100644 index 0000000..792824c --- /dev/null +++ b/pkg/state/degraphql_route_test.go @@ -0,0 +1,290 @@ +package state + +import ( + "testing" + + "github.com/kong/go-kong/kong" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func degraphqlRoutesCollection() *DegraphqlRoutesCollection { + return state().DegraphqlRoutes +} + +func TestDegraphqlRouteAdd(t *testing.T) { + collection := degraphqlRoutesCollection() + + tests := []struct { + name string + degraphqlRoute DegraphqlRoute + wantErr bool + }{ + { + name: "adds a degraphql route to the collection", + degraphqlRoute: DegraphqlRoute{ + DegraphqlRoute: kong.DegraphqlRoute{ + ID: kong.String("first"), + URI: kong.String("/foo"), + Query: kong.String("query { hello }"), + Service: &kong.Service{ + ID: kong.String("some-service"), + }, + Methods: kong.StringSlice("GET"), + }, + }, + wantErr: false, + }, + { + name: "adds a degraphql route with complex query to the collection", + degraphqlRoute: DegraphqlRoute{ + DegraphqlRoute: kong.DegraphqlRoute{ + ID: kong.String("second"), + URI: kong.String("/bar"), + Query: kong.String(`query SearchPosts($filters: PostsFilters) { + posts(filter: $filters) { + id + title + author + } + }`), + Service: &kong.Service{ + ID: kong.String("some-service"), + }, + Methods: kong.StringSlice("GET", "POST"), + }, + }, + wantErr: false, + }, + { + name: "returns an error when the degraphql route already exists", + degraphqlRoute: DegraphqlRoute{ + DegraphqlRoute: kong.DegraphqlRoute{ + ID: kong.String("first"), + URI: kong.String("/foo"), + Query: kong.String("query { hello }"), + Service: &kong.Service{ + ID: kong.String("some-service"), + }, + Methods: kong.StringSlice("GET"), + }, + }, + wantErr: true, + }, + { + name: "returns an error if an id is not provided", + degraphqlRoute: DegraphqlRoute{ + DegraphqlRoute: kong.DegraphqlRoute{ + URI: kong.String("/foo"), + Query: kong.String("query { hello }"), + Service: &kong.Service{ + ID: kong.String("some-service"), + }, + Methods: kong.StringSlice("GET"), + }, + }, + wantErr: true, + }, + { + name: "returns an error if an empty degraphql route is provided", + degraphqlRoute: DegraphqlRoute{ + DegraphqlRoute: kong.DegraphqlRoute{}, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := collection.Add(tt.degraphqlRoute); (err != nil) != tt.wantErr { + t.Errorf("DegraphqlRoutesCollection.Add() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDegraphqlRouteGet(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + collection := degraphqlRoutesCollection() + + degraphqlRoute := DegraphqlRoute{ + DegraphqlRoute: kong.DegraphqlRoute{ + ID: kong.String("example"), + URI: kong.String("/foo"), + Query: kong.String("query { hello }"), + Service: &kong.Service{ + ID: kong.String("some-service"), + }, + Methods: kong.StringSlice("GET"), + }, + } + + err := collection.Add(degraphqlRoute) + require.NoError(err, "error adding degraphql route") + + // Fetch the currently added entity + res, err := collection.Get("example") + require.NoError(err, "error getting degraphql route") + require.NotNil(res) + assert.Equal("example", *res.ID) + assert.Equal("some-service", *res.Service.ID) + + // Fetch non-existent entity + res, err = collection.Get("does-not-exist") + require.Error(err) + require.Nil(res) +} + +func TestDegraphqlRouteUpdate(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + collection := degraphqlRoutesCollection() + + degraphqlRoute := DegraphqlRoute{ + DegraphqlRoute: kong.DegraphqlRoute{ + ID: kong.String("example"), + URI: kong.String("/foo"), + Query: kong.String("query { hello }"), + Service: &kong.Service{ + ID: kong.String("some-service"), + }, + Methods: kong.StringSlice("GET"), + }, + } + + err := collection.Add(degraphqlRoute) + require.NoError(err, "error adding degraphql route") + + // Fetch the currently added entity + res, err := collection.Get("example") + require.NoError(err, "error getting degraphql route") + require.NotNil(res) + assert.Equal("query { hello }", *res.Query) + + // Update query field + res.Query = kong.String("query { hello world }") + err = collection.Update(*res) + require.NoError(err, "error updating degraphql route") + + // Fetch again + res, err = collection.Get("example") + require.NoError(err, "error getting degraphql route") + require.NotNil(res) + assert.Equal("query { hello world }", *res.Query) +} + +func TestDegraphqlRouteDelete(t *testing.T) { + require := require.New(t) + collection := degraphqlRoutesCollection() + + degraphqlRoute := DegraphqlRoute{ + DegraphqlRoute: kong.DegraphqlRoute{ + ID: kong.String("example"), + URI: kong.String("/foo"), + Query: kong.String("query { hello }"), + Service: &kong.Service{ + ID: kong.String("some-service"), + }, + Methods: kong.StringSlice("GET"), + }, + } + + err := collection.Add(degraphqlRoute) + require.NoError(err, "error adding degraphql route") + + // Fetch the currently added entity + res, err := collection.Get("example") + require.NoError(err, "error getting degraphql route") + require.NotNil(res) + + // Delete entity + err = collection.Delete(*res.ID) + require.NoError(err, "error deleting degraphql route") + + // Fetch again + res, err = collection.Get("example") + require.Error(err) + require.Nil(res) +} + +func TestDegraphqlRouteGetAll(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + collection := degraphqlRoutesCollection() + + populateDegraphqlRoutes(t, collection) + + degraphqlRoutes, err := collection.GetAll() + require.NoError(err, "error getting all degraphql routes") + assert.Equal(5, len(degraphqlRoutes)) + assert.IsType([]*DegraphqlRoute{}, degraphqlRoutes) +} + +func populateDegraphqlRoutes(t *testing.T, + collection *DegraphqlRoutesCollection, +) { + require := require.New(t) + degraphqlRoutes := []DegraphqlRoute{ + { + DegraphqlRoute: kong.DegraphqlRoute{ + ID: kong.String("first"), + URI: kong.String("/foo"), + Query: kong.String("query { hello }"), + Service: &kong.Service{ + ID: kong.String("some-service"), + }, + Methods: kong.StringSlice("GET"), + }, + }, + { + DegraphqlRoute: kong.DegraphqlRoute{ + ID: kong.String("second"), + URI: kong.String("/bar"), + Query: kong.String("query { hello }"), + Service: &kong.Service{ + ID: kong.String("some-service"), + }, + Methods: kong.StringSlice("GET"), + }, + }, + { + DegraphqlRoute: kong.DegraphqlRoute{ + ID: kong.String("third"), + URI: kong.String("/foo"), + Query: kong.String("query { hello }"), + Service: &kong.Service{ + ID: kong.String("some-service"), + }, + Methods: kong.StringSlice("GET"), + }, + }, + { + DegraphqlRoute: kong.DegraphqlRoute{ + ID: kong.String("fourth"), + URI: kong.String("/bar"), + Query: kong.String("query { hello }"), + Service: &kong.Service{ + ID: kong.String("some-service"), + }, + Methods: kong.StringSlice("GET"), + }, + }, + { + DegraphqlRoute: kong.DegraphqlRoute{ + ID: kong.String("fifth"), + URI: kong.String("/foo"), + Query: kong.String("query { hello }"), + Service: &kong.Service{ + ID: kong.String("some-service"), + }, + Methods: kong.StringSlice("GET"), + }, + }, + } + + for _, d := range degraphqlRoutes { + err := collection.Add(d) + require.NoError(err, "error adding degraphql route") + } +} diff --git a/pkg/state/types.go b/pkg/state/types.go index 43e2bc7..a979d72 100644 --- a/pkg/state/types.go +++ b/pkg/state/types.go @@ -1806,6 +1806,10 @@ type DegraphqlRoute struct { // GetCustomEntityID returns the ID of the DegraphqlRoute. func (d *DegraphqlRoute) GetCustomEntityID() string { + if d.ID == nil { + return "" + } + return *d.ID } diff --git a/tests/integration/sync_test.go b/tests/integration/sync_test.go index 8a4b336..a159e69 100644 --- a/tests/integration/sync_test.go +++ b/tests/integration/sync_test.go @@ -1710,6 +1710,15 @@ var ( }} ) +const complexQueryForDegraphqlRoute = `query SearchPosts($filters: PostsFilters) { + posts(filter: $filters) { + id + title + author + } +} +` + // test scope: // - 1.4.3 func Test_Sync_ServicesRoutes_Till_1_4_3(t *testing.T) { @@ -7567,3 +7576,81 @@ func Test_Sync_Scoped_Plugins_3x(t *testing.T) { }) } } + +func Test_Sync_DegraphqlRoutes(t *testing.T) { + client, err := getTestClient() + if err != nil { + t.Fatalf(err.Error()) + } + + ctx := context.Background() + dumpConfig := deckDump.Config{CustomEntityTypes: []string{"degraphql_routes"}} + + runWhen(t, "enterprise", ">=3.0.0") + + t.Run("create degraphql route", func(t *testing.T) { + mustResetKongState(ctx, t, client, dumpConfig) + currentState, err := fetchCurrentState(ctx, client, dumpConfig) + require.NoError(t, err) + + targetState := stateFromFile(ctx, t, "testdata/sync/036-degraphql-routes/kong.yaml", client, dumpConfig) + syncer, err := deckDiff.NewSyncer(deckDiff.SyncerOpts{ + CurrentState: currentState, + TargetState: targetState, + + KongClient: client, + }) + require.NoError(t, err) + + stats, errs, changes := syncer.Solve(ctx, 1, false, true) + require.Len(t, errs, 0, "Should have no errors in syncing") + logEntityChanges(t, stats, changes) + + newState, err := fetchCurrentState(ctx, client, dumpConfig) + require.NoError(t, err) + + degraphqlRoutes, err := newState.DegraphqlRoutes.GetAll() + require.NoError(t, err) + + assert.Equal(t, 1, len(degraphqlRoutes)) + assert.Equal(t, "/foo", *degraphqlRoutes[0].URI) + assert.Equal(t, "query{ foo { bar } }", *degraphqlRoutes[0].Query) + + expectedMethods := kong.StringSlice("GET") + assert.Equal(t, expectedMethods, degraphqlRoutes[0].Methods) + }) + + t.Run("create degraphql route - complex query", func(t *testing.T) { + mustResetKongState(ctx, t, client, dumpConfig) + currentState, err := fetchCurrentState(ctx, client, dumpConfig) + require.NoError(t, err) + + targetState := stateFromFile(ctx, t, "testdata/sync/036-degraphql-routes/kong-complex-query.yaml", client, dumpConfig) + syncer, err := deckDiff.NewSyncer(deckDiff.SyncerOpts{ + CurrentState: currentState, + TargetState: targetState, + + KongClient: client, + }) + require.NoError(t, err) + + stats, errs, changes := syncer.Solve(ctx, 1, false, true) + require.Len(t, errs, 0, "Should have no errors in syncing") + logEntityChanges(t, stats, changes) + + newState, err := fetchCurrentState(ctx, client, dumpConfig) + require.NoError(t, err) + + degraphqlRoutes, err := newState.DegraphqlRoutes.GetAll() + require.NoError(t, err) + + assert.Equal(t, 1, len(degraphqlRoutes)) + assert.Equal(t, "/search/posts", *degraphqlRoutes[0].URI) + + expectedQuery := kong.String(complexQueryForDegraphqlRoute) + assert.Equal(t, expectedQuery, degraphqlRoutes[0].Query) + + expectedMethods := kong.StringSlice("POST") + assert.Equal(t, expectedMethods, degraphqlRoutes[0].Methods) + }) +} diff --git a/tests/integration/testdata/sync/036-degraphql-routes/kong-complex-query.yaml b/tests/integration/testdata/sync/036-degraphql-routes/kong-complex-query.yaml new file mode 100644 index 0000000..c3ef037 --- /dev/null +++ b/tests/integration/testdata/sync/036-degraphql-routes/kong-complex-query.yaml @@ -0,0 +1,35 @@ +_format_version: "3.0" +plugins: +- config: + graphql_server_path: /graphql + enabled: true + name: degraphql + protocols: + - grpc + - grpcs + - http + - https +services: +- connect_timeout: 60000 + host: mockbin.org + name: svc1 + port: 80 + protocol: http + read_timeout: 60000 + retries: 5 + write_timeout: 60000 +custom_entities: + - type: degraphql_routes + fields: + uri: "/search/posts" + methods: + - "POST" + query: | + query SearchPosts($filters: PostsFilters) { + posts(filter: $filters) { + id + title + author + } + } + service: svc1 \ No newline at end of file diff --git a/tests/integration/testdata/sync/036-degraphql-routes/kong.yaml b/tests/integration/testdata/sync/036-degraphql-routes/kong.yaml new file mode 100644 index 0000000..31669aa --- /dev/null +++ b/tests/integration/testdata/sync/036-degraphql-routes/kong.yaml @@ -0,0 +1,26 @@ +_format_version: "3.0" +plugins: +- config: + graphql_server_path: /graphql + enabled: true + name: degraphql + protocols: + - grpc + - grpcs + - http + - https +services: +- connect_timeout: 60000 + host: mockbin.org + name: svc1 + port: 80 + protocol: http + read_timeout: 60000 + retries: 5 + write_timeout: 60000 +custom_entities: + - type: degraphql_routes + fields: + uri: "/foo" + query: "query{ foo { bar } }" + service: svc1 \ No newline at end of file