diff --git a/internal/server/http/imposter.go b/internal/server/http/imposter.go index e5ae638..5ca451b 100644 --- a/internal/server/http/imposter.go +++ b/internal/server/http/imposter.go @@ -151,8 +151,8 @@ func NewImposterFS(path string) (ImposterFs, error) { }, nil } -func (i ImposterFs) FindImposters(impostersCh chan []Imposter) error { - err := fs.WalkDir(i.fs, ".", func(path string, info fs.DirEntry, err error) error { +func (ifs ImposterFs) FindImposters(impostersCh chan []Imposter) error { + err := fs.WalkDir(ifs.fs, ".", func(path string, info fs.DirEntry, err error) error { if err != nil { return fmt.Errorf("%w: error finding imposters", err) } @@ -168,7 +168,7 @@ func (i ImposterFs) FindImposters(impostersCh chan []Imposter) error { default: return nil } - imposters, err := i.unmarshalImposters(cfg) + imposters, err := ifs.unmarshalImposters(cfg) if err != nil { return err } @@ -176,11 +176,12 @@ func (i ImposterFs) FindImposters(impostersCh chan []Imposter) error { } return nil }) + close(impostersCh) return err } -func (i ImposterFs) unmarshalImposters(imposterConfig ImposterConfig) ([]Imposter, error) { - imposterFile, _ := i.fs.Open(imposterConfig.FilePath) +func (ifs ImposterFs) unmarshalImposters(imposterConfig ImposterConfig) ([]Imposter, error) { + imposterFile, _ := ifs.fs.Open(imposterConfig.FilePath) defer imposterFile.Close() bytes, _ := io.ReadAll(imposterFile) @@ -202,7 +203,7 @@ func (i ImposterFs) unmarshalImposters(imposterConfig ImposterConfig) ([]Imposte } for i := range imposters { - imposters[i].BasePath = filepath.Dir(imposterConfig.FilePath) + imposters[i].BasePath = filepath.Dir(filepath.Join(ifs.path, imposterConfig.FilePath)) imposters[i].Path = imposterConfig.FilePath } diff --git a/internal/server/http/imposter_test.go b/internal/server/http/imposter_test.go index 6eb9e0c..3077a92 100644 --- a/internal/server/http/imposter_test.go +++ b/internal/server/http/imposter_test.go @@ -1,9 +1,9 @@ package http import ( + "encoding/json" "testing" - "encoding/json" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" @@ -22,6 +22,138 @@ func TestNewImposterFS(t *testing.T) { }) } +func TestImposterFS_FindImposters(t *testing.T) { + // Set up + const expected = 7 + ifs, err := NewImposterFS("test/testdata/imposters") + require.NoError(t, err) + + // We trigger the imposters search. + // We expect exactly [expected] imposters. + ch := make(chan []Imposter, expected) + err = ifs.FindImposters(ch) + require.NoError(t, err) + + // We collect all the imposters. + received := make([]Imposter, 0, expected) + for ii := range ch { + received = append(received, ii...) + } + require.Len(t, received, expected) + + // Imposter 1 + schemaFile := "schemas/create_gopher_request.json" + bodyFile := "responses/create_gopher_response.json" + assert.EqualValues(t, Imposter{ + BasePath: "test/testdata/imposters", + Path: "create_gopher.imp.json", + Request: Request{ + Method: "POST", + Endpoint: "/gophers", + SchemaFile: &schemaFile, + Params: &map[string]string{ + "gopherColor": "{v:[a-z]+}", + }, + Headers: &map[string]string{ + "Content-Type": "application/json", + }, + }, + Response: Responses{{ + Status: 200, + Headers: &map[string]string{ + "Content-Type": "application/json", + }, + BodyFile: &bodyFile, + }}, + }, received[0]) + + // Imposter 2 + assert.EqualValues(t, Imposter{ + BasePath: "test/testdata/imposters", + Path: "create_gopher.imp.json", + Request: Request{}, + }, received[1]) + + // Imposter 3 + assert.EqualValues(t, Imposter{ + BasePath: "test/testdata/imposters", + Path: "test_request.imp.json", + Request: Request{ + Method: "GET", + Endpoint: "/testRequest", + }, + Response: Responses{{ + Status: 200, + Body: "Handled", + }}, + }, received[2]) + + // Imposter 4 + assert.EqualValues(t, Imposter{ + BasePath: "test/testdata/imposters", + Path: "test_request.imp.yaml", + Request: Request{ + Method: "GET", + Endpoint: "/yamlTestRequest", + }, + Response: Responses{{ + Status: 200, + Body: "Yaml Handled", + }}, + }, received[3]) + + // Imposter 5 + assert.EqualValues(t, Imposter{ + BasePath: "test/testdata/imposters", + Path: "test_request.imp.yml", + Request: Request{ + Method: "GET", + Endpoint: "/ymlTestRequest", + }, + Response: Responses{{ + Status: 200, + Body: "Yml Handled", + Delay: ResponseDelay{ + delay: 1000000000, + offset: 4000000000, + }, + }}, + }, received[4]) + + // Imposter 6 + assert.EqualValues(t, Imposter{ + BasePath: "test/testdata/imposters", + Path: "test_request.imp.yml", + Request: Request{ + Method: "POST", + Endpoint: "/yamlGophers", + Headers: &map[string]string{ + "Content-Type": "application/json", + }, + }, + Response: Responses{{ + Status: 201, + Headers: &map[string]string{ + "Content-Type": "application/json", + "X-Source": "YAML", + }, + BodyFile: &bodyFile, + }}, + }, received[5]) + + // Imposter 7 + assert.EqualValues(t, Imposter{ + BasePath: "test/testdata/imposters", + Path: "test_request.imp.yml", + Request: Request{}, + }, received[6]) + + // Finally, once the search is done, + // the channel must be closed. + _, open := <-ch + require.False(t, open) +} + func TestResponses_MarshalJSON(t *testing.T) { tcs := map[string]struct { rr *Responses diff --git a/internal/server/http/route_matchers.go b/internal/server/http/route_matchers.go index 0f6cbf2..f243b37 100644 --- a/internal/server/http/route_matchers.go +++ b/internal/server/http/route_matchers.go @@ -55,7 +55,7 @@ func validateSchema(imposter Imposter, req *http.Request) error { return fmt.Errorf("unexpected empty body request") } - schemaFilePath, _ := filepath.Abs(schemaFile) + schemaFilePath, err := filepath.Abs(schemaFile) if err != nil { return fmt.Errorf("%w: impossible find the schema", err) } diff --git a/internal/server/http/server.go b/internal/server/http/server.go index d334e84..794cc78 100644 --- a/internal/server/http/server.go +++ b/internal/server/http/server.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" _ "embed" + "errors" "log" "net/http" @@ -86,23 +87,19 @@ func (s *Server) Build() error { } var impostersCh = make(chan []Imposter) - var done = make(chan struct{}) go func() { s.imposterFs.FindImposters(impostersCh) - done <- struct{}{} }() loop: for { - select { - case imposters := <-impostersCh: - s.addImposterHandler(imposters) - log.Printf("imposter %s loaded\n", imposters[0].Path) - case <-done: - close(impostersCh) - close(done) + imposters, ok := <-impostersCh + if !ok { break loop } + + s.addImposterHandler(imposters) + log.Printf("imposter %s loaded\n", imposters[0].Path) } if s.proxy.mode == killgrave.ProxyMissing { s.router.NotFoundHandler = s.proxy.Handler() @@ -120,7 +117,7 @@ func (s *Server) Run() { } log.Printf("The fake server is on tap now: %s%s\n", s.httpServer.Addr, tlsString) err := s.run(s.secure) - if err != http.ErrServerClosed { + if !errors.Is(err, http.ErrServerClosed) { log.Fatal(err) } }() @@ -143,7 +140,7 @@ func (s *Server) run(secure bool) error { return s.httpServer.ListenAndServeTLS("", "") } -// Shutdown shutdown the current http server +// Shutdown shutdowns the current http server func (s *Server) Shutdown() error { log.Println("stopping server...") if err := s.httpServer.Shutdown(context.TODO()); err != nil {