Add initial tests for reqlog package

This commit is contained in:
David Stotijn
2022-01-01 16:11:49 +01:00
parent 4e2eaea499
commit b3225bfb99
7 changed files with 959 additions and 26 deletions

View File

@ -20,7 +20,7 @@ import (
type Resolver struct { type Resolver struct {
RequestLogService *reqlog.Service RequestLogService *reqlog.Service
ProjectService *proj.Service ProjectService proj.Service
ScopeService *scope.Scope ScopeService *scope.Scope
} }

View File

@ -15,7 +15,17 @@ type (
) )
// Service is used for managing projects. // Service is used for managing projects.
type Service struct { type Service interface {
Open(ctx context.Context, name string) (Project, error)
Close() error
Delete(name string) error
ActiveProject() (Project, error)
Projects() ([]Project, error)
OnProjectOpen(fn OnProjectOpenFn)
OnProjectClose(fn OnProjectCloseFn)
}
type service struct {
repo Repository repo Repository
activeProject string activeProject string
onProjectOpenFns []OnProjectOpenFn onProjectOpenFns []OnProjectOpenFn
@ -37,14 +47,14 @@ var (
var nameRegexp = regexp.MustCompile(`^[\w\d\s]+$`) var nameRegexp = regexp.MustCompile(`^[\w\d\s]+$`)
// NewService returns a new Service. // NewService returns a new Service.
func NewService(repo Repository) (*Service, error) { func NewService(repo Repository) (Service, error) {
return &Service{ return &service{
repo: repo, repo: repo,
}, nil }, nil
} }
// Close closes the currently open project database (if there is one). // Close closes the currently open project database (if there is one).
func (svc *Service) Close() error { func (svc *service) Close() error {
svc.mu.Lock() svc.mu.Lock()
defer svc.mu.Unlock() defer svc.mu.Unlock()
@ -62,7 +72,7 @@ func (svc *Service) Close() error {
} }
// Delete removes a project database file from disk (if there is one). // Delete removes a project database file from disk (if there is one).
func (svc *Service) Delete(name string) error { func (svc *service) Delete(name string) error {
if name == "" { if name == "" {
return errors.New("proj: name cannot be empty") return errors.New("proj: name cannot be empty")
} }
@ -80,7 +90,7 @@ func (svc *Service) Delete(name string) error {
// Open opens a database identified with `name`. If a database with this // Open opens a database identified with `name`. If a database with this
// identifier doesn't exist yet, it will be automatically created. // identifier doesn't exist yet, it will be automatically created.
func (svc *Service) Open(ctx context.Context, name string) (Project, error) { func (svc *service) Open(ctx context.Context, name string) (Project, error) {
if !nameRegexp.MatchString(name) { if !nameRegexp.MatchString(name) {
return Project{}, ErrInvalidName return Project{}, ErrInvalidName
} }
@ -105,7 +115,7 @@ func (svc *Service) Open(ctx context.Context, name string) (Project, error) {
}, nil }, nil
} }
func (svc *Service) ActiveProject() (Project, error) { func (svc *service) ActiveProject() (Project, error) {
activeProject := svc.activeProject activeProject := svc.activeProject
if activeProject == "" { if activeProject == "" {
return Project{}, ErrNoProject return Project{}, ErrNoProject
@ -116,7 +126,7 @@ func (svc *Service) ActiveProject() (Project, error) {
}, nil }, nil
} }
func (svc *Service) Projects() ([]Project, error) { func (svc *service) Projects() ([]Project, error) {
projects, err := svc.repo.Projects() projects, err := svc.repo.Projects()
if err != nil { if err != nil {
return nil, fmt.Errorf("proj: could not get projects: %w", err) return nil, fmt.Errorf("proj: could not get projects: %w", err)
@ -125,21 +135,21 @@ func (svc *Service) Projects() ([]Project, error) {
return projects, nil return projects, nil
} }
func (svc *Service) OnProjectOpen(fn OnProjectOpenFn) { func (svc *service) OnProjectOpen(fn OnProjectOpenFn) {
svc.mu.Lock() svc.mu.Lock()
defer svc.mu.Unlock() defer svc.mu.Unlock()
svc.onProjectOpenFns = append(svc.onProjectOpenFns, fn) svc.onProjectOpenFns = append(svc.onProjectOpenFns, fn)
} }
func (svc *Service) OnProjectClose(fn OnProjectCloseFn) { func (svc *service) OnProjectClose(fn OnProjectCloseFn) {
svc.mu.Lock() svc.mu.Lock()
defer svc.mu.Unlock() defer svc.mu.Unlock()
svc.onProjectCloseFns = append(svc.onProjectCloseFns, fn) svc.onProjectCloseFns = append(svc.onProjectCloseFns, fn)
} }
func (svc *Service) emitProjectOpened() { func (svc *service) emitProjectOpened() {
for _, fn := range svc.onProjectOpenFns { for _, fn := range svc.onProjectOpenFns {
if err := fn(svc.activeProject); err != nil { if err := fn(svc.activeProject); err != nil {
log.Printf("[ERROR] Could not execute onProjectOpen function: %v", err) log.Printf("[ERROR] Could not execute onProjectOpen function: %v", err)
@ -147,7 +157,7 @@ func (svc *Service) emitProjectOpened() {
} }
} }
func (svc *Service) emitProjectClosed(name string) { func (svc *service) emitProjectClosed(name string) {
for _, fn := range svc.onProjectCloseFns { for _, fn := range svc.onProjectCloseFns {
if err := fn(name); err != nil { if err := fn(name); err != nil {
log.Printf("[ERROR] Could not execute onProjectClose function: %v", err) log.Printf("[ERROR] Could not execute onProjectClose function: %v", err)

View File

@ -0,0 +1,318 @@
// Code generated by moq; DO NOT EDIT.
// github.com/matryer/moq
package reqlog_test
import (
"context"
"github.com/dstotijn/hetty/pkg/proj"
"sync"
)
// Ensure, that ProjServiceMock does implement proj.Service.
// If this is not the case, regenerate this file with moq.
var _ proj.Service = &ProjServiceMock{}
// ProjServiceMock is a mock implementation of proj.Service.
//
// func TestSomethingThatUsesService(t *testing.T) {
//
// // make and configure a mocked proj.Service
// mockedService := &ProjServiceMock{
// ActiveProjectFunc: func() (proj.Project, error) {
// panic("mock out the ActiveProject method")
// },
// CloseFunc: func() error {
// panic("mock out the Close method")
// },
// DeleteFunc: func(name string) error {
// panic("mock out the Delete method")
// },
// OnProjectCloseFunc: func(fn proj.OnProjectCloseFn) {
// panic("mock out the OnProjectClose method")
// },
// OnProjectOpenFunc: func(fn proj.OnProjectOpenFn) {
// panic("mock out the OnProjectOpen method")
// },
// OpenFunc: func(ctx context.Context, name string) (proj.Project, error) {
// panic("mock out the Open method")
// },
// ProjectsFunc: func() ([]proj.Project, error) {
// panic("mock out the Projects method")
// },
// }
//
// // use mockedService in code that requires proj.Service
// // and then make assertions.
//
// }
type ProjServiceMock struct {
// ActiveProjectFunc mocks the ActiveProject method.
ActiveProjectFunc func() (proj.Project, error)
// CloseFunc mocks the Close method.
CloseFunc func() error
// DeleteFunc mocks the Delete method.
DeleteFunc func(name string) error
// OnProjectCloseFunc mocks the OnProjectClose method.
OnProjectCloseFunc func(fn proj.OnProjectCloseFn)
// OnProjectOpenFunc mocks the OnProjectOpen method.
OnProjectOpenFunc func(fn proj.OnProjectOpenFn)
// OpenFunc mocks the Open method.
OpenFunc func(ctx context.Context, name string) (proj.Project, error)
// ProjectsFunc mocks the Projects method.
ProjectsFunc func() ([]proj.Project, error)
// calls tracks calls to the methods.
calls struct {
// ActiveProject holds details about calls to the ActiveProject method.
ActiveProject []struct {
}
// Close holds details about calls to the Close method.
Close []struct {
}
// Delete holds details about calls to the Delete method.
Delete []struct {
// Name is the name argument value.
Name string
}
// OnProjectClose holds details about calls to the OnProjectClose method.
OnProjectClose []struct {
// Fn is the fn argument value.
Fn proj.OnProjectCloseFn
}
// OnProjectOpen holds details about calls to the OnProjectOpen method.
OnProjectOpen []struct {
// Fn is the fn argument value.
Fn proj.OnProjectOpenFn
}
// Open holds details about calls to the Open method.
Open []struct {
// Ctx is the ctx argument value.
Ctx context.Context
// Name is the name argument value.
Name string
}
// Projects holds details about calls to the Projects method.
Projects []struct {
}
}
lockActiveProject sync.RWMutex
lockClose sync.RWMutex
lockDelete sync.RWMutex
lockOnProjectClose sync.RWMutex
lockOnProjectOpen sync.RWMutex
lockOpen sync.RWMutex
lockProjects sync.RWMutex
}
// ActiveProject calls ActiveProjectFunc.
func (mock *ProjServiceMock) ActiveProject() (proj.Project, error) {
if mock.ActiveProjectFunc == nil {
panic("ProjServiceMock.ActiveProjectFunc: method is nil but Service.ActiveProject was just called")
}
callInfo := struct {
}{}
mock.lockActiveProject.Lock()
mock.calls.ActiveProject = append(mock.calls.ActiveProject, callInfo)
mock.lockActiveProject.Unlock()
return mock.ActiveProjectFunc()
}
// ActiveProjectCalls gets all the calls that were made to ActiveProject.
// Check the length with:
// len(mockedService.ActiveProjectCalls())
func (mock *ProjServiceMock) ActiveProjectCalls() []struct {
} {
var calls []struct {
}
mock.lockActiveProject.RLock()
calls = mock.calls.ActiveProject
mock.lockActiveProject.RUnlock()
return calls
}
// Close calls CloseFunc.
func (mock *ProjServiceMock) Close() error {
if mock.CloseFunc == nil {
panic("ProjServiceMock.CloseFunc: method is nil but Service.Close was just called")
}
callInfo := struct {
}{}
mock.lockClose.Lock()
mock.calls.Close = append(mock.calls.Close, callInfo)
mock.lockClose.Unlock()
return mock.CloseFunc()
}
// CloseCalls gets all the calls that were made to Close.
// Check the length with:
// len(mockedService.CloseCalls())
func (mock *ProjServiceMock) CloseCalls() []struct {
} {
var calls []struct {
}
mock.lockClose.RLock()
calls = mock.calls.Close
mock.lockClose.RUnlock()
return calls
}
// Delete calls DeleteFunc.
func (mock *ProjServiceMock) Delete(name string) error {
if mock.DeleteFunc == nil {
panic("ProjServiceMock.DeleteFunc: method is nil but Service.Delete was just called")
}
callInfo := struct {
Name string
}{
Name: name,
}
mock.lockDelete.Lock()
mock.calls.Delete = append(mock.calls.Delete, callInfo)
mock.lockDelete.Unlock()
return mock.DeleteFunc(name)
}
// DeleteCalls gets all the calls that were made to Delete.
// Check the length with:
// len(mockedService.DeleteCalls())
func (mock *ProjServiceMock) DeleteCalls() []struct {
Name string
} {
var calls []struct {
Name string
}
mock.lockDelete.RLock()
calls = mock.calls.Delete
mock.lockDelete.RUnlock()
return calls
}
// OnProjectClose calls OnProjectCloseFunc.
func (mock *ProjServiceMock) OnProjectClose(fn proj.OnProjectCloseFn) {
if mock.OnProjectCloseFunc == nil {
panic("ProjServiceMock.OnProjectCloseFunc: method is nil but Service.OnProjectClose was just called")
}
callInfo := struct {
Fn proj.OnProjectCloseFn
}{
Fn: fn,
}
mock.lockOnProjectClose.Lock()
mock.calls.OnProjectClose = append(mock.calls.OnProjectClose, callInfo)
mock.lockOnProjectClose.Unlock()
mock.OnProjectCloseFunc(fn)
}
// OnProjectCloseCalls gets all the calls that were made to OnProjectClose.
// Check the length with:
// len(mockedService.OnProjectCloseCalls())
func (mock *ProjServiceMock) OnProjectCloseCalls() []struct {
Fn proj.OnProjectCloseFn
} {
var calls []struct {
Fn proj.OnProjectCloseFn
}
mock.lockOnProjectClose.RLock()
calls = mock.calls.OnProjectClose
mock.lockOnProjectClose.RUnlock()
return calls
}
// OnProjectOpen calls OnProjectOpenFunc.
func (mock *ProjServiceMock) OnProjectOpen(fn proj.OnProjectOpenFn) {
if mock.OnProjectOpenFunc == nil {
panic("ProjServiceMock.OnProjectOpenFunc: method is nil but Service.OnProjectOpen was just called")
}
callInfo := struct {
Fn proj.OnProjectOpenFn
}{
Fn: fn,
}
mock.lockOnProjectOpen.Lock()
mock.calls.OnProjectOpen = append(mock.calls.OnProjectOpen, callInfo)
mock.lockOnProjectOpen.Unlock()
mock.OnProjectOpenFunc(fn)
}
// OnProjectOpenCalls gets all the calls that were made to OnProjectOpen.
// Check the length with:
// len(mockedService.OnProjectOpenCalls())
func (mock *ProjServiceMock) OnProjectOpenCalls() []struct {
Fn proj.OnProjectOpenFn
} {
var calls []struct {
Fn proj.OnProjectOpenFn
}
mock.lockOnProjectOpen.RLock()
calls = mock.calls.OnProjectOpen
mock.lockOnProjectOpen.RUnlock()
return calls
}
// Open calls OpenFunc.
func (mock *ProjServiceMock) Open(ctx context.Context, name string) (proj.Project, error) {
if mock.OpenFunc == nil {
panic("ProjServiceMock.OpenFunc: method is nil but Service.Open was just called")
}
callInfo := struct {
Ctx context.Context
Name string
}{
Ctx: ctx,
Name: name,
}
mock.lockOpen.Lock()
mock.calls.Open = append(mock.calls.Open, callInfo)
mock.lockOpen.Unlock()
return mock.OpenFunc(ctx, name)
}
// OpenCalls gets all the calls that were made to Open.
// Check the length with:
// len(mockedService.OpenCalls())
func (mock *ProjServiceMock) OpenCalls() []struct {
Ctx context.Context
Name string
} {
var calls []struct {
Ctx context.Context
Name string
}
mock.lockOpen.RLock()
calls = mock.calls.Open
mock.lockOpen.RUnlock()
return calls
}
// Projects calls ProjectsFunc.
func (mock *ProjServiceMock) Projects() ([]proj.Project, error) {
if mock.ProjectsFunc == nil {
panic("ProjServiceMock.ProjectsFunc: method is nil but Service.Projects was just called")
}
callInfo := struct {
}{}
mock.lockProjects.Lock()
mock.calls.Projects = append(mock.calls.Projects, callInfo)
mock.lockProjects.Unlock()
return mock.ProjectsFunc()
}
// ProjectsCalls gets all the calls that were made to Projects.
// Check the length with:
// len(mockedService.ProjectsCalls())
func (mock *ProjServiceMock) ProjectsCalls() []struct {
} {
var calls []struct {
}
mock.lockProjects.RLock()
calls = mock.calls.Projects
mock.lockProjects.RUnlock()
return calls
}

View File

@ -0,0 +1,420 @@
// Code generated by moq; DO NOT EDIT.
// github.com/matryer/moq
package reqlog_test
import (
"context"
"github.com/dstotijn/hetty/pkg/reqlog"
"github.com/dstotijn/hetty/pkg/scope"
"net/http"
"sync"
"time"
)
// Ensure, that RepoMock does implement reqlog.Repository.
// If this is not the case, regenerate this file with moq.
var _ reqlog.Repository = &RepoMock{}
// RepoMock is a mock implementation of reqlog.Repository.
//
// func TestSomethingThatUsesRepository(t *testing.T) {
//
// // make and configure a mocked reqlog.Repository
// mockedRepository := &RepoMock{
// AddRequestLogFunc: func(ctx context.Context, req http.Request, body []byte, timestamp time.Time) (*reqlog.Request, error) {
// panic("mock out the AddRequestLog method")
// },
// AddResponseLogFunc: func(ctx context.Context, reqID int64, res http.Response, body []byte, timestamp time.Time) (*reqlog.Response, error) {
// panic("mock out the AddResponseLog method")
// },
// ClearRequestLogsFunc: func(ctx context.Context) error {
// panic("mock out the ClearRequestLogs method")
// },
// FindRequestLogByIDFunc: func(ctx context.Context, id int64) (reqlog.Request, error) {
// panic("mock out the FindRequestLogByID method")
// },
// FindRequestLogsFunc: func(ctx context.Context, filter reqlog.FindRequestsFilter, scopeMoqParam *scope.Scope) ([]reqlog.Request, error) {
// panic("mock out the FindRequestLogs method")
// },
// FindSettingsByModuleFunc: func(ctx context.Context, module string, settings interface{}) error {
// panic("mock out the FindSettingsByModule method")
// },
// UpsertSettingsFunc: func(ctx context.Context, module string, settings interface{}) error {
// panic("mock out the UpsertSettings method")
// },
// }
//
// // use mockedRepository in code that requires reqlog.Repository
// // and then make assertions.
//
// }
type RepoMock struct {
// AddRequestLogFunc mocks the AddRequestLog method.
AddRequestLogFunc func(ctx context.Context, req http.Request, body []byte, timestamp time.Time) (*reqlog.Request, error)
// AddResponseLogFunc mocks the AddResponseLog method.
AddResponseLogFunc func(ctx context.Context, reqID int64, res http.Response, body []byte, timestamp time.Time) (*reqlog.Response, error)
// ClearRequestLogsFunc mocks the ClearRequestLogs method.
ClearRequestLogsFunc func(ctx context.Context) error
// FindRequestLogByIDFunc mocks the FindRequestLogByID method.
FindRequestLogByIDFunc func(ctx context.Context, id int64) (reqlog.Request, error)
// FindRequestLogsFunc mocks the FindRequestLogs method.
FindRequestLogsFunc func(ctx context.Context, filter reqlog.FindRequestsFilter, scopeMoqParam *scope.Scope) ([]reqlog.Request, error)
// FindSettingsByModuleFunc mocks the FindSettingsByModule method.
FindSettingsByModuleFunc func(ctx context.Context, module string, settings interface{}) error
// UpsertSettingsFunc mocks the UpsertSettings method.
UpsertSettingsFunc func(ctx context.Context, module string, settings interface{}) error
// calls tracks calls to the methods.
calls struct {
// AddRequestLog holds details about calls to the AddRequestLog method.
AddRequestLog []struct {
// Ctx is the ctx argument value.
Ctx context.Context
// Req is the req argument value.
Req http.Request
// Body is the body argument value.
Body []byte
// Timestamp is the timestamp argument value.
Timestamp time.Time
}
// AddResponseLog holds details about calls to the AddResponseLog method.
AddResponseLog []struct {
// Ctx is the ctx argument value.
Ctx context.Context
// ReqID is the reqID argument value.
ReqID int64
// Res is the res argument value.
Res http.Response
// Body is the body argument value.
Body []byte
// Timestamp is the timestamp argument value.
Timestamp time.Time
}
// ClearRequestLogs holds details about calls to the ClearRequestLogs method.
ClearRequestLogs []struct {
// Ctx is the ctx argument value.
Ctx context.Context
}
// FindRequestLogByID holds details about calls to the FindRequestLogByID method.
FindRequestLogByID []struct {
// Ctx is the ctx argument value.
Ctx context.Context
// ID is the id argument value.
ID int64
}
// FindRequestLogs holds details about calls to the FindRequestLogs method.
FindRequestLogs []struct {
// Ctx is the ctx argument value.
Ctx context.Context
// Filter is the filter argument value.
Filter reqlog.FindRequestsFilter
// ScopeMoqParam is the scopeMoqParam argument value.
ScopeMoqParam *scope.Scope
}
// FindSettingsByModule holds details about calls to the FindSettingsByModule method.
FindSettingsByModule []struct {
// Ctx is the ctx argument value.
Ctx context.Context
// Module is the module argument value.
Module string
// Settings is the settings argument value.
Settings interface{}
}
// UpsertSettings holds details about calls to the UpsertSettings method.
UpsertSettings []struct {
// Ctx is the ctx argument value.
Ctx context.Context
// Module is the module argument value.
Module string
// Settings is the settings argument value.
Settings interface{}
}
}
lockAddRequestLog sync.RWMutex
lockAddResponseLog sync.RWMutex
lockClearRequestLogs sync.RWMutex
lockFindRequestLogByID sync.RWMutex
lockFindRequestLogs sync.RWMutex
lockFindSettingsByModule sync.RWMutex
lockUpsertSettings sync.RWMutex
}
// AddRequestLog calls AddRequestLogFunc.
func (mock *RepoMock) AddRequestLog(ctx context.Context, req http.Request, body []byte, timestamp time.Time) (*reqlog.Request, error) {
if mock.AddRequestLogFunc == nil {
panic("RepoMock.AddRequestLogFunc: method is nil but Repository.AddRequestLog was just called")
}
callInfo := struct {
Ctx context.Context
Req http.Request
Body []byte
Timestamp time.Time
}{
Ctx: ctx,
Req: req,
Body: body,
Timestamp: timestamp,
}
mock.lockAddRequestLog.Lock()
mock.calls.AddRequestLog = append(mock.calls.AddRequestLog, callInfo)
mock.lockAddRequestLog.Unlock()
return mock.AddRequestLogFunc(ctx, req, body, timestamp)
}
// AddRequestLogCalls gets all the calls that were made to AddRequestLog.
// Check the length with:
// len(mockedRepository.AddRequestLogCalls())
func (mock *RepoMock) AddRequestLogCalls() []struct {
Ctx context.Context
Req http.Request
Body []byte
Timestamp time.Time
} {
var calls []struct {
Ctx context.Context
Req http.Request
Body []byte
Timestamp time.Time
}
mock.lockAddRequestLog.RLock()
calls = mock.calls.AddRequestLog
mock.lockAddRequestLog.RUnlock()
return calls
}
// AddResponseLog calls AddResponseLogFunc.
func (mock *RepoMock) AddResponseLog(ctx context.Context, reqID int64, res http.Response, body []byte, timestamp time.Time) (*reqlog.Response, error) {
if mock.AddResponseLogFunc == nil {
panic("RepoMock.AddResponseLogFunc: method is nil but Repository.AddResponseLog was just called")
}
callInfo := struct {
Ctx context.Context
ReqID int64
Res http.Response
Body []byte
Timestamp time.Time
}{
Ctx: ctx,
ReqID: reqID,
Res: res,
Body: body,
Timestamp: timestamp,
}
mock.lockAddResponseLog.Lock()
mock.calls.AddResponseLog = append(mock.calls.AddResponseLog, callInfo)
mock.lockAddResponseLog.Unlock()
return mock.AddResponseLogFunc(ctx, reqID, res, body, timestamp)
}
// AddResponseLogCalls gets all the calls that were made to AddResponseLog.
// Check the length with:
// len(mockedRepository.AddResponseLogCalls())
func (mock *RepoMock) AddResponseLogCalls() []struct {
Ctx context.Context
ReqID int64
Res http.Response
Body []byte
Timestamp time.Time
} {
var calls []struct {
Ctx context.Context
ReqID int64
Res http.Response
Body []byte
Timestamp time.Time
}
mock.lockAddResponseLog.RLock()
calls = mock.calls.AddResponseLog
mock.lockAddResponseLog.RUnlock()
return calls
}
// ClearRequestLogs calls ClearRequestLogsFunc.
func (mock *RepoMock) ClearRequestLogs(ctx context.Context) error {
if mock.ClearRequestLogsFunc == nil {
panic("RepoMock.ClearRequestLogsFunc: method is nil but Repository.ClearRequestLogs was just called")
}
callInfo := struct {
Ctx context.Context
}{
Ctx: ctx,
}
mock.lockClearRequestLogs.Lock()
mock.calls.ClearRequestLogs = append(mock.calls.ClearRequestLogs, callInfo)
mock.lockClearRequestLogs.Unlock()
return mock.ClearRequestLogsFunc(ctx)
}
// ClearRequestLogsCalls gets all the calls that were made to ClearRequestLogs.
// Check the length with:
// len(mockedRepository.ClearRequestLogsCalls())
func (mock *RepoMock) ClearRequestLogsCalls() []struct {
Ctx context.Context
} {
var calls []struct {
Ctx context.Context
}
mock.lockClearRequestLogs.RLock()
calls = mock.calls.ClearRequestLogs
mock.lockClearRequestLogs.RUnlock()
return calls
}
// FindRequestLogByID calls FindRequestLogByIDFunc.
func (mock *RepoMock) FindRequestLogByID(ctx context.Context, id int64) (reqlog.Request, error) {
if mock.FindRequestLogByIDFunc == nil {
panic("RepoMock.FindRequestLogByIDFunc: method is nil but Repository.FindRequestLogByID was just called")
}
callInfo := struct {
Ctx context.Context
ID int64
}{
Ctx: ctx,
ID: id,
}
mock.lockFindRequestLogByID.Lock()
mock.calls.FindRequestLogByID = append(mock.calls.FindRequestLogByID, callInfo)
mock.lockFindRequestLogByID.Unlock()
return mock.FindRequestLogByIDFunc(ctx, id)
}
// FindRequestLogByIDCalls gets all the calls that were made to FindRequestLogByID.
// Check the length with:
// len(mockedRepository.FindRequestLogByIDCalls())
func (mock *RepoMock) FindRequestLogByIDCalls() []struct {
Ctx context.Context
ID int64
} {
var calls []struct {
Ctx context.Context
ID int64
}
mock.lockFindRequestLogByID.RLock()
calls = mock.calls.FindRequestLogByID
mock.lockFindRequestLogByID.RUnlock()
return calls
}
// FindRequestLogs calls FindRequestLogsFunc.
func (mock *RepoMock) FindRequestLogs(ctx context.Context, filter reqlog.FindRequestsFilter, scopeMoqParam *scope.Scope) ([]reqlog.Request, error) {
if mock.FindRequestLogsFunc == nil {
panic("RepoMock.FindRequestLogsFunc: method is nil but Repository.FindRequestLogs was just called")
}
callInfo := struct {
Ctx context.Context
Filter reqlog.FindRequestsFilter
ScopeMoqParam *scope.Scope
}{
Ctx: ctx,
Filter: filter,
ScopeMoqParam: scopeMoqParam,
}
mock.lockFindRequestLogs.Lock()
mock.calls.FindRequestLogs = append(mock.calls.FindRequestLogs, callInfo)
mock.lockFindRequestLogs.Unlock()
return mock.FindRequestLogsFunc(ctx, filter, scopeMoqParam)
}
// FindRequestLogsCalls gets all the calls that were made to FindRequestLogs.
// Check the length with:
// len(mockedRepository.FindRequestLogsCalls())
func (mock *RepoMock) FindRequestLogsCalls() []struct {
Ctx context.Context
Filter reqlog.FindRequestsFilter
ScopeMoqParam *scope.Scope
} {
var calls []struct {
Ctx context.Context
Filter reqlog.FindRequestsFilter
ScopeMoqParam *scope.Scope
}
mock.lockFindRequestLogs.RLock()
calls = mock.calls.FindRequestLogs
mock.lockFindRequestLogs.RUnlock()
return calls
}
// FindSettingsByModule calls FindSettingsByModuleFunc.
func (mock *RepoMock) FindSettingsByModule(ctx context.Context, module string, settings interface{}) error {
if mock.FindSettingsByModuleFunc == nil {
panic("RepoMock.FindSettingsByModuleFunc: method is nil but Repository.FindSettingsByModule was just called")
}
callInfo := struct {
Ctx context.Context
Module string
Settings interface{}
}{
Ctx: ctx,
Module: module,
Settings: settings,
}
mock.lockFindSettingsByModule.Lock()
mock.calls.FindSettingsByModule = append(mock.calls.FindSettingsByModule, callInfo)
mock.lockFindSettingsByModule.Unlock()
return mock.FindSettingsByModuleFunc(ctx, module, settings)
}
// FindSettingsByModuleCalls gets all the calls that were made to FindSettingsByModule.
// Check the length with:
// len(mockedRepository.FindSettingsByModuleCalls())
func (mock *RepoMock) FindSettingsByModuleCalls() []struct {
Ctx context.Context
Module string
Settings interface{}
} {
var calls []struct {
Ctx context.Context
Module string
Settings interface{}
}
mock.lockFindSettingsByModule.RLock()
calls = mock.calls.FindSettingsByModule
mock.lockFindSettingsByModule.RUnlock()
return calls
}
// UpsertSettings calls UpsertSettingsFunc.
func (mock *RepoMock) UpsertSettings(ctx context.Context, module string, settings interface{}) error {
if mock.UpsertSettingsFunc == nil {
panic("RepoMock.UpsertSettingsFunc: method is nil but Repository.UpsertSettings was just called")
}
callInfo := struct {
Ctx context.Context
Module string
Settings interface{}
}{
Ctx: ctx,
Module: module,
Settings: settings,
}
mock.lockUpsertSettings.Lock()
mock.calls.UpsertSettings = append(mock.calls.UpsertSettings, callInfo)
mock.lockUpsertSettings.Unlock()
return mock.UpsertSettingsFunc(ctx, module, settings)
}
// UpsertSettingsCalls gets all the calls that were made to UpsertSettings.
// Check the length with:
// len(mockedRepository.UpsertSettingsCalls())
func (mock *RepoMock) UpsertSettingsCalls() []struct {
Ctx context.Context
Module string
Settings interface{}
} {
var calls []struct {
Ctx context.Context
Module string
Settings interface{}
}
mock.lockUpsertSettings.RLock()
calls = mock.calls.UpsertSettings
mock.lockUpsertSettings.RUnlock()
return calls
}

View File

@ -59,7 +59,7 @@ type FindRequestsFilter struct {
type Config struct { type Config struct {
Scope *scope.Scope Scope *scope.Scope
Repository Repository Repository Repository
ProjectService *proj.Service ProjectService proj.Service
BypassOutOfScopeRequests bool BypassOutOfScopeRequests bool
} }
@ -71,7 +71,7 @@ func NewService(cfg Config) *Service {
} }
cfg.ProjectService.OnProjectOpen(func(_ string) error { cfg.ProjectService.OnProjectOpen(func(_ string) error {
err := svc.loadSettings() err := svc.repo.FindSettingsByModule(context.Background(), moduleName, svc)
if errors.Is(err, proj.ErrNoSettings) { if errors.Is(err, proj.ErrNoSettings) {
return nil return nil
} }
@ -82,7 +82,8 @@ func NewService(cfg Config) *Service {
return nil return nil
}) })
cfg.ProjectService.OnProjectClose(func(_ string) error { cfg.ProjectService.OnProjectClose(func(_ string) error {
svc.unloadSettings() svc.BypassOutOfScopeRequests = false
svc.FindReqsFilter = FindRequestsFilter{}
return nil return nil
}) })
@ -252,12 +253,3 @@ func (f *FindRequestsFilter) UnmarshalJSON(b []byte) error {
return nil return nil
} }
func (svc *Service) loadSettings() error {
return svc.repo.FindSettingsByModule(context.Background(), moduleName, svc)
}
func (svc *Service) unloadSettings() {
svc.BypassOutOfScopeRequests = false
svc.FindReqsFilter = FindRequestsFilter{}
}

193
pkg/reqlog/reqlog_test.go Normal file
View File

@ -0,0 +1,193 @@
package reqlog_test
//go:generate moq -out proj_mock_test.go -pkg reqlog_test ../proj Service:ProjServiceMock
//go:generate moq -out repo_mock_test.go -pkg reqlog_test . Repository:RepoMock
import (
"context"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/dstotijn/hetty/pkg/proj"
"github.com/dstotijn/hetty/pkg/proxy"
"github.com/dstotijn/hetty/pkg/reqlog"
)
//nolint:paralleltest
func TestNewService(t *testing.T) {
projSvcMock := &ProjServiceMock{
OnProjectOpenFunc: func(fn proj.OnProjectOpenFn) {},
OnProjectCloseFunc: func(fn proj.OnProjectCloseFn) {},
}
repoMock := &RepoMock{
FindSettingsByModuleFunc: func(_ context.Context, _ string, _ interface{}) error {
return nil
},
}
svc := reqlog.NewService(reqlog.Config{
ProjectService: projSvcMock,
Repository: repoMock,
})
t.Run("registered handlers for project open and close", func(t *testing.T) {
got := len(projSvcMock.OnProjectOpenCalls())
if exp := 1; exp != got {
t.Fatalf("incorrect `proj.Service.OnProjectOpen` calls (expected: %v, got: %v)", exp, got)
}
got = len(projSvcMock.OnProjectCloseCalls())
if exp := 1; exp != got {
t.Fatalf("incorrect `proj.Service.OnProjectClose` calls (expected: %v, got: %v)", exp, got)
}
})
t.Run("calls handler when project is opened", func(t *testing.T) {
// Mock opening a project.
err := projSvcMock.OnProjectOpenCalls()[0].Fn("foobar")
if err != nil {
t.Errorf("unexpected error (expected: nil, got: %v)", err)
}
// Assert that settings were fetched from repository, with `svc` as the
// destination.
got := len(repoMock.FindSettingsByModuleCalls())
if exp := 1; exp != got {
t.Fatalf("incorrect `proj.Service.OnProjectOpen` calls (expected: %v, got: %v)", exp, got)
}
findSettingsByModuleCall := repoMock.FindSettingsByModuleCalls()[0]
expModule := "reqlog"
expSettings := svc
if expModule != findSettingsByModuleCall.Module {
t.Fatalf("incorrect `module` argument for `proj.Service.OnProjectOpen` (expected: %v, got: %v)",
expModule, findSettingsByModuleCall.Module)
}
if expSettings != findSettingsByModuleCall.Settings {
t.Fatalf("incorrect `settings` argument for `proj.Service.OnProjectOpen` (expected: %v, got: %v)",
expModule, findSettingsByModuleCall.Settings)
}
})
t.Run("calls handler when project is closed", func(t *testing.T) {
// Mock updating service settings.
svc.BypassOutOfScopeRequests = true
svc.FindReqsFilter = reqlog.FindRequestsFilter{OnlyInScope: true}
// Mock closing a project.
err := projSvcMock.OnProjectCloseCalls()[0].Fn("foobar")
if err != nil {
t.Errorf("unexpected error (expected: nil, got: %v)", err)
}
// Assert that settings were set to defaults on project close.
expBypassOutOfScopeReqs := false
expFindReqsFilter := reqlog.FindRequestsFilter{}
if expBypassOutOfScopeReqs != svc.BypassOutOfScopeRequests {
t.Fatalf("incorrect `Service.BypassOutOfScopeRequests` value (expected: %v, got: %v)",
expBypassOutOfScopeReqs, svc.BypassOutOfScopeRequests)
}
if expFindReqsFilter != svc.FindReqsFilter {
t.Fatalf("incorrect `Service.FindReqsFilter` value (expected: %v, got: %v)",
expFindReqsFilter, svc.FindReqsFilter)
}
})
}
//nolint:paralleltest
func TestRequestModifier(t *testing.T) {
projSvcMock := &ProjServiceMock{
OnProjectOpenFunc: func(fn proj.OnProjectOpenFn) {},
OnProjectCloseFunc: func(fn proj.OnProjectCloseFn) {},
}
repoMock := &RepoMock{
AddRequestLogFunc: func(_ context.Context, _ http.Request, _ []byte, _ time.Time) (*reqlog.Request, error) {
return &reqlog.Request{}, nil
},
}
svc := reqlog.NewService(reqlog.Config{
ProjectService: projSvcMock,
Repository: repoMock,
})
next := func(req *http.Request) {
req.Body = ioutil.NopCloser(strings.NewReader("modified body"))
}
reqModFn := svc.RequestModifier(next)
req := httptest.NewRequest("GET", "https://example.com/", strings.NewReader("bar"))
reqModFn(req)
t.Run("request log was stored in repository", func(t *testing.T) {
got := len(repoMock.AddRequestLogCalls())
if exp := 1; exp != got {
t.Fatalf("incorrect `proj.Service.AddRequestLog` calls (expected: %v, got: %v)", exp, got)
}
})
t.Run("ran next modifier first, before calling repository", func(t *testing.T) {
got := repoMock.AddRequestLogCalls()[0].Body
if exp := "modified body"; exp != string(got) {
t.Fatalf("incorrect `body` argument for `Repository.AddRequestLogCalls` (expected: %v, got: %v)", exp, string(got))
}
})
}
//nolint:paralleltest
func TestResponseModifier(t *testing.T) {
projSvcMock := &ProjServiceMock{
OnProjectOpenFunc: func(fn proj.OnProjectOpenFn) {},
OnProjectCloseFunc: func(fn proj.OnProjectCloseFn) {},
}
repoMock := &RepoMock{
AddResponseLogFunc: func(_ context.Context, _ int64, _ http.Response,
_ []byte, _ time.Time) (*reqlog.Response, error) {
return &reqlog.Response{}, nil
},
}
svc := reqlog.NewService(reqlog.Config{
ProjectService: projSvcMock,
Repository: repoMock,
})
next := func(res *http.Response) error {
res.Body = ioutil.NopCloser(strings.NewReader("modified body"))
return nil
}
resModFn := svc.ResponseModifier(next)
req := httptest.NewRequest("GET", "https://example.com/", strings.NewReader("bar"))
req = req.WithContext(context.WithValue(req.Context(), proxy.ReqIDKey, int64(42)))
res := &http.Response{
Request: req,
Body: ioutil.NopCloser(strings.NewReader("bar")),
}
if err := resModFn(res); err != nil {
t.Fatalf("unexpected error (expected: nil, got: %v)", err)
}
t.Run("request log was stored in repository", func(t *testing.T) {
// Dirty (but simple) wait for other goroutine to finish calling repository.
time.Sleep(10 * time.Millisecond)
got := len(repoMock.AddResponseLogCalls())
if exp := 1; exp != got {
t.Fatalf("incorrect `proj.Service.AddResponseLog` calls (expected: %v, got: %v)", exp, got)
}
})
t.Run("ran next modifier first, before calling repository", func(t *testing.T) {
got := repoMock.AddResponseLogCalls()[0].Body
if exp := "modified body"; exp != string(got) {
t.Fatalf("incorrect `body` argument for `Repository.AddResponseLogCalls` (expected: %v, got: %v)", exp, string(got))
}
})
}

View File

@ -32,7 +32,7 @@ type Header struct {
Value *regexp.Regexp Value *regexp.Regexp
} }
func New(repo Repository, projService *proj.Service) *Scope { func New(repo Repository, projService proj.Service) *Scope {
s := &Scope{ s := &Scope{
repo: repo, repo: repo,
} }