Compare commits

...

2 Commits

Author SHA1 Message Date
Mariano 950ba5cbd2
Merge 5c1f958b56 into ce1b7707fb 2024-04-28 03:01:36 +00:00
Mariano 5c1f958b56 add unit-tests 2024-04-28 00:01:22 -03:00
2 changed files with 256 additions and 16 deletions

View File

@ -229,6 +229,26 @@ func (r *Repository) matchOperationByTxHash(ctx context.Context, txHash string)
func (r *Repository) FindByChainAndAppId(ctx context.Context, query OperationQuery) ([]*OperationDto, error) {
pipeline := BuildPipelineSearchByChainAndAppID(query)
cur, err := r.collections.parsedVaa.Aggregate(ctx, pipeline)
if err != nil {
r.logger.Error("failed execute aggregation pipeline", zap.Error(err))
return nil, err
}
// Read results from cursor
var operations []*OperationDto
err = cur.All(ctx, &operations)
if err != nil {
r.logger.Error("failed to decode cursor", zap.Error(err))
return nil, err
}
return operations, nil
}
func BuildPipelineSearchByChainAndAppID(query OperationQuery) mongo.Pipeline {
var pipeline mongo.Pipeline
if len(query.SourceChainIDs) > 0 || len(query.TargetChainIDs) > 0 {
@ -272,22 +292,7 @@ func (r *Repository) FindByChainAndAppId(ctx context.Context, query OperationQue
// unset
pipeline = append(pipeline, bson.D{{Key: "$unset", Value: bson.A{"transferPrices"}}})
cur, err := r.collections.parsedVaa.Aggregate(ctx, pipeline)
if err != nil {
r.logger.Error("failed execute aggregation pipeline", zap.Error(err))
return nil, err
}
// Read results from cursor
var operations []*OperationDto
err = cur.All(ctx, &operations)
if err != nil {
r.logger.Error("failed to decode cursor", zap.Error(err))
return nil, err
}
return operations, nil
return pipeline
}
// FindAll returns all operations filtered by q.

View File

@ -0,0 +1,235 @@
package operations_test
import (
"github.com/stretchr/testify/assert"
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/operations"
sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"testing"
)
var sortStage = bson.D{{"$sort", bson.D{{"timestamp", -1}, {"_id", -1}}}}
var skipStage = bson.D{{"$skip", int64(0)}}
var limitStage = bson.D{{"$limit", int64(0)}}
var lookupVaasStage = bson.D{
{"$lookup", bson.D{
{"from", "vaas"},
{"localField", "_id"},
{"foreignField", "_id"},
{"as", "vaas"}}}}
var lookupTransferPricesStage = bson.D{{"$lookup", bson.D{
{"from", "transferPrices"},
{"localField", "_id"},
{"foreignField", "_id"},
{"as", "transferPrices"},
}}}
var lookupGlobalTransactionsStage = bson.D{{"$lookup", bson.D{
{"from", "globalTransactions"},
{"localField", "_id"},
{"foreignField", "_id"},
{"as", "globalTransactions"},
}}}
var addFieldsStage = bson.D{{"$addFields", bson.D{
{"payload", "$parsedPayload"},
{"vaa", bson.D{{"$arrayElemAt", bson.A{"$vaas", 0}}}},
{"symbol", bson.D{{"$arrayElemAt", bson.A{"$transferPrices.symbol", 0}}}},
{"usdAmount", bson.D{{"$arrayElemAt", bson.A{"$transferPrices.usdAmount", 0}}}},
{"tokenAmount", bson.D{{"$arrayElemAt", bson.A{"$transferPrices.tokenAmount", 0}}}},
{"originTx", bson.D{{"$arrayElemAt", bson.A{"$globalTransactions.originTx", 0}}}},
{"destinationTx", bson.D{{"$arrayElemAt", bson.A{"$globalTransactions.destinationTx", 0}}}},
}}}
var unSetStage = bson.D{{"$unset", bson.A{"transferPrices"}}}
func TestPipeline_FindByChainAndAppId(t *testing.T) {
cases := []struct {
name string
query operations.OperationQuery
expected mongo.Pipeline
}{
{
name: "Search with no query filters",
query: operations.OperationQuery{},
expected: mongo.Pipeline{
sortStage,
skipStage,
limitStage,
lookupVaasStage,
lookupTransferPricesStage,
lookupGlobalTransactionsStage,
addFieldsStage,
unSetStage,
},
},
{
name: "Search with single source_chain ",
query: operations.OperationQuery{
SourceChainIDs: []sdk.ChainID{1},
},
expected: mongo.Pipeline{
bson.D{{"$match", bson.M{"$and": bson.A{
bson.M{"rawStandardizedProperties.fromChain": bson.M{"$in": []sdk.ChainID{1}}},
}}}},
sortStage,
skipStage,
limitStage,
lookupVaasStage,
lookupTransferPricesStage,
lookupGlobalTransactionsStage,
addFieldsStage,
unSetStage,
},
},
{
name: "Search with multiple source_chain ",
query: operations.OperationQuery{
SourceChainIDs: []sdk.ChainID{1, 2},
},
expected: mongo.Pipeline{
bson.D{{"$match", bson.M{"$and": bson.A{
bson.M{"rawStandardizedProperties.fromChain": bson.M{"$in": []sdk.ChainID{1, 2}}},
}}}},
sortStage,
skipStage,
limitStage,
lookupVaasStage,
lookupTransferPricesStage,
lookupGlobalTransactionsStage,
addFieldsStage,
unSetStage,
},
},
{
name: "Search with single target_chain ",
query: operations.OperationQuery{
TargetChainIDs: []sdk.ChainID{1},
},
expected: mongo.Pipeline{
bson.D{{"$match", bson.M{"$and": bson.A{
bson.M{"rawStandardizedProperties.toChain": bson.M{"$in": []sdk.ChainID{1}}},
}}}},
sortStage,
skipStage,
limitStage,
lookupVaasStage,
lookupTransferPricesStage,
lookupGlobalTransactionsStage,
addFieldsStage,
unSetStage,
},
},
{
name: "Search with single target_chain ",
query: operations.OperationQuery{
TargetChainIDs: []sdk.ChainID{1, 2},
},
expected: mongo.Pipeline{
bson.D{{"$match", bson.M{"$and": bson.A{
bson.M{"rawStandardizedProperties.toChain": bson.M{"$in": []sdk.ChainID{1, 2}}},
}}}},
sortStage,
skipStage,
limitStage,
lookupVaasStage,
lookupTransferPricesStage,
lookupGlobalTransactionsStage,
addFieldsStage,
unSetStage,
},
},
{
name: "Search with same source and target chain",
query: operations.OperationQuery{
SourceChainIDs: []sdk.ChainID{1},
TargetChainIDs: []sdk.ChainID{1},
},
expected: mongo.Pipeline{
bson.D{{"$match", bson.M{"$or": bson.A{
bson.M{"rawStandardizedProperties.fromChain": bson.M{"$in": []sdk.ChainID{1}}},
bson.M{"rawStandardizedProperties.toChain": bson.M{"$in": []sdk.ChainID{1}}},
}}}},
sortStage,
skipStage,
limitStage,
lookupVaasStage,
lookupTransferPricesStage,
lookupGlobalTransactionsStage,
addFieldsStage,
unSetStage,
},
},
{
name: "Search with different source and target chain",
query: operations.OperationQuery{
SourceChainIDs: []sdk.ChainID{1},
TargetChainIDs: []sdk.ChainID{2},
},
expected: mongo.Pipeline{
bson.D{{"$match", bson.M{"$and": bson.A{
bson.M{"rawStandardizedProperties.fromChain": bson.M{"$in": []sdk.ChainID{1}}},
bson.M{"rawStandardizedProperties.toChain": bson.M{"$in": []sdk.ChainID{2}}},
}}}},
sortStage,
skipStage,
limitStage,
lookupVaasStage,
lookupTransferPricesStage,
lookupGlobalTransactionsStage,
addFieldsStage,
unSetStage,
},
},
{
name: "Search by appID exclusive",
query: operations.OperationQuery{
AppIDs: []string{"CCTP_WORMHOLE_INTEGRATION", "PORTAL_TOKEN_BRIDGE"},
ExclusiveAppId: true,
},
expected: mongo.Pipeline{
bson.D{{"$match", bson.M{"$or": bson.A{
bson.M{"$and": bson.A{
bson.M{"rawStandardizedProperties.appIds": bson.M{"$eq": "CCTP_WORMHOLE_INTEGRATION"}},
bson.M{"rawStandardizedProperties.appIds": bson.M{"$size": 1}},
}},
bson.M{"$and": bson.A{
bson.M{"rawStandardizedProperties.appIds": bson.M{"$eq": "PORTAL_TOKEN_BRIDGE"}},
bson.M{"rawStandardizedProperties.appIds": bson.M{"$size": 1}},
}},
}}}},
sortStage,
skipStage,
limitStage,
lookupVaasStage,
lookupTransferPricesStage,
lookupGlobalTransactionsStage,
addFieldsStage,
unSetStage,
},
},
{
name: "Search by appID exclusive false",
query: operations.OperationQuery{
AppIDs: []string{"CCTP_WORMHOLE_INTEGRATION", "PORTAL_TOKEN_BRIDGE"},
ExclusiveAppId: false,
},
expected: mongo.Pipeline{
bson.D{{"$match", bson.M{"rawStandardizedProperties.appIds": bson.M{"$in": []string{"CCTP_WORMHOLE_INTEGRATION", "PORTAL_TOKEN_BRIDGE"}}}}},
sortStage,
skipStage,
limitStage,
lookupVaasStage,
lookupTransferPricesStage,
lookupGlobalTransactionsStage,
addFieldsStage,
unSetStage,
},
},
}
for _, testCase := range cases {
t.Run(testCase.name, func(t *testing.T) {
result := operations.BuildPipelineSearchByChainAndAppID(testCase.query)
assert.Equal(t, testCase.expected, result, "Expected pipeline did not match actual pipeline")
})
}
}