Написал недостающие юнит тесты.

master
Andrey Ivanov 2020-11-03 19:25:35 +03:00 committed by Andrey Ivanov
parent e87099550a
commit 2bf1cb7397
13 changed files with 189 additions and 30 deletions

View File

@ -15,4 +15,5 @@ linters:
- testpackage
- wsl
- nlreturn
- whitespace
- whitespace
- bodyclose

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

1
go.mod
View File

@ -5,6 +5,7 @@ go 1.14
require (
github.com/BurntSushi/toml v0.3.1
github.com/amitrai48/logger v0.0.0-20190214092904-448001c055ec
github.com/anthonynsimon/bild v0.13.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/pkg/errors v0.9.1 // indirect
github.com/stretchr/testify v1.6.1

31
go.sum
View File

@ -2,25 +2,47 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/amitrai48/logger v0.0.0-20190214092904-448001c055ec h1:tDOPo9NAXCjvoK35HgZyzQSNLmb3chZqN2tnO273Bro=
github.com/amitrai48/logger v0.0.0-20190214092904-448001c055ec/go.mod h1:RZEHP3cxXvQlMuMjkpdh6qXA4b0CpjxnUBNxOpR0r30=
github.com/anthonynsimon/bild v0.13.0 h1:mN3tMaNds1wBWi1BrJq0ipDBhpkooYfu7ZFSMhXt1C8=
github.com/anthonynsimon/bild v0.13.0/go.mod h1:tpzzp0aYkAsMi1zmfhimaDyX1xjn2OUc1AJZK/TF0AE=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/golangci/golangci-lint v1.32.0 h1:3wL5pvhTpRvlvtosoZecS+hu40IAiJl1qlZQuXIFBAg=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
@ -29,12 +51,19 @@ go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1YthsFqr/5mxHduZW2A=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=

View File

@ -3,7 +3,10 @@ Address = "localhost"
Port = "80"
[Cache]
Capasity = 20
Capacity = 20
[Query]
Timeout = 15
[Log]
File = "previewer.log"

View File

@ -14,6 +14,7 @@ type App struct {
*http.Server
Log logger.Interface
Cache cache.Cache
Conf config.Config
}
func New(conf config.Config) *App {
@ -21,13 +22,13 @@ func New(conf config.Config) *App {
if err != nil {
oslog.Fatal("не удалось прикрутить логгер: ", err.Error())
}
c := cache.NewCache(conf.Cache.Capasity)
return &App{Server: &http.Server{Addr: net.JoinHostPort(conf.Server.Address, conf.Server.Port)}, Log: loger, Cache: c}
c := cache.NewCache(conf.Cache.Capacity)
return &App{Server: &http.Server{Addr: net.JoinHostPort(conf.Server.Address, conf.Server.Port)}, Log: loger, Cache: c, Conf: conf}
}
func (s *App) Start() error {
s.Log.Infof("Server starting")
s.Handler = loggingMiddleware(handler(s.Cache), s.Log)
s.Handler = loggingMiddleware(handler(s.Cache, s.Conf), s.Log)
_ = s.ListenAndServe()
s.Log.Infof("Server stoped")
return nil

View File

@ -6,11 +6,12 @@ import (
"time"
"github.com/tiburon-777/OTUS_Project/previewer/cache"
"github.com/tiburon-777/OTUS_Project/previewer/config"
"github.com/tiburon-777/OTUS_Project/previewer/converter"
"github.com/tiburon-777/OTUS_Project/previewer/logger"
)
func handler(c cache.Cache) http.Handler {
func handler(c cache.Cache, conf config.Config) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
q, err := buildQuery(r.URL)
if err != nil {
@ -24,8 +25,8 @@ func handler(c cache.Cache) http.Handler {
writeResponse(w, nil, pic)
return
}
pic, _, err = q.fromOrigin()
if err != nil {
pic, res, err := q.fromOrigin(time.Duration(conf.Query.Timeout) * time.Second)
if err != nil || res.StatusCode != 200 {
http.Error(w, "Pic not found in origin", http.StatusNotFound)
return
}

View File

@ -8,6 +8,7 @@ import (
"net/url"
"strconv"
"strings"
"time"
)
type Query struct {
@ -41,9 +42,11 @@ func (q Query) id() string {
return strconv.Itoa(q.Width) + "/" + strconv.Itoa(q.Height) + "/" + q.URL.Path
}
func (q Query) fromOrigin() ([]byte, http.Header, error) {
func (q Query) fromOrigin(timeout time.Duration) ([]byte, *http.Response, error) {
client := &http.Client{}
req, err := http.NewRequestWithContext(context.Background(), "GET", "https://"+q.URL.Host+"/"+q.URL.Path, nil)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", "http://"+q.URL.Host+"/"+q.URL.Path, nil)
if err != nil {
return nil, nil, err
}
@ -59,5 +62,5 @@ func (q Query) fromOrigin() ([]byte, http.Header, error) {
if err = res.Body.Close(); err != nil {
return nil, nil, err
}
return body, res.Header, nil
return body, res, nil
}

View File

@ -13,7 +13,10 @@ type Config struct {
Port string
}
Cache struct {
Capasity int
Capacity int
}
Query struct {
Timeout int
}
Log struct {
File string
@ -23,16 +26,29 @@ type Config struct {
}
func NewConfig(configFile string) (Config, error) {
var config Config
f, err := os.Open(configFile)
if err != nil {
return Config{}, err
return config, err
}
defer f.Close()
s, err := ioutil.ReadAll(f)
if err != nil {
return Config{}, err
return config, err
}
var config Config
_, err = toml.Decode(string(s), &config)
return config, err
}
func (c *Config) SetDefault() {
c.Server = struct {
Address string
Port string
}{Address: "localhost", Port: "80"}
c.Cache = struct{ Capacity int }{Capacity: 20}
c.Log = struct {
File string
Level string
MuteStdout bool
}{File: "previewer.log", Level: "INFO", MuteStdout: false}
}

View File

@ -11,7 +11,7 @@ import (
"net/http"
"sync"
"github.com/nfnt/resize"
"github.com/anthonynsimon/bild/transform"
)
type Image struct {
@ -80,8 +80,11 @@ func NewImage(img image.Image) Image {
func (img *Image) convert(width int, height int) error {
widthOrig := img.Bounds().Max.X
heightOrig := img.Bounds().Max.Y
sfOriginal := sizeFactor(widthOrig, heightOrig)
sfNew := sizeFactor(width, height)
if width <= 0 || height <= 0 {
return errors.New("can't reduce toOrBelow zero")
}
sfOriginal, _ := sizeFactor(widthOrig, heightOrig)
sfNew, _ := sizeFactor(width, height)
switch {
case sfOriginal > sfNew:
@ -114,7 +117,7 @@ func (img *Image) resize(width, height int) error {
if width <= 0 || height <= 0 {
return errors.New("can't resize to zero or negative value")
}
tmpImg := resize.Resize(uint(width), uint(height), img, resize.Bicubic)
tmpImg := transform.Resize(img, width, height, transform.Linear)
img.Image = tmpImg
return nil
}
@ -135,6 +138,9 @@ func (img *Image) crop(p1 image.Point, p2 image.Point) error {
return nil
}
func sizeFactor(width int, height int) float64 {
return float64(width) / float64(height)
func sizeFactor(width int, height int) (float64, error) {
if height == 0 {
return 0, errors.New("can't divide by zero")
}
return float64(width) / float64(height), nil
}

View File

@ -92,7 +92,6 @@ func TestCrop(t *testing.T) {
}
}
/*
func TestConvert(t *testing.T) {
originalAspect := 800.0 / 600.0
releasedValue := 3000
@ -105,19 +104,19 @@ func TestConvert(t *testing.T) {
msg string
}{
{
width: 400, height: 600, expectedX: 400, expectedY: int(400 / originalAspect), err: false, msg: "Reducing the image size by horizontal",
width: 400, height: 600, expectedX: 400, expectedY: 600, err: false, msg: "Reducing the image size by horizontal",
},
{
width: 800, height: 400, expectedX: int(400 * originalAspect), expectedY: 400, err: false, msg: "Reducing the image size by vertical",
width: 800, height: 400, expectedX: 800, expectedY: 400, err: false, msg: "Reducing the image size by vertical",
},
{
width: 400, height: int(400 / originalAspect), expectedX: 400, expectedY: int(400 / originalAspect), err: false, msg: "Resize to original aspect ratio",
},
{
width: 1000, height: releasedValue, expectedX: 1000, expectedY: int(1000 / originalAspect), err: false, msg: "Increasing the image size by horizontal",
width: 1000, height: releasedValue, expectedX: 1000, expectedY: releasedValue, err: false, msg: "Increasing the image size by horizontal",
},
{
width: releasedValue, height: 1000, expectedX: int(1000 * originalAspect), expectedY: 1000, err: false, msg: "Increasing the image size by vertical",
width: releasedValue, height: 1000, expectedX: releasedValue, expectedY: 1000, err: false, msg: "Increasing the image size by vertical",
},
{
width: 0, height: 0, expectedX: 800, expectedY: 600, err: true, msg: "Resize to zero",
@ -141,7 +140,6 @@ func TestConvert(t *testing.T) {
})
}
}
*/
func createImage(w, h int) image.Image {
res := image.NewRGBA(image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: w, Y: h}})

View File

@ -2,7 +2,6 @@ package main
import (
"flag"
oslog "log"
"os"
"os/signal"
@ -16,7 +15,7 @@ func main() {
flag.Parse()
conf, err := config.NewConfig(*ConfigFile)
if err != nil {
oslog.Fatal("не удалось открыть файл конфигурации:", err.Error())
conf.SetDefault()
}
app := application.New(conf)

101
previewer/main_test.go Normal file
View File

@ -0,0 +1,101 @@
package main
import (
"context"
"github.com/stretchr/testify/require"
"io/ioutil"
"log"
"net/http"
"sync"
"testing"
"time"
)
func TestIntegrationPositive(t *testing.T) {
wg := sync.WaitGroup{}
server := &http.Server{Addr: ":3000", Handler: http.FileServer(http.Dir("../examples"))}
go server.ListenAndServe()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
go func(ctx context.Context) {
main()
}(ctx)
// Реализовать тесты логики приложения (ресайзы по разным требованиям):
wg.Add(2)
t.Run("remote server return jpeg", func(t *testing.T) {
defer wg.Done()
body, _, err := request("http://localhost/fill/1024/504/localhost:3000/gopher_original_1024x504.jpg", 15*time.Second)
require.NoError(t, err)
require.NotNil(t, body)
//require.Equal(t, 200, resp.StatusCode)
})
t.Run("found pic in cache", func(t *testing.T) {
defer wg.Done()
})
// Закрыть сервер и приложение
wg.Wait()
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatal("can't stop publishing test static")
}
}
func TestIntegrationNegative(t *testing.T) {
// Развернуть веб сервис со статическими картинками
wg := sync.WaitGroup{}
server := &http.Server{Addr: ":3000", Handler: http.FileServer(http.Dir("../examples"))}
go server.ListenAndServe()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
// Запустить наше приложение
go func(ctx context.Context) {
main()
}(ctx)
// Реализовать тесты отказа:
wg.Add(5)
t.Run("remote server not exist", func(t *testing.T) {
defer wg.Done()
})
t.Run("remote server exists, but pic not found (404 Not Found)", func(t *testing.T) {
defer wg.Done()
})
t.Run("remote server exists, but pic is not pic", func(t *testing.T) {
defer wg.Done()
})
t.Run("remote server return ISE (500)", func(t *testing.T) {
defer wg.Done()
})
t.Run("remote server return plain html or test", func(t *testing.T) {
defer wg.Done()
})
// Закрыть сервер и приложение
wg.Wait()
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatal("can't stop publishing test static")
}
}
func request(addr string, timeout time.Duration) ([]byte, *http.Response, error) {
client := &http.Client{}
ctx, _ := context.WithTimeout(context.Background(), timeout)
req, err := http.NewRequestWithContext(ctx, "GET", addr, nil)
if err != nil {
return nil, nil, err
}
req.Close = true
res, err := client.Do(req)
if err != nil {
return nil, nil, err
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, nil, err
}
if err = res.Body.Close(); err != nil {
return nil, nil, err
}
return body, res, nil
}