diff --git a/OpenApi.yml b/OpenApi.yml index 44e857a4..ecb5909e 100644 --- a/OpenApi.yml +++ b/OpenApi.yml @@ -683,34 +683,38 @@ components: rsk: type: string type: object - SummariesResponse: - properties: - peginSummary: - $ref: '#/components/schemas/SummaryData' - type: object - pegoutSummary: - $ref: '#/components/schemas/SummaryData' - type: object - type: object SummaryData: properties: - confirmedQuotesCount: + acceptedQuotesCount: type: integer lpEarnings: - type: string + $ref: '#/components/schemas/Wei' + paidQuotesAmount: + $ref: '#/components/schemas/Wei' + paidQuotesCount: + type: integer refundedQuotesCount: type: integer totalAcceptedQuotedAmount: - type: string - totalAcceptedQuotesCount: - type: integer + $ref: '#/components/schemas/Wei' totalFeesCollected: - type: string + $ref: '#/components/schemas/Wei' totalPenaltyAmount: - type: string - totalQuotedAmount: - type: string + $ref: '#/components/schemas/Wei' + totalQuotesCount: + type: integer + type: object + SummaryResult: + properties: + peginSummary: + $ref: '#/components/schemas/SummaryData' + type: object + pegoutSummary: + $ref: '#/components/schemas/SummaryData' + type: object type: object + Wei: {} + entities.Wei: {} pkg.AcceptQuoteRequest: properties: quoteHash: @@ -1086,7 +1090,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/SummariesResponse' + $ref: '#/components/schemas/SummaryResult' description: Financial data for the given period summary: Summaries /userQuotes: diff --git a/internal/adapters/dataproviders/database/mongo/common.go b/internal/adapters/dataproviders/database/mongo/common.go index 70992232..611aa3e2 100644 --- a/internal/adapters/dataproviders/database/mongo/common.go +++ b/internal/adapters/dataproviders/database/mongo/common.go @@ -2,12 +2,9 @@ package mongo import ( "context" - "fmt" "time" log "github.com/sirupsen/logrus" - - "go.mongodb.org/mongo-driver/bson" ) const ( @@ -78,200 +75,3 @@ func (c *Connection) CheckConnection(ctx context.Context) bool { } return err == nil } - -type QuoteResult[Q any, R QuoteHashProvider] struct { - Quotes []Q - RetainedQuotes []R - QuoteHashToIndex map[string]int - Error error -} - -type QuoteQuery struct { - Ctx context.Context - Conn *Connection - StartDate time.Time - EndDate time.Time - QuoteCollection string - RetainedCollection string -} - -func ListQuotesByDateRange[Q any, R QuoteHashProvider]( - query QuoteQuery, - mapper func(bson.D) Q, -) QuoteResult[Q, R] { - dbCtx, cancel := context.WithTimeout(query.Ctx, query.Conn.timeout) - defer cancel() - quotes, quoteHashes, err := fetchQuotesByDateRange(dbCtx, query.Conn, query.StartDate, query.EndDate, query.QuoteCollection, mapper) - if err != nil { - return QuoteResult[Q, R]{Error: err} - } - quoteHashToIndex := make(map[string]int, len(quoteHashes)) - for i, hash := range quoteHashes { - if hash != "" { - quoteHashToIndex[hash] = i - } - } - retainedQuotes, additionalHashes, err := fetchRetainedQuotes[R](dbCtx, query.Conn, query.StartDate, query.EndDate, query.RetainedCollection, quoteHashes) - if err != nil { - return QuoteResult[Q, R]{Error: err} - } - if len(additionalHashes) > 0 { - additionalQuotes, additionalHashIndices, err := fetchAdditionalQuotes(dbCtx, query.Conn, query.QuoteCollection, additionalHashes, mapper) - if err != nil { - log.Errorf("Error processing additional quotes: %v", err) - } else { - baseIndex := len(quotes) - for i, hash := range additionalHashIndices { - if hash != "" { - quoteHashToIndex[hash] = baseIndex + i - } - } - quotes = append(quotes, additionalQuotes...) - } - } - logDbInteraction(Read, fmt.Sprintf("Found %d quotes and %d retained quotes in date range", - len(quotes), len(retainedQuotes))) - return QuoteResult[Q, R]{ - Quotes: quotes, - RetainedQuotes: retainedQuotes, - QuoteHashToIndex: quoteHashToIndex, - Error: nil, - } -} - -func fetchQuotesByDateRange[Q any]( - ctx context.Context, - conn *Connection, - startDate, endDate time.Time, - collectionName string, - mapper func(bson.D) Q, -) ([]Q, []string, error) { - quoteFilter := bson.D{ - {Key: "agreement_timestamp", Value: bson.D{ - {Key: "$gte", Value: startDate.Unix()}, - {Key: "$lte", Value: endDate.Unix()}, - }}, - } - var storedQuotes []bson.D - quoteCursor, err := conn.Collection(collectionName).Find(ctx, quoteFilter) - if err != nil { - return nil, nil, err - } - if err = quoteCursor.All(ctx, &storedQuotes); err != nil { - return nil, nil, err - } - quoteHashes := make([]string, 0, len(storedQuotes)) - quotes := make([]Q, 0, len(storedQuotes)) - for _, stored := range storedQuotes { - quoteObj := mapper(stored) - quotes = append(quotes, quoteObj) - hashValue, ok := getStringValueFromBSON(stored, "hash") - if ok { - quoteHashes = append(quoteHashes, hashValue) - } - } - return quotes, quoteHashes, nil -} - -func getStringValueFromBSON(doc bson.D, key string) (string, bool) { - data, err := bson.Marshal(doc) - if err != nil { - return "", false - } - rawValue := bson.Raw(data).Lookup(key) - return rawValue.StringValueOK() -} - -type QuoteHashProvider interface { - GetQuoteHash() string -} - -func fetchRetainedQuotes[R QuoteHashProvider]( - ctx context.Context, - conn *Connection, - startDate, endDate time.Time, - collectionName string, - existingQuoteHashes []string, -) ([]R, []string, error) { - retainedFilter := createRetainedFilter(startDate, endDate, existingQuoteHashes) - var retainedQuotes []R - retainedCursor, err := conn.Collection(collectionName).Find(ctx, retainedFilter) - if err != nil { - return nil, nil, err - } - if err = retainedCursor.All(ctx, &retainedQuotes); err != nil { - return nil, nil, err - } - additionalHashes := findAdditionalQuoteHashes(retainedQuotes, existingQuoteHashes) - return retainedQuotes, additionalHashes, nil -} - -func createRetainedFilter(startDate, endDate time.Time, quoteHashes []string) bson.D { - return bson.D{ - {Key: "$or", Value: bson.A{ - bson.D{{Key: "quote_hash", Value: bson.D{ - {Key: "$in", Value: quoteHashes}, - }}}, - bson.D{ - {Key: "created_at", Value: bson.D{ - {Key: "$gte", Value: startDate.Unix()}, - {Key: "$lte", Value: endDate.Unix()}, - }}, - }, - }}, - } -} - -func findAdditionalQuoteHashes[R QuoteHashProvider](retainedQuotes []R, existingQuoteHashes []string) []string { - existingMap := make(map[string]bool, len(existingQuoteHashes)) - for _, hash := range existingQuoteHashes { - existingMap[hash] = true - } - additionalMap := make(map[string]bool) - for i := range retainedQuotes { - hash := retainedQuotes[i].GetQuoteHash() - if !existingMap[hash] { - additionalMap[hash] = true - } - } - additionalHashes := make([]string, 0, len(additionalMap)) - for hash := range additionalMap { - additionalHashes = append(additionalHashes, hash) - } - return additionalHashes -} - -func fetchAdditionalQuotes[Q any]( - ctx context.Context, - conn *Connection, - collectionName string, - hashes []string, - mapper func(bson.D) Q, -) ([]Q, []string, error) { - quoteFilter := bson.D{ - {Key: "hash", Value: bson.D{ - {Key: "$in", Value: hashes}, - }}, - } - var storedQuotes []bson.D - quoteCursor, err := conn.Collection(collectionName).Find(ctx, quoteFilter) - if err != nil { - return nil, nil, err - } - if err = quoteCursor.All(ctx, &storedQuotes); err != nil { - return nil, nil, err - } - quotes := make([]Q, 0, len(storedQuotes)) - resultHashes := make([]string, 0, len(storedQuotes)) - for _, stored := range storedQuotes { - quoteObj := mapper(stored) - quotes = append(quotes, quoteObj) - hashValue, ok := getStringValueFromBSON(stored, "hash") - if ok { - resultHashes = append(resultHashes, hashValue) - } else { - resultHashes = append(resultHashes, "") - } - } - return quotes, resultHashes, nil -} diff --git a/internal/adapters/dataproviders/database/mongo/common_test.go b/internal/adapters/dataproviders/database/mongo/common_test.go index e4e94f59..559233fa 100644 --- a/internal/adapters/dataproviders/database/mongo/common_test.go +++ b/internal/adapters/dataproviders/database/mongo/common_test.go @@ -11,9 +11,6 @@ import ( "github.com/rsksmart/liquidity-provider-server/test/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" - mongodriver "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/readpref" ) @@ -117,242 +114,3 @@ func getClientAndDatabaseMocks() (*mocks.DbClientBindingMock, *mocks.DbBindingMo client.On("Database", mongo.DbName).Return(db) return client, db } - -func createCursorFromList[T any](t *testing.T, documents []T) *mongodriver.Cursor { - docsInterface := make([]interface{}, len(documents)) - for i, v := range documents { - docsInterface[i] = v - } - cursor, err := mongodriver.NewCursorFromDocuments(docsInterface, nil, nil) - assert.NoError(t, err) - return cursor -} - -type TestStoredQuote struct { - Hash string - TestQuote TestQuote -} - -type TestQuote struct { - Value int -} - -type TestRetainedQuote struct { - QuoteHash string - State string -} - -func (r TestRetainedQuote) GetQuoteHash() string { - return r.QuoteHash -} - -func setupQuoteTestData() (startDate, endDate time.Time, startTimestamp, endTimestamp int64) { - startDate = time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) - endDate = time.Date(2024, 1, 2, 0, 0, 0, 0, time.UTC) - startTimestamp = startDate.Unix() - endTimestamp = endDate.Unix() - return -} - -func setupTestCollections() (*mocks.DbClientBindingMock, *mocks.CollectionBindingMock, *mocks.CollectionBindingMock) { - client, db := getClientAndDatabaseMocks() - quoteCollection := &mocks.CollectionBindingMock{} - retainedCollection := &mocks.CollectionBindingMock{} - db.On("Collection", "quoteCollection").Return(quoteCollection) - db.On("Collection", "retainedCollection").Return(retainedCollection) - return client, quoteCollection, retainedCollection -} - -func createQuoteFilter(startTimestamp, endTimestamp int64) bson.D { - return bson.D{ - {Key: "agreement_timestamp", Value: bson.D{ - {Key: "$gte", Value: startTimestamp}, - {Key: "$lte", Value: endTimestamp}, - }}, - } -} - -func createRetainedFilter(quoteHashes []string, startTimestamp, endTimestamp int64) bson.D { - return bson.D{ - {Key: "$or", Value: bson.A{ - bson.D{{Key: "quote_hash", Value: bson.D{ - {Key: "$in", Value: quoteHashes}, - }}}, - bson.D{ - {Key: "created_at", Value: bson.D{ - {Key: "$gte", Value: startTimestamp}, - {Key: "$lte", Value: endTimestamp}, - }}, - }, - }}, - } -} - -func createTestQuery(client *mocks.DbClientBindingMock, startDate, endDate time.Time) mongo.QuoteQuery { - conn := mongo.NewConnection(client, time.Duration(1)) - return mongo.QuoteQuery{ - Ctx: context.Background(), - Conn: conn, - StartDate: startDate, - EndDate: endDate, - QuoteCollection: "quoteCollection", - RetainedCollection: "retainedCollection", - } -} - -func createQuoteExtractor() func(doc bson.D) TestQuote { - return func(doc bson.D) TestQuote { - var stored TestStoredQuote - bsonBytes, err := bson.Marshal(doc) - if err != nil { - return TestQuote{} - } - if err := bson.Unmarshal(bsonBytes, &stored); err != nil { - return TestQuote{} - } - return stored.TestQuote - } -} - -func TestListQuotesByDateRange_SuccessfulRetrieval(t *testing.T) { - startDate, endDate, startTimestamp, endTimestamp := setupQuoteTestData() - client, quoteCollection, retainedCollection := setupTestCollections() - storedQuotes := []TestStoredQuote{ - {Hash: "hash1", TestQuote: TestQuote{Value: 1}}, - {Hash: "hash2", TestQuote: TestQuote{Value: 2}}, - } - retainedQuotes := []TestRetainedQuote{ - {QuoteHash: "hash1", State: "state1"}, - {QuoteHash: "hash2", State: "state2"}, - } - quoteFilter := createQuoteFilter(startTimestamp, endTimestamp) - quoteCursor := createCursorFromList(t, storedQuotes) - quoteCollection.On("Find", mock.Anything, quoteFilter).Return(quoteCursor, nil) - retainedFilter := createRetainedFilter([]string{"hash1", "hash2"}, startTimestamp, endTimestamp) - retainedCursor := createCursorFromList(t, retainedQuotes) - retainedCollection.On("Find", mock.Anything, retainedFilter).Return(retainedCursor, nil) - query := createTestQuery(client, startDate, endDate) - result := mongo.ListQuotesByDateRange[TestQuote, TestRetainedQuote](query, createQuoteExtractor()) - require.NoError(t, result.Error) - assert.Len(t, result.Quotes, 2) - assert.Len(t, result.RetainedQuotes, 2) - assert.Equal(t, TestQuote{Value: 1}, result.Quotes[0]) - assert.Equal(t, TestQuote{Value: 2}, result.Quotes[1]) - assert.Equal(t, TestRetainedQuote{QuoteHash: "hash1", State: "state1"}, result.RetainedQuotes[0]) - assert.Equal(t, TestRetainedQuote{QuoteHash: "hash2", State: "state2"}, result.RetainedQuotes[1]) -} - -func TestListQuotesByDateRange_EmptyResultSet(t *testing.T) { - startDate, endDate, startTimestamp, endTimestamp := setupQuoteTestData() - client, quoteCollection, retainedCollection := setupTestCollections() - quoteFilter := createQuoteFilter(startTimestamp, endTimestamp) - quoteCursor := createCursorFromList(t, []TestStoredQuote{}) - quoteCollection.On("Find", mock.Anything, quoteFilter).Return(quoteCursor, nil) - retainedFilter := createRetainedFilter([]string{}, startTimestamp, endTimestamp) - retainedCursor := createCursorFromList(t, []TestRetainedQuote{}) - retainedCollection.On("Find", mock.Anything, retainedFilter).Return(retainedCursor, nil) - query := createTestQuery(client, startDate, endDate) - result := mongo.ListQuotesByDateRange[TestQuote, TestRetainedQuote](query, createQuoteExtractor()) - require.NoError(t, result.Error) - assert.Empty(t, result.Quotes) - assert.Empty(t, result.RetainedQuotes) -} - -func TestListQuotesByDateRange_DatabaseErrorOnQuoteCollection(t *testing.T) { - startDate, endDate, startTimestamp, endTimestamp := setupQuoteTestData() - client, quoteCollection, _ := setupTestCollections() - quoteFilter := createQuoteFilter(startTimestamp, endTimestamp) - quoteCollection.On("Find", mock.Anything, quoteFilter).Return(nil, assert.AnError) - query := createTestQuery(client, startDate, endDate) - result := mongo.ListQuotesByDateRange[TestQuote, TestRetainedQuote](query, createQuoteExtractor()) - require.Error(t, result.Error) - assert.Empty(t, result.Quotes) - assert.Empty(t, result.RetainedQuotes) -} - -func TestListQuotesByDateRange_DatabaseErrorOnRetainedCollection(t *testing.T) { - startDate, endDate, startTimestamp, endTimestamp := setupQuoteTestData() - client, quoteCollection, retainedCollection := setupTestCollections() - storedQuotes := []TestStoredQuote{ - {Hash: "hash1", TestQuote: TestQuote{Value: 1}}, - } - quoteFilter := createQuoteFilter(startTimestamp, endTimestamp) - quoteCursor := createCursorFromList(t, storedQuotes) - quoteCollection.On("Find", mock.Anything, quoteFilter).Return(quoteCursor, nil) - retainedFilter := createRetainedFilter([]string{"hash1"}, startTimestamp, endTimestamp) - retainedCollection.On("Find", mock.Anything, retainedFilter).Return(nil, assert.AnError) - query := createTestQuery(client, startDate, endDate) - result := mongo.ListQuotesByDateRange[TestQuote, TestRetainedQuote](query, createQuoteExtractor()) - require.Error(t, result.Error) - assert.Empty(t, result.Quotes) - assert.Empty(t, result.RetainedQuotes) -} - -func TestListQuotesByDateRange_FetchesAdditionalQuotes(t *testing.T) { - startDate, endDate, startTimestamp, endTimestamp := setupQuoteTestData() - client, quoteCollection, retainedCollection := setupTestCollections() - storedQuotes := []TestStoredQuote{ - {Hash: "hash1", TestQuote: TestQuote{Value: 1}}, - } - retainedQuotes := []TestRetainedQuote{ - {QuoteHash: "hash1", State: "state1"}, - {QuoteHash: "hash2", State: "state2"}, - } - additionalQuotes := []TestStoredQuote{ - {Hash: "hash2", TestQuote: TestQuote{Value: 2}}, - } - quoteFilter := createQuoteFilter(startTimestamp, endTimestamp) - quoteCursor := createCursorFromList(t, storedQuotes) - quoteCollection.On("Find", mock.Anything, quoteFilter).Return(quoteCursor, nil) - retainedFilter := createRetainedFilter([]string{"hash1"}, startTimestamp, endTimestamp) - retainedCursor := createCursorFromList(t, retainedQuotes) - retainedCollection.On("Find", mock.Anything, retainedFilter).Return(retainedCursor, nil) - additionalFilter := bson.D{ - {Key: "hash", Value: bson.D{ - {Key: "$in", Value: []string{"hash2"}}, - }}, - } - additionalCursor := createCursorFromList(t, additionalQuotes) - quoteCollection.On("Find", mock.Anything, additionalFilter).Return(additionalCursor, nil) - query := createTestQuery(client, startDate, endDate) - result := mongo.ListQuotesByDateRange[TestQuote, TestRetainedQuote](query, createQuoteExtractor()) - require.NoError(t, result.Error) - assert.Len(t, result.Quotes, 2) - assert.Len(t, result.RetainedQuotes, 2) - assert.Contains(t, []int{1, 2}, result.Quotes[0].Value) - assert.Contains(t, []int{1, 2}, result.Quotes[1].Value) - assert.Contains(t, []string{"state1", "state2"}, result.RetainedQuotes[0].State) - assert.Contains(t, []string{"state1", "state2"}, result.RetainedQuotes[1].State) -} - -func TestListQuotesByDateRange_HandlesErrorWhenFetchingAdditionalQuotes(t *testing.T) { - startDate, endDate, startTimestamp, endTimestamp := setupQuoteTestData() - client, quoteCollection, retainedCollection := setupTestCollections() - storedQuotes := []TestStoredQuote{ - {Hash: "hash1", TestQuote: TestQuote{Value: 1}}, - } - retainedQuotes := []TestRetainedQuote{ - {QuoteHash: "hash1", State: "state1"}, - {QuoteHash: "hash2", State: "state2"}, - } - quoteFilter := createQuoteFilter(startTimestamp, endTimestamp) - quoteCursor := createCursorFromList(t, storedQuotes) - quoteCollection.On("Find", mock.Anything, quoteFilter).Return(quoteCursor, nil) - retainedFilter := createRetainedFilter([]string{"hash1"}, startTimestamp, endTimestamp) - retainedCursor := createCursorFromList(t, retainedQuotes) - retainedCollection.On("Find", mock.Anything, retainedFilter).Return(retainedCursor, nil) - additionalFilter := bson.D{ - {Key: "hash", Value: bson.D{ - {Key: "$in", Value: []string{"hash2"}}, - }}, - } - quoteCollection.On("Find", mock.Anything, additionalFilter).Return(nil, assert.AnError) - query := createTestQuery(client, startDate, endDate) - result := mongo.ListQuotesByDateRange[TestQuote, TestRetainedQuote](query, createQuoteExtractor()) - require.NoError(t, result.Error) - assert.Len(t, result.Quotes, 1) - assert.Len(t, result.RetainedQuotes, 2) - assert.Equal(t, TestQuote{Value: 1}, result.Quotes[0]) - assert.Equal(t, "state1", result.RetainedQuotes[0].State) - assert.Equal(t, "state2", result.RetainedQuotes[1].State) -} diff --git a/internal/adapters/dataproviders/database/mongo/pegin.go b/internal/adapters/dataproviders/database/mongo/pegin.go index 9ffd4a92..ec776163 100644 --- a/internal/adapters/dataproviders/database/mongo/pegin.go +++ b/internal/adapters/dataproviders/database/mongo/pegin.go @@ -211,37 +211,59 @@ func (repo *peginMongoRepository) DeleteQuotes(ctx context.Context, quotes []str return uint(peginResult.DeletedCount + retainedResult.DeletedCount + creationDataResult.DeletedCount), nil } -func (repo *peginMongoRepository) ListQuotesByDateRange(ctx context.Context, startDate, endDate time.Time) (quote.PeginQuoteResult, error) { - query := QuoteQuery{ - Ctx: ctx, - Conn: repo.conn, - StartDate: startDate, - EndDate: endDate, - QuoteCollection: PeginQuoteCollection, - RetainedCollection: RetainedPeginQuoteCollection, - } - result := ListQuotesByDateRange[quote.PeginQuote, quote.RetainedPeginQuote]( - query, - func(doc bson.D) quote.PeginQuote { - var stored StoredPeginQuote - bsonBytes, err := bson.Marshal(doc) - if err != nil { - log.Errorf("Error marshaling BSON: %v", err) - return quote.PeginQuote{} - } - if err := bson.Unmarshal(bsonBytes, &stored); err != nil { - log.Errorf("Error unmarshaling BSON: %v", err) - return quote.PeginQuote{} - } - return stored.PeginQuote +func (repo *peginMongoRepository) ListQuotesByDateRange(ctx context.Context, startDate, endDate time.Time) ([]quote.PeginQuote, []quote.RetainedPeginQuote, error) { + quotes := make([]quote.PeginQuote, 0) + retainedQuotes := make([]quote.RetainedPeginQuote, 0) + dbCtx, cancel := context.WithTimeout(ctx, repo.conn.timeout) + defer cancel() + startTimestamp := int64(startDate.Unix()) + endTimestamp := int64(endDate.Unix()) + quoteCollection := repo.conn.Collection(PeginQuoteCollection) + quoteFilter := bson.D{ + primitive.E{ + Key: "agreement_timestamp", + Value: bson.D{ + primitive.E{Key: "$gte", Value: startTimestamp}, + primitive.E{Key: "$lte", Value: endTimestamp}, + }, }, - ) - if result.Error != nil { - return quote.PeginQuoteResult{}, result.Error - } - return quote.PeginQuoteResult{ - Quotes: result.Quotes, - RetainedQuotes: result.RetainedQuotes, - QuoteHashToIndex: result.QuoteHashToIndex, - }, nil + } + quoteCursor, err := quoteCollection.Find(dbCtx, quoteFilter) + if err != nil { + return quotes, retainedQuotes, err + } + defer quoteCursor.Close(dbCtx) + var storedQuotes []StoredPeginQuote + if err = quoteCursor.All(dbCtx, &storedQuotes); err != nil { + return quotes, retainedQuotes, err + } + quoteHashes := make([]string, 0, len(storedQuotes)) + for _, stored := range storedQuotes { + quotes = append(quotes, stored.PeginQuote) + quoteHashes = append(quoteHashes, stored.Hash) + } + if len(quoteHashes) > 0 { + retainedCollection := repo.conn.Collection(RetainedPeginQuoteCollection) + retainedFilter := bson.D{ + primitive.E{ + Key: "quote_hash", + Value: bson.D{ + primitive.E{Key: "$in", Value: quoteHashes}, + }, + }, + } + retainedCursor, err := retainedCollection.Find(dbCtx, retainedFilter) + if err != nil { + return quotes, retainedQuotes, err + } + defer retainedCursor.Close(dbCtx) + if err = retainedCursor.All(dbCtx, &retainedQuotes); err != nil { + return quotes, retainedQuotes, err + } + } + logDbInteraction(Read, map[string]interface{}{ + "quotes": quotes, + "retainedQuotes": retainedQuotes, + }) + return quotes, retainedQuotes, nil } diff --git a/internal/adapters/dataproviders/database/mongo/pegout.go b/internal/adapters/dataproviders/database/mongo/pegout.go index 5add9740..cdf613f9 100644 --- a/internal/adapters/dataproviders/database/mongo/pegout.go +++ b/internal/adapters/dataproviders/database/mongo/pegout.go @@ -322,37 +322,60 @@ func (repo *pegoutMongoRepository) UpsertPegoutDeposits(ctx context.Context, dep return err } -func (repo *pegoutMongoRepository) ListQuotesByDateRange(ctx context.Context, startDate, endDate time.Time) (quote.PegoutQuoteResult, error) { - query := QuoteQuery{ - Ctx: ctx, - Conn: repo.conn, - StartDate: startDate, - EndDate: endDate, - QuoteCollection: PegoutQuoteCollection, - RetainedCollection: RetainedPegoutQuoteCollection, - } - result := ListQuotesByDateRange[quote.PegoutQuote, quote.RetainedPegoutQuote]( - query, - func(doc bson.D) quote.PegoutQuote { - var stored StoredPegoutQuote - bsonBytes, err := bson.Marshal(doc) - if err != nil { - log.Errorf("Error marshaling BSON: %v", err) - return quote.PegoutQuote{} - } - if err := bson.Unmarshal(bsonBytes, &stored); err != nil { - log.Errorf("Error unmarshaling BSON: %v", err) - return quote.PegoutQuote{} - } - return stored.PegoutQuote +func (repo *pegoutMongoRepository) ListQuotesByDateRange(ctx context.Context, startDate, endDate time.Time) ([]quote.PegoutQuote, []quote.RetainedPegoutQuote, error) { + quotes := make([]quote.PegoutQuote, 0) + retainedQuotes := make([]quote.RetainedPegoutQuote, 0) + + dbCtx, cancel := context.WithTimeout(ctx, repo.conn.timeout) + defer cancel() + startTimestamp := int64(startDate.Unix()) + endTimestamp := int64(endDate.Unix()) + quoteCollection := repo.conn.Collection(PegoutQuoteCollection) + quoteFilter := bson.D{ + primitive.E{ + Key: "agreement_timestamp", + Value: bson.D{ + primitive.E{Key: "$gte", Value: startTimestamp}, + primitive.E{Key: "$lte", Value: endTimestamp}, + }, }, - ) - if result.Error != nil { - return quote.PegoutQuoteResult{}, result.Error - } - return quote.PegoutQuoteResult{ - Quotes: result.Quotes, - RetainedQuotes: result.RetainedQuotes, - QuoteHashToIndex: result.QuoteHashToIndex, - }, nil + } + quoteCursor, err := quoteCollection.Find(dbCtx, quoteFilter) + if err != nil { + return quotes, retainedQuotes, err + } + defer quoteCursor.Close(dbCtx) + var storedQuotes []StoredPegoutQuote + if err = quoteCursor.All(dbCtx, &storedQuotes); err != nil { + return quotes, retainedQuotes, err + } + quoteHashes := make([]string, 0, len(storedQuotes)) + for _, stored := range storedQuotes { + quotes = append(quotes, stored.PegoutQuote) + quoteHashes = append(quoteHashes, stored.Hash) + } + if len(quoteHashes) > 0 { + retainedCollection := repo.conn.Collection(RetainedPegoutQuoteCollection) + retainedFilter := bson.D{ + primitive.E{ + Key: "quote_hash", + Value: bson.D{ + primitive.E{Key: "$in", Value: quoteHashes}, + }, + }, + } + retainedCursor, err := retainedCollection.Find(dbCtx, retainedFilter) + if err != nil { + return quotes, retainedQuotes, err + } + defer retainedCursor.Close(dbCtx) + if err = retainedCursor.All(dbCtx, &retainedQuotes); err != nil { + return quotes, retainedQuotes, err + } + } + logDbInteraction(Read, map[string]interface{}{ + "quotes": quotes, + "retainedQuotes": retainedQuotes, + }) + return quotes, retainedQuotes, nil } diff --git a/internal/adapters/entrypoints/rest/assets/static/management.js b/internal/adapters/entrypoints/rest/assets/static/management.js index 94998269..c3e7377b 100644 --- a/internal/adapters/entrypoints/rest/assets/static/management.js +++ b/internal/adapters/entrypoints/rest/assets/static/management.js @@ -577,9 +577,10 @@ const displaySummaryData = (container, data) => { const table = document.createElement('table'); table.classList.add('table', 'table-striped'); const rows = [ - { label: 'Total Accepted Quotes', value: data.totalAcceptedQuotesCount }, - { label: 'Confirmed Quotes', value: data.confirmedQuotesCount }, - { label: 'Total Quoted Amount', value: data.totalQuotedAmount }, + { label: 'Total Quotes', value: data.totalQuotesCount }, + { label: 'Accepted Quotes', value: data.acceptedQuotesCount }, + { label: 'Paid Quotes', value: data.paidQuotesCount }, + { label: 'Paid Quotes Amount', value: data.paidQuotesAmount }, { label: 'Total Accepted Amount', value: data.totalAcceptedQuotedAmount }, { label: 'Total Fees Collected', value: data.totalFeesCollected }, { label: 'Refunded Quotes', value: data.refundedQuotesCount }, diff --git a/internal/adapters/entrypoints/rest/common.go b/internal/adapters/entrypoints/rest/common.go index 3b8969de..670d4e62 100644 --- a/internal/adapters/entrypoints/rest/common.go +++ b/internal/adapters/entrypoints/rest/common.go @@ -16,11 +16,12 @@ import ( const ( HeaderContentType = "Content-Type" -) -const ( ContentTypeJson = "application/json" ContentTypeForm = "application/x-www-form-urlencoded" + + StartDateParam = "startDate" + EndDateParam = "endDate" ) var RequestValidator = validator.New(validator.WithRequiredStructEnabled()) @@ -156,47 +157,35 @@ func RequiredQueryParam(name string) error { return fmt.Errorf("required query parameter %s is missing", name) } -func ValidateDateRange(w http.ResponseWriter, req *http.Request, dateFormat string) (time.Time, time.Time, bool) { - startParam := "startDate" - endParam := "endDate" - start := req.URL.Query().Get(startParam) - end := req.URL.Query().Get(endParam) +func ParseDateRange(req *http.Request, dateFormat string) (time.Time, time.Time, error) { + start := req.URL.Query().Get(StartDateParam) + end := req.URL.Query().Get(EndDateParam) if start == "" || end == "" { missing := []string{} if start == "" { - missing = append(missing, startParam) + missing = append(missing, StartDateParam) } if end == "" { - missing = append(missing, endParam) + missing = append(missing, EndDateParam) } - jsonErr := NewErrorResponseWithDetails("missing required parameters", map[string]any{ - "missing": missing, - }, true) - JsonErrorResponse(w, http.StatusBadRequest, jsonErr) - return time.Time{}, time.Time{}, false + return time.Time{}, time.Time{}, fmt.Errorf("missing required parameters: %v", missing) } - var err error startDate, err := time.Parse(dateFormat, start) if err != nil { - jsonErr := NewErrorResponseWithDetails("invalid date format", DetailsFromError(err), true) - JsonErrorResponse(w, http.StatusBadRequest, jsonErr) - return time.Time{}, time.Time{}, false + return time.Time{}, time.Time{}, fmt.Errorf("invalid start date format: %w", err) } endDate, err := time.Parse(dateFormat, end) if err != nil { - jsonErr := NewErrorResponseWithDetails("invalid date format", DetailsFromError(err), true) - JsonErrorResponse(w, http.StatusBadRequest, jsonErr) - return time.Time{}, time.Time{}, false + return time.Time{}, time.Time{}, fmt.Errorf("invalid end date format: %w", err) } endDate = time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 23, 59, 59, 0, time.UTC) + return startDate, endDate, nil +} + +func ValidateDateRange(startDate, endDate time.Time, dateFormat string) error { if endDate.Before(startDate) { - details := map[string]any{ - startParam: startDate.Format(dateFormat), - endParam: endDate.Format(dateFormat), - } - jsonErr := NewErrorResponseWithDetails("invalid date range", details, true) - JsonErrorResponse(w, http.StatusBadRequest, jsonErr) - return time.Time{}, time.Time{}, false + return fmt.Errorf("invalid date range: end date %s is before start date %s", + endDate.Format(dateFormat), startDate.Format(dateFormat)) } - return startDate, endDate, true + return nil } diff --git a/internal/adapters/entrypoints/rest/common_test.go b/internal/adapters/entrypoints/rest/common_test.go index 6d524e5f..04b2c4b4 100644 --- a/internal/adapters/entrypoints/rest/common_test.go +++ b/internal/adapters/entrypoints/rest/common_test.go @@ -275,34 +275,80 @@ func getDateRangeTestCases() []struct { //nolint:funlen } } -func TestValidateDateRange(t *testing.T) { +func TestParseDateRange(t *testing.T) { dateFormat := "2006-01-02" tests := getDateRangeTestCases() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/test", nil) q := req.URL.Query() for key, value := range tt.queryParams { q.Add(key, value) } req.URL.RawQuery = q.Encode() - startDate, endDate, valid := rest.ValidateDateRange(w, req, dateFormat) - assert.Equal(t, tt.expectedValid, valid) - assert.Equal(t, tt.expectedStatus, w.Code) - if valid { - expectedStartDate, err := time.Parse(dateFormat, tt.queryParams["startDate"]) - require.NoError(t, err) - expectedEndDate, err := time.Parse(dateFormat, tt.queryParams["endDate"]) + startDate, endDate, err := rest.ParseDateRange(req, dateFormat) + if tt.name == "valid_date_range" || tt.name == "endDate_before_startDate" { require.NoError(t, err) + expectedStartDate, parseErr := time.Parse(dateFormat, tt.queryParams["startDate"]) + require.NoError(t, parseErr) + expectedEndDate, parseErr := time.Parse(dateFormat, tt.queryParams["endDate"]) + require.NoError(t, parseErr) expectedEndDate = time.Date(expectedEndDate.Year(), expectedEndDate.Month(), expectedEndDate.Day(), 23, 59, 59, 0, time.UTC) assert.Equal(t, expectedStartDate, startDate) assert.Equal(t, expectedEndDate, endDate) + } else if tt.name == "missing_startDate" || tt.name == "missing_endDate" { + require.Error(t, err) + assert.Contains(t, err.Error(), "missing required parameters") + } else if tt.name == "invalid_startDate_format" { + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid start date format") + } else if tt.name == "invalid_endDate_format" { + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid end date format") + } + }) + } +} + +func TestValidateDateRange(t *testing.T) { + dateFormat := "2006-01-02" + validStart, err := time.Parse(dateFormat, "2023-01-01") + require.NoError(t, err) + validEnd, err := time.Parse(dateFormat, "2023-01-31") + require.NoError(t, err) + validEnd = time.Date(validEnd.Year(), validEnd.Month(), validEnd.Day(), 23, 59, 59, 0, time.UTC) + invalidStart, err := time.Parse(dateFormat, "2023-02-01") + require.NoError(t, err) + invalidEnd, err := time.Parse(dateFormat, "2023-01-31") + require.NoError(t, err) + invalidEnd = time.Date(invalidEnd.Year(), invalidEnd.Month(), invalidEnd.Day(), 23, 59, 59, 0, time.UTC) + tests := []struct { + name string + startDate time.Time + endDate time.Time + expectError bool + }{ + { + name: "valid_date_range", + startDate: validStart, + endDate: validEnd, + expectError: false, + }, + { + name: "end_before_start", + startDate: invalidStart, + endDate: invalidEnd, + expectError: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := rest.ValidateDateRange(tt.startDate, tt.endDate, dateFormat) + if tt.expectError { + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid date range") } else { - var errorResponse rest.ErrorResponse - err := json.NewDecoder(w.Body).Decode(&errorResponse) require.NoError(t, err) - assert.True(t, errorResponse.Recoverable) } }) } diff --git a/internal/adapters/entrypoints/rest/handlers/get_report_summaries.go b/internal/adapters/entrypoints/rest/handlers/get_report_summaries.go index a83c52fc..eea2cdbc 100644 --- a/internal/adapters/entrypoints/rest/handlers/get_report_summaries.go +++ b/internal/adapters/entrypoints/rest/handlers/get_report_summaries.go @@ -14,19 +14,29 @@ import ( // @Description Returns financial data for a given period // @Param startDate query string true "Start date in YYYY-MM-DD format" Format(date) // @Param endDate query string true "End date in YYYY-MM-DD format" Format(date) -// @Success 200 {object} liquidity_provider.SummariesResponse "Financial data for the given period" +// @Success 200 {object} liquidity_provider.SummaryResult "Financial data for the given period" // @Router /report/summaries [get] func NewGetReportSummariesHandler(useCase *liquidity_provider.SummariesUseCase) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { - startDate, endDate, valid := rest.ValidateDateRange(w, req, liquidity_provider.DateFormat) - if !valid { + startDate, endDate, err := rest.ParseDateRange(req, liquidity_provider.DateFormat) + if err != nil { + log.Errorf("Error parsing date range: %v", err) + rest.JsonErrorResponse(w, http.StatusBadRequest, + rest.NewErrorResponseWithDetails("Invalid date range", rest.DetailsFromError(err), true)) + return + } + validateErr := rest.ValidateDateRange(startDate, endDate, liquidity_provider.DateFormat) + if validateErr != nil { + log.Errorf("Error validating date range: %v", validateErr) + rest.JsonErrorResponse(w, http.StatusBadRequest, + rest.NewErrorResponseWithDetails("Invalid date range", rest.DetailsFromError(validateErr), true)) return } response, err := useCase.Run(req.Context(), startDate, endDate) if err != nil { log.Errorf("Error running summaries use case: %v", err) - jsonErr := rest.NewErrorResponseWithDetails(UnknownErrorMessage, rest.DetailsFromError(err), false) - rest.JsonErrorResponse(w, http.StatusInternalServerError, jsonErr) + rest.JsonErrorResponse(w, http.StatusInternalServerError, + rest.NewErrorResponseWithDetails(UnknownErrorMessage, rest.DetailsFromError(err), false)) return } rest.JsonResponseWithBody(w, http.StatusOK, &response) diff --git a/internal/adapters/entrypoints/rest/handlers/get_report_summaries_test.go b/internal/adapters/entrypoints/rest/handlers/get_report_summaries_test.go index ef04ad14..46aad2eb 100644 --- a/internal/adapters/entrypoints/rest/handlers/get_report_summaries_test.go +++ b/internal/adapters/entrypoints/rest/handlers/get_report_summaries_test.go @@ -1,4 +1,4 @@ -package handlers +package handlers_test import ( "context" @@ -9,149 +9,141 @@ import ( "testing" "time" - "github.com/rsksmart/liquidity-provider-server/internal/adapters/entrypoints/rest" + "github.com/rsksmart/liquidity-provider-server/internal/adapters/entrypoints/rest/handlers" + "github.com/rsksmart/liquidity-provider-server/internal/entities" + "github.com/rsksmart/liquidity-provider-server/internal/entities/quote" "github.com/rsksmart/liquidity-provider-server/internal/usecases/liquidity_provider" + "github.com/rsksmart/liquidity-provider-server/test/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) -type MockSummariesUseCase struct { - mock.Mock -} - -func (m *MockSummariesUseCase) Run(ctx context.Context, startDate, endDate time.Time) (liquidity_provider.SummariesResponse, error) { - args := m.Called(ctx, startDate, endDate) - if args.Get(0) == nil { - return liquidity_provider.SummariesResponse{}, args.Error(1) - } - response, ok := args.Get(0).(liquidity_provider.SummariesResponse) - if !ok { - return liquidity_provider.SummariesResponse{}, errors.New("invalid response type") - } - return response, args.Error(1) -} - -func getReportSummariesHandlerForTest(useCase *MockSummariesUseCase) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - startDate, endDate, valid := rest.ValidateDateRange(w, req, liquidity_provider.DateFormat) - if !valid { - return - } - response, err := useCase.Run(req.Context(), startDate, endDate) - if err != nil { - jsonErr := rest.NewErrorResponseWithDetails("An error occurred while processing your request", rest.DetailsFromError(err), false) - rest.JsonErrorResponse(w, http.StatusInternalServerError, jsonErr) - return - } - rest.JsonResponseWithBody(w, http.StatusOK, &response) - } -} - func TestGetReportSummariesHandler(t *testing.T) { //nolint:funlen tests := []struct { name string url string expectedStatus int - mockResponse liquidity_provider.SummariesResponse + mockResponse liquidity_provider.SummaryResult mockErr error + setupMocks func(*testing.T, *mocks.PeginQuoteRepositoryMock, *mocks.PegoutQuoteRepositoryMock) }{ { name: "Success with valid date range", url: "/report/summaries?startDate=2023-01-01&endDate=2023-01-31", expectedStatus: http.StatusOK, - mockResponse: liquidity_provider.SummariesResponse{ + mockResponse: liquidity_provider.SummaryResult{ PeginSummary: liquidity_provider.SummaryData{ - TotalAcceptedQuotesCount: 10, - ConfirmedQuotesCount: 8, - TotalQuotedAmount: "1000", - TotalAcceptedQuotedAmount: "800", - TotalFeesCollected: "50", - RefundedQuotesCount: 2, - TotalPenaltyAmount: "20", - LpEarnings: "30", + TotalQuotesCount: 10, + AcceptedQuotesCount: 8, + PaidQuotesCount: 6, + PaidQuotesAmount: entities.NewWei(1000), + TotalFeesCollected: entities.NewWei(50), + RefundedQuotesCount: 2, + TotalPenaltyAmount: entities.NewWei(20), + LpEarnings: entities.NewWei(30), }, PegoutSummary: liquidity_provider.SummaryData{ - TotalAcceptedQuotesCount: 5, - ConfirmedQuotesCount: 4, - TotalQuotedAmount: "500", - TotalAcceptedQuotedAmount: "400", - TotalFeesCollected: "40", - RefundedQuotesCount: 1, - TotalPenaltyAmount: "0", - LpEarnings: "20", + TotalQuotesCount: 5, + AcceptedQuotesCount: 4, + PaidQuotesCount: 3, + PaidQuotesAmount: entities.NewWei(500), + TotalFeesCollected: entities.NewWei(40), + RefundedQuotesCount: 1, + TotalPenaltyAmount: entities.NewWei(0), + LpEarnings: entities.NewWei(40), }, }, mockErr: nil, + setupMocks: func(t *testing.T, peginRepo *mocks.PeginQuoteRepositoryMock, pegoutRepo *mocks.PegoutQuoteRepositoryMock) { + startDate, err := time.Parse(liquidity_provider.DateFormat, "2023-01-01") + require.NoError(t, err) + endDate, err := time.Parse(liquidity_provider.DateFormat, "2023-01-31") + require.NoError(t, err) + endDate = time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 23, 59, 59, 0, endDate.Location()) + peginRepo.On("ListQuotesByDateRange", mock.Anything, startDate, endDate). + Return([]quote.PeginQuote{}, []quote.RetainedPeginQuote{}, nil) + pegoutRepo.On("ListQuotesByDateRange", mock.Anything, startDate, endDate). + Return([]quote.PegoutQuote{}, []quote.RetainedPegoutQuote{}, nil) + }, }, { name: "Missing startDate parameter", url: "/report/summaries?endDate=2023-01-31", expectedStatus: http.StatusBadRequest, - mockResponse: liquidity_provider.SummariesResponse{}, + mockResponse: liquidity_provider.SummaryResult{}, mockErr: nil, + setupMocks: func(*testing.T, *mocks.PeginQuoteRepositoryMock, *mocks.PegoutQuoteRepositoryMock) {}, }, { name: "Missing endDate parameter", url: "/report/summaries?startDate=2023-01-01", expectedStatus: http.StatusBadRequest, - mockResponse: liquidity_provider.SummariesResponse{}, + mockResponse: liquidity_provider.SummaryResult{}, mockErr: nil, + setupMocks: func(*testing.T, *mocks.PeginQuoteRepositoryMock, *mocks.PegoutQuoteRepositoryMock) {}, }, { name: "Invalid startDate format", url: "/report/summaries?startDate=01/01/2023&endDate=2023-01-31", expectedStatus: http.StatusBadRequest, - mockResponse: liquidity_provider.SummariesResponse{}, + mockResponse: liquidity_provider.SummaryResult{}, mockErr: nil, + setupMocks: func(*testing.T, *mocks.PeginQuoteRepositoryMock, *mocks.PegoutQuoteRepositoryMock) {}, }, { name: "Invalid endDate format", url: "/report/summaries?startDate=2023-01-01&endDate=31/01/2023", expectedStatus: http.StatusBadRequest, - mockResponse: liquidity_provider.SummariesResponse{}, + mockResponse: liquidity_provider.SummaryResult{}, mockErr: nil, + setupMocks: func(*testing.T, *mocks.PeginQuoteRepositoryMock, *mocks.PegoutQuoteRepositoryMock) {}, }, { name: "EndDate before StartDate", url: "/report/summaries?startDate=2023-02-01&endDate=2023-01-31", expectedStatus: http.StatusBadRequest, - mockResponse: liquidity_provider.SummariesResponse{}, + mockResponse: liquidity_provider.SummaryResult{}, mockErr: nil, + setupMocks: func(*testing.T, *mocks.PeginQuoteRepositoryMock, *mocks.PegoutQuoteRepositoryMock) {}, }, { name: "Error in use case", url: "/report/summaries?startDate=2023-01-01&endDate=2023-01-31", expectedStatus: http.StatusInternalServerError, - mockResponse: liquidity_provider.SummariesResponse{}, + mockResponse: liquidity_provider.SummaryResult{}, mockErr: errors.New("test error"), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockUseCase := new(MockSummariesUseCase) - if tt.expectedStatus == http.StatusOK || tt.expectedStatus == http.StatusInternalServerError { + setupMocks: func(t *testing.T, peginRepo *mocks.PeginQuoteRepositoryMock, pegoutRepo *mocks.PegoutQuoteRepositoryMock) { startDate, err := time.Parse(liquidity_provider.DateFormat, "2023-01-01") require.NoError(t, err) endDate, err := time.Parse(liquidity_provider.DateFormat, "2023-01-31") require.NoError(t, err) endDate = time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 23, 59, 59, 0, endDate.Location()) - mockUseCase.On("Run", mock.Anything, startDate, endDate).Return(tt.mockResponse, tt.mockErr) - } - handler := getReportSummariesHandlerForTest(mockUseCase) + peginRepo.On("ListQuotesByDateRange", mock.Anything, startDate, endDate). + Return([]quote.PeginQuote{}, []quote.RetainedPeginQuote{}, errors.New("test error")) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + peginRepoMock := mocks.NewPeginQuoteRepositoryMock(t) + pegoutRepoMock := mocks.NewPegoutQuoteRepositoryMock(t) + tt.setupMocks(t, peginRepoMock, pegoutRepoMock) + useCase := liquidity_provider.NewSummariesUseCase(peginRepoMock, pegoutRepoMock) + handler := handlers.NewGetReportSummariesHandler(useCase) req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, tt.url, nil) require.NoError(t, err) rr := httptest.NewRecorder() handler.ServeHTTP(rr, req) assert.Equal(t, tt.expectedStatus, rr.Code) if tt.expectedStatus == http.StatusOK { - var response liquidity_provider.SummariesResponse + var response liquidity_provider.SummaryResult err = json.Unmarshal(rr.Body.Bytes(), &response) require.NoError(t, err) - - assert.Equal(t, tt.mockResponse, response) + assert.NotNil(t, response) } - mockUseCase.AssertExpectations(t) + peginRepoMock.AssertExpectations(t) + pegoutRepoMock.AssertExpectations(t) }) } } diff --git a/internal/entities/quote/common.go b/internal/entities/quote/common.go index 722c7bc0..3228f892 100644 --- a/internal/entities/quote/common.go +++ b/internal/entities/quote/common.go @@ -4,10 +4,11 @@ import ( "encoding/hex" "errors" "fmt" + "math/big" + "github.com/rsksmart/liquidity-provider-server/internal/entities" "github.com/rsksmart/liquidity-provider-server/internal/entities/utils" log "github.com/sirupsen/logrus" - "math/big" ) type AcceptedQuote struct { diff --git a/internal/entities/quote/pegin_quote.go b/internal/entities/quote/pegin_quote.go index ed4e48d5..fd3d8dbe 100644 --- a/internal/entities/quote/pegin_quote.go +++ b/internal/entities/quote/pegin_quote.go @@ -36,13 +36,7 @@ type PeginQuoteRepository interface { GetRetainedQuoteByState(ctx context.Context, states ...PeginState) ([]RetainedPeginQuote, error) // DeleteQuotes deletes both regular and retained quotes DeleteQuotes(ctx context.Context, quotes []string) (uint, error) - ListQuotesByDateRange(ctx context.Context, startDate, endDate time.Time) (PeginQuoteResult, error) -} - -type PeginQuoteResult struct { - Quotes []PeginQuote - RetainedQuotes []RetainedPeginQuote - QuoteHashToIndex map[string]int + ListQuotesByDateRange(ctx context.Context, startDate, endDate time.Time) ([]PeginQuote, []RetainedPeginQuote, error) } type CreatedPeginQuote struct { diff --git a/internal/entities/quote/pegout_quote.go b/internal/entities/quote/pegout_quote.go index f119833d..f06840df 100644 --- a/internal/entities/quote/pegout_quote.go +++ b/internal/entities/quote/pegout_quote.go @@ -43,13 +43,7 @@ type PegoutQuoteRepository interface { DeleteQuotes(ctx context.Context, quotes []string) (uint, error) UpsertPegoutDeposit(ctx context.Context, deposit PegoutDeposit) error UpsertPegoutDeposits(ctx context.Context, deposits []PegoutDeposit) error - ListQuotesByDateRange(ctx context.Context, startDate, endDate time.Time) (PegoutQuoteResult, error) -} - -type PegoutQuoteResult struct { - Quotes []PegoutQuote - RetainedQuotes []RetainedPegoutQuote - QuoteHashToIndex map[string]int + ListQuotesByDateRange(ctx context.Context, startDate, endDate time.Time) ([]PegoutQuote, []RetainedPegoutQuote, error) } type CreatedPegoutQuote struct { diff --git a/internal/usecases/liquidity_provider/summaries.go b/internal/usecases/liquidity_provider/summaries.go index f45b20ad..3b7293b5 100644 --- a/internal/usecases/liquidity_provider/summaries.go +++ b/internal/usecases/liquidity_provider/summaries.go @@ -14,54 +14,21 @@ const ( DateFormat = time.DateOnly ) -type SummariesResponse struct { +type SummaryResult struct { PeginSummary SummaryData `json:"peginSummary"` PegoutSummary SummaryData `json:"pegoutSummary"` } type SummaryData struct { - TotalAcceptedQuotesCount int64 `json:"totalAcceptedQuotesCount"` - ConfirmedQuotesCount int64 `json:"confirmedQuotesCount"` - TotalQuotedAmount string `json:"totalQuotedAmount"` - TotalAcceptedQuotedAmount string `json:"totalAcceptedQuotedAmount"` - TotalFeesCollected string `json:"totalFeesCollected"` - RefundedQuotesCount int64 `json:"refundedQuotesCount"` - TotalPenaltyAmount string `json:"totalPenaltyAmount"` - LpEarnings string `json:"lpEarnings"` -} - -type Quote interface { - Total() *entities.Wei -} - -type RetainedQuote interface { - GetQuoteHash() string -} - -type FeeProvider interface { - GetCallFee() *entities.Wei - GetGasFee() *entities.Wei - GetProductFee() *entities.Wei - GetPenaltyFee() *entities.Wei -} - -type feeAdapter struct { - callFee *entities.Wei - gasFee *entities.Wei - productFee *entities.Wei - penaltyFee *entities.Wei -} - -type QuoteResult[Q any, R RetainedQuote] interface { - GetQuotes() []Q - GetRetainedQuotes() []R - GetQuoteHashToIndex() map[string]int -} - -type quoteResultAdapter[Q any, R RetainedQuote] struct { - quotes []Q - retainedQuotes []R - quoteHashToIndex map[string]int + TotalQuotesCount int64 `json:"totalQuotesCount"` + AcceptedQuotesCount int64 `json:"acceptedQuotesCount"` + PaidQuotesCount int64 `json:"paidQuotesCount"` + PaidQuotesAmount *entities.Wei `json:"paidQuotesAmount"` + TotalAcceptedQuotedAmount *entities.Wei `json:"totalAcceptedQuotedAmount"` + TotalFeesCollected *entities.Wei `json:"totalFeesCollected"` + RefundedQuotesCount int64 `json:"refundedQuotesCount"` + TotalPenaltyAmount *entities.Wei `json:"totalPenaltyAmount"` + LpEarnings *entities.Wei `json:"lpEarnings"` } type SummariesUseCase struct { @@ -71,285 +38,244 @@ type SummariesUseCase struct { func NewSummaryData() SummaryData { return SummaryData{ - TotalAcceptedQuotesCount: 0, - ConfirmedQuotesCount: 0, - TotalQuotedAmount: "0", - TotalAcceptedQuotedAmount: "0", - TotalFeesCollected: "0", + TotalQuotesCount: 0, + AcceptedQuotesCount: 0, + PaidQuotesCount: 0, + PaidQuotesAmount: entities.NewWei(0), + TotalAcceptedQuotedAmount: entities.NewWei(0), + TotalFeesCollected: entities.NewWei(0), RefundedQuotesCount: 0, - TotalPenaltyAmount: "0", - LpEarnings: "0", + TotalPenaltyAmount: entities.NewWei(0), + LpEarnings: entities.NewWei(0), } } -func NewSummariesUseCase( - peginRepo quote.PeginQuoteRepository, - pegoutRepo quote.PegoutQuoteRepository, -) *SummariesUseCase { - return &SummariesUseCase{ - peginRepo: peginRepo, - pegoutRepo: pegoutRepo, - } +func NewSummariesUseCase(peginRepo quote.PeginQuoteRepository, pegoutRepo quote.PegoutQuoteRepository) *SummariesUseCase { + return &SummariesUseCase{peginRepo: peginRepo, pegoutRepo: pegoutRepo} } -func (u *SummariesUseCase) Run(ctx context.Context, startDate, endDate time.Time) (SummariesResponse, error) { +func (u *SummariesUseCase) Run(ctx context.Context, startDate, endDate time.Time) (SummaryResult, error) { peginData, err := u.aggregatePeginData(ctx, startDate, endDate) if err != nil { log.Errorf("Error aggregating pegin data: %v", err) - return SummariesResponse{}, err + return SummaryResult{}, err } pegoutData, err := u.aggregatePegoutData(ctx, startDate, endDate) if err != nil { log.Errorf("Error aggregating pegout data: %v", err) - return SummariesResponse{}, err + return SummaryResult{}, err } - return SummariesResponse{ - PeginSummary: peginData, - PegoutSummary: pegoutData, - }, nil + return SummaryResult{PeginSummary: peginData, PegoutSummary: pegoutData}, nil } -func mapPeginQuote(q *quote.PeginQuote) FeeProvider { - if q == nil { - return &feeAdapter{} +func processPeginRetainedQuote( + ctx context.Context, + retained quote.RetainedPeginQuote, + quotesByHash map[string]*quote.PeginQuote, + getQuote func(context.Context, string) (*quote.PeginQuote, error), + data *SummaryData, + totalAmount, acceptedTotalAmount, totalFees, callFees, totalPenalty *entities.Wei, +) { + hash := retained.QuoteHash + q, exists := quotesByHash[hash] + if !exists { + var err error + q, err = getQuote(ctx, hash) + if err != nil || q == nil { + if err != nil { + log.Errorf("Error getting quote %s: %v", hash, err) + } + return + } + quotesByHash[hash] = q + totalAmount.Add(totalAmount, q.Total()) + } + callFee, gasFee, productFee, penaltyFee := getPeginFees(q) + acceptedTotalAmount.Add(acceptedTotalAmount, q.Total()) + if isPeginPaidQuote(retained) { + data.PaidQuotesCount++ + callFees.Add(callFees, callFee) + totalFees.Add(totalFees, callFee) + totalFees.Add(totalFees, gasFee) + totalFees.Add(totalFees, productFee) } - return &feeAdapter{ - callFee: q.CallFee, - gasFee: q.GasFee, - productFee: entities.NewUWei(q.ProductFeeAmount), - penaltyFee: q.PenaltyFee, + if isPeginRefundedQuote(retained) { + data.RefundedQuotesCount++ + totalPenalty.Add(totalPenalty, penaltyFee) } } -func mapPegoutQuote(q *quote.PegoutQuote) FeeProvider { - if q == nil { - return &feeAdapter{} +func processPegoutRetainedQuote( + ctx context.Context, + retained quote.RetainedPegoutQuote, + quotesByHash map[string]*quote.PegoutQuote, + getQuote func(context.Context, string) (*quote.PegoutQuote, error), + data *SummaryData, + totalAmount, acceptedTotalAmount, totalFees, callFees, totalPenalty *entities.Wei, +) { + hash := retained.QuoteHash + q, exists := quotesByHash[hash] + if !exists { + var err error + q, err = getQuote(ctx, hash) + if err != nil || q == nil { + if err != nil { + log.Errorf("Error getting quote %s: %v", hash, err) + } + return + } + quotesByHash[hash] = q + totalAmount.Add(totalAmount, q.Total()) + } + callFee, gasFee, productFee, penaltyFee := getPegoutFees(q) + acceptedTotalAmount.Add(acceptedTotalAmount, q.Total()) + if isPegoutPaidQuote(retained) { + data.PaidQuotesCount++ + callFees.Add(callFees, callFee) + totalFees.Add(totalFees, callFee) + totalFees.Add(totalFees, gasFee) + totalFees.Add(totalFees, productFee) } - return &feeAdapter{ - callFee: q.CallFee, - gasFee: q.GasFee, - productFee: entities.NewUWei(q.ProductFeeAmount), - penaltyFee: entities.NewUWei(q.PenaltyFee), + if isPegoutRefundedQuote(retained) { + data.RefundedQuotesCount++ + totalPenalty.Add(totalPenalty, penaltyFee) } } -func processQuoteData[Q any, R RetainedQuote, F FeeProvider]( - ctx context.Context, - quotes []Q, - retainedQuotes []R, - quoteHashToIndex map[string]int, - getQuote func(context.Context, string) (*Q, error), - isAccepted func(R) bool, - isRefunded func(R) bool, - feeProvider func(*Q) F, -) SummaryData { +func processPeginQuoteData(ctx context.Context, quotes []*quote.PeginQuote, retainedQuotes []quote.RetainedPeginQuote, getQuote func(context.Context, string) (*quote.PeginQuote, error)) SummaryData { data := NewSummaryData() - totalAmount := calculateTotalAmount(quotes) + data.TotalQuotesCount = int64(len(quotes)) + totalAmount := entities.NewWei(0) + for i := range quotes { + totalAmount.Add(totalAmount, quotes[i].Total()) + } + data.PaidQuotesAmount = totalAmount if len(retainedQuotes) == 0 { - data.TotalAcceptedQuotesCount = int64(len(quotes)) - data.TotalQuotedAmount = totalAmount.String() return data } - data.TotalAcceptedQuotesCount = int64(len(retainedQuotes)) - quotesByHash := createQuoteHashMap(quotes, quoteHashToIndex) - fetchMissingQuotes(ctx, quotesByHash, retainedQuotes, totalAmount, getQuote) - data.TotalQuotedAmount = totalAmount.String() + data.AcceptedQuotesCount = int64(len(retainedQuotes)) + quotesByHash := make(map[string]*quote.PeginQuote) acceptedTotalAmount := entities.NewWei(0) totalFees := entities.NewWei(0) callFees := entities.NewWei(0) totalPenalty := entities.NewWei(0) for _, retained := range retainedQuotes { - quoteHash := retained.GetQuoteHash() - quoteObj, exists := quotesByHash[quoteHash] - if !exists { - continue - } - fees := feeProvider(quoteObj) - if isAccepted(retained) { - data.ConfirmedQuotesCount++ - if q, ok := any(quoteObj).(Quote); ok { - acceptedTotalAmount.Add(acceptedTotalAmount, q.Total()) - } - callFee := fees.GetCallFee() - callFees.Add(callFees, callFee) - totalFees.Add(totalFees, callFee) - totalFees.Add(totalFees, fees.GetGasFee()) - totalFees.Add(totalFees, fees.GetProductFee()) - } - if isRefunded(retained) { - data.RefundedQuotesCount++ - totalPenalty.Add(totalPenalty, fees.GetPenaltyFee()) - } + processPeginRetainedQuote(ctx, retained, quotesByHash, getQuote, &data, + totalAmount, acceptedTotalAmount, totalFees, callFees, totalPenalty) } lpEarnings := new(entities.Wei) lpEarnings.Sub(callFees, totalPenalty) - data.TotalAcceptedQuotedAmount = acceptedTotalAmount.String() - data.TotalFeesCollected = totalFees.String() - data.TotalPenaltyAmount = totalPenalty.String() - data.LpEarnings = lpEarnings.String() + data.TotalAcceptedQuotedAmount = acceptedTotalAmount + data.TotalFeesCollected = totalFees + data.TotalPenaltyAmount = totalPenalty + data.LpEarnings = lpEarnings return data } -func calculateTotalAmount[T any](quotes []T) *entities.Wei { +func processPegoutQuoteData(ctx context.Context, quotes []*quote.PegoutQuote, retainedQuotes []quote.RetainedPegoutQuote, getQuote func(context.Context, string) (*quote.PegoutQuote, error)) SummaryData { + data := NewSummaryData() + data.TotalQuotesCount = int64(len(quotes)) totalAmount := entities.NewWei(0) for i := range quotes { - var total *entities.Wei - if q, ok := any("es[i]).(Quote); ok { - total = q.Total() - } - if total != nil { - totalAmount.Add(totalAmount, total) - } + totalAmount.Add(totalAmount, quotes[i].Total()) } - return totalAmount -} - -func createQuoteHashMap[T any](quotes []T, quoteHashToIndex map[string]int) map[string]*T { - quotesByHash := make(map[string]*T, len(quoteHashToIndex)) - for hash, index := range quoteHashToIndex { - if index >= 0 && index < len(quotes) { - quoteCopy := quotes[index] - quotesByHash[hash] = "eCopy - } + data.PaidQuotesAmount = totalAmount + if len(retainedQuotes) == 0 { + return data } - return quotesByHash -} - -func fetchMissingQuotes[Q any, R RetainedQuote]( - ctx context.Context, - quotesByHash map[string]*Q, - retainedQuotes []R, - totalAmount *entities.Wei, - getQuote func(context.Context, string) (*Q, error), -) { + data.AcceptedQuotesCount = int64(len(retainedQuotes)) + quotesByHash := make(map[string]*quote.PegoutQuote) + acceptedTotalAmount := entities.NewWei(0) + totalFees := entities.NewWei(0) + callFees := entities.NewWei(0) + totalPenalty := entities.NewWei(0) for _, retained := range retainedQuotes { - quoteHash := retained.GetQuoteHash() - if _, exists := quotesByHash[quoteHash]; exists { - continue - } - quoteObj, err := getQuote(ctx, quoteHash) - if err != nil { - log.Errorf("Error getting quote %s: %v", quoteHash, err) - continue - } - if quoteObj == nil { - log.Debugf("Quote not found for hash %s", quoteHash) - continue - } - quotesByHash[quoteHash] = quoteObj - if q, ok := any(quoteObj).(Quote); ok { - totalAmount.Add(totalAmount, q.Total()) - } + processPegoutRetainedQuote(ctx, retained, quotesByHash, getQuote, &data, + totalAmount, acceptedTotalAmount, totalFees, callFees, totalPenalty) } + lpEarnings := new(entities.Wei) + lpEarnings.Sub(callFees, totalPenalty) + data.TotalAcceptedQuotedAmount = acceptedTotalAmount + data.TotalFeesCollected = totalFees + data.TotalPenaltyAmount = totalPenalty + data.LpEarnings = lpEarnings + return data } -func adaptPeginResult(result quote.PeginQuoteResult) QuoteResult[quote.PeginQuote, quote.RetainedPeginQuote] { - return quoteResultAdapter[quote.PeginQuote, quote.RetainedPeginQuote]{ - quotes: result.Quotes, - retainedQuotes: result.RetainedQuotes, - quoteHashToIndex: result.QuoteHashToIndex, +func (u *SummariesUseCase) aggregatePeginData(ctx context.Context, startDate, endDate time.Time) (SummaryData, error) { + var ( + quotes []quote.PeginQuote + retainedQuotes []quote.RetainedPeginQuote + err error + ) + quotes, retainedQuotes, err = u.peginRepo.ListQuotesByDateRange(ctx, startDate, endDate) + if err != nil { + return NewSummaryData(), err } -} - -func adaptPegoutResult(result quote.PegoutQuoteResult) QuoteResult[quote.PegoutQuote, quote.RetainedPegoutQuote] { - return quoteResultAdapter[quote.PegoutQuote, quote.RetainedPegoutQuote]{ - quotes: result.Quotes, - retainedQuotes: result.RetainedQuotes, - quoteHashToIndex: result.QuoteHashToIndex, + quotesPtr := make([]*quote.PeginQuote, len(quotes)) + for i := range quotes { + quotesPtr[i] = "es[i] } + getQuote := func(ctx context.Context, hash string) (*quote.PeginQuote, error) { + q, err := u.peginRepo.GetQuote(ctx, hash) + return q, err + } + return processPeginQuoteData(ctx, quotesPtr, retainedQuotes, getQuote), nil } -func aggregateData[Q any, RQ RetainedQuote]( - ctx context.Context, - startDate, endDate time.Time, - listQuotes func(context.Context, time.Time, time.Time) (QuoteResult[Q, RQ], error), - getQuote func(context.Context, string) (*Q, error), - isAccepted func(RQ) bool, - isRefunded func(RQ) bool, - toFeeProvider func(*Q) FeeProvider, -) (SummaryData, error) { - result, err := listQuotes(ctx, startDate, endDate) +func (u *SummariesUseCase) aggregatePegoutData(ctx context.Context, startDate, endDate time.Time) (SummaryData, error) { + var ( + quotes []quote.PegoutQuote + retainedQuotes []quote.RetainedPegoutQuote + err error + ) + quotes, retainedQuotes, err = u.pegoutRepo.ListQuotesByDateRange(ctx, startDate, endDate) if err != nil { - log.Errorf("Error listing quotes: %v", err) return NewSummaryData(), err } - return processQuoteData( - ctx, - result.GetQuotes(), - result.GetRetainedQuotes(), - result.GetQuoteHashToIndex(), - getQuote, - isAccepted, - isRefunded, - toFeeProvider, - ), nil -} - -func (u *SummariesUseCase) aggregatePeginData(ctx context.Context, startDate, endDate time.Time) (SummaryData, error) { - listPeginQuotes := func(ctx context.Context, start, end time.Time) (QuoteResult[quote.PeginQuote, quote.RetainedPeginQuote], error) { - result, err := u.peginRepo.ListQuotesByDateRange(ctx, start, end) - if err != nil { - return nil, err - } - return adaptPeginResult(result), nil + quotesPtr := make([]*quote.PegoutQuote, len(quotes)) + for i := range quotes { + quotesPtr[i] = "es[i] } - return aggregateData( - ctx, startDate, endDate, - listPeginQuotes, - u.peginRepo.GetQuote, - isAcceptedPegin, - isRefundedPegin, - mapPeginQuote, - ) + getQuote := func(ctx context.Context, hash string) (*quote.PegoutQuote, error) { + q, err := u.pegoutRepo.GetQuote(ctx, hash) + return q, err + } + return processPegoutQuoteData(ctx, quotesPtr, retainedQuotes, getQuote), nil } -func (u *SummariesUseCase) aggregatePegoutData(ctx context.Context, startDate, endDate time.Time) (SummaryData, error) { - listPegoutQuotes := func(ctx context.Context, start, end time.Time) (QuoteResult[quote.PegoutQuote, quote.RetainedPegoutQuote], error) { - result, err := u.pegoutRepo.ListQuotesByDateRange(ctx, start, end) - if err != nil { - return nil, err - } - return adaptPegoutResult(result), nil +func getPeginFees(q *quote.PeginQuote) (callFee, gasFee, productFee, penaltyFee *entities.Wei) { + callFee, gasFee, productFee, penaltyFee = entities.NewWei(0), entities.NewWei(0), entities.NewWei(0), entities.NewWei(0) + if q != nil { + callFee, gasFee = q.CallFee, q.GasFee + productFee = entities.NewUWei(q.ProductFeeAmount) + penaltyFee = q.PenaltyFee } - return aggregateData( - ctx, startDate, endDate, - listPegoutQuotes, - u.pegoutRepo.GetQuote, - isAcceptedPegout, - isRefundedPegout, - mapPegoutQuote, - ) + return } -func (a *feeAdapter) GetCallFee() *entities.Wei { return a.callFee } -func (a *feeAdapter) GetGasFee() *entities.Wei { return a.gasFee } -func (a *feeAdapter) GetProductFee() *entities.Wei { return a.productFee } -func (a *feeAdapter) GetPenaltyFee() *entities.Wei { return a.penaltyFee } - -func isAcceptedPegout(retained quote.RetainedPegoutQuote) bool { - return retained.State == quote.PegoutStateSendPegoutSucceeded +func getPegoutFees(q *quote.PegoutQuote) (callFee, gasFee, productFee, penaltyFee *entities.Wei) { + callFee, gasFee, productFee, penaltyFee = entities.NewWei(0), entities.NewWei(0), entities.NewWei(0), entities.NewWei(0) + if q != nil { + callFee, gasFee = q.CallFee, q.GasFee + productFee = entities.NewUWei(q.ProductFeeAmount) + penaltyFee = entities.NewUWei(q.PenaltyFee) + } + return } -func isAcceptedPegin(retained quote.RetainedPeginQuote) bool { +func isPeginPaidQuote(retained quote.RetainedPeginQuote) bool { return retained.State == quote.PeginStateCallForUserSucceeded } -func isRefundedPegout(retained quote.RetainedPegoutQuote) bool { - return retained.State == quote.PegoutStateRefundPegOutSucceeded +func isPegoutPaidQuote(retained quote.RetainedPegoutQuote) bool { + return retained.State == quote.PegoutStateSendPegoutSucceeded } -func isRefundedPegin(retained quote.RetainedPeginQuote) bool { +func isPeginRefundedQuote(retained quote.RetainedPeginQuote) bool { return retained.State == quote.PeginStateRegisterPegInSucceeded } -func (a quoteResultAdapter[Q, R]) GetQuotes() []Q { - return a.quotes -} - -func (a quoteResultAdapter[Q, R]) GetRetainedQuotes() []R { - return a.retainedQuotes -} - -func (a quoteResultAdapter[Q, R]) GetQuoteHashToIndex() map[string]int { - return a.quoteHashToIndex +func isPegoutRefundedQuote(retained quote.RetainedPegoutQuote) bool { + return retained.State == quote.PegoutStateRefundPegOutSucceeded || retained.State == quote.PegoutStateBridgeTxSucceeded } diff --git a/internal/usecases/liquidity_provider/summaries_test.go b/internal/usecases/liquidity_provider/summaries_test.go index 18ab828a..a5e67a4f 100644 --- a/internal/usecases/liquidity_provider/summaries_test.go +++ b/internal/usecases/liquidity_provider/summaries_test.go @@ -9,210 +9,18 @@ import ( "github.com/rsksmart/liquidity-provider-server/internal/entities" "github.com/rsksmart/liquidity-provider-server/internal/entities/quote" "github.com/rsksmart/liquidity-provider-server/internal/usecases/liquidity_provider" + "github.com/rsksmart/liquidity-provider-server/test/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) -type mockPeginQuoteRepo struct { - mock.Mock -} - -func (m *mockPeginQuoteRepo) InsertQuote(ctx context.Context, q quote.CreatedPeginQuote) error { - args := m.Called(ctx, q) - return args.Error(0) -} - -func (m *mockPeginQuoteRepo) InsertRetainedQuote(ctx context.Context, q quote.RetainedPeginQuote) error { - args := m.Called(ctx, q) - return args.Error(0) -} - -func (m *mockPeginQuoteRepo) UpdateRetainedQuote(ctx context.Context, q quote.RetainedPeginQuote) error { - args := m.Called(ctx, q) - return args.Error(0) -} - -func (m *mockPeginQuoteRepo) UpdateRetainedQuotes(ctx context.Context, quotes []quote.RetainedPeginQuote) error { - args := m.Called(ctx, quotes) - return args.Error(0) -} - -func (m *mockPeginQuoteRepo) GetQuote(ctx context.Context, hash string) (*quote.PeginQuote, error) { - args := m.Called(ctx, hash) - if args.Get(0) == nil { - return nil, args.Error(1) - } - quote, ok := args.Get(0).(*quote.PeginQuote) - if !ok && args.Get(0) != nil { - return nil, errors.New("invalid pegin quote type") - } - return quote, args.Error(1) -} - -func (m *mockPeginQuoteRepo) GetPeginCreationData(ctx context.Context, hash string) quote.PeginCreationData { - args := m.Called(ctx, hash) - data, ok := args.Get(0).(quote.PeginCreationData) - if !ok && args.Get(0) != nil { - panic("invalid pegin creation data type") - } - return data -} - -func (m *mockPeginQuoteRepo) GetRetainedQuote(ctx context.Context, hash string) (*quote.RetainedPeginQuote, error) { - args := m.Called(ctx, hash) - if args.Get(0) == nil { - return nil, args.Error(1) - } - quote, ok := args.Get(0).(*quote.RetainedPeginQuote) - if !ok && args.Get(0) != nil { - return nil, errors.New("invalid retained pegin quote type") - } - return quote, args.Error(1) -} - -func (m *mockPeginQuoteRepo) GetRetainedQuoteByState(ctx context.Context, states ...quote.PeginState) ([]quote.RetainedPeginQuote, error) { - args := m.Called(ctx, states) - quotes, ok := args.Get(0).([]quote.RetainedPeginQuote) - if !ok && args.Get(0) != nil { - return nil, errors.New("invalid retained pegin quotes type") - } - return quotes, args.Error(1) -} - -func (m *mockPeginQuoteRepo) ListQuotesByDateRange(ctx context.Context, startDate, endDate time.Time) (quote.PeginQuoteResult, error) { - args := m.Called(ctx, startDate, endDate) - if args.Get(0) == nil { - return quote.PeginQuoteResult{}, args.Error(1) - } - result, ok := args.Get(0).(quote.PeginQuoteResult) - if !ok { - return quote.PeginQuoteResult{}, errors.New("invalid pegin quote result type") - } - return result, args.Error(1) -} - -func (m *mockPeginQuoteRepo) DeleteQuotes(ctx context.Context, quotes []string) (uint, error) { - args := m.Called(ctx, quotes) - count, ok := args.Get(0).(uint) - if !ok && args.Get(0) != nil { - return 0, errors.New("invalid count type") - } - return count, args.Error(1) -} - -type mockPegoutQuoteRepo struct { - mock.Mock -} - -func (m *mockPegoutQuoteRepo) InsertQuote(ctx context.Context, q quote.CreatedPegoutQuote) error { - args := m.Called(ctx, q) - return args.Error(0) -} - -func (m *mockPegoutQuoteRepo) InsertRetainedQuote(ctx context.Context, q quote.RetainedPegoutQuote) error { - args := m.Called(ctx, q) - return args.Error(0) -} - -func (m *mockPegoutQuoteRepo) UpdateRetainedQuote(ctx context.Context, q quote.RetainedPegoutQuote) error { - args := m.Called(ctx, q) - return args.Error(0) -} - -func (m *mockPegoutQuoteRepo) UpdateRetainedQuotes(ctx context.Context, quotes []quote.RetainedPegoutQuote) error { - args := m.Called(ctx, quotes) - return args.Error(0) -} - -func (m *mockPegoutQuoteRepo) GetQuote(ctx context.Context, hash string) (*quote.PegoutQuote, error) { - args := m.Called(ctx, hash) - if args.Get(0) == nil { - return nil, args.Error(1) - } - quote, ok := args.Get(0).(*quote.PegoutQuote) - if !ok && args.Get(0) != nil { - return nil, errors.New("invalid pegout quote type") - } - return quote, args.Error(1) -} - -func (m *mockPegoutQuoteRepo) GetPegoutCreationData(ctx context.Context, hash string) quote.PegoutCreationData { - args := m.Called(ctx, hash) - data, ok := args.Get(0).(quote.PegoutCreationData) - if !ok && args.Get(0) != nil { - panic("invalid pegout creation data type") - } - return data -} - -func (m *mockPegoutQuoteRepo) GetRetainedQuote(ctx context.Context, hash string) (*quote.RetainedPegoutQuote, error) { - args := m.Called(ctx, hash) - if args.Get(0) == nil { - return nil, args.Error(1) - } - quote, ok := args.Get(0).(*quote.RetainedPegoutQuote) - if !ok && args.Get(0) != nil { - return nil, errors.New("invalid retained pegout quote type") - } - return quote, args.Error(1) -} - -func (m *mockPegoutQuoteRepo) GetRetainedQuoteByState(ctx context.Context, states ...quote.PegoutState) ([]quote.RetainedPegoutQuote, error) { - args := m.Called(ctx, states) - quotes, ok := args.Get(0).([]quote.RetainedPegoutQuote) - if !ok && args.Get(0) != nil { - return nil, errors.New("invalid retained pegout quotes type") - } - return quotes, args.Error(1) -} - -func (m *mockPegoutQuoteRepo) ListPegoutDepositsByAddress(ctx context.Context, address string) ([]quote.PegoutDeposit, error) { - args := m.Called(ctx, address) - deposits, ok := args.Get(0).([]quote.PegoutDeposit) - if !ok && args.Get(0) != nil { - return nil, errors.New("invalid pegout deposits type") - } - return deposits, args.Error(1) -} - -func (m *mockPegoutQuoteRepo) ListQuotesByDateRange(ctx context.Context, startDate, endDate time.Time) (quote.PegoutQuoteResult, error) { - args := m.Called(ctx, startDate, endDate) - if args.Get(0) == nil { - return quote.PegoutQuoteResult{}, args.Error(1) - } - result, ok := args.Get(0).(quote.PegoutQuoteResult) - if !ok { - return quote.PegoutQuoteResult{}, errors.New("invalid pegout quote result type") - } - return result, args.Error(1) -} - -func (m *mockPegoutQuoteRepo) DeleteQuotes(ctx context.Context, quotes []string) (uint, error) { - args := m.Called(ctx, quotes) - count, ok := args.Get(0).(uint) - if !ok && args.Get(0) != nil { - return 0, errors.New("invalid count type") - } - return count, args.Error(1) -} - -func (m *mockPegoutQuoteRepo) UpsertPegoutDeposit(ctx context.Context, deposit quote.PegoutDeposit) error { - args := m.Called(ctx, deposit) - return args.Error(0) -} - -func (m *mockPegoutQuoteRepo) UpsertPegoutDeposits(ctx context.Context, deposits []quote.PegoutDeposit) error { - args := m.Called(ctx, deposits) - return args.Error(0) -} - func TestSummariesUseCase_Run(t *testing.T) { //nolint:funlen,maintidx startDate := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC) endDate := time.Date(2023, 1, 31, 23, 59, 59, 0, time.UTC) t.Run("Success with full set of data", func(t *testing.T) { - peginRepo := new(mockPeginQuoteRepo) - pegoutRepo := new(mockPegoutQuoteRepo) + peginRepo := mocks.NewPeginQuoteRepositoryMock(t) + pegoutRepo := mocks.NewPegoutQuoteRepositoryMock(t) peginQuotes := []quote.PeginQuote{ { Value: entities.NewWei(100), @@ -276,19 +84,13 @@ func TestSummariesUseCase_Run(t *testing.T) { //nolint:funlen,maintidx }, } peginRepo.On("ListQuotesByDateRange", mock.Anything, startDate, endDate). - Return(quote.PeginQuoteResult{ - Quotes: peginQuotes, - RetainedQuotes: retainedPeginQuotes, - }, nil) + Return(peginQuotes, retainedPeginQuotes, nil) peginRepo.On("GetQuote", mock.Anything, "hash1"). Return(&peginQuotes[0], nil) peginRepo.On("GetQuote", mock.Anything, "hash2"). Return(&peginQuotes[1], nil) pegoutRepo.On("ListQuotesByDateRange", mock.Anything, startDate, endDate). - Return(quote.PegoutQuoteResult{ - Quotes: pegoutQuotes, - RetainedQuotes: retainedPegoutQuotes, - }, nil) + Return(pegoutQuotes, retainedPegoutQuotes, nil) pegoutRepo.On("GetQuote", mock.Anything, "hash3"). Return(&pegoutQuotes[0], nil) pegoutRepo.On("GetQuote", mock.Anything, "hash4"). @@ -296,28 +98,26 @@ func TestSummariesUseCase_Run(t *testing.T) { //nolint:funlen,maintidx useCase := liquidity_provider.NewSummariesUseCase(peginRepo, pegoutRepo) result, err := useCase.Run(context.Background(), startDate, endDate) require.NoError(t, err) - assert.Equal(t, int64(len(retainedPeginQuotes)), result.PeginSummary.TotalAcceptedQuotesCount) - assert.Equal(t, int64(1), result.PeginSummary.ConfirmedQuotesCount) - assert.Equal(t, int64(0), result.PeginSummary.RefundedQuotesCount) - assert.Equal(t, "660", result.PeginSummary.TotalQuotedAmount) - assert.Equal(t, "110", result.PeginSummary.TotalAcceptedQuotedAmount) - assert.Equal(t, "10", result.PeginSummary.TotalFeesCollected) - assert.Equal(t, "0", result.PeginSummary.TotalPenaltyAmount) - assert.Equal(t, "5", result.PeginSummary.LpEarnings) - assert.Equal(t, int64(len(retainedPegoutQuotes)), result.PegoutSummary.TotalAcceptedQuotesCount) - assert.Equal(t, int64(1), result.PegoutSummary.ConfirmedQuotesCount) - assert.Equal(t, int64(0), result.PegoutSummary.RefundedQuotesCount) - assert.Equal(t, "1540", result.PegoutSummary.TotalQuotedAmount) - assert.Equal(t, "330", result.PegoutSummary.TotalAcceptedQuotedAmount) - assert.Equal(t, "30", result.PegoutSummary.TotalFeesCollected) - assert.Equal(t, "0", result.PegoutSummary.TotalPenaltyAmount) - assert.Equal(t, "15", result.PegoutSummary.LpEarnings) + assert.Equal(t, int64(len(peginQuotes)), result.PeginSummary.TotalQuotesCount) + assert.Equal(t, int64(len(retainedPeginQuotes)), result.PeginSummary.AcceptedQuotesCount) + assert.Equal(t, int64(1), result.PeginSummary.PaidQuotesCount) + assert.Equal(t, 0, result.PeginSummary.PaidQuotesAmount.Cmp(entities.NewWei(660))) + assert.Equal(t, 0, result.PeginSummary.TotalFeesCollected.Cmp(entities.NewWei(10))) + assert.Equal(t, 0, result.PeginSummary.TotalPenaltyAmount.Cmp(entities.NewWei(0))) + assert.Equal(t, 0, result.PeginSummary.LpEarnings.Cmp(entities.NewWei(5))) + assert.Equal(t, int64(len(pegoutQuotes)), result.PegoutSummary.TotalQuotesCount) + assert.Equal(t, int64(len(retainedPegoutQuotes)), result.PegoutSummary.AcceptedQuotesCount) + assert.Equal(t, int64(1), result.PegoutSummary.PaidQuotesCount) + assert.Equal(t, 0, result.PegoutSummary.PaidQuotesAmount.Cmp(entities.NewWei(1540))) + assert.Equal(t, 0, result.PegoutSummary.TotalFeesCollected.Cmp(entities.NewWei(30))) + assert.Equal(t, 0, result.PegoutSummary.TotalPenaltyAmount.Cmp(entities.NewWei(0))) + assert.Equal(t, 0, result.PegoutSummary.LpEarnings.Cmp(entities.NewWei(15))) peginRepo.AssertExpectations(t) pegoutRepo.AssertExpectations(t) }) t.Run("Success with only regular quotes (no retained quotes)", func(t *testing.T) { - peginRepo := new(mockPeginQuoteRepo) - pegoutRepo := new(mockPegoutQuoteRepo) + peginRepo := mocks.NewPeginQuoteRepositoryMock(t) + pegoutRepo := mocks.NewPegoutQuoteRepositoryMock(t) peginQuotes := []quote.PeginQuote{ { Value: entities.NewWei(100), @@ -346,40 +146,32 @@ func TestSummariesUseCase_Run(t *testing.T) { //nolint:funlen,maintidx } retainedPegoutQuotes := []quote.RetainedPegoutQuote{} peginRepo.On("ListQuotesByDateRange", mock.Anything, startDate, endDate). - Return(quote.PeginQuoteResult{ - Quotes: peginQuotes, - RetainedQuotes: retainedPeginQuotes, - }, nil) + Return(peginQuotes, retainedPeginQuotes, nil) pegoutRepo.On("ListQuotesByDateRange", mock.Anything, startDate, endDate). - Return(quote.PegoutQuoteResult{ - Quotes: pegoutQuotes, - RetainedQuotes: retainedPegoutQuotes, - }, nil) + Return(pegoutQuotes, retainedPegoutQuotes, nil) useCase := liquidity_provider.NewSummariesUseCase(peginRepo, pegoutRepo) result, err := useCase.Run(context.Background(), startDate, endDate) require.NoError(t, err) - assert.Equal(t, int64(len(peginQuotes)), result.PeginSummary.TotalAcceptedQuotesCount) - assert.Equal(t, int64(0), result.PeginSummary.ConfirmedQuotesCount) - assert.Equal(t, int64(0), result.PeginSummary.RefundedQuotesCount) - assert.Equal(t, "330", result.PeginSummary.TotalQuotedAmount) - assert.Equal(t, "0", result.PeginSummary.TotalAcceptedQuotedAmount) - assert.Equal(t, "0", result.PeginSummary.TotalFeesCollected) - assert.Equal(t, "0", result.PeginSummary.TotalPenaltyAmount) - assert.Equal(t, "0", result.PeginSummary.LpEarnings) - assert.Equal(t, int64(len(pegoutQuotes)), result.PegoutSummary.TotalAcceptedQuotesCount) - assert.Equal(t, int64(0), result.PegoutSummary.ConfirmedQuotesCount) - assert.Equal(t, int64(0), result.PegoutSummary.RefundedQuotesCount) - assert.Equal(t, "330", result.PegoutSummary.TotalQuotedAmount) - assert.Equal(t, "0", result.PegoutSummary.TotalAcceptedQuotedAmount) - assert.Equal(t, "0", result.PegoutSummary.TotalFeesCollected) - assert.Equal(t, "0", result.PegoutSummary.TotalPenaltyAmount) - assert.Equal(t, "0", result.PegoutSummary.LpEarnings) + assert.Equal(t, int64(len(peginQuotes)), result.PeginSummary.TotalQuotesCount) + assert.Equal(t, int64(len(retainedPeginQuotes)), result.PeginSummary.AcceptedQuotesCount) + assert.Equal(t, int64(0), result.PeginSummary.PaidQuotesCount) + assert.Equal(t, 0, result.PeginSummary.PaidQuotesAmount.Cmp(entities.NewWei(330))) + assert.Equal(t, 0, result.PeginSummary.TotalFeesCollected.Cmp(entities.NewWei(0))) + assert.Equal(t, 0, result.PeginSummary.TotalPenaltyAmount.Cmp(entities.NewWei(0))) + assert.Equal(t, 0, result.PeginSummary.LpEarnings.Cmp(entities.NewWei(0))) + assert.Equal(t, int64(len(pegoutQuotes)), result.PegoutSummary.TotalQuotesCount) + assert.Equal(t, int64(len(retainedPegoutQuotes)), result.PegoutSummary.AcceptedQuotesCount) + assert.Equal(t, int64(0), result.PegoutSummary.PaidQuotesCount) + assert.Equal(t, 0, result.PegoutSummary.PaidQuotesAmount.Cmp(entities.NewWei(330))) + assert.Equal(t, 0, result.PegoutSummary.TotalFeesCollected.Cmp(entities.NewWei(0))) + assert.Equal(t, 0, result.PegoutSummary.TotalPenaltyAmount.Cmp(entities.NewWei(0))) + assert.Equal(t, 0, result.PegoutSummary.LpEarnings.Cmp(entities.NewWei(0))) peginRepo.AssertExpectations(t) pegoutRepo.AssertExpectations(t) }) t.Run("Success with only retained quotes (no regular quotes)", func(t *testing.T) { - peginRepo := new(mockPeginQuoteRepo) - pegoutRepo := new(mockPegoutQuoteRepo) + peginRepo := mocks.NewPeginQuoteRepositoryMock(t) + pegoutRepo := mocks.NewPegoutQuoteRepositoryMock(t) peginQuotes := []quote.PeginQuote{} retainedPeginQuotes := []quote.RetainedPeginQuote{ { @@ -414,46 +206,38 @@ func TestSummariesUseCase_Run(t *testing.T) { //nolint:funlen,maintidx ProductFeeAmount: 9, } peginRepo.On("ListQuotesByDateRange", mock.Anything, startDate, endDate). - Return(quote.PeginQuoteResult{ - Quotes: peginQuotes, - RetainedQuotes: retainedPeginQuotes, - }, nil) + Return(peginQuotes, retainedPeginQuotes, nil) peginRepo.On("GetQuote", mock.Anything, "hash1"). Return(&peginQuote, nil) pegoutRepo.On("ListQuotesByDateRange", mock.Anything, startDate, endDate). - Return(quote.PegoutQuoteResult{ - Quotes: pegoutQuotes, - RetainedQuotes: retainedPegoutQuotes, - }, nil) + Return(pegoutQuotes, retainedPegoutQuotes, nil) pegoutRepo.On("GetQuote", mock.Anything, "hash3"). Return(&pegoutQuote, nil) useCase := liquidity_provider.NewSummariesUseCase(peginRepo, pegoutRepo) result, err := useCase.Run(context.Background(), startDate, endDate) require.NoError(t, err) - assert.Equal(t, int64(len(retainedPeginQuotes)), result.PeginSummary.TotalAcceptedQuotesCount) - assert.Equal(t, int64(1), result.PeginSummary.ConfirmedQuotesCount) - assert.Equal(t, int64(0), result.PeginSummary.RefundedQuotesCount) - assert.Equal(t, "110", result.PeginSummary.TotalQuotedAmount) - assert.Equal(t, "110", result.PeginSummary.TotalAcceptedQuotedAmount) - assert.Equal(t, "10", result.PeginSummary.TotalFeesCollected) - assert.Equal(t, "0", result.PeginSummary.TotalPenaltyAmount) - assert.Equal(t, "5", result.PeginSummary.LpEarnings) - assert.Equal(t, int64(len(retainedPegoutQuotes)), result.PegoutSummary.TotalAcceptedQuotesCount) - assert.Equal(t, int64(1), result.PegoutSummary.ConfirmedQuotesCount) - assert.Equal(t, int64(0), result.PegoutSummary.RefundedQuotesCount) - assert.Equal(t, "330", result.PegoutSummary.TotalQuotedAmount) - assert.Equal(t, "330", result.PegoutSummary.TotalAcceptedQuotedAmount) - assert.Equal(t, "30", result.PegoutSummary.TotalFeesCollected) - assert.Equal(t, "0", result.PegoutSummary.TotalPenaltyAmount) - assert.Equal(t, "15", result.PegoutSummary.LpEarnings) + assert.Equal(t, int64(len(peginQuotes)), result.PeginSummary.TotalQuotesCount) + assert.Equal(t, int64(len(retainedPeginQuotes)), result.PeginSummary.AcceptedQuotesCount) + assert.Equal(t, int64(1), result.PeginSummary.PaidQuotesCount) + assert.Equal(t, 0, result.PeginSummary.PaidQuotesAmount.Cmp(entities.NewWei(110))) + assert.Equal(t, 0, result.PeginSummary.TotalFeesCollected.Cmp(entities.NewWei(10))) + assert.Equal(t, 0, result.PeginSummary.TotalPenaltyAmount.Cmp(entities.NewWei(0))) + assert.Equal(t, 0, result.PeginSummary.LpEarnings.Cmp(entities.NewWei(5))) + assert.Equal(t, int64(len(pegoutQuotes)), result.PegoutSummary.TotalQuotesCount) + assert.Equal(t, int64(len(retainedPegoutQuotes)), result.PegoutSummary.AcceptedQuotesCount) + assert.Equal(t, int64(1), result.PegoutSummary.PaidQuotesCount) + assert.Equal(t, 0, result.PegoutSummary.PaidQuotesAmount.Cmp(entities.NewWei(330))) + assert.Equal(t, 0, result.PegoutSummary.TotalFeesCollected.Cmp(entities.NewWei(30))) + assert.Equal(t, 0, result.PegoutSummary.TotalPenaltyAmount.Cmp(entities.NewWei(0))) + assert.Equal(t, 0, result.PegoutSummary.LpEarnings.Cmp(entities.NewWei(15))) peginRepo.AssertExpectations(t) pegoutRepo.AssertExpectations(t) }) t.Run("Error getting pegin quotes", func(t *testing.T) { - peginRepo := new(mockPeginQuoteRepo) - pegoutRepo := new(mockPegoutQuoteRepo) + peginRepo := mocks.NewPeginQuoteRepositoryMock(t) + pegoutRepo := mocks.NewPegoutQuoteRepositoryMock(t) peginRepo.On("ListQuotesByDateRange", mock.Anything, startDate, endDate). - Return(quote.PeginQuoteResult{}, errors.New("db error")) + Return([]quote.PeginQuote{}, []quote.RetainedPeginQuote{}, errors.New("db error")) useCase := liquidity_provider.NewSummariesUseCase(peginRepo, pegoutRepo) _, err := useCase.Run(context.Background(), startDate, endDate) require.Error(t, err) @@ -462,12 +246,12 @@ func TestSummariesUseCase_Run(t *testing.T) { //nolint:funlen,maintidx pegoutRepo.AssertExpectations(t) }) t.Run("Error getting pegout quotes", func(t *testing.T) { - peginRepo := new(mockPeginQuoteRepo) - pegoutRepo := new(mockPegoutQuoteRepo) + peginRepo := mocks.NewPeginQuoteRepositoryMock(t) + pegoutRepo := mocks.NewPegoutQuoteRepositoryMock(t) peginRepo.On("ListQuotesByDateRange", mock.Anything, startDate, endDate). - Return(quote.PeginQuoteResult{}, nil) + Return([]quote.PeginQuote{}, []quote.RetainedPeginQuote{}, nil) pegoutRepo.On("ListQuotesByDateRange", mock.Anything, startDate, endDate). - Return(quote.PegoutQuoteResult{}, errors.New("db error")) + Return([]quote.PegoutQuote{}, []quote.RetainedPegoutQuote{}, errors.New("db error")) useCase := liquidity_provider.NewSummariesUseCase(peginRepo, pegoutRepo) _, err := useCase.Run(context.Background(), startDate, endDate) require.Error(t, err) diff --git a/test/mocks/pegin_quote_repository_mock.go b/test/mocks/pegin_quote_repository_mock.go index 446451c8..a69200b3 100644 --- a/test/mocks/pegin_quote_repository_mock.go +++ b/test/mocks/pegin_quote_repository_mock.go @@ -414,31 +414,42 @@ func (_c *PeginQuoteRepositoryMock_InsertRetainedQuote_Call) RunAndReturn(run fu } // ListQuotesByDateRange provides a mock function with given fields: ctx, startDate, endDate -func (_m *PeginQuoteRepositoryMock) ListQuotesByDateRange(ctx context.Context, startDate time.Time, endDate time.Time) (quote.PeginQuoteResult, error) { +func (_m *PeginQuoteRepositoryMock) ListQuotesByDateRange(ctx context.Context, startDate time.Time, endDate time.Time) ([]quote.PeginQuote, []quote.RetainedPeginQuote, error) { ret := _m.Called(ctx, startDate, endDate) if len(ret) == 0 { panic("no return value specified for ListQuotesByDateRange") } - var r0 quote.PeginQuoteResult - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, time.Time, time.Time) (quote.PeginQuoteResult, error)); ok { + var r0 []quote.PeginQuote + var r1 []quote.RetainedPeginQuote + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, time.Time, time.Time) ([]quote.PeginQuote, []quote.RetainedPeginQuote, error)); ok { return rf(ctx, startDate, endDate) } - if rf, ok := ret.Get(0).(func(context.Context, time.Time, time.Time) quote.PeginQuoteResult); ok { + if rf, ok := ret.Get(0).(func(context.Context, time.Time, time.Time) []quote.PeginQuote); ok { r0 = rf(ctx, startDate, endDate) } else { - r0 = ret.Get(0).(quote.PeginQuoteResult) + if ret.Get(0) != nil { + r0 = ret.Get(0).([]quote.PeginQuote) + } } - if rf, ok := ret.Get(1).(func(context.Context, time.Time, time.Time) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, time.Time, time.Time) []quote.RetainedPeginQuote); ok { r1 = rf(ctx, startDate, endDate) } else { - r1 = ret.Error(1) + if ret.Get(1) != nil { + r1 = ret.Get(1).([]quote.RetainedPeginQuote) + } } - return r0, r1 + if rf, ok := ret.Get(2).(func(context.Context, time.Time, time.Time) error); ok { + r2 = rf(ctx, startDate, endDate) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 } // PeginQuoteRepositoryMock_ListQuotesByDateRange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListQuotesByDateRange' @@ -461,12 +472,12 @@ func (_c *PeginQuoteRepositoryMock_ListQuotesByDateRange_Call) Run(run func(ctx return _c } -func (_c *PeginQuoteRepositoryMock_ListQuotesByDateRange_Call) Return(_a0 quote.PeginQuoteResult, _a1 error) *PeginQuoteRepositoryMock_ListQuotesByDateRange_Call { - _c.Call.Return(_a0, _a1) +func (_c *PeginQuoteRepositoryMock_ListQuotesByDateRange_Call) Return(_a0 []quote.PeginQuote, _a1 []quote.RetainedPeginQuote, _a2 error) *PeginQuoteRepositoryMock_ListQuotesByDateRange_Call { + _c.Call.Return(_a0, _a1, _a2) return _c } -func (_c *PeginQuoteRepositoryMock_ListQuotesByDateRange_Call) RunAndReturn(run func(context.Context, time.Time, time.Time) (quote.PeginQuoteResult, error)) *PeginQuoteRepositoryMock_ListQuotesByDateRange_Call { +func (_c *PeginQuoteRepositoryMock_ListQuotesByDateRange_Call) RunAndReturn(run func(context.Context, time.Time, time.Time) ([]quote.PeginQuote, []quote.RetainedPeginQuote, error)) *PeginQuoteRepositoryMock_ListQuotesByDateRange_Call { _c.Call.Return(run) return _c } diff --git a/test/mocks/pegout_quote_repository_mock.go b/test/mocks/pegout_quote_repository_mock.go index 46a9d4aa..5ae353c4 100644 --- a/test/mocks/pegout_quote_repository_mock.go +++ b/test/mocks/pegout_quote_repository_mock.go @@ -473,31 +473,42 @@ func (_c *PegoutQuoteRepositoryMock_ListPegoutDepositsByAddress_Call) RunAndRetu } // ListQuotesByDateRange provides a mock function with given fields: ctx, startDate, endDate -func (_m *PegoutQuoteRepositoryMock) ListQuotesByDateRange(ctx context.Context, startDate time.Time, endDate time.Time) (quote.PegoutQuoteResult, error) { +func (_m *PegoutQuoteRepositoryMock) ListQuotesByDateRange(ctx context.Context, startDate time.Time, endDate time.Time) ([]quote.PegoutQuote, []quote.RetainedPegoutQuote, error) { ret := _m.Called(ctx, startDate, endDate) if len(ret) == 0 { panic("no return value specified for ListQuotesByDateRange") } - var r0 quote.PegoutQuoteResult - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, time.Time, time.Time) (quote.PegoutQuoteResult, error)); ok { + var r0 []quote.PegoutQuote + var r1 []quote.RetainedPegoutQuote + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, time.Time, time.Time) ([]quote.PegoutQuote, []quote.RetainedPegoutQuote, error)); ok { return rf(ctx, startDate, endDate) } - if rf, ok := ret.Get(0).(func(context.Context, time.Time, time.Time) quote.PegoutQuoteResult); ok { + if rf, ok := ret.Get(0).(func(context.Context, time.Time, time.Time) []quote.PegoutQuote); ok { r0 = rf(ctx, startDate, endDate) } else { - r0 = ret.Get(0).(quote.PegoutQuoteResult) + if ret.Get(0) != nil { + r0 = ret.Get(0).([]quote.PegoutQuote) + } } - if rf, ok := ret.Get(1).(func(context.Context, time.Time, time.Time) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, time.Time, time.Time) []quote.RetainedPegoutQuote); ok { r1 = rf(ctx, startDate, endDate) } else { - r1 = ret.Error(1) + if ret.Get(1) != nil { + r1 = ret.Get(1).([]quote.RetainedPegoutQuote) + } } - return r0, r1 + if rf, ok := ret.Get(2).(func(context.Context, time.Time, time.Time) error); ok { + r2 = rf(ctx, startDate, endDate) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 } // PegoutQuoteRepositoryMock_ListQuotesByDateRange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListQuotesByDateRange' @@ -520,12 +531,12 @@ func (_c *PegoutQuoteRepositoryMock_ListQuotesByDateRange_Call) Run(run func(ctx return _c } -func (_c *PegoutQuoteRepositoryMock_ListQuotesByDateRange_Call) Return(_a0 quote.PegoutQuoteResult, _a1 error) *PegoutQuoteRepositoryMock_ListQuotesByDateRange_Call { - _c.Call.Return(_a0, _a1) +func (_c *PegoutQuoteRepositoryMock_ListQuotesByDateRange_Call) Return(_a0 []quote.PegoutQuote, _a1 []quote.RetainedPegoutQuote, _a2 error) *PegoutQuoteRepositoryMock_ListQuotesByDateRange_Call { + _c.Call.Return(_a0, _a1, _a2) return _c } -func (_c *PegoutQuoteRepositoryMock_ListQuotesByDateRange_Call) RunAndReturn(run func(context.Context, time.Time, time.Time) (quote.PegoutQuoteResult, error)) *PegoutQuoteRepositoryMock_ListQuotesByDateRange_Call { +func (_c *PegoutQuoteRepositoryMock_ListQuotesByDateRange_Call) RunAndReturn(run func(context.Context, time.Time, time.Time) ([]quote.PegoutQuote, []quote.RetainedPegoutQuote, error)) *PegoutQuoteRepositoryMock_ListQuotesByDateRange_Call { _c.Call.Return(run) return _c }