Remove database mocks, replace Service interface indirection

This commit is contained in:
David Stotijn
2025-01-04 00:39:40 +01:00
parent d23b4ed024
commit 24c2ecfa4b
11 changed files with 133 additions and 1294 deletions

View File

@ -1,291 +0,0 @@
// 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"
"github.com/oklog/ulid"
"sync"
)
// 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{
// ClearRequestLogsFunc: func(ctx context.Context, projectID ulid.ULID) error {
// panic("mock out the ClearRequestLogs method")
// },
// FindRequestLogByIDFunc: func(ctx context.Context, id ulid.ULID) (reqlog.RequestLog, error) {
// panic("mock out the FindRequestLogByID method")
// },
// FindRequestLogsFunc: func(ctx context.Context, filter reqlog.FindRequestsFilter, scopeMoqParam *scope.Scope) ([]reqlog.RequestLog, error) {
// panic("mock out the FindRequestLogs method")
// },
// StoreRequestLogFunc: func(ctx context.Context, reqLog reqlog.RequestLog) error {
// panic("mock out the StoreRequestLog method")
// },
// StoreResponseLogFunc: func(ctx context.Context, reqLogID ulid.ULID, resLog reqlog.ResponseLog) error {
// panic("mock out the StoreResponseLog method")
// },
// }
//
// // use mockedRepository in code that requires reqlog.Repository
// // and then make assertions.
//
// }
type RepoMock struct {
// ClearRequestLogsFunc mocks the ClearRequestLogs method.
ClearRequestLogsFunc func(ctx context.Context, projectID ulid.ULID) error
// FindRequestLogByIDFunc mocks the FindRequestLogByID method.
FindRequestLogByIDFunc func(ctx context.Context, id ulid.ULID) (reqlog.RequestLog, error)
// FindRequestLogsFunc mocks the FindRequestLogs method.
FindRequestLogsFunc func(ctx context.Context, filter reqlog.FindRequestsFilter, scopeMoqParam *scope.Scope) ([]reqlog.RequestLog, error)
// StoreRequestLogFunc mocks the StoreRequestLog method.
StoreRequestLogFunc func(ctx context.Context, reqLog reqlog.RequestLog) error
// StoreResponseLogFunc mocks the StoreResponseLog method.
StoreResponseLogFunc func(ctx context.Context, reqLogID ulid.ULID, resLog reqlog.ResponseLog) error
// calls tracks calls to the methods.
calls struct {
// ClearRequestLogs holds details about calls to the ClearRequestLogs method.
ClearRequestLogs []struct {
// Ctx is the ctx argument value.
Ctx context.Context
// ProjectID is the projectID argument value.
ProjectID ulid.ULID
}
// 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 ulid.ULID
}
// 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
}
// StoreRequestLog holds details about calls to the StoreRequestLog method.
StoreRequestLog []struct {
// Ctx is the ctx argument value.
Ctx context.Context
// ReqLog is the reqLog argument value.
ReqLog reqlog.RequestLog
}
// StoreResponseLog holds details about calls to the StoreResponseLog method.
StoreResponseLog []struct {
// Ctx is the ctx argument value.
Ctx context.Context
// ReqLogID is the reqLogID argument value.
ReqLogID ulid.ULID
// ResLog is the resLog argument value.
ResLog reqlog.ResponseLog
}
}
lockClearRequestLogs sync.RWMutex
lockFindRequestLogByID sync.RWMutex
lockFindRequestLogs sync.RWMutex
lockStoreRequestLog sync.RWMutex
lockStoreResponseLog sync.RWMutex
}
// ClearRequestLogs calls ClearRequestLogsFunc.
func (mock *RepoMock) ClearRequestLogs(ctx context.Context, projectID ulid.ULID) error {
if mock.ClearRequestLogsFunc == nil {
panic("RepoMock.ClearRequestLogsFunc: method is nil but Repository.ClearRequestLogs was just called")
}
callInfo := struct {
Ctx context.Context
ProjectID ulid.ULID
}{
Ctx: ctx,
ProjectID: projectID,
}
mock.lockClearRequestLogs.Lock()
mock.calls.ClearRequestLogs = append(mock.calls.ClearRequestLogs, callInfo)
mock.lockClearRequestLogs.Unlock()
return mock.ClearRequestLogsFunc(ctx, projectID)
}
// 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
ProjectID ulid.ULID
} {
var calls []struct {
Ctx context.Context
ProjectID ulid.ULID
}
mock.lockClearRequestLogs.RLock()
calls = mock.calls.ClearRequestLogs
mock.lockClearRequestLogs.RUnlock()
return calls
}
// FindRequestLogByID calls FindRequestLogByIDFunc.
func (mock *RepoMock) FindRequestLogByID(ctx context.Context, id ulid.ULID) (reqlog.RequestLog, error) {
if mock.FindRequestLogByIDFunc == nil {
panic("RepoMock.FindRequestLogByIDFunc: method is nil but Repository.FindRequestLogByID was just called")
}
callInfo := struct {
Ctx context.Context
ID ulid.ULID
}{
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 ulid.ULID
} {
var calls []struct {
Ctx context.Context
ID ulid.ULID
}
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.RequestLog, 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
}
// StoreRequestLog calls StoreRequestLogFunc.
func (mock *RepoMock) StoreRequestLog(ctx context.Context, reqLog reqlog.RequestLog) error {
if mock.StoreRequestLogFunc == nil {
panic("RepoMock.StoreRequestLogFunc: method is nil but Repository.StoreRequestLog was just called")
}
callInfo := struct {
Ctx context.Context
ReqLog reqlog.RequestLog
}{
Ctx: ctx,
ReqLog: reqLog,
}
mock.lockStoreRequestLog.Lock()
mock.calls.StoreRequestLog = append(mock.calls.StoreRequestLog, callInfo)
mock.lockStoreRequestLog.Unlock()
return mock.StoreRequestLogFunc(ctx, reqLog)
}
// StoreRequestLogCalls gets all the calls that were made to StoreRequestLog.
// Check the length with:
// len(mockedRepository.StoreRequestLogCalls())
func (mock *RepoMock) StoreRequestLogCalls() []struct {
Ctx context.Context
ReqLog reqlog.RequestLog
} {
var calls []struct {
Ctx context.Context
ReqLog reqlog.RequestLog
}
mock.lockStoreRequestLog.RLock()
calls = mock.calls.StoreRequestLog
mock.lockStoreRequestLog.RUnlock()
return calls
}
// StoreResponseLog calls StoreResponseLogFunc.
func (mock *RepoMock) StoreResponseLog(ctx context.Context, reqLogID ulid.ULID, resLog reqlog.ResponseLog) error {
if mock.StoreResponseLogFunc == nil {
panic("RepoMock.StoreResponseLogFunc: method is nil but Repository.StoreResponseLog was just called")
}
callInfo := struct {
Ctx context.Context
ReqLogID ulid.ULID
ResLog reqlog.ResponseLog
}{
Ctx: ctx,
ReqLogID: reqLogID,
ResLog: resLog,
}
mock.lockStoreResponseLog.Lock()
mock.calls.StoreResponseLog = append(mock.calls.StoreResponseLog, callInfo)
mock.lockStoreResponseLog.Unlock()
return mock.StoreResponseLogFunc(ctx, reqLogID, resLog)
}
// StoreResponseLogCalls gets all the calls that were made to StoreResponseLog.
// Check the length with:
// len(mockedRepository.StoreResponseLogCalls())
func (mock *RepoMock) StoreResponseLogCalls() []struct {
Ctx context.Context
ReqLogID ulid.ULID
ResLog reqlog.ResponseLog
} {
var calls []struct {
Ctx context.Context
ReqLogID ulid.ULID
ResLog reqlog.ResponseLog
}
mock.lockStoreResponseLog.RLock()
calls = mock.calls.StoreResponseLog
mock.lockStoreResponseLog.RUnlock()
return calls
}

View File

@ -51,21 +51,7 @@ type ResponseLog struct {
Body []byte
}
type Service interface {
FindRequests(ctx context.Context) ([]RequestLog, error)
FindRequestLogByID(ctx context.Context, id ulid.ULID) (RequestLog, error)
ClearRequests(ctx context.Context, projectID ulid.ULID) error
RequestModifier(next proxy.RequestModifyFunc) proxy.RequestModifyFunc
ResponseModifier(next proxy.ResponseModifyFunc) proxy.ResponseModifyFunc
SetActiveProjectID(id ulid.ULID)
ActiveProjectID() ulid.ULID
SetBypassOutOfScopeRequests(bool)
BypassOutOfScopeRequests() bool
SetFindReqsFilter(filter FindRequestsFilter)
FindReqsFilter() FindRequestsFilter
}
type service struct {
type Service struct {
bypassOutOfScopeRequests bool
findReqsFilter FindRequestsFilter
activeProjectID ulid.ULID
@ -86,8 +72,8 @@ type Config struct {
Logger log.Logger
}
func NewService(cfg Config) Service {
s := &service{
func NewService(cfg Config) *Service {
s := &Service{
repo: cfg.Repository,
scope: cfg.Scope,
logger: cfg.Logger,
@ -100,19 +86,19 @@ func NewService(cfg Config) Service {
return s
}
func (svc *service) FindRequests(ctx context.Context) ([]RequestLog, error) {
func (svc *Service) FindRequests(ctx context.Context) ([]RequestLog, error) {
return svc.repo.FindRequestLogs(ctx, svc.findReqsFilter, svc.scope)
}
func (svc *service) FindRequestLogByID(ctx context.Context, id ulid.ULID) (RequestLog, error) {
func (svc *Service) FindRequestLogByID(ctx context.Context, id ulid.ULID) (RequestLog, error) {
return svc.repo.FindRequestLogByID(ctx, id)
}
func (svc *service) ClearRequests(ctx context.Context, projectID ulid.ULID) error {
func (svc *Service) ClearRequests(ctx context.Context, projectID ulid.ULID) error {
return svc.repo.ClearRequestLogs(ctx, projectID)
}
func (svc *service) storeResponse(ctx context.Context, reqLogID ulid.ULID, res *http.Response) error {
func (svc *Service) storeResponse(ctx context.Context, reqLogID ulid.ULID, res *http.Response) error {
resLog, err := ParseHTTPResponse(res)
if err != nil {
return err
@ -121,7 +107,7 @@ func (svc *service) storeResponse(ctx context.Context, reqLogID ulid.ULID, res *
return svc.repo.StoreResponseLog(ctx, reqLogID, resLog)
}
func (svc *service) RequestModifier(next proxy.RequestModifyFunc) proxy.RequestModifyFunc {
func (svc *Service) RequestModifier(next proxy.RequestModifyFunc) proxy.RequestModifyFunc {
return func(req *http.Request) {
next(req)
@ -199,7 +185,7 @@ func (svc *service) RequestModifier(next proxy.RequestModifyFunc) proxy.RequestM
}
}
func (svc *service) ResponseModifier(next proxy.ResponseModifyFunc) proxy.ResponseModifyFunc {
func (svc *Service) ResponseModifier(next proxy.ResponseModifyFunc) proxy.ResponseModifyFunc {
return func(res *http.Response) error {
if err := next(res); err != nil {
return err
@ -241,27 +227,27 @@ func (svc *service) ResponseModifier(next proxy.ResponseModifyFunc) proxy.Respon
}
}
func (svc *service) SetActiveProjectID(id ulid.ULID) {
func (svc *Service) SetActiveProjectID(id ulid.ULID) {
svc.activeProjectID = id
}
func (svc *service) ActiveProjectID() ulid.ULID {
func (svc *Service) ActiveProjectID() ulid.ULID {
return svc.activeProjectID
}
func (svc *service) SetFindReqsFilter(filter FindRequestsFilter) {
func (svc *Service) SetFindReqsFilter(filter FindRequestsFilter) {
svc.findReqsFilter = filter
}
func (svc *service) FindReqsFilter() FindRequestsFilter {
func (svc *Service) FindReqsFilter() FindRequestsFilter {
return svc.findReqsFilter
}
func (svc *service) SetBypassOutOfScopeRequests(bypass bool) {
func (svc *Service) SetBypassOutOfScopeRequests(bypass bool) {
svc.bypassOutOfScopeRequests = bypass
}
func (svc *service) BypassOutOfScopeRequests() bool {
func (svc *Service) BypassOutOfScopeRequests() bool {
return svc.bypassOutOfScopeRequests
}

View File

@ -1,7 +1,5 @@
package reqlog_test
//go:generate go run github.com/matryer/moq -out repo_mock_test.go -pkg reqlog_test . Repository:RepoMock
import (
"context"
"io"
@ -15,6 +13,8 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/oklog/ulid"
badgerdb "github.com/dgraph-io/badger/v3"
"github.com/dstotijn/hetty/pkg/db/badger"
"github.com/dstotijn/hetty/pkg/proxy"
"github.com/dstotijn/hetty/pkg/reqlog"
"github.com/dstotijn/hetty/pkg/scope"
@ -25,13 +25,13 @@ var ulidEntropy = rand.New(rand.NewSource(time.Now().UnixNano()))
//nolint:paralleltest
func TestRequestModifier(t *testing.T) {
repoMock := &RepoMock{
StoreRequestLogFunc: func(_ context.Context, _ reqlog.RequestLog) error {
return nil
},
db, err := badger.OpenDatabase(badgerdb.DefaultOptions("").WithInMemory(true))
if err != nil {
t.Fatalf("failed to open database: %v", err)
}
svc := reqlog.NewService(reqlog.Config{
Repository: repoMock,
Repository: db,
Scope: &scope.Scope{},
})
svc.SetActiveProjectID(ulid.MustNew(ulid.Timestamp(time.Now()), ulidEntropy))
@ -47,13 +47,8 @@ func TestRequestModifier(t *testing.T) {
reqModFn(req)
t.Run("request log was stored in repository", func(t *testing.T) {
gotCount := len(repoMock.StoreRequestLogCalls())
if expCount := 1; expCount != gotCount {
t.Fatalf("incorrect `proj.Service.AddRequestLog` calls (expected: %v, got: %v)", expCount, gotCount)
}
exp := reqlog.RequestLog{
ID: ulid.ULID{}, // Empty value
ID: reqID,
ProjectID: svc.ActiveProjectID(),
Method: req.Method,
URL: req.URL,
@ -61,8 +56,11 @@ func TestRequestModifier(t *testing.T) {
Header: req.Header,
Body: []byte("modified body"),
}
got := repoMock.StoreRequestLogCalls()[0].ReqLog
got.ID = ulid.ULID{} // Override to empty value so we can compare against expected value.
got, err := svc.FindRequestLogByID(context.Background(), reqID)
if err != nil {
t.Fatalf("failed to find request by id: %v", err)
}
if diff := cmp.Diff(exp, got); diff != "" {
t.Fatalf("request log not equal (-exp, +got):\n%v", diff)
@ -72,13 +70,13 @@ func TestRequestModifier(t *testing.T) {
//nolint:paralleltest
func TestResponseModifier(t *testing.T) {
repoMock := &RepoMock{
StoreResponseLogFunc: func(_ context.Context, _ ulid.ULID, _ reqlog.ResponseLog) error {
return nil
},
db, err := badger.OpenDatabase(badgerdb.DefaultOptions("").WithInMemory(true))
if err != nil {
t.Fatalf("failed to open database: %v", err)
}
svc := reqlog.NewService(reqlog.Config{
Repository: repoMock,
Repository: db,
})
svc.SetActiveProjectID(ulid.MustNew(ulid.Timestamp(time.Now()), ulidEntropy))
@ -92,6 +90,13 @@ func TestResponseModifier(t *testing.T) {
reqLogID := ulid.MustNew(ulid.Timestamp(time.Now()), ulidEntropy)
req = req.WithContext(context.WithValue(req.Context(), reqlog.ReqLogIDKey, reqLogID))
err = db.StoreRequestLog(context.Background(), reqlog.RequestLog{
ID: reqLogID,
})
if err != nil {
t.Fatalf("failed to store request log: %v", err)
}
res := &http.Response{
Request: req,
Body: io.NopCloser(strings.NewReader("bar")),
@ -104,23 +109,15 @@ func TestResponseModifier(t *testing.T) {
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.StoreResponseLogCalls())
if exp := 1; exp != got {
t.Fatalf("incorrect `proj.Service.AddResponseLog` calls (expected: %v, got: %v)", exp, got)
got, err := svc.FindRequestLogByID(context.Background(), reqLogID)
if err != nil {
t.Fatalf("failed to find request by id: %v", err)
}
t.Run("ran next modifier first, before calling repository", func(t *testing.T) {
got := repoMock.StoreResponseLogCalls()[0].ResLog.Body
if exp := "modified body"; exp != string(got) {
t.Fatalf("incorrect `ResponseLog.Body` value (expected: %v, got: %v)", exp, string(got))
}
})
t.Run("called repository with request log id", func(t *testing.T) {
got := repoMock.StoreResponseLogCalls()[0].ReqLogID
if exp := reqLogID; exp.Compare(got) != 0 {
t.Fatalf("incorrect `reqLogID` argument for `Repository.AddResponseLogCalls` (expected: %v, got: %v)",
exp.String(), got.String())
if exp := "modified body"; exp != string(got.Response.Body) {
t.Fatalf("incorrect `ResponseLog.Body` value (expected: %v, got: %v)", exp, string(got.Response.Body))
}
})
})