From 1af06ae1fdb215485fc8afdbda83ed683a521fea Mon Sep 17 00:00:00 2001 From: Roman Terentev Date: Mon, 27 Jan 2025 18:05:28 +1000 Subject: [PATCH 1/3] add: multipart form-data basic support --- README-ru.md | 158 ++++++++++++----- README.md | 160 +++++++++++++----- models/test.go | 3 +- runner/request.go | 70 +++++--- runner/request_multipart_form_data_test.go | 51 ++++++ runner/runner_upload_file_test.go | 14 +- .../multipart/form-data/form-data.yaml | 33 ++++ .../testdata/upload-files/upload-files.yaml | 20 +++ variables/variables.go | 8 +- 9 files changed, 396 insertions(+), 121 deletions(-) create mode 100644 runner/request_multipart_form_data_test.go create mode 100644 runner/testdata/multipart/form-data/form-data.yaml diff --git a/README-ru.md b/README-ru.md index 87e211d..1eaf536 100644 --- a/README-ru.md +++ b/README-ru.md @@ -14,46 +14,69 @@ Gonkey протестирует ваши сервисы, используя их ## Содержание -- [Использование консольной утилиты](#использование-консольной-утилиты) -- [Использование gonkey как библиотеки](#использование-gonkey-как-библиотеки) -- [Пример тестового сценария](#пример-тестового-сценария) -- [Статус теста](#статус-теста) -- [HTTP-запрос](#http-запрос) -- [HTTP-ответ](#http-ответ) -- [Переменные](#переменные) - - [Способы присвоения](#способы-присвоения) - - [В описании самого теста](#в-описании-самого-теста) - - [Из результатов предыдущего запроса](#из-результатов-предыдущего-запроса) - - [Из результата текущего запроса](#из-результата-текущего-запроса) - - [В переменных окружения или в env-файле](#в-переменных-окружения-или-в-env-файле) - - [В cases](#в-cases) -- [Загрузка файлов](#загрузка-файлов) -- [Фикстуры](#фикстуры) - - [Удаление данных из таблиц](#удаление-данных-из-таблиц) - - [Шаблоны записей](#шаблоны-записей) - - [Наследование записей](#наследование-записей) - - [Связывание записей](#связывание-записей) - - [Выражения](#выражения) - - [Aerospike](#aerospike) - - [Redis](#redis) -- [Моки](#моки) - - [Запуск моков при использовании gonkey как библиотеки](#запуск-моков-при-использовании-gonkey-как-библиотеки) - - [Описание моков в файле с тестом](#описание-моков-в-файле-с-тестом) - - [Проверки запросов (requestConstraints)](#проверки-запросов-requestconstraints) - - [Стратегии ответов (strategy)](#стратегии-ответов-strategy) - - [Подсчет количества вызовов](#подсчет-количества-вызовов) -- [Использование shell скриптов](#использование-shell-скриптов) - - [Описание скрипта](#описание-скрипта) - - [Запуск скрипта с параметризацией](#запуск-скрипта-с-параметризацией) -- [Запрос в Базу данных](#запрос-в-базу-данных) - - [Формат описания запросов](#формат-описания-запросов) - - [Описание запроса](#описание-запроса) - - [Описание ответа на запрос в Базу данных](#описание-ответа-на-запрос-в-базу-данных) - - [Параметризация при запросах в Базу данных](#параметризация-при-запросах-в-базу-данных) - - [Игнорирование порядка записей в ответе на запрос в базу данных](#игнорирование-порядка-записей-в-ответе-на-запрос-в-базу-данных) -- [JSON-schema](#json-schema) - - [Настройка на IDE Jetbrains](#настройка-на-ide-jetbrains) - - [Настройка на IDE VSCode](#настройка-на-ide-vscode) +- [Gonkey: инструмент автоматизации тестирования](#gonkey-инструмент-автоматизации-тестирования) + - [Содержание](#содержание) + - [Использование консольной утилиты](#использование-консольной-утилиты) + - [Использование gonkey как библиотеки](#использование-gonkey-как-библиотеки) + - [Пример тестового сценария](#пример-тестового-сценария) + - [Статус теста](#статус-теста) + - [HTTP-запрос](#http-запрос) + - [HTTP-ответ](#http-ответ) + - [Переменные](#переменные) + - [Способы присвоения](#способы-присвоения) + - [В описании самого теста](#в-описании-самого-теста) + - [Из результатов предыдущего запроса](#из-результатов-предыдущего-запроса) + - [Из результата текущего запроса](#из-результата-текущего-запроса) + - [В переменных окружения или в env-файле](#в-переменных-окружения-или-в-env-файле) + - [В cases](#в-cases) + - [Запросы с multipart/form-data](#запросы-с-multipartform-data) + - [Данные полей формы](#данные-полей-формы) + - [Загрузка файлов](#загрузка-файлов) + - [Фикстуры](#фикстуры) + - [Удаление данных из таблиц](#удаление-данных-из-таблиц) + - [Шаблоны записей](#шаблоны-записей) + - [Наследование записей](#наследование-записей) + - [Связывание записей](#связывание-записей) + - [Выражения](#выражения) + - [Aerospike](#aerospike) + - [Redis](#redis) + - [Моки](#моки) + - [Запуск моков при использовании gonkey как библиотеки](#запуск-моков-при-использовании-gonkey-как-библиотеки) + - [Описание моков в файле с тестом](#описание-моков-в-файле-с-тестом) + - [Проверки запросов (requestConstraints)](#проверки-запросов-requestconstraints) + - [nop](#nop) + - [bodyMatchesJSON](#bodymatchesjson) + - [bodyJSONFieldMatchesJSON](#bodyjsonfieldmatchesjson) + - [pathMatches](#pathmatches) + - [queryMatches](#querymatches) + - [queryMatchesRegexp](#querymatchesregexp) + - [methodIs](#methodis) + - [headerIs](#headeris) + - [bodyMatchesText](#bodymatchestext) + - [bodyMatchesXML](#bodymatchesxml) + - [Стратегии ответов (strategy)](#стратегии-ответов-strategy) + - [nop](#nop-1) + - [file](#file) + - [constant](#constant) + - [template](#template) + - [uriVary](#urivary) + - [methodVary](#methodvary) + - [sequence](#sequence) + - [basedOnRequest](#basedonrequest) + - [dropRequest](#droprequest) + - [Подсчет количества вызовов](#подсчет-количества-вызовов) + - [Использование shell скриптов](#использование-shell-скриптов) + - [Описание скрипта](#описание-скрипта) + - [Запуск скрипта с параметризацией](#запуск-скрипта-с-параметризацией) + - [Запрос в Базу данных](#запрос-в-базу-данных) + - [Формат описания запросов](#формат-описания-запросов) + - [Описание запроса](#описание-запроса) + - [Описание ответа на запрос в Базу данных](#описание-ответа-на-запрос-в-базу-данных) + - [Параметризация при запросах в Базу данных](#параметризация-при-запросах-в-базу-данных) + - [Игнорирование порядка записей в ответе на запрос в базу данных](#игнорирование-порядка-записей-в-ответе-на-запрос-в-базу-данных) + - [JSON-schema](#json-schema) + - [Настройка на IDE Jetbrains](#настройка-на-ide-jetbrains) + - [Настройка на IDE VSCode](#настройка-на-ide-vscode) ## Использование консольной утилиты @@ -346,6 +369,7 @@ responseHeaders: - body для моков - headers для моков - requestConstraints для моков +- form для multipart/form-data Пример использования: @@ -487,12 +511,41 @@ env-файл, например, удобно использовать, когд Такие переменные будут доступны и в других кейсах, если не будут переопределены. -## Загрузка файлов - -В тестовом запросе можно загружать файлы. Для этого нужно указать тип запроса - POST и заголовок: +## Запросы с multipart/form-data +Нужно указать тип запроса +- POST +Заголовок (необязательно): > Content-Type: multipart/form-data +или с указанием _boundary_ (необязательно): +> Content-Type: multipart/form-data; boundary=--some-boundary + +### Данные полей формы +Пример: + +```yaml + - name: "upload-form" + method: POST + form: + fields: + field_name1: "field_name1 value" + name2: "name2 value" + "custom_struct_field[0]": "custom_struct_field 0" + "custom_struct_field[1]": "custom_struct_field 1" + "custom_struct_field[inner_obj][field]": "inner_obj field value" + headers: + Content-Type: multipart/form-data # case-sensitive, can be omitted + response: + 200: | + { + "status": "OK" + } +``` + +### Загрузка файлов +*по пути с файловой системы + Пример: ```yaml @@ -502,6 +555,25 @@ env-файл, например, удобно использовать, когд files: file1: "testdata/upload-files/file1.txt" file2: "testdata/upload-files/file2.log" + headers: + Content-Type: multipart/form-data + response: + 200: | + { + "status": "OK" + } +``` + +с данными формы: +```yaml + - name: "upload-multipart-form-data" + method: POST + form: + fields: + field_name1: "field_name1 value" + files: + file1: "testdata/upload-files/file1.txt" + file2: "testdata/upload-files/file2.log" headers: Content-Type: multipart/form-data # case-sensitive, can be omitted response: diff --git a/README.md b/README.md index 1cda5f8..331f3ed 100644 --- a/README.md +++ b/README.md @@ -16,46 +16,69 @@ Capabilities: ## Table of contents -- [Using the CLI](#using-the-cli) -- [Using gonkey as a library](#using-gonkey-as-a-library) -- [Test scenario example](#test-scenario-example) -- [Test status](#test-status) -- [HTTP-request](#http-request) -- [HTTP-response](#http-response) -- [Variables](#variables) - - [Assignment](#assignment) - - [In the description of the test](#in-the-description-of-the-test) - - [From the response of the previous test](#from-the-response-of-the-previous-test) - - [From the response of currently running test](#from-the-response-of-currently-running-test) - - [From environment variables or from env-file](#from-environment-variables-or-from-env-file) - - [From cases](#from-cases) -- [Files uploading](#files-uploading) -- [Fixtures](#fixtures) - - [Deleting data from tables](#deleting-data-from-tables) - - [Record templates](#record-templates) - - [Record inheritance](#record-inheritance) - - [Record linking](#record-linking) - - [Expressions](#expressions) - - [Aerospike](#aerospike) - - [Redis](#redis) -- [Mocks](#mocks) - - [Running mocks while using gonkey as a library](#running-mocks-while-using-gonkey-as-a-library) - - [Mocks definition in the test file](#mocks-definition-in-the-test-file) - - [Request constraints (requestConstraints)](#request-constraints-requestconstraints) - - [Response strategies (strategy)](#response-strategies-strategy) - - [Calls count](#calls-count) -- [Shell scripts usage](#shell-scripts-usage) - - [Script definition](#script-definition) - - [Running a script with parameterization](#running-a-script-with-parameterization) -- [A DB query](#a-db-query) - - [Test Format](#test-format) - - [Query definition](#query-definition) - - [Definition of DB request response](#definition-of-db-request-response) - - [DB request parameterization](#db-request-parameterization) - - [Ignoring ordering in DB response](#ignoring-ordering-in-db-response) -- [JSON-schema](#json-schema) - - [Setup in Jetbrains IDE](#setup-in-jetbrains-ide) - - [Setup is VSCode IDE](#setup-is-vscode-ide) +- [Gonkey: testing automation tool](#gonkey-testing-automation-tool) + - [Table of contents](#table-of-contents) + - [Using the CLI](#using-the-cli) + - [Using gonkey as a library](#using-gonkey-as-a-library) + - [Test scenario example](#test-scenario-example) + - [Test status](#test-status) + - [HTTP-request](#http-request) + - [HTTP-response](#http-response) + - [Variables](#variables) + - [Assignment](#assignment) + - [In the description of the test](#in-the-description-of-the-test) + - [From the response of the previous test](#from-the-response-of-the-previous-test) + - [From the response of currently running test](#from-the-response-of-currently-running-test) + - [From environment variables or from env-file](#from-environment-variables-or-from-env-file) + - [From cases](#from-cases) + - [multipart/form-data requests](#multipartform-data-requests) + - [Form](#form) + - [File upload](#file-upload) + - [Fixtures](#fixtures) + - [Deleting data from tables](#deleting-data-from-tables) + - [Record templates](#record-templates) + - [Record inheritance](#record-inheritance) + - [Record linking](#record-linking) + - [Expressions](#expressions) + - [Aerospike](#aerospike) + - [Redis](#redis) + - [Mocks](#mocks) + - [Running mocks while using gonkey as a library](#running-mocks-while-using-gonkey-as-a-library) + - [Mocks definition in the test file](#mocks-definition-in-the-test-file) + - [Request constraints (requestConstraints)](#request-constraints-requestconstraints) + - [nop](#nop) + - [bodyMatchesJSON](#bodymatchesjson) + - [bodyJSONFieldMatchesJSON](#bodyjsonfieldmatchesjson) + - [pathMatches](#pathmatches) + - [queryMatches](#querymatches) + - [queryMatchesRegexp](#querymatchesregexp) + - [methodIs](#methodis) + - [headerIs](#headeris) + - [bodyMatchesText](#bodymatchestext) + - [bodyMatchesXML](#bodymatchesxml) + - [Response strategies (strategy)](#response-strategies-strategy) + - [nop](#nop-1) + - [file](#file) + - [constant](#constant) + - [template](#template) + - [uriVary](#urivary) + - [methodVary](#methodvary) + - [sequence](#sequence) + - [basedOnRequest](#basedonrequest) + - [dropRequest](#droprequest) + - [Calls count](#calls-count) + - [Shell scripts usage](#shell-scripts-usage) + - [Script definition](#script-definition) + - [Running a script with parameterization](#running-a-script-with-parameterization) + - [A DB query](#a-db-query) + - [Test Format](#test-format) + - [Query definition](#query-definition) + - [Definition of DB request response](#definition-of-db-request-response) + - [DB request parameterization](#db-request-parameterization) + - [Ignoring ordering in DB response](#ignoring-ordering-in-db-response) + - [JSON-schema](#json-schema) + - [Setup in Jetbrains IDE](#setup-in-jetbrains-ide) + - [Setup is VSCode IDE](#setup-is-vscode-ide) ## Using the CLI @@ -349,6 +372,7 @@ You can use variables in the description of the test, the following fields are s - mocks body - mocks headers - mocks requestConstraints +- form Example: @@ -487,12 +511,43 @@ Example: Variables like these will be available through another cases if not redefined. -## Files uploading - -You can upload files in test request. For this you must specify the type of request - POST and header: +## multipart/form-data requests +You must specify the type of request: +- POST +Header (optional): > Content-Type: multipart/form-data +with _boundary_ (optional): +> Content-Type: multipart/form-data; boundary=--some-boundary + + +### Form +Example: + +```yaml + - name: "upload-form" + method: POST + form: + fields: + field_name1: "field_name1 value" + name2: "name2 value" + "custom_struct_field[0]": "custom_struct_field 0" + "custom_struct_field[1]": "custom_struct_field 1" + "custom_struct_field[inner_obj][field]": "inner_obj field value" + headers: + Content-Type: multipart/form-data # case-sensitive, can be omitted + response: + 200: | + { + "status": "OK" + } +``` + +### File upload +You can upload files in test request. +Example: + Example: ```yaml @@ -502,6 +557,25 @@ Example: files: file1: "testdata/upload-files/file1.txt" file2: "testdata/upload-files/file2.log" + headers: + Content-Type: multipart/form-data + response: + 200: | + { + "status": "OK" + } +``` + +with form: +```yaml + - name: "upload-multipart-form-data" + method: POST + form: + fields: + field_name1: "field_name1 value" + files: + file1: "testdata/upload-files/file1.txt" + file2: "testdata/upload-files/file2.log" headers: Content-Type: multipart/form-data # case-sensitive, can be omitted response: diff --git a/models/test.go b/models/test.go index a81a715..e0f56b7 100644 --- a/models/test.go +++ b/models/test.go @@ -66,7 +66,8 @@ type TestInterface interface { // TODO: add support for form fields type Form struct { - Files map[string]string `json:"files" yaml:"files"` + Fields map[string]string `json:"fields" yaml:"fields"` + Files map[string]string `json:"files" yaml:"files"` } type Summary struct { diff --git a/runner/request.go b/runner/request.go index 01b5816..95b1c69 100644 --- a/runner/request.go +++ b/runner/request.go @@ -5,11 +5,13 @@ import ( "crypto/tls" "fmt" "io" + "mime" "mime/multipart" "net/http" "net/url" "os" "path/filepath" + "slices" "strings" "github.com/lamoda/gonkey/models" @@ -50,22 +52,35 @@ func newRequest(host string, test models.TestInterface) (req *http.Request, err } func newMultipartRequest(host string, test models.TestInterface) (*http.Request, error) { - if test.ContentType() != "" && test.ContentType() != "multipart/form-data" { - return nil, fmt.Errorf( - "test has unexpected Content-Type: %s, expected: multipart/form-data", - test.ContentType(), - ) + var boundary string + + if test.ContentType() != "" { + contentType, params, err := mime.ParseMediaType(test.ContentType()) + if err != nil { + return nil, err + } + if contentType != "multipart/form-data" { + return nil, fmt.Errorf( + "test has unexpected Content-Type: %s, expected: multipart/form-data", + test.ContentType(), + ) + } + + if b, ok := params["boundary"]; ok { + boundary = b + } } var b bytes.Buffer w := multipart.NewWriter(&b) - params, err := url.ParseQuery(test.GetRequest()) - if err != nil { - return nil, err + if boundary != "" { + if err := w.SetBoundary(boundary); err != nil { + return nil, fmt.Errorf("SetBoundary : %w", err) + } } - err = addFields(params, w) + err := addFields(test.GetForm().Fields, w) if err != nil { return nil, err } @@ -90,7 +105,6 @@ func newMultipartRequest(host string, test models.TestInterface) (*http.Request, func addFiles(files map[string]string, w *multipart.Writer) error { for name, path := range files { - err := addFile(path, w, name) if err != nil { return err @@ -100,6 +114,24 @@ func addFiles(files map[string]string, w *multipart.Writer) error { return nil } +func addFields(fields map[string]string, w *multipart.Writer) error { + // TODO: sort fields better + fieldNames := make([]string, 0, len(fields)) + for n, _ := range fields { + fieldNames = append(fieldNames, n) + } + slices.Sort(fieldNames) + + for _, name := range fieldNames { + n := name + v := fields[n] + if err := w.WriteField(n, v); err != nil { + return err + } + } + + return nil +} func addFile(path string, w *multipart.Writer, name string) error { f, err := os.Open(path) @@ -120,24 +152,6 @@ func addFile(path string, w *multipart.Writer, name string) error { return nil } -func addFields(params url.Values, w *multipart.Writer) error { - for k, vv := range params { - for _, v := range vv { - fw, err := w.CreateFormField(k) - if err != nil { - return err - } - - _, err = fw.Write([]byte(v)) - if err != nil { - return err - } - } - } - - return nil -} - func newCommonRequest(host string, test models.TestInterface) (*http.Request, error) { body, err := test.ToJSON() if err != nil { diff --git a/runner/request_multipart_form_data_test.go b/runner/request_multipart_form_data_test.go new file mode 100644 index 0000000..c5d9d3c --- /dev/null +++ b/runner/request_multipart_form_data_test.go @@ -0,0 +1,51 @@ +package runner + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMultipartFormData(t *testing.T) { + srv := testServerMultipartFormData(t) + defer srv.Close() + + // TODO: refactor RunWithTesting() for testing negative scenario (when tests has expected errors) + RunWithTesting(t, &RunWithTestingParams{ + Server: srv, + TestsDir: filepath.Join("testdata", "multipart", "form-data"), + }) +} + +type multipartResponse struct { + ContentTypeHeader string `json:"content_type_header"` + RequestBodyContent string `json:"request_body_content"` +} + +func testServerMultipartFormData(t *testing.T) *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + require.NoError(t, err) + r.Body = io.NopCloser(bytes.NewReader(body)) + + resp := multipartResponse{ + ContentTypeHeader: r.Header.Get("Content-Type"), + RequestBodyContent: string(body), + } + + respData, err := json.Marshal(resp) + require.NoError(t, err) + + w.Header().Set("Content-Type", "application/json") + + _, err = w.Write(respData) + require.NoError(t, err) + + })) +} diff --git a/runner/runner_upload_file_test.go b/runner/runner_upload_file_test.go index 9daf7b6..191e46c 100644 --- a/runner/runner_upload_file_test.go +++ b/runner/runner_upload_file_test.go @@ -23,11 +23,13 @@ func TestUploadFiles(t *testing.T) { } type response struct { - Status string `json:"status"` - File1Name string `json:"file_1_name"` - File1Content string `json:"file_1_content"` - File2Name string `json:"file_2_name"` - File2Content string `json:"file_2_content"` + Status string `json:"status"` + File1Name string `json:"file_1_name"` + File1Content string `json:"file_1_content"` + File2Name string `json:"file_2_name"` + File2Content string `json:"file_2_content"` + FieldsTestName string `json:"fields_test_name"` + FieldsTestContent string `json:"fields_test_content"` } func testServerUpload(t *testing.T) *httptest.Server { @@ -40,6 +42,8 @@ func testServerUpload(t *testing.T) *httptest.Server { resp.File1Name, resp.File1Content = formFile(t, r, "file1") resp.File2Name, resp.File2Content = formFile(t, r, "file2") + resp.FieldsTestName, resp.FieldsTestContent = "fieldTest", r.FormValue("fieldTest") + respData, err := json.Marshal(resp) require.NoError(t, err) diff --git a/runner/testdata/multipart/form-data/form-data.yaml b/runner/testdata/multipart/form-data/form-data.yaml new file mode 100644 index 0000000..52804c0 --- /dev/null +++ b/runner/testdata/multipart/form-data/form-data.yaml @@ -0,0 +1,33 @@ +- name: "form-data: simple form" + method: POST + path: /dontcare + headers: + Content-Type: multipart/form-data; boundary=somebound + form: + fields: + field_test: test value + field2: test value 2 + info: 100 + response: + 200: | + { + "content_type_header":"multipart/form-data; boundary=somebound", + "request_body_content":"--somebound\r\nContent-Disposition: form-data; name=\"field2\"\r\n\r\ntest value 2\r\n--somebound\r\nContent-Disposition: form-data; name=\"field_test\"\r\n\r\ntest value\r\n--somebound\r\nContent-Disposition: form-data; name=\"info\"\r\n\r\n100\r\n--somebound--\r\n" + } + +- name: "form-data: simple form, several fields" + method: POST + path: /dontcare + headers: + Content-Type: multipart/form-data; boundary=somebound + form: + fields: + field_test: test value + "fieldobj[2][one]": fieldobj 2 prop one + "fieldobj[2][two]": fieldobj 2 prop two + response: + 200: | + { + "content_type_header":"multipart/form-data; boundary=somebound", + "request_body_content":"--somebound\r\nContent-Disposition: form-data; name=\"field_test\"\r\n\r\ntest value\r\n--somebound\r\nContent-Disposition: form-data; name=\"fieldobj[2][one]\"\r\n\r\nfieldobj 2 prop one\r\n--somebound\r\nContent-Disposition: form-data; name=\"fieldobj[2][two]\"\r\n\r\nfieldobj 2 prop two\r\n--somebound--\r\n" + } diff --git a/runner/testdata/upload-files/upload-files.yaml b/runner/testdata/upload-files/upload-files.yaml index 6d3473f..f469e18 100644 --- a/runner/testdata/upload-files/upload-files.yaml +++ b/runner/testdata/upload-files/upload-files.yaml @@ -32,4 +32,24 @@ "file_2_content": "file2_content" } +- name: "upload-files: with form fields" + method: POST + form: + fields: + fieldTest: "test_field value" + files: + file1: "testdata/upload-files/file1.txt" + file2: "testdata/upload-files/file2.log" + response: + 200: | + { + "status": "OK", + "file_1_name": "file1.txt", + "file_1_content": "file1_some_text", + "file_2_name": "file2.log", + "file_2_content": "file2_content", + "fields_test_name": "fieldTest", + "fields_test_content": "test_field value" + } + # TODO: test with incorrect Content-Type \ No newline at end of file diff --git a/variables/variables.go b/variables/variables.go index 21bae00..27a8f62 100644 --- a/variables/variables.go +++ b/variables/variables.go @@ -143,7 +143,13 @@ func (vs *Variables) performForm(form *models.Form) *models.Form { files[k] = vs.perform(v) } - return &models.Form{Files: files} + fields := make(map[string]string, len(form.Fields)) + + for k, v := range form.Fields { + fields[k] = vs.perform(v) + } + + return &models.Form{Files: files, Fields: fields} } func (vs *Variables) performHeaders(headers map[string]string) map[string]string { From a235ba537654b1fa3b54ab32ebdc87a6373af7bc Mon Sep 17 00:00:00 2001 From: Roman Terentev Date: Tue, 28 Jan 2025 10:35:55 +1000 Subject: [PATCH 2/3] add: form definition into gonkey.json schema --- gonkey.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/gonkey.json b/gonkey.json index 899f50c..96fd2da 100644 --- a/gonkey.json +++ b/gonkey.json @@ -163,6 +163,23 @@ "dbResponseArgs": {"$ref": "#/$defs/requestArgs"} } } + }, + "form":{ + "$ref": "#/$defs/form" + } + } + }, + "form": { + "type": "object", + "description": "multipart/form-data form content", + "properties":{ + "fields": { + "type": "object", + "description": "fields of multipart/form-data form" + }, + "files":{ + "type": "object", + "description": "files of multipart/form-data form" } } }, From cad1e21ec4e3d0d439c8582f2b667e88a17e11de Mon Sep 17 00:00:00 2001 From: Roman Terentev Date: Tue, 28 Jan 2025 17:08:57 +1000 Subject: [PATCH 3/3] fix: clear todo --- models/test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/models/test.go b/models/test.go index e0f56b7..d0ca830 100644 --- a/models/test.go +++ b/models/test.go @@ -64,10 +64,9 @@ type TestInterface interface { Clone() TestInterface } -// TODO: add support for form fields type Form struct { - Fields map[string]string `json:"fields" yaml:"fields"` Files map[string]string `json:"files" yaml:"files"` + Fields map[string]string `json:"fields" yaml:"fields"` } type Summary struct {