Skip to content

Commit

Permalink
add auth tests, server tests, user e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
adwski committed Jan 29, 2024
1 parent c16d0ee commit 3b4f3bc
Show file tree
Hide file tree
Showing 14 changed files with 605 additions and 34 deletions.
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ docker-dev:
docker compose up -d
docker ps

docker-infra:
cd docker/compose ;\
docker compose -f docker-compose.infra.yml up -d
docker ps

docker-infra-clean:
cd docker/compose ;\
docker compose -f docker-compose.infra.yml down -v
docker ps

docker-dev-build:
cd docker/compose ;\
docker compose up -d --build
Expand Down Expand Up @@ -36,7 +46,16 @@ test-nginx:
.PHONY: unittests
unittests:
go test ./... -v -count=1 -cover -coverpkg=./... -coverprofile=profile.cov ./...
go tool cover -func profile.cov

.PHONY: cover
cover:
go tool cover -html profile.cov -o coverage.html


.PHONY: test-all
test-all: docker-infra
go test -v -count=1 -cover -coverpkg=./... -coverprofile=profile.cov --tags e2e ./...
go tool cover -func profile.cov
$(MAKE) cover
$(MAKE) docker-infra-clean
2 changes: 1 addition & 1 deletion docker/compose/.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
COMPOSE_PROJECT_NAME=vidi-dev
COMPOSE_FILE=docker-compose.infra.yml:docker-compose.api.yml:docker-compose.media.yml
COMPOSE_FILE=docker-compose.infra.yml:docker-compose.api.yml:docker-compose.media.yml:docker-compose.nginx.yml
12 changes: 0 additions & 12 deletions docker/compose/docker-compose.infra.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,6 @@ x-logging: &logging
max-file: "5"

services:
nginx:
logging: *logging
restart: unless-stopped
image: nginx:1.25.3-bookworm
volumes:
- "./nginx.conf:/etc/nginx/nginx.conf:ro"
- "./player:/var/www/player:ro"
ports:
- "8080:80"
networks:
- vidi

minio:
logging: *logging
restart: unless-stopped
Expand Down
26 changes: 26 additions & 0 deletions docker/compose/docker-compose.nginx.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
version: "3.8"

x-logging: &logging
driver: "json-file"
options:
max-size: "100k"
max-file: "5"

services:
nginx:
logging: *logging
restart: unless-stopped
image: nginx:1.25.3-bookworm
volumes:
- "./nginx.conf:/etc/nginx/nginx.conf:ro"
- "./player:/var/www/player:ro"
ports:
- "8080:80"
networks:
- vidi

networks:
vidi:
name: vidi-net
driver: bridge
135 changes: 135 additions & 0 deletions e2e/e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
//go:build e2e
// +build e2e

package e2e

import (
"context"
"net/http"
"os"
"testing"
"time"

common "github.com/adwski/vidi/internal/api/model"
"github.com/adwski/vidi/internal/api/user/model"
"github.com/adwski/vidi/internal/app/user"
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/require"
)

func TestMain(m *testing.M) {
var (
done = make(chan struct{})
ctx, cancel = context.WithCancel(context.Background())
)

go func() {
user.NewApp().RunWithContextAndConfig(ctx, "userapi.yaml")
done <- struct{}{}
}()

time.Sleep(time.Second)

code := m.Run()
cancel()
<-done
defer func() {
os.Exit(code)
}()
}

func TestE2EUserRegistration(t *testing.T) {
//-------------------------------------------------------------------------------
// Login with not-existent user
//-------------------------------------------------------------------------------
userLoginFail(t, &model.UserRequest{
Username: "qweqweqwe",
Password: "asdasdasd",
})

//-------------------------------------------------------------------------------
// Register user
//-------------------------------------------------------------------------------
cookie := userRegister(t, &model.UserRequest{
Username: "qweqweqwe",
Password: "asdasdasd",
})
t.Logf("user is registered, token: %v", cookie.Value)

//-------------------------------------------------------------------------------
// Login with existent user
//-------------------------------------------------------------------------------
cookie2 := userLogin(t, &model.UserRequest{
Username: "qweqweqwe",
Password: "asdasdasd",
})
t.Logf("user is logged in, token: %v", cookie2.Value)

//-------------------------------------------------------------------------------
// Login with not-existent user
//-------------------------------------------------------------------------------
userLoginFail(t, &model.UserRequest{
Username: "qweqweqwe1",
Password: "asdasdasd2",
})
}

func userRegister(t *testing.T, user *model.UserRequest) *http.Cookie {
t.Helper()

resp, body := makeCommonRequest(t, "http://localhost:18081/api/user/register", user)
require.True(t, resp.IsSuccess())
require.Empty(t, body.Error)
require.Equal(t, "registration complete", body.Message)
return getCookieWithToken(t, resp.Cookies())
}

