Skip to content

Commit

Permalink
Added example simple usecase test, mocking enforcers and repositories.
Browse files Browse the repository at this point in the history
  • Loading branch information
Antoine Popineau committed Jan 22, 2025
1 parent fecfc7b commit 28879a3
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 4 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ require (
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
github.com/getkin/kin-openapi v0.124.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-faker/faker/v4 v4.5.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
Expand Down Expand Up @@ -167,6 +168,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
github.com/opencontainers/runc v1.1.14 // indirect
github.com/pashagolub/pgxmock/v4 v4.4.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-faker/faker/v4 v4.5.0 h1:ARzAY2XoOL9tOUK+KSecUQzyXQsUaZHefjyF8x6YFHc=
github.com/go-faker/faker/v4 v4.5.0/go.mod h1:p3oq1GRjG2PZ7yqeFFfQI20Xm61DoBDlCA8RiSyZ48M=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
Expand Down Expand Up @@ -389,6 +391,8 @@ github.com/opencontainers/runc v1.1.14 h1:rgSuzbmgz5DUJjeSnw337TxDbRuqjs6iqQck/2
github.com/opencontainers/runc v1.1.14/go.mod h1:E4C2z+7BxR7GHXp0hAY53mek+x49X1LjPNeMTfRGvOA=
github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4=
github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg=
github.com/pashagolub/pgxmock/v4 v4.4.0 h1:zrZHBzqlzIFrq5Iw6nQpmpEd77eLqGIC2ol4ZTeojz0=
github.com/pashagolub/pgxmock/v4 v4.4.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
Expand Down
39 changes: 39 additions & 0 deletions usecases/executor_factory/executor_factory_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package executor_factory

import (
"context"

"github.com/checkmarble/marble-backend/models"
"github.com/checkmarble/marble-backend/repositories"
"github.com/pashagolub/pgxmock/v4"
)

type ExecutorFactoryStub struct {
Mock pgxmock.PgxPoolIface
}

func NewExecutorFactoryStub() ExecutorFactoryStub {
pool, _ := pgxmock.NewPool()

return ExecutorFactoryStub{
Mock: pool,
}
}

type PgExecutorStub struct {
pgxmock.PgxPoolIface
}

func (stub ExecutorFactoryStub) NewClientDbExecutor(ctx context.Context, organizationId string) (repositories.Executor, error) {
return nil, nil
}

func (stub ExecutorFactoryStub) NewExecutor() repositories.Executor {
return PgExecutorStub{
stub.Mock,
}
}

func (stub PgExecutorStub) DatabaseSchema() models.DatabaseSchema {
return models.DatabaseSchema{}
}
19 changes: 15 additions & 4 deletions usecases/sanction_check_usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@ import (
"github.com/checkmarble/marble-backend/models"
"github.com/checkmarble/marble-backend/repositories"
"github.com/checkmarble/marble-backend/usecases/executor_factory"
"github.com/checkmarble/marble-backend/usecases/security"
"github.com/checkmarble/marble-backend/utils"
"github.com/pkg/errors"
)

type SanctionCheckEnforceSecurityDecision interface {
ReadDecision(models.Decision) error
}

type SanctionCheckEnforceSecurityCase interface {
ReadOrUpdateCase(models.Case, []string) error
}

type SanctionCheckProvider interface {
Search(context.Context, models.SanctionCheckConfig,
models.OpenSanctionsQuery) (models.SanctionCheck, error)
Expand All @@ -20,6 +27,10 @@ type SanctionCheckDecisionRepository interface {
DecisionsById(ctx context.Context, exec repositories.Executor, decisionIds []string) ([]models.Decision, error)
}

type SanctionCheckOrganizationRepository interface {
GetOrganizationById(ctx context.Context, exec repositories.Executor, organizationId string) (models.Organization, error)
}

type SanctionCheckRepository interface {
ListSanctionChecksForDecision(context.Context, repositories.Executor, string) ([]models.SanctionCheck, error)
GetSanctionCheck(context.Context, repositories.Executor, string) (models.SanctionCheck, error)
Expand All @@ -38,10 +49,10 @@ type SanctionCheckRepository interface {
}

type SanctionCheckUsecase struct {
enforceSecurityDecision security.EnforceSecurityDecision
enforceSecurityCase security.EnforceSecurityCase
enforceSecurityDecision SanctionCheckEnforceSecurityDecision
enforceSecurityCase SanctionCheckEnforceSecurityCase

organizationRepository repositories.OrganizationRepository
organizationRepository SanctionCheckOrganizationRepository
decisionRepository SanctionCheckDecisionRepository
openSanctionsProvider SanctionCheckProvider
repository SanctionCheckRepository
Expand Down
45 changes: 45 additions & 0 deletions usecases/sanction_check_usecase_mock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package usecases

import (
"context"

"github.com/checkmarble/marble-backend/models"
"github.com/checkmarble/marble-backend/repositories"
"github.com/checkmarble/marble-backend/utils"
)

type sanctionCheckEnforcerMock struct{}

func (sanctionCheckEnforcerMock) ReadDecision(models.Decision) error {
return nil
}

func (sanctionCheckEnforcerMock) ReadOrUpdateCase(models.Case, []string) error {
return nil
}

type sanctionCheckRepositoryMock struct{}

func (sanctionCheckRepositoryMock) GetOrganizationById(ctx context.Context,
exec repositories.Executor, organizationId string,
) (models.Organization, error) {
return models.Organization{
Id: "orgid",
Name: "ACME Inc.",
OpenSanctionsConfig: models.OrganizationOpenSanctionsConfig{
Datasets: []string{"ds1", "ds2"},
MatchThreshold: utils.Ptr(42),
MatchLimit: utils.Ptr(10),
},
}, nil
}

func (sanctionCheckRepositoryMock) DecisionsById(ctx context.Context, exec repositories.Executor, decisionIds []string) ([]models.Decision, error) {
decisions := []models.Decision{
{
DecisionId: "decisionid",
},
}

return decisions, nil
}
62 changes: 62 additions & 0 deletions usecases/sanction_check_usecase_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package usecases

import (
"context"
"testing"

"github.com/checkmarble/marble-backend/repositories"
"github.com/checkmarble/marble-backend/repositories/dbmodels"
"github.com/checkmarble/marble-backend/usecases/executor_factory"
"github.com/checkmarble/marble-backend/utils"
"github.com/pashagolub/pgxmock/v4"
"github.com/stretchr/testify/assert"
)

func buildUsecase() (SanctionCheckUsecase, executor_factory.ExecutorFactoryStub) {
enforceSecurity := sanctionCheckEnforcerMock{}
mock := sanctionCheckRepositoryMock{}
exec := executor_factory.NewExecutorFactoryStub()

uc := SanctionCheckUsecase{
enforceSecurityDecision: enforceSecurity,
enforceSecurityCase: enforceSecurity,
organizationRepository: mock,
decisionRepository: mock,
repository: &repositories.MarbleDbRepository{},
executorFactory: exec,
}

return uc, exec
}

func TestGetSanctionCheckOnDecision(t *testing.T) {
uc, exec := buildUsecase()
mockSc, mockScRow := utils.FakeStruct[dbmodels.DBSanctionCheck]()
mockScMatch, mockScMatchRow := utils.FakeStruct[dbmodels.DBSanctionCheckMatchWithComments]()

exec.Mock.ExpectQuery(`SELECT .* FROM sanction_checks WHERE decision_id = \$1`).
WithArgs("decisionid").
WillReturnRows(
pgxmock.NewRows(dbmodels.SelectSanctionChecksColumn).
AddRow(mockScRow...),
)

exec.Mock.ExpectQuery(`SELECT .* FROM sanction_check_matches matches LEFT JOIN sanction_check_match_comments comments ON matches.id = comments.sanction_check_match_id WHERE sanction_check_id = \$1 GROUP BY matches.id`).
WithArgs(mockSc.Id).
WillReturnRows(
pgxmock.NewRows(utils.ColumnList[dbmodels.DBSanctionCheckMatchWithComments]()).
AddRow(mockScMatchRow...).
AddRow(utils.FakeStructRow[dbmodels.DBSanctionCheckMatchWithComments]()...).
AddRow(utils.FakeStructRow[dbmodels.DBSanctionCheckMatchWithComments]()...),
)

scs, err := uc.ListSanctionChecks(context.TODO(), "decisionid")

assert.NoError(t, exec.Mock.ExpectationsWereMet())
assert.NoError(t, err)
assert.Len(t, scs, 1)
assert.Equal(t, mockSc.Status, scs[0].Status)
assert.Len(t, scs[0].Matches, 3)
assert.Equal(t, mockScMatch.Status, scs[0].Matches[0].Status)
assert.Equal(t, mockScMatch.CommentCount, scs[0].Matches[0].CommentCount)
}
50 changes: 50 additions & 0 deletions utils/testing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package utils

import (
"reflect"

"github.com/go-faker/faker/v4"
)

func FakeStruct[T any]() (T, []any) {
var object T

_ = faker.FakeData(&object)

return object, StructToMockRow(object)
}

func FakeStructRow[T any]() []any {
_, row := FakeStruct[T]()

return row
}

func StructToMockRow[T any](object T) []any {
f := reflect.ValueOf(object)
t := reflect.TypeOf(object)

if f.Kind() != reflect.Struct {
panic("StructToMockRow should only be used on structs")
}

slice := make([]any, 0)

for i := 0; i < f.NumField(); i++ {
sf := f.Field(i)

switch sf.Kind() {
case reflect.Struct:
switch t.Field(i).Anonymous {
case true:
slice = append(slice, StructToMockRow(sf.Interface())...)
default:
slice = append(slice, sf.Interface())
}
default:
slice = append(slice, sf.Interface())
}
}

return slice
}

0 comments on commit 28879a3

Please sign in to comment.