From 936884644a9085e60d4fb87197137efbfcc3d742 Mon Sep 17 00:00:00 2001 From: Martins Irbe Date: Tue, 28 May 2024 13:58:02 +0100 Subject: [PATCH 1/3] Add tests for property examples usage in mock responses --- mock/mock_engine_test.go | 133 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/mock/mock_engine_test.go b/mock/mock_engine_test.go index f37a117..26e8d03 100644 --- a/mock/mock_engine_test.go +++ b/mock/mock_engine_test.go @@ -1323,3 +1323,136 @@ paths: assert.Equal(t, http.StatusNoContent, status) assert.Empty(t, b) } + +func TestNewMockEngine_GenerateResponse_CombinedExampleObject(t *testing.T) { + spec := `openapi: 3.0.3 +info: + title: Example API + description: An example API for testing purposes + version: 1.0.0 +paths: + /examples: + get: + summary: Get example data + description: Retrieve an example response with various fields + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response' +components: + schemas: + ID: + type: integer + + Response: + type: object + required: + - id + - name + properties: + id: + $ref: '#/components/schemas/ID' + name: + type: string + username: + type: string + active: + type: boolean + balance: + type: number + format: float + tags: + type: array + items: + type: string + example: + id: 123 + name: "John Doe" + username: "jack" + active: true + balance: 99.99 + tags: ["tag1", "tag2", "tag3"]` + + d, _ := libopenapi.NewDocument([]byte(spec)) + doc, _ := d.BuildV3Model() + + me := NewMockEngine(&doc.Model, false) + + request, err := http.NewRequest(http.MethodGet, "https://api.pb33f.io/examples", http.NoBody) + require.NoError(t, err) + + b, status, err := me.GenerateResponse(request) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, status) + assert.Equal(t, `{"active":true,"balance":99.99,"id":123,"name":"John Doe","tags":["tag1","tag2","tag3"],"username":"jack"}`, string(b)) +} + +func TestNewMockEngine_GenerateResponse_IndividualPropertyExamples(t *testing.T) { + spec := `openapi: 3.0.3 +info: + title: Example API + description: An example API for testing purposes + version: 1.0.0 +paths: + /examples: + get: + summary: Get example data + description: Retrieve an example response with various fields + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response' +components: + schemas: + ID: + type: integer + example: 123 + + Response: + type: object + required: + - id + - name + properties: + id: + $ref: '#/components/schemas/ID' + name: + type: string + example: "John Doe" + username: + type: string + example: "jack" + active: + type: boolean + example: true + balance: + type: number + format: float + example: 99.99 + tags: + type: array + items: + type: string + example: ["tag1", "tag2", "tag3"]` + + d, _ := libopenapi.NewDocument([]byte(spec)) + doc, _ := d.BuildV3Model() + + me := NewMockEngine(&doc.Model, false) + + request, err := http.NewRequest(http.MethodGet, "https://api.pb33f.io/examples", http.NoBody) + require.NoError(t, err) + + b, status, err := me.GenerateResponse(request) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, status) + assert.Equal(t, `{"active":true,"balance":99.99,"id":123,"name":"John Doe","tags":["tag1","tag2","tag3"],"username":"jack"}`, string(b)) +} From 902954c8b513817f94f684e2337cacefe55c4dcd Mon Sep 17 00:00:00 2001 From: Martins Irbe Date: Tue, 28 May 2024 14:38:19 +0100 Subject: [PATCH 2/3] Disable required check on response mock engine --- go.mod | 2 +- go.sum | 4 ++-- mock/mock_engine.go | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d68275d..9639eb0 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/google/uuid v1.6.0 github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb // indirect github.com/pb33f/harhar v0.0.0-20240111233202-e393c2a39a60 - github.com/pb33f/libopenapi v0.16.6 + github.com/pb33f/libopenapi v0.16.8 github.com/pb33f/libopenapi-validator v0.0.56 github.com/pb33f/ranch v0.4.0 github.com/pterm/pterm v0.12.79 diff --git a/go.sum b/go.sum index 8ab45a7..6be7c1d 100644 --- a/go.sum +++ b/go.sum @@ -138,8 +138,8 @@ github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/pb33f/harhar v0.0.0-20240111233202-e393c2a39a60 h1:5Nt5F1BxaYfbLolyD0kIflqoOOnzZlp8rmvZL93HbDc= github.com/pb33f/harhar v0.0.0-20240111233202-e393c2a39a60/go.mod h1:sJHiXr/Rdkg2j8P5FYGEcqPP1ptdk8zn6xeJ3aZfAvw= -github.com/pb33f/libopenapi v0.16.6 h1:w0qrbAziEVvUlzGP7rTjMYS8bv9CfuIOW8OTOBgFGuo= -github.com/pb33f/libopenapi v0.16.6/go.mod h1:PEXNwvtT4KNdjrwudp5OYnD1ryqK6uJ68aMNyWvoMuc= +github.com/pb33f/libopenapi v0.16.8 h1:EceJ+JTHViwjKNyP3LJ8J39jSQ4F2oOvHw6KfWCcVxM= +github.com/pb33f/libopenapi v0.16.8/go.mod h1:PEXNwvtT4KNdjrwudp5OYnD1ryqK6uJ68aMNyWvoMuc= github.com/pb33f/libopenapi-validator v0.0.56 h1:kYO8DRsSRpnxhUExuEfsgARuT/hkc/ISC77CX0BMze4= github.com/pb33f/libopenapi-validator v0.0.56/go.mod h1:Cwdf0GUmnUrbw1LBpcGV1Wx5q/LC9akREV4Jc6/yJDc= github.com/pb33f/ranch v0.4.0 h1:fve9Lozc9m0IGkygWD3EFkBtbRua+2gyi9yxYHvufh4= diff --git a/mock/mock_engine.go b/mock/mock_engine.go index 6eca569..197a70a 100644 --- a/mock/mock_engine.go +++ b/mock/mock_engine.go @@ -31,6 +31,8 @@ func NewMockEngine(document *v3.Document, pretty bool) *ResponseMockEngine { if pretty { me.SetPretty() } + me.DisableRequiredCheck() + return &ResponseMockEngine{ doc: document, validator: validation.NewHttpValidator(document), From 7bfbfb0c2047c06d0340908d144dd4cbec4c983d Mon Sep 17 00:00:00 2001 From: Martins Irbe Date: Mon, 3 Jun 2024 17:11:16 +0100 Subject: [PATCH 3/3] Make mock response field example usage configurable --- cmd/root_command.go | 11 +++ daemon/wiretap_service.go | 3 +- mock/mock_engine.go | 7 +- mock/mock_engine_test.go | 148 +++++++++++++++++++++++++++----------- shared/config.go | 6 +- 5 files changed, 129 insertions(+), 46 deletions(-) diff --git a/cmd/root_command.go b/cmd/root_command.go index d8808e5..ff91a27 100644 --- a/cmd/root_command.go +++ b/cmd/root_command.go @@ -63,6 +63,7 @@ var ( // mock mode var mockMode bool + var useAllMockResponseFields bool certFlag, _ := cmd.Flags().GetString("cert") if certFlag != "" { @@ -82,6 +83,7 @@ var ( debug, _ := cmd.Flags().GetBool("debug") mockMode, _ = cmd.Flags().GetBool("mock-mode") + useAllMockResponseFields, _ = cmd.Flags().GetBool("enable-all-mock-response-fields") hardError, _ = cmd.Flags().GetBool("hard-validation") hardErrorCode, _ = cmd.Flags().GetInt("hard-validation-code") hardErrorReturnCode, _ = cmd.Flags().GetInt("hard-validation-return-code") @@ -182,6 +184,11 @@ var ( config.MockMode = true } } + if useAllMockResponseFields { + if !config.UseAllMockResponseFields { + config.UseAllMockResponseFields = true + } + } if streamReport { if !config.StreamReport { config.StreamReport = true @@ -217,6 +224,9 @@ var ( if mockMode { config.MockMode = true } + if useAllMockResponseFields { + config.UseAllMockResponseFields = true + } if streamReport { config.StreamReport = true } @@ -654,6 +664,7 @@ func Execute(version, commit, date string, fs embed.FS) { rootCmd.Flags().IntP("hard-validation-code", "q", 400, "Set a custom http error code for non-compliant requests when using the hard-error flag") rootCmd.Flags().IntP("hard-validation-return-code", "y", 502, "Set a custom http error code for non-compliant responses when using the hard-error flag") rootCmd.Flags().BoolP("mock-mode", "x", false, "Run in mock mode, responses are mocked and no traffic is sent to the target API (requires OpenAPI spec)") + rootCmd.Flags().BoolP("enable-all-mock-response-fields", "o", true, "Enable usage of all property examples in mock responses. When set to false, only required field examples will be used.") rootCmd.Flags().StringP("config", "c", "", "Location of wiretap configuration file to use (default is .wiretap in current directory)") rootCmd.Flags().StringP("base", "b", "", "Set a base path to resolve relative file references from, or a overriding base URL to resolve remote references from") rootCmd.Flags().BoolP("debug", "l", false, "Enable debug logging") diff --git a/daemon/wiretap_service.go b/daemon/wiretap_service.go index 1b1d7d0..8750a4d 100644 --- a/daemon/wiretap_service.go +++ b/daemon/wiretap_service.go @@ -73,7 +73,8 @@ func NewWiretapService(document libopenapi.Document, config *shared.WiretapConfi } // create a new mock engine - wts.mockEngine = mock.NewMockEngine(wts.docModel, config.MockModePretty) + wts.mockEngine = mock.NewMockEngine(wts.docModel, config.MockModePretty, + config.UseAllMockResponseFields) // hard-wire the config, change this later if needed. wts.config = config diff --git a/mock/mock_engine.go b/mock/mock_engine.go index 197a70a..d8dc702 100644 --- a/mock/mock_engine.go +++ b/mock/mock_engine.go @@ -26,12 +26,15 @@ type ResponseMockEngine struct { pretty bool } -func NewMockEngine(document *v3.Document, pretty bool) *ResponseMockEngine { +func NewMockEngine(document *v3.Document, pretty, useAllPropertyExamples bool) *ResponseMockEngine { me := renderer.NewMockGenerator(renderer.JSON) if pretty { me.SetPretty() } - me.DisableRequiredCheck() + + if useAllPropertyExamples { + me.DisableRequiredCheck() + } return &ResponseMockEngine{ doc: document, diff --git a/mock/mock_engine_test.go b/mock/mock_engine_test.go index 26e8d03..f5c0e04 100644 --- a/mock/mock_engine_test.go +++ b/mock/mock_engine_test.go @@ -12,7 +12,7 @@ import ( "github.com/pb33f/libopenapi" "github.com/pb33f/libopenapi-validator/helpers" - v3 "github.com/pb33f/libopenapi/datamodel/high/v3" + "github.com/pb33f/libopenapi/datamodel/high/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -49,7 +49,7 @@ func resetPetstoreState() *v3.Document { func TestNewMockEngine_findPath(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/wiretap/giftshop/products", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -60,7 +60,7 @@ func TestNewMockEngine_findPath(t *testing.T) { func TestNewMockEngine_findPathNegative(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/wiretap/giftshop/invalid", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -72,7 +72,7 @@ func TestNewMockEngine_findPathNegative(t *testing.T) { func TestNewMockEngine_findOperation(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/wiretap/giftshop/products", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -85,7 +85,7 @@ func TestNewMockEngine_findOperation(t *testing.T) { func TestNewMockEngine_findOperationNegative(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodPatch, "https://api.pb33f.io/wiretap/giftshop/products", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -97,7 +97,7 @@ func TestNewMockEngine_findOperationNegative(t *testing.T) { func TestNewMockEngine_ValidateSecurity_FailAPIKey_Header(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodPost, "https://api.pb33f.io/wiretap/giftshop/products", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -110,7 +110,7 @@ func TestNewMockEngine_ValidateSecurity_FailAPIKey_Header(t *testing.T) { func TestNewMockEngine_ValidateSecurity_PassAPIKey_Header(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodPost, "https://api.pb33f.io/wiretap/giftshop/products", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -123,7 +123,7 @@ func TestNewMockEngine_ValidateSecurity_PassAPIKey_Header(t *testing.T) { func TestNewMockEngine_ValidateSecurity_FailAPIKey_Query(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodPost, "https://api.pb33f.io/wiretap/giftshop/products", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -141,7 +141,7 @@ func TestNewMockEngine_ValidateSecurity_FailAPIKey_Query(t *testing.T) { func TestNewMockEngine_ValidateSecurity_PassAPIKey_Query(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodPost, "https://api.pb33f.io/wiretap/giftshop/products?pizza-burger-cake=123", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -158,7 +158,7 @@ func TestNewMockEngine_ValidateSecurity_PassAPIKey_Query(t *testing.T) { func TestNewMockEngine_ValidateSecurity_FailAPIKey_Cookie(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodPost, "https://api.pb33f.io/wiretap/giftshop/products", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -176,7 +176,7 @@ func TestNewMockEngine_ValidateSecurity_FailAPIKey_Cookie(t *testing.T) { func TestNewMockEngine_ValidateSecurity_PassAPIKey_Cookie(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodPost, "https://api.pb33f.io/wiretap/giftshop/products", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -197,7 +197,7 @@ func TestNewMockEngine_ValidateSecurity_PassAPIKey_Cookie(t *testing.T) { func TestNewMockEngine_ValidateSecurity_FailHTTP_Bearer(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodPost, "https://api.pb33f.io/wiretap/giftshop/products", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -216,7 +216,7 @@ func TestNewMockEngine_ValidateSecurity_FailHTTP_Bearer(t *testing.T) { func TestNewMockEngine_ValidateSecurity_PassHTTP_Bearer(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodPost, "https://api.pb33f.io/wiretap/giftshop/products", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -235,7 +235,7 @@ func TestNewMockEngine_ValidateSecurity_PassHTTP_Bearer(t *testing.T) { func TestNewMockEngine_BuildResponse_SimpleValid(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/wiretap/giftshop/products", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -257,7 +257,7 @@ func TestNewMockEngine_BuildResponse_SimpleValid(t *testing.T) { //func TestNewMockEngine_BuildResponse_SimpleInvalid_BadContentType(t *testing.T) { // doc := resetGiftshopState() -// me := NewMockEngine(doc, false) +// me := NewMockEngine(doc, false, true) // // request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/wiretap/giftshop/products", nil) // request.Header.Set(helpers.ContentTypeHeader, "cup/tea") @@ -275,7 +275,7 @@ func TestNewMockEngine_BuildResponse_SimpleValid(t *testing.T) { func TestNewMockEngine_BuildResponse_SimpleValid_Pretty(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, true) + me := NewMockEngine(doc, true, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/wiretap/giftshop/products/bd1f3f70-d46f-4ea7-b178-de9a5abfe4d8", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -291,7 +291,7 @@ func TestNewMockEngine_BuildResponse_SimpleValid_Pretty(t *testing.T) { func TestNewMockEngine_BuildResponse_MissingPath_404(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/minky/monkey/moo", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -312,7 +312,7 @@ func TestNewMockEngine_BuildResponse_MissingPath_404(t *testing.T) { func TestNewMockEngine_BuildResponse_MissingOperation_404(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodPatch, "https://api.pb33f.io/wiretap/giftshop/products", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -335,7 +335,7 @@ func TestNewMockEngine_BuildResponse_MissingOperation_404(t *testing.T) { func TestNewMockEngine_BuildResponse_CreateProduct_NoSecurity_Invalid(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) product := make(map[string]any) product["price"] = 400.23 @@ -361,7 +361,7 @@ func TestNewMockEngine_BuildResponse_CreateProduct_NoSecurity_Invalid(t *testing func TestNewMockEngine_BuildResponse_CreateProduct_WithSecurity_Invalid(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) product := make(map[string]any) product["price"] = 400.23 @@ -387,7 +387,7 @@ func TestNewMockEngine_BuildResponse_CreateProduct_WithSecurity_Invalid(t *testi func TestNewMockEngine_BuildResponse_Petstore_Sexurirt(t *testing.T) { doc := resetGiftshopState() - me := NewMockEngine(doc, false) + me := NewMockEngine(doc, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/minky/monkey/moo", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -433,7 +433,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/go", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -483,7 +483,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodPost, "https://api.pb33f.io/auth", nil) request.Header.Set(helpers.ContentTypeHeader, "application/json") @@ -529,7 +529,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) payload := `{"basicAuth":{"password":"testPass","username":"testUser"}}` buf := bytes.NewBuffer([]byte(payload)) @@ -573,7 +573,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/test", nil) request.Header.Set(helpers.AuthorizationHeader, "ding-a-ling") @@ -625,7 +625,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/test", nil) @@ -671,7 +671,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/test", nil) @@ -726,7 +726,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/test", nil) request.Header.Set(helpers.Preferred, "robocop") @@ -776,7 +776,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/test", nil) @@ -827,7 +827,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/test", nil) request.Header.Set(helpers.Preferred, "1") @@ -874,7 +874,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/test", nil) @@ -918,7 +918,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/test", nil) @@ -995,7 +995,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/test", nil) request.Header.Set(helpers.Preferred, "sadcop") @@ -1086,7 +1086,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) // Check that we don't panic if first 2xx does not match media type request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/test", nil) @@ -1155,7 +1155,7 @@ paths: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/chip-shop", nil) @@ -1214,7 +1214,7 @@ paths: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/chip-shop", nil) @@ -1262,7 +1262,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, _ := http.NewRequest(http.MethodGet, "https://api.pb33f.io/test", nil) request.Header.Set("wiretap-status-code", "418") @@ -1309,7 +1309,7 @@ paths: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, err := http.NewRequest(http.MethodPost, "https://api.pb33f.io/test", bytes.NewBufferString("{\"message\": \"Hello, this is a test message.\"}")) @@ -1379,7 +1379,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, err := http.NewRequest(http.MethodGet, "https://api.pb33f.io/examples", http.NoBody) require.NoError(t, err) @@ -1391,7 +1391,7 @@ components: assert.Equal(t, `{"active":true,"balance":99.99,"id":123,"name":"John Doe","tags":["tag1","tag2","tag3"],"username":"jack"}`, string(b)) } -func TestNewMockEngine_GenerateResponse_IndividualPropertyExamples(t *testing.T) { +func TestNewMockEngine_GenerateResponse_DefaultWithAllPropertyExamplesInResponse(t *testing.T) { spec := `openapi: 3.0.3 info: title: Example API @@ -1445,7 +1445,7 @@ components: d, _ := libopenapi.NewDocument([]byte(spec)) doc, _ := d.BuildV3Model() - me := NewMockEngine(&doc.Model, false) + me := NewMockEngine(&doc.Model, false, true) request, err := http.NewRequest(http.MethodGet, "https://api.pb33f.io/examples", http.NoBody) require.NoError(t, err) @@ -1456,3 +1456,69 @@ components: assert.Equal(t, http.StatusOK, status) assert.Equal(t, `{"active":true,"balance":99.99,"id":123,"name":"John Doe","tags":["tag1","tag2","tag3"],"username":"jack"}`, string(b)) } + +func TestNewMockEngine_GenerateResponse_OnlyRequiredPropertyExamplesInResponse(t *testing.T) { + spec := `openapi: 3.0.3 +info: + title: Example API + description: An example API for testing purposes + version: 1.0.0 +paths: + /examples: + get: + summary: Get example data + description: Retrieve an example response with various fields + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response' +components: + schemas: + ID: + type: integer + example: 123 + + Response: + type: object + required: + - id + - name + properties: + id: + $ref: '#/components/schemas/ID' + name: + type: string + example: "John Doe" + username: + type: string + example: "jack" + active: + type: boolean + example: true + balance: + type: number + format: float + example: 99.99 + tags: + type: array + items: + type: string + example: ["tag1", "tag2", "tag3"]` + + d, _ := libopenapi.NewDocument([]byte(spec)) + doc, _ := d.BuildV3Model() + + me := NewMockEngine(&doc.Model, false, false) + + request, err := http.NewRequest(http.MethodGet, "https://api.pb33f.io/examples", http.NoBody) + require.NoError(t, err) + + b, status, err := me.GenerateResponse(request) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, status) + assert.Equal(t, `{"id":123,"name":"John Doe"}`, string(b)) +} diff --git a/shared/config.go b/shared/config.go index 52748e4..020d91d 100644 --- a/shared/config.go +++ b/shared/config.go @@ -6,10 +6,11 @@ package shared import ( "embed" "fmt" - "github.com/gobwas/glob" - "github.com/pb33f/harhar" "log/slog" "regexp" + + "github.com/gobwas/glob" + "github.com/pb33f/harhar" ) type WiretapConfiguration struct { @@ -38,6 +39,7 @@ type WiretapConfiguration struct { HardErrorReturnCode int `json:"hardValidationReturnCode,omitempty" yaml:"hardValidationReturnCode,omitempty"` PathDelays map[string]int `json:"pathDelays,omitempty" yaml:"pathDelays,omitempty"` MockMode bool `json:"mockMode,omitempty" yaml:"mockMode,omitempty"` + UseAllMockResponseFields bool `json:"useAllMockResponseFields,omitempty" yaml:"useAllMockResponseFields,omitempty"` MockModePretty bool `json:"mockModePretty,omitempty" yaml:"mockModePretty,omitempty"` Base string `json:"base,omitempty" yaml:"base,omitempty"` HAR string `json:"har,omitempty" yaml:"har,omitempty"`