func userLogin(t *testing.T, user *model.UserRequest) *http.Cookie {
t.Helper()

resp, body := makeCommonRequest(t, "http://localhost:18081/api/user/login", user)
require.True(t, resp.IsSuccess())
require.Empty(t, body.Error)
require.Equal(t, "login ok", body.Message)
return getCookieWithToken(t, resp.Cookies())
}

func userLoginFail(t *testing.T, user *model.UserRequest) {
t.Helper()

resp, body := makeCommonRequest(t, "http://localhost:18081/api/user/login", user)
require.Truef(t, resp.IsError(), "user should not exist")
require.Empty(t, body.Message)
require.NotEmpty(t, body.Error)
}

func makeCommonRequest(t *testing.T, url string, reqBody interface{}) (*resty.Response, *common.Response) {
t.Helper()

var (
body common.Response
)
resp, err := resty.New().R().SetHeader("Accept", "application/json").
SetError(&body).
SetResult(&body).
SetBody(reqBody).Post(url)
require.NoError(t, err)
return resp, &body
}

func getCookieWithToken(t *testing.T, cookies []*http.Cookie) *http.Cookie {
t.Helper()

var (
userCookie *http.Cookie
)
for _, cookie := range cookies {
if cookie.Name == "vidiSessID" {
userCookie = cookie
break
}
}
require.NotNilf(t, userCookie, "cookie should exist")
require.NotEmpty(t, userCookie.Value, "cookie should not be empty")
return userCookie
}
8 changes: 8 additions & 0 deletions e2e/userapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
log:
level: debug
server:
address: ":18081"
api:
prefix: /api/user
database:
dsn: postgres://userapi:userapi@localhost:5400/userapi?sslmode=disable
1 change: 1 addition & 0 deletions internal/api/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func (s *Server) Run(ctx context.Context, wg *sync.WaitGroup, errc chan<- error)
defer wg.Done()
if s.srv.Handler == nil {
errc <- errors.New("server handler is not set")
return
}

errSrv := make(chan error)
Expand Down
137 changes: 137 additions & 0 deletions internal/api/server/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package server

import (
"context"
"net/http"
"sync"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)

func TestNewServerNilLogger(t *testing.T) {
_, errS := NewServer(&Config{
ListenAddress: ":8888",
ReadTimeout: time.Second,
ReadHeaderTimeout: time.Second,
WriteTimeout: time.Second,
IdleTimeout: time.Second,
})
assert.ErrorContains(t, errS, "nil logger")
}

func TestServer_RunNoHandler(t *testing.T) {
logger, err := zap.NewDevelopment()
require.NoError(t, err)

s, errS := NewServer(&Config{
Logger: logger,
ListenAddress: ":8881",
ReadTimeout: time.Second,
ReadHeaderTimeout: time.Second,
WriteTimeout: time.Second,
IdleTimeout: time.Second,
})
require.NoError(t, errS)

var (
ctx, cancel = context.WithCancel(context.Background())
wg = &sync.WaitGroup{}
errc = make(chan error)
)

wg.Add(1)
go s.Run(ctx, wg, errc)

select {
case err := <-errc:
require.ErrorContains(t, err, "server handler is not set")
case <-time.After(time.Second):
assert.Fail(t, "no error was returned")
}
cancel()
wg.Wait()
}

func TestServer_RunCancel(t *testing.T) {
logger, err := zap.NewDevelopment()
require.NoError(t, err)

s, errS := NewServer(&Config{
Logger: logger,
ListenAddress: ":8888",
ReadTimeout: time.Second,
ReadHeaderTimeout: time.Second,
WriteTimeout: time.Second,
IdleTimeout: time.Second,
})
require.NoError(t, errS)

s.SetHandler(stub{})

var (
ctx, cancel = context.WithCancel(context.Background())
wg = &sync.WaitGroup{}
errc = make(chan error)
done = make(chan struct{})
)

wg.Add(1)
go s.Run(ctx, wg, errc)

go func() {
cancel()
wg.Wait()
done <- struct{}{}
}()

select {
case <-done:
case <-time.After(time.Second):
assert.Fail(t, "did not shutdown in time")
}
}

func TestServer_RunError(t *testing.T) {
logger, err := zap.NewDevelopment()
require.NoError(t, err)

s, errS := NewServer(&Config{
Logger: logger,
ListenAddress: ":888888",
ReadTimeout: time.Second,
ReadHeaderTimeout: time.Second,
WriteTimeout: time.Second,
IdleTimeout: time.Second,
})
require.NoError(t, errS)

s.SetHandler(stub{})

var (
ctx, cancel = context.WithCancel(context.Background())
wg = &sync.WaitGroup{}
errc = make(chan error)
)
defer cancel()

wg.Add(1)
go s.Run(ctx, wg, errc)

select {
case err := <-errc:
assert.Error(t, err)
case <-time.After(time.Second):
assert.Fail(t, "did not return error")
}
wg.Wait()
}

type stub struct{}

func (s stub) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}
Loading

0 comments on commit 3b4f3bc

Please sign in to comment.