Skip to content

Commit

Permalink
Add unit test to business logic (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
hudymi authored Sep 6, 2019
1 parent f2041b3 commit 0bc7366
Show file tree
Hide file tree
Showing 13 changed files with 409 additions and 38 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Build
run: go build -v .
- name: Test
run: go test ./...
run: go test ./... -cover
build-docker:
name: Build Docker image
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pre.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Build
run: go build -v .
- name: Test
run: go test ./...
run: go test ./... -cover
build-docker:
name: Build Docker image
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ address: :8080
# The list of endpoints
endpoints:
- name: hello # Name of the endpoint
# The list of valid methods
# The list of valid methods, if not set validation is skipped
methods:
- GET
- POST
# Default HTTP response code, if not provided 200
defaultResponseCode: 200
# Default response content
defaultResponseContent: "# Sample service"
defaultResponseContent: "Sample service"
# Default response content-type, if not provided "text/plain; charset=utf-8"
defaultResponseContentType: text/plain; charset=utf-8

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/michal-hudy/mockice
go 1.12

require (
github.com/onsi/gomega v1.7.0
github.com/pkg/errors v0.8.1
github.com/sirupsen/logrus v1.4.2
gopkg.in/yaml.v2 v2.2.2
Expand Down
22 changes: 22 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -11,9 +21,21 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
29 changes: 19 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"flag"
"io/ioutil"
"os"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand All @@ -11,6 +12,7 @@ import (
"github.com/michal-hudy/mockice/pkg/endpoint"
"github.com/michal-hudy/mockice/pkg/log"
"github.com/michal-hudy/mockice/pkg/service"
"github.com/michal-hudy/mockice/pkg/signal"
)

type options struct {
Expand All @@ -19,8 +21,8 @@ type options struct {
}

type config struct {
Address string `yaml:"address"`
EndpointsConfiguration []endpoint.EndpointConfig `yaml:"endpoints"`
Address string `yaml:"address"`
EndpointsConfiguration []endpoint.Config `yaml:"endpoints"`
}

func gatherOptions() options {
Expand All @@ -37,43 +39,50 @@ func main() {
options := gatherOptions()
log.Setup(options.verbose)

cfg, err := loadConfig(options)
ctx := signal.Context()

cfg, err := loadConfig(options.config)
if err != nil {
logrus.Fatal(err)
}

svc := service.New(cfg.Address)
for _, config := range cfg.EndpointsConfiguration {
endpoint := endpoint.New(config)

logrus.Infof("Registering /%s endpoint", endpoint.Name())
svc.Register(endpoint)
err := svc.Register(endpoint)
if err != nil {
logrus.Fatal(err)
}
}

logrus.Infof("Service listen at %s", cfg.Address)
err = svc.Start()
err = svc.Start(ctx)
if err != nil {
logrus.Error(err)
os.Exit(1)
}
}

func loadConfig(ops options) (config, error) {
func loadConfig(path string) (config, error) {
cfg := config{
Address: ":8080",
}

if ops.config == "" {
if path == "" {
cfg.EndpointsConfiguration = endpoint.DefaultConfig()
return cfg, nil
}

content, err := ioutil.ReadFile(ops.config)
content, err := ioutil.ReadFile(path)
if err != nil {
return config{}, errors.Wrapf(err, "while reading configuration from %s", ops.config)
return config{}, errors.Wrapf(err, "while reading configuration from %s", path)
}

err = yaml.Unmarshal(content, &cfg)
if err != nil {
return config{}, errors.Wrapf(err, "while loading configuration from %s", ops.config)
return config{}, errors.Wrapf(err, "while loading configuration from %s", path)
}

return cfg, nil
Expand Down
40 changes: 22 additions & 18 deletions pkg/endpoint/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"github.com/sirupsen/logrus"
)

// EndpointConfig stores endpoint configuration
type EndpointConfig struct {
// Config stores endpoint configuration
type Config struct {
Name string
Methods []string
DefaultResponseCode *int
Expand All @@ -17,33 +17,37 @@ type EndpointConfig struct {
}

// DefaultConfig generates default configuration with /hello endpoint
func DefaultConfig() []EndpointConfig {
return []EndpointConfig{
func DefaultConfig() []Config {
return []Config{
{
Name: "hello",
DefaultResponseContent: "Hello World! Mockice here!",
},
}
}

type endpoint struct {
config EndpointConfig
// Endpoint is a structure responsible for handling HTTP requests
type Endpoint struct {
config Config
log *logrus.Entry
}

// New creates a new endpoint from config
func New(config EndpointConfig) *endpoint {
return &endpoint{
func New(config Config) *Endpoint {
return &Endpoint{
config: config,
log: logrus.WithField("Endpoint", config.Name),
}
}

// Handle is responsible for handling incoming requests
func (e *endpoint) Handle(writer http.ResponseWriter, request *http.Request) {
func (e *Endpoint) Handle(writer http.ResponseWriter, request *http.Request) {
defer request.Body.Close()
e.log.Infof("Handle %s request from %s", request.Method, request.RemoteAddr)

if len(e.config.Methods) > 0 && !e.contains(e.config.Methods, request.Method) {
http.Error(writer, "Invalid request method", http.StatusMethodNotAllowed)
logrus.Errorf("Invalid request method %s", request.Method)
e.log.Errorf("Invalid request method %s", request.Method)
return
}

Expand All @@ -53,25 +57,25 @@ func (e *endpoint) Handle(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
}

if e.config.DefaultResponseCode != nil {
writer.WriteHeader(*e.config.DefaultResponseCode)
}

_, err := writer.Write([]byte(e.config.DefaultResponseContent))
if err != nil {
err = errors.Wrapf(err, "while writing response from /%s endpoint", e.config.Name)
err = errors.Wrapf(err, "while writing response")
http.Error(writer, err.Error(), http.StatusInternalServerError)
logrus.Error(err)
e.log.Error(err)
return
}

if e.config.DefaultResponseCode != nil {
writer.WriteHeader(*e.config.DefaultResponseCode)
}
}

// Name returns name of the endpoint
func (e *endpoint) Name() string {
func (e *Endpoint) Name() string {
return e.config.Name
}

func (e *endpoint) contains(values []string, value string) bool {
func (e *Endpoint) contains(values []string, value string) bool {
for _, item := range values {
if item == value {
return true
Expand Down
Loading

0 comments on commit 0bc7366

Please sign in to comment.