diff --git a/README.md b/README.md index 5c7f9f0..c8ebc2d 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,13 @@ Killgrave is a simulator for HTTP-based APIs, in simple words a **Mock Server**, * [Compile by yourself](#compile-by-yourself) * [Other](#other) - [Getting Started](#getting-started) - * [Using Killgrave by command line](#using-killgrave-by-command-line) + * [Using Killgrave from the command line](#using-killgrave-from-the-command-line) * [Using Killgrave by config file](#using-killgrave-by-config-file) * [Configure CORS](#configure-cors) - * [Prepare Killgrave for Proxy Mode](#prepare-killgrave-for-proxy-mode) - * [Create an Imposter](#create-an-imposter) + * [Preparing Killgrave for Proxy Mode](#preparing-killgrave-for-proxy-mode) + * [Creating an Imposter](#creating-an-imposter) * [Imposters structure](#imposters-structure) - * [Create an Imposter using regex](#create-an-imposter-using-regex) + * [Regex in the headers](#regex-in-the-headers) * [Create an imposter using JSON Schema](#create-an-imposter-using-json-schema) * [Create an imposter with delay](#create-an-imposter-with-delay) * [Create an imposter with dynamic responses](#create-an-imposter-with-dynamic-responses) @@ -159,6 +159,8 @@ $ killgrave -h proxy mode you can choose between (all, missing or none) (default "none") -proxy-url string proxy url, you need to choose a proxy-mode + - record-file-path string + the file path where the imposters will be recorded if you selected proxy-mode record -version show the _version of the application -watcher @@ -179,8 +181,8 @@ port: 3000 host: "localhost" proxy: url: https://example.com - mode: missing -watcher: true + record_file_path: "imposters/record.imp.json" + mode: record cors: methods: ["GET"] headers: ["Content-Type"] @@ -256,6 +258,7 @@ In the CORS section of the file you can find the following options: You can use Killgrave in proxy mode using the flags `proxy-mode` and `proxy-url` or their equivalent fields in the configuration file. The following three proxy modes are available: * `none`: Default. Killgrave will not behave as a proxy and the mock server will only use the configured imposters. * `missing`: With this mode the mock server will try to match the request with a configured imposter, but if no matching endpoint was found, the mock server will call to the real server, declared in the `proxy-url` configuration variable. +* `record`: This mode works as the same of the `missing` mode but with one exception, when `Killgrave` not match with some `URL` will record into a `record imposter`. You will need to declare `record-file-path` to indicate the path where you want to record the imposters (i.e. `imposters/record.imp.json`) * `all`: The mock server will always call to the real server, declared in the `proxy-url` configuration variable. The `proxy-url` must be the root path of the proxied server. For example, if we have an API running on `http://example.com/things`, the `proxy-url` will be `http://example.com`. diff --git a/internal/app/cmd/http/http.go b/internal/app/cmd/http/http.go index 98be850..1bf76c8 100644 --- a/internal/app/cmd/http/http.go +++ b/internal/app/cmd/http/http.go @@ -19,10 +19,10 @@ import ( ) const ( - _defaultHost = "localhost" - _defaultPort = 3000 - _defaultProxyMode = killgrave.ProxyNone - _defaultStrictSlash = true + _defaultHost = "localhost" + _defaultPort = 3000 + _defaultProxyMode = killgrave.ProxyNone + _defaultStrictSlash = true ) var ( @@ -30,7 +30,6 @@ var ( errGetDataFromHostFlag = errors.New("error trying to get data from host flag") errGetDataFromPortFlag = errors.New("error trying to get data from port flag") errGetDataFromSecureFlag = errors.New("error trying to get data from secure flag") - errMandatoryURL = errors.New("the field url is mandatory if you selected a proxy mode") ) // NewHTTPCmd returns cobra.Command to run http sub command, this command will be used to run the mock server @@ -59,8 +58,9 @@ func NewHTTPCmd() *cobra.Command { cmd.PersistentFlags().IntP("port", "P", _defaultPort, "Port to run the server") cmd.PersistentFlags().BoolP("watcher", "w", false, "File watcher will reload the server on each file change") cmd.PersistentFlags().BoolP("secure", "s", false, "Run mock server using TLS (https)") - cmd.Flags().StringP("proxy", "p", _defaultProxyMode.String(), "Proxy mode, the options are all, missing or none") + cmd.Flags().StringP("proxy", "p", _defaultProxyMode.String(), "Proxy mode, the options are all, missing, record or none") cmd.Flags().StringP("url", "u", "", "The url where the proxy will redirect to") + cmd.Flags().StringP("record-file-path", "o", "", "The record file path when the proxy is on record mode") return cmd } @@ -101,7 +101,8 @@ func runServer(cfg killgrave.Config) server.Server { Handler: handlers.CORS(server.PrepareAccessControl(cfg.CORS)...)(router), } - proxyServer, err := server.NewProxy(cfg.Proxy.Url, cfg.Proxy.Mode) + recorder := server.NewRecorder(cfg.Proxy.RecordFilePath) + proxyServer, err := server.NewProxy(cfg.Proxy.Url, cfg.ImpostersPath, cfg.Proxy.Mode, recorder) if err != nil { log.Fatal(err) } @@ -183,17 +184,8 @@ func configureProxyMode(cmd *cobra.Command, cfg *killgrave.Config) error { return err } - var url string - if mode != killgrave.ProxyNone.String() { - url, err = cmd.Flags().GetString("url") - if err != nil { - return err - } + url, _ := cmd.Flags().GetString("url") + recordFilePath, _ := cmd.Flags().GetString("record-file-path") - if url == "" { - return errMandatoryURL - } - } - cfg.ConfigureProxy(pMode, url) - return nil + return cfg.ConfigureProxy(pMode, url, recordFilePath) } diff --git a/internal/config.go b/internal/config.go index e2b1e70..0b72160 100644 --- a/internal/config.go +++ b/internal/config.go @@ -10,6 +10,11 @@ import ( "gopkg.in/yaml.v2" ) +var ( + errMandatoryURL = errors.New("the field url is mandatory if you selected a proxy mode") + errMandatoryRecordFilePath = errors.New("the field outputRecordFile is mandatory if you selected a proxy record") +) + // Config representation of config file yaml type Config struct { ImpostersPath string `yaml:"imposters_path"` @@ -32,8 +37,9 @@ type ConfigCORS struct { // ConfigProxy is a representation of section proxy of the yaml type ConfigProxy struct { - Url string `yaml:"url"` - Mode ProxyMode `yaml:"mode"` + Url string `yaml:"url"` + Mode ProxyMode `yaml:"mode"` + RecordFilePath string `yaml:"record_file_path"` } // ProxyMode is enumeration of proxy server modes @@ -44,6 +50,8 @@ const ( ProxyNone ProxyMode = iota // ProxyMissing handle only missing requests are proxied ProxyMissing + // ProxyRecord proxy the missing requests and record them into imposter + ProxyRecord // ProxyAll all requests are proxied ProxyAll ) @@ -55,10 +63,15 @@ var ( errInvalidPort = errors.New("invalid port") ) +func (p ProxyMode) Is(mode ProxyMode) bool { + return p == mode +} + func (p ProxyMode) String() string { m := map[ProxyMode]string{ ProxyNone: "none", ProxyMissing: "missing", + ProxyRecord: "record", ProxyAll: "all", } @@ -74,6 +87,7 @@ func StringToProxyMode(t string) (ProxyMode, error) { m := map[string]ProxyMode{ "none": ProxyNone, "missing": ProxyMissing, + "record": ProxyRecord, "all": ProxyAll, } @@ -102,9 +116,24 @@ func (p *ProxyMode) UnmarshalYAML(unmarshal func(interface{}) error) error { } // ConfigureProxy preparing the server with the proxy configuration that the user has indicated -func (cfg *Config) ConfigureProxy(proxyMode ProxyMode, proxyURL string) { +func (cfg *Config) ConfigureProxy(proxyMode ProxyMode, proxyURL, recordFilePath string) error { + if proxyMode.Is(ProxyNone) { + return nil + } + + if proxyURL == "" { + return errMandatoryURL + } + + if proxyMode.Is(ProxyRecord) && recordFilePath == "" { + return errMandatoryRecordFilePath + } + cfg.Proxy.Mode = proxyMode cfg.Proxy.Url = proxyURL + cfg.Proxy.RecordFilePath = recordFilePath + + return nil } // ConfigOpt function to encapsulate optional parameters @@ -151,6 +180,11 @@ func NewConfigFromFile(cfgPath string) (Config, error) { return Config{}, fmt.Errorf("%w: error while unmarshalling configFile file %s, using default configuration instead", err, cfgPath) } + recordFilePath := path.Join(path.Dir(cfgPath), cfg.Proxy.RecordFilePath) + if err := cfg.ConfigureProxy(cfg.Proxy.Mode, cfg.Proxy.Url, recordFilePath); err != nil { + return Config{}, err + } + cfg.ImpostersPath = path.Join(path.Dir(cfgPath), cfg.ImpostersPath) return cfg, nil diff --git a/internal/config_test.go b/internal/config_test.go index 38c0076..570a833 100644 --- a/internal/config_test.go +++ b/internal/config_test.go @@ -64,6 +64,7 @@ func TestProxyModeUnmarshal(t *testing.T) { }{ "valid mode all": {"all", ProxyAll, false}, "valid mode missing": {"missing", ProxyMissing, false}, + "valid mode record": {"record", ProxyRecord, false}, "valid mode none": {"none", ProxyNone, false}, "empty mode": {"", ProxyNone, true}, "invalid mode": {"nonsens23e", ProxyNone, true}, @@ -122,10 +123,15 @@ func TestProxyMode_String(t *testing.T) { "none", }, { - "ProxyNone must be return missing string", + "ProxyMissing must be return missing string", ProxyMissing, "missing", }, + { + "ProxyRecord must be return record string", + ProxyRecord, + "record", + }, { "ProxyNone must be return all string", ProxyAll, @@ -211,19 +217,43 @@ func TestNewConfig(t *testing.T) { } func TestConfig_ConfigureProxy(t *testing.T) { - expected := Config{ - ImpostersPath: "imposters", - Port: 80, - Host: "localhost", - Proxy: ConfigProxy{ - Url: "https://friendsofgo.tech", - Mode: ProxyAll, - }, + tests := []struct{ + name string + url string + mode ProxyMode + err error + }{ + {"valid proxy configuration", "https://friendsofgo.tech", ProxyAll, nil}, + {"none proxy configuration", "", ProxyNone, nil}, + {"invalid proxy configuration", "", ProxyAll, errMandatoryURL}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T){ + cfg, err := NewConfig("imposters", "localhost", 80, false) + assert.NoError(t, err) + + err = cfg.ConfigureProxy(tt.mode, tt.url, "") + assert.Equal(t, tt.err, err) + }) } +} - got, err := NewConfig("imposters", "localhost", 80, false) - assert.NoError(t, err) +func TestConfig_ConfigureProxyRecord(t *testing.T) { + tests := []struct { + name string + recordFilePath string + err error + }{ + {"valid configuration for Proxy Record", "some_file.imp.json", nil}, + {"invalid configuration for Proxy Record", "", errMandatoryRecordFilePath}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg, err := NewConfig("imposters", "localhost", 80, false) + assert.NoError(t, err) - got.ConfigureProxy(ProxyAll, "https://friendsofgo.tech") - assert.Equal(t, expected, got) + err = cfg.ConfigureProxy(ProxyRecord, "https://friendsofgo.tech", tt.recordFilePath) + assert.Equal(t, tt.err, err) + }) + } } diff --git a/internal/server/http/imposter.go b/internal/server/http/imposter.go index 0d4e831..6b0c427 100644 --- a/internal/server/http/imposter.go +++ b/internal/server/http/imposter.go @@ -58,18 +58,18 @@ func (i *Imposter) CalculateFilePath(filePath string) string { type Request struct { Method string `json:"method"` Endpoint string `json:"endpoint"` - SchemaFile *string `json:"schemaFile"` - Params *map[string]string `json:"params"` - Headers *map[string]string `json:"headers"` + SchemaFile *string `json:"schemaFile,omitempty"` + Params *map[string]string `json:"params,omitempty"` + Headers *map[string]string `json:"headers,omitempty"` } // Response represent the structure of real response type Response struct { Status int `json:"status"` Body string `json:"body"` - BodyFile *string `json:"bodyFile" yaml:"bodyFile"` - Headers *map[string]string `json:"headers"` - Delay ResponseDelay `json:"delay" yaml:"delay"` + BodyFile *string `json:"bodyFile,omitempty" yaml:"bodyFile,omitempty"` + Headers *map[string]string `json:"headers,omitempty"` + Delay *ResponseDelay `json:"delay,omitempty" yaml:"delay,omitempty"` } type ImposterFs struct { diff --git a/internal/server/http/proxy.go b/internal/server/http/proxy.go index f4c4492..16dc957 100644 --- a/internal/server/http/proxy.go +++ b/internal/server/http/proxy.go @@ -1,38 +1,74 @@ package http import ( + "bytes" + "io/ioutil" "net/http" "net/http/httputil" "net/url" + "strconv" killgrave "github.com/friendsofgo/killgrave/internal" ) // Proxy represent reverse proxy server. type Proxy struct { - server *httputil.ReverseProxy - mode killgrave.ProxyMode - url *url.URL + server *httputil.ReverseProxy + mode killgrave.ProxyMode + url *url.URL + impostersPath string + + recorder RecorderHTTP } // NewProxy creates new proxy server. -func NewProxy(rawurl string, mode killgrave.ProxyMode) (*Proxy, error) { +func NewProxy(rawurl, impostersPath string, mode killgrave.ProxyMode, recorder RecorderHTTP) (*Proxy, error) { u, err := url.Parse(rawurl) if err != nil { return nil, err } reverseProxy := httputil.NewSingleHostReverseProxy(u) - return &Proxy{server: reverseProxy, mode: mode, url: u}, nil + return &Proxy{ + server: reverseProxy, + mode: mode, + url: u, + recorder: recorder}, nil } // Handler returns handler that sends request to another server. -func (p *Proxy) Handler() http.HandlerFunc { +func (p Proxy) Handler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { r.URL.Host = p.url.Host r.URL.Scheme = p.url.Scheme r.Header.Set("X-Forwarded-Host", r.Header.Get("Host")) r.Host = p.url.Host + if p.mode == killgrave.ProxyRecord { + p.server.ModifyResponse = p.recordProxy + } p.server.ServeHTTP(w, r) } } + +func (p Proxy) recordProxy(resp *http.Response) error { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + defer resp.Body.Close() + + bodyStr := string(b) + b = bytes.Replace(b, []byte("server"), []byte("schmerver"), -1) + body := ioutil.NopCloser(bytes.NewReader(b)) + resp.Body = body + resp.ContentLength = int64(len(b)) + resp.Header.Set("Content-Length", strconv.Itoa(len(b))) + + responseRecorder := ResponseRecorder{ + Headers: resp.Header, + Status: resp.StatusCode, + Body: bodyStr, + } + + return p.recorder.Record(resp.Request, responseRecorder) +} diff --git a/internal/server/http/proxy_test.go b/internal/server/http/proxy_test.go index 11ee159..f63e1b1 100644 --- a/internal/server/http/proxy_test.go +++ b/internal/server/http/proxy_test.go @@ -22,7 +22,7 @@ func TestNewProxy(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { - proxy, err := NewProxy(tc.rawURL, tc.mode) + proxy, err := NewProxy(tc.rawURL, "", tc.mode, RecorderNoop{}) if tc.wantErr { assert.Error(t, err) return @@ -41,7 +41,7 @@ func TestProxyHandler(t *testing.T) { })) defer backend.Close() - proxy, err := NewProxy(backend.URL, killgrave.ProxyAll) + proxy, err := NewProxy(backend.URL, "", killgrave.ProxyAll, RecorderNoop{}) assert.NoError(t, err) frontend := httptest.NewServer(proxy.Handler()) diff --git a/internal/server/http/recorder.go b/internal/server/http/recorder.go new file mode 100644 index 0000000..3e37dde --- /dev/null +++ b/internal/server/http/recorder.go @@ -0,0 +1,190 @@ +package http + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "strings" + + "gopkg.in/yaml.v2" +) + +var ( + errCreatingRecordDir = errors.New("impossible create record directory") + errOpenRecordFile = errors.New("impossible open record file") + errReadingOutputRecordFile = errors.New("error trying to parse the record file") + errMarshallingRecordFile = errors.New("error during the marshalling process of the record file") + errWritingRecordFile = errors.New("error trying to write on the record file") + errUnrecognizedExtension = errors.New("file extension not recognized") +) + +// RecorderHTTP service to Record the return output of the request +type RecorderHTTP interface { + // Record save the return output from the missing request on the imposters + Record(req *http.Request, resp ResponseRecorder) error +} + +// Recorder implementation of the RecorderHTTP +type Recorder struct { + outputPathFile string +} + +// ResponseRecorder response data transfer object +type ResponseRecorder struct { + Status int + Headers http.Header + Body string +} + +// NewRecorder initialise the Recorder +func NewRecorder(outputPathFile string) Recorder { + return Recorder{ + outputPathFile: outputPathFile, + } +} + +func (r Recorder) Record(req *http.Request, resp ResponseRecorder) error { + imposterRequest := r.prepareImposterRequest(req) + imposterResponse := r.prepareImposterResponse(resp) + + imposter := Imposter{ + Request: imposterRequest, + Response: imposterResponse, + } + + f, err := r.prepareOutputFile() + if err != nil { + return err + } + defer f.Close() + + var b []byte + switch { + case strings.HasSuffix(r.outputPathFile, jsonImposterExtension): + b, err = r.recordOnJSON(f, imposter) + if err != nil { + return err + } + case strings.HasSuffix(r.outputPathFile, yamlImposterExtension) || strings.HasSuffix(r.outputPathFile, ymlImposterExtension): + b, err = r.recordOnYAML(f, imposter) + if err != nil { + return err + } + } + + _ = f.Truncate(0) + _, _ = f.Seek(0, 0) + + if _, err := f.Write(b); err != nil { + return fmt.Errorf("%v: %w", err, errWritingRecordFile) + } + + return nil +} + +// RecorderNoop an implementation of the RecorderHTTP without any functionality +type RecorderNoop struct{} + +func (r RecorderNoop) Record(req *http.Request, resp ResponseRecorder) error { + return nil +} + +func (r Recorder) prepareOutputFile() (*os.File, error) { + if !strings.HasSuffix(r.outputPathFile, jsonImposterExtension) && + !strings.HasSuffix(r.outputPathFile, yamlImposterExtension) && !strings.HasSuffix(r.outputPathFile, ymlImposterExtension) { + return nil, errUnrecognizedExtension + } + + dir := filepath.Dir(r.outputPathFile) + if _, err := os.Stat(dir); os.IsNotExist(err) { + err := os.Mkdir(dir, 0755) + if err != nil { + return nil, fmt.Errorf("%v: %w", err, errCreatingRecordDir) + } + } + + f, err := os.OpenFile(r.outputPathFile, os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + return nil, fmt.Errorf("%v: %w", err, errOpenRecordFile) + } + + return f, nil +} + +func (r Recorder) prepareImposterRequest(req *http.Request) Request { + headers := make(map[string]string, len(req.Header)) + for k, v := range req.Header { + for _, val := range v { + // TODO: configure which headers don't you want to store or more commons like Postman?? + headers[k] = val + } + } + + params := make(map[string]string, len(req.URL.Query())) + query := req.URL.Query() + for k, v := range query { + params[k] = v[0] + } + + imposterRequest := Request{ + Method: req.Method, + Endpoint: req.URL.Path, + Headers: &headers, + Params: ¶ms, + } + + return imposterRequest +} + +func (r Recorder) prepareImposterResponse(resp ResponseRecorder) Response { + headers := make(map[string]string, len(resp.Headers)) + for k, v := range resp.Headers { + for _, val := range v { + headers[k] = val + } + } + + response := Response{ + Status: resp.Status, + Headers: &headers, + Body: resp.Body, + } + + return response +} + +func (r Recorder) recordOnJSON(file *os.File, imposter Imposter) ([]byte, error) { + var imposters []Imposter + bytes, _ := ioutil.ReadAll(file) + if err := json.Unmarshal(bytes, &imposters); err != nil && len(bytes) > 0 { + return nil, fmt.Errorf("%v: %w", err, errReadingOutputRecordFile) + } + + imposters = append(imposters, imposter) + b, err := json.Marshal(imposters) + if err != nil { + return nil, fmt.Errorf("%v: %w", err, errMarshallingRecordFile) + } + + return b, nil +} + +func (r Recorder) recordOnYAML(file *os.File, imposter Imposter) ([]byte, error) { + var imposters []Imposter + bytes, _ := ioutil.ReadAll(file) + if err := yaml.Unmarshal(bytes, &imposters); err != nil && len(bytes) > 0 { + return nil, fmt.Errorf("%v: %w", err, errReadingOutputRecordFile) + } + + imposters = append(imposters, imposter) + b, err := yaml.Marshal(imposters) + if err != nil { + return nil, fmt.Errorf("%v: %w", err, errMarshallingRecordFile) + } + + return b, nil +} diff --git a/internal/server/http/recorder_test.go b/internal/server/http/recorder_test.go new file mode 100644 index 0000000..8eec7f4 --- /dev/null +++ b/internal/server/http/recorder_test.go @@ -0,0 +1,72 @@ +package http + +import ( + "net/http" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRecorder_Record(t *testing.T) { + tests := []struct { + name string + recordPath string + }{ + { + name: "JSON record", + recordPath: "test/testdata/recorder/output.imp.json", + }, + { + name: "YAML record", + recordPath: "test/testdata/recorder/output.imp.yaml", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T){ + recorder := NewRecorder(tt.recordPath) + req, err := http.NewRequest(http.MethodGet, "http://localhost/items?limit=100&offset=200", nil) + assert.NoError(t, err) + + req.Header.Set("ItemUser", "Conan") + req.Header.Set("Item-Key", "25") + + bodyStr := `{"id": 25, name": "Umbrella"}` + + resp := ResponseRecorder{ + Status: http.StatusOK, + Body: bodyStr, + } + + err = recorder.Record(req, resp) + assert.NoError(t, err) + + f, err := os.Stat(tt.recordPath) + assert.NoError(t, err) + + assert.Greater(t, f.Size(), int64(0), "empty file") + + dir := filepath.Dir(tt.recordPath) + _ = os.Remove(tt.recordPath) + _ = os.RemoveAll(dir) + }) + } +} + +func TestRecorder_RecordWithInvalidExtension(t *testing.T) { + recorder := NewRecorder("test/testdata/recorder/output.imp.dist") + + req, err := http.NewRequest(http.MethodGet, "http://localhost/items?limit=100&offset=200", nil) + assert.NoError(t, err) + + resp := ResponseRecorder{ + Status: http.StatusOK, + Body: "", + } + + err = recorder.Record(req, resp) + assert.Error(t, err) + assert.Equal(t, err, errUnrecognizedExtension) +} \ No newline at end of file diff --git a/internal/server/http/response_delay.go b/internal/server/http/response_delay.go index b837291..7f6a010 100644 --- a/internal/server/http/response_delay.go +++ b/internal/server/http/response_delay.go @@ -16,6 +16,10 @@ type ResponseDelay struct { // Delay return random time.Duration with respect to specified time range. func (d *ResponseDelay) Delay() time.Duration { + if d == nil { + return 0 + } + offset := d.offset if offset > 0 { offset = rand.Int63n(d.offset) @@ -46,7 +50,6 @@ func (d *ResponseDelay) UnmarshalJSON(data []byte) error { return d.parseDelay(input) } - func (d *ResponseDelay) parseDelay(input string) error { const delimiter = ":" diff --git a/internal/server/http/server.go b/internal/server/http/server.go index f34d438..4c2b5f6 100644 --- a/internal/server/http/server.go +++ b/internal/server/http/server.go @@ -83,7 +83,7 @@ func PrepareAccessControl(config killgrave.ConfigCORS) (h []handlers.CORSOption) // Build read all the files on the impostersPath and add different // handlers for each imposter func (s *Server) Build() error { - if s.proxy.mode == killgrave.ProxyAll { + if s.proxy.mode.Is(killgrave.ProxyAll) { // not necessary load the imposters if you will use the tool as a proxy s.router.PathPrefix("/").HandlerFunc(s.proxy.Handler()) return nil @@ -111,7 +111,7 @@ loop: break loop } } - if s.proxy.mode == killgrave.ProxyMissing { + if s.proxy.mode.Is(killgrave.ProxyMissing) || s.proxy.mode.Is(killgrave.ProxyRecord){ s.router.NotFoundHandler = s.proxy.Handler() } return nil diff --git a/internal/server/http/server_test.go b/internal/server/http/server_test.go index e083af5..64940af 100644 --- a/internal/server/http/server_test.go +++ b/internal/server/http/server_test.go @@ -55,9 +55,10 @@ func TestBuildProxyMode(t *testing.T) { })) defer proxyServer.Close() makeServer := func(mode killgrave.ProxyMode) (*Server, func()) { + impostersPath := "test/testdata/imposters" router := mux.NewRouter() httpServer := &http.Server{Handler: router} - proxyServer, err := NewProxy(proxyServer.URL, mode) + proxyServer, err := NewProxy(proxyServer.URL, impostersPath, mode, RecorderNoop{}) assert.Nil(t, err) imposterFs := NewImposterFS(afero.NewOsFs()) server := NewServer("test/testdata/imposters", router, httpServer, proxyServer, false, imposterFs) @@ -128,15 +129,16 @@ func TestBuildSecureMode(t *testing.T) { defer proxyServer.Close() makeServer := func(mode killgrave.ProxyMode) (*Server, func()) { + impostersPath := "test/testdata/imposters_secure" router := mux.NewRouter() cert, _ := tls.X509KeyPair(serverCert, serverKey) httpServer := &http.Server{Handler: router, Addr: ":4430", TLSConfig: &tls.Config{ Certificates: []tls.Certificate{cert}, }} - proxyServer, err := NewProxy(proxyServer.URL, mode) + proxyServer, err := NewProxy(proxyServer.URL, impostersPath, mode, RecorderNoop{}) assert.Nil(t, err) imposterFs := NewImposterFS(afero.NewOsFs()) - server := NewServer("test/testdata/imposters_secure", router, httpServer, proxyServer, true, imposterFs) + server := NewServer(impostersPath, router, httpServer, proxyServer, true, imposterFs) return &server, func() { httpServer.Close() } @@ -169,7 +171,7 @@ func TestBuildSecureMode(t *testing.T) { defer cleanUp() err := s.Build() - assert.Nil(t, err) + assert.NoError(t, err) s.Run() client := tc.server.Client()