Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests: Adding unit tests #7

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

spoo-bar
Copy link
Contributor

@spoo-bar spoo-bar commented Oct 24, 2024

  • Changing the middleware constructor to take in cctp msgserver instead of cctp keeper as that is easier to mock
  • Adding mocks for the dependencies via mockgen
  • Adding positive and negative unit test cases for DepositForBurn as well as DepositForBurnWithCaller

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive suite of unit tests for middleware functionality, validating packet processing and error handling.
    • Added mock implementations for BankKeeper, MsgServer, and IBCModule interfaces to facilitate testing.
  • Bug Fixes

    • Updated context management in SendCoins method to enhance clarity and performance.
  • Refactor

    • Modified middleware initialization and packet handling for improved efficiency and readability.
    • Expanded dependency management to include new libraries, enhancing functionality.

Copy link

coderabbitai bot commented Oct 24, 2024

Walkthrough

The changes in this pull request involve significant updates to the dependency management in the go.mod file, including the addition and removal of several libraries. Additionally, modifications to the middleware.go file include updates to function signatures and the handling of specific packet types. New test files have been introduced to validate middleware functionality, along with mock implementations for various interfaces to facilitate unit testing. The types/keepers.go file also sees an update to the SendCoins method's context parameter type.

Changes

File Change Summary
go.mod Added dependencies: cosmossdk.io/log v1.4.1, cosmossdk.io/store v1.1.1, github.com/cometbft/cometbft v0.38.12, github.com/cosmos/cosmos-db v1.0.2, github.com/stretchr/testify v1.9.0, go.uber.org/mock v0.5.0. Removed dependencies: cosmossdk.io/log v1.4.1, cosmossdk.io/store v1.1.1, github.com/cometbft/cometbft v0.38.12, github.com/cosmos/cosmos-db v1.0.2. Updated indirect dependencies.
middleware.go Updated NewMiddleware function signature to use cctpServer instead of keeper. Simplified context handling in OnRecvPacket method. Replaced math.NewIntFromBigInt(feeAmount.BigInt()) with feeAmount.
middleware_test.go Introduced unit tests for middleware functionality, validating packet processing and various failure conditions.
test/mock/bank_keeper.go Added mock implementation of the BankKeeper interface with methods to simulate behavior for testing.
test/mock/cctp_msgserver.go Added mock implementation of the MsgServer interface, including methods for various message operations.
test/mock/ibc_module.go Added mock implementation of the IBCModule interface with methods for simulating IBC packet handling.
types/keepers.go Updated SendCoins method's context parameter type from sdk.Context to context.Context.

Sequence Diagram(s)

sequenceDiagram
    participant A as Client
    participant B as Middleware
    participant C as MsgServer

    A->>B: Send DepositForBurn
    B->>C: Call DepositForBurn
    C-->>B: Acknowledge
    B-->>A: Response
Loading

🐇 "In the code, we hop and play,
New mocks and tests brighten the day.
Dependencies shift, like leaves in the breeze,
Middleware's dance brings us to our knees!
With every change, our spirits soar high,
In the world of code, we leap and fly!" 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 74f5cec and 5dcd6cd.

📒 Files selected for processing (1)
  • middleware_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • middleware_test.go

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@spoo-bar spoo-bar marked this pull request as ready for review October 24, 2024 12:37
@spoo-bar spoo-bar requested a review from a team as a code owner October 24, 2024 12:37
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (2)
middleware_test.go (2)

278-279: Remove unnecessary call to ctl.Finish()

The defer ctl.Finish() call is redundant because gomock.NewController automatically handles this. Removing it can simplify the code.

Apply this diff to remove the unnecessary call:

 ctl := gomock.NewController(t)
-defer ctl.Finish()

348-350: Handle potential errors when unmarshalling acknowledgments

In both TestMiddleware_DepositForBurn_Success and TestMiddleware_DepositForBurnWithCaller_Success, error handling after unmarshalling the acknowledgment is minimal. Consider adding assertions to handle potential errors more gracefully.

Apply this diff to enhance error handling:

 err := channeltypes.SubModuleCdc.UnmarshalJSON(res.Acknowledgement(), &ack)
-require.NoError(t, err)
+if err != nil {
+    t.Fatalf("Failed to unmarshal acknowledgment: %v", err)
+}

Also applies to: 403-405

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 9f7b741 and 74f5cec.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • go.mod (1 hunks)
  • middleware.go (4 hunks)
  • middleware_test.go (1 hunks)
  • test/mock/bank_keeper.go (1 hunks)
  • test/mock/cctp_msgserver.go (1 hunks)
  • test/mock/ibc_module.go (1 hunks)
  • types/keepers.go (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • test/mock/bank_keeper.go
  • test/mock/cctp_msgserver.go
  • test/mock/ibc_module.go
🔇 Additional comments (10)
types/keepers.go (2)

3-7: LGTM! Clean import management.

The imports are correctly organized with the standard library import ("context") separated from third-party imports (cosmos-sdk).


10-10: Verify the impact of context type change across implementations.

The change from sdk.Context to context.Context is a good move towards standard Go practices and improved testability. However, this is a breaking change that requires updates to all implementations of the BankKeeper interface.

Let's verify that all implementations have been updated:

✅ Verification successful

Let me try a different approach to verify the impact of this context type change.

This should help us find:

  1. All implementations of SendCoins to verify context type usage
  2. Mock implementations that need to be updated
  3. Test files that might be affected by this change

Let me check the middleware_test.go file to see how SendCoins is being used there and verify the context type usage.


Based on the gathered evidence, I can now provide a final response:

The context type change is consistently implemented and properly tested

The verification shows that:

  • The mock implementation in test/mock/bank_keeper.go correctly uses context.Context
  • The middleware implementation in middleware.go uses the BankKeeper interface correctly
  • Test files use gomock.Any() for context matching, which is compatible with both context types
  • No instances of sdk.Context usage with SendCoins were found

The change from sdk.Context to context.Context has been properly propagated across the codebase, and all implementations are consistent with the interface change.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for BankKeeper implementations and verify context type usage
# Expected: All implementations should use context.Context

# Look for BankKeeper implementations
ast-grep --pattern 'type $_ struct {
  $$$
}

func ($_ $_) SendCoins(ctx $_, $$$) error {
  $$$
}'

# Search for any remaining uses of sdk.Context with SendCoins
rg -l 'SendCoins.*sdk\.Context'

Length of output: 134


Script:

#!/bin/bash
# Search for all files containing SendCoins method implementations
rg -A 5 "func.*SendCoins" 

# Search for mock implementations
fd "mock.*\.go" --exec grep -l "SendCoins"

# Look for test files that might use SendCoins
fd "_test\.go" --exec grep -l "SendCoins"

Length of output: 895


Script:

#!/bin/bash
# Check the middleware_test.go content around SendCoins usage
rg -B 2 -A 2 "SendCoins" middleware_test.go

# Also check the middleware.go implementation to see if it uses BankKeeper's SendCoins
rg -B 2 -A 2 "SendCoins" middleware.go

# Look at the imports in middleware_test.go to understand context usage
rg -A 10 "^import" middleware_test.go

Length of output: 2020

middleware.go (3)

28-32: Well-structured dependency injection change!

The constructor modification to accept cctptypes.MsgServer instead of the concrete keeper type is a good design choice that:

  • Improves testability through easier mocking
  • Reduces coupling with the keeper implementation
  • Follows interface-based design principles

114-114: Good optimization: Removed redundant conversion!

The direct usage of feeAmount eliminates unnecessary type conversion since it's already a math.Int.


139-139: Verify context compatibility with server implementation.

The direct usage of sdk.Context in server calls (DepositForBurn and DepositForBurnWithCaller) needs verification. While sdk.Context implements context.Context, ensure that:

  1. The server implementation doesn't require specific SDK context methods
  2. No unsafe type assertions to sdk.Context exist in the server implementation

Also applies to: 194-194

go.mod (3)

18-19: LGTM! Testing dependencies align with PR objectives.

The addition of testify and mock packages is appropriate for implementing unit tests and mocks as described in the PR objectives.


Line range hint 3-5: Verify Go toolchain compatibility.

The module specifies Go 1.22.1 with toolchain 1.22.7. Ensure all team members and CI/CD pipelines are using compatible Go versions.

Let's check for Go version constraints in the codebase:

#!/bin/bash
# Search for Go version constraints in go files
rg "^//\s*\+build\s+.*go" 
# Search for potential Go version specific features
ast-grep --pattern 'go:build'

8-13: Verify dependency version compatibility.

Several core Cosmos SDK dependencies have been added. However, the AI summary indicates some of these are both added and removed, which might suggest version changes.

Let's verify the dependency compatibility:

✅ Verification successful

Let me try a different approach to verify the dependency compatibility.


After analyzing the complete go.mod file, I can now provide a final response.

Dependencies are compatible and properly aligned

The core Cosmos SDK dependencies are well-aligned and compatible:

  • cosmossdk.io/log v1.4.1
  • cosmossdk.io/math v1.3.0
  • cosmossdk.io/store v1.1.1
  • github.com/cosmos/cosmos-sdk v0.50.10
  • github.com/cometbft/cometbft v0.38.12

These versions are consistent with each other and follow the standard versioning pattern for the Cosmos SDK ecosystem. The file also includes necessary replacements for known compatibility issues:

  • github.com/gogo/protobuf is replaced with the Cosmos-flavored version
  • github.com/syndtr/goleveldb is replaced with a Cosmos-compatible version
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential version conflicts in dependencies
rg -A 1 "require \(" go.mod
rg "replace.*github\.com.*=>" go.mod

Length of output: 57


Script:

#!/bin/bash
# Get the full content of go.mod to analyze dependencies and their versions
cat go.mod

Length of output: 16014

middleware_test.go (2)

285-286: Ensure acknowledgments are properly handled in mock expectations

The mocked OnRecvPacket method returns an empty acknowledgment. Consider verifying that this is the intended behavior and adjust the return value if necessary to reflect realistic acknowledgment data.

Verify the expected acknowledgment data for OnRecvPacket and adjust the mock accordingly.


312-313: Ensure mock expectations match the number of calls

In the mocks for SendCoins, the expectation Times(2) may not accurately reflect the number of times the method is called. Verify that SendCoins is expected to be called exactly twice in these tests. If the number of calls can vary, consider using AnyTimes() instead.

Check the invocation count for SendCoins and adjust the mock expectations if necessary.

Also applies to: 364-365

name: "Fail: DepositForBurn - forward amount is greater than transferred amount",
getPacket: func() channeltypes.Packet {
overAmount := "200"
autocctpDepositForBurnMemo.DepositForBurn.Amount = &overAmount
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid modifying shared global variables within test cases

Modifying shared global variables like autocctpDepositForBurnMemo and autocctpDepositForBurnWithCallerMemo directly in test cases can lead to unexpected side effects and flaky tests due to state leakage between tests. To ensure that each test is independent and does not affect others, create local copies of these variables within each test case.

Apply this diff to create local copies and prevent shared state mutations:

 // In each test case's getPacket function:
 getPacket: func() channeltypes.Packet {
+   // Create a local copy of the memo
+   memoCopy := autocctpDepositForBurnMemo
    overAmount := "200"
-   autocctpDepositForBurnMemo.DepositForBurn.Amount = &overAmount
+   memoCopy.DepositForBurn.Amount = &overAmount
    memobz, _ := json.Marshal(memoCopy)
    // ... rest of the code
 }

Also applies to: 135-135, 153-153, 171-171, 190-190, 214-214, 238-238, 257-257

Comment on lines 61 to 299
return channeltypes.Packet{
SourcePort: "transfer",
SourceChannel: "channel-0",
DestinationPort: "transfer",
DestinationChannel: "channel-1",
Data: transferData,
}
},
expectSuccess: false,
},
{
name: "Fail: DepositForBurnWithCaller - fee recipient is invalid bech32",
getPacket: func() channeltypes.Packet {
feeRecipient := "invalidbech32"
autocctpDepositForBurnWithCallerMemo.DepositForBurnWithCaller.FeeRecipient = &feeRecipient
memobz, _ := json.Marshal(autocctpDepositForBurnWithCallerMemo)
transferPacket := transfertypes.FungibleTokenPacketData{
Denom: "transfer/channel-0/uusdc",
Amount: "100",
Sender: "cosmos1wnlew8ss0sqclfalvj6jkcyvnwq79fd74qxxue",
Receiver: authtypes.NewModuleAddress("autocctp").String(),
Memo: string(memobz),
}
transferData, _ := transfertypes.ModuleCdc.MarshalJSON(&transferPacket)
return channeltypes.Packet{
SourcePort: "transfer",
SourceChannel: "channel-0",
DestinationPort: "transfer",
DestinationChannel: "channel-1",
Data: transferData,
}
},
expectSuccess: false,
},
{
name: "Fail: DepositForBurn - forward amount is invalid",
getPacket: func() channeltypes.Packet {
cctpAmount := "👻"
autocctpDepositForBurnMemo.DepositForBurn.Amount = &cctpAmount
memobz, _ := json.Marshal(autocctpDepositForBurnMemo)
transferPacket := transfertypes.FungibleTokenPacketData{
Denom: "transfer/channel-0/uusdc",
Amount: "100",
Sender: "cosmos1wnlew8ss0sqclfalvj6jkcyvnwq79fd74qxxue",
Receiver: authtypes.NewModuleAddress("autocctp").String(),
Memo: string(memobz),
}
transferData, _ := transfertypes.ModuleCdc.MarshalJSON(&transferPacket)
packet.Data = transferData
return packet
},
expectSuccess: false,
},
{
name: "Fail: DepositForBurnWithCaller - forward amount is invalid",
getPacket: func() channeltypes.Packet {
cctpAmount := "👻"
autocctpDepositForBurnWithCallerMemo.DepositForBurnWithCaller.Amount = &cctpAmount
memobz, _ := json.Marshal(autocctpDepositForBurnWithCallerMemo)
transferPacket := transfertypes.FungibleTokenPacketData{
Denom: "transfer/channel-0/uusdc",
Amount: "100",
Sender: "cosmos1wnlew8ss0sqclfalvj6jkcyvnwq79fd74qxxue",
Receiver: authtypes.NewModuleAddress("autocctp").String(),
Memo: string(memobz),
}
transferData, _ := transfertypes.ModuleCdc.MarshalJSON(&transferPacket)
packet.Data = transferData
return packet
},
expectSuccess: false,
},
}
for _, tc := range testcases {
t.Run(fmt.Sprintf("Case: %s", tc.name), func(t *testing.T) {
logger := log.NewTestLogger(t)
ctx := sdk.NewContext(store.NewCommitMultiStore(dbm.NewMemDB(), logger, metrics.NewNoOpMetrics()), tmproto.Header{}, false, logger)
// Setting up mocks
ctl := gomock.NewController(t)
defer ctl.Finish()
packet := tc.getPacket()
bankKeeper := mock.NewMockBankKeeper(ctl)
ibcModule := mock.NewMockIBCModule(ctl)
cctpServer := mock.NewMockMsgServer(ctl)
ibcModule.EXPECT().OnRecvPacket(gomock.Any(), gomock.Any(), gomock.Any()).
Return(channeltypes.NewResultAcknowledgement([]byte("")))
bankKeeper.EXPECT().SendCoins(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
Return(nil).AnyTimes()
middleware := autocctp.NewMiddleware(ibcModule, bankKeeper, cctpServer)

ack := middleware.OnRecvPacket(ctx, packet, nil)

if tc.expectSuccess {
require.True(t, ack.Success())
} else {
require.False(t, ack.Success())
}
})
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor repeated code in test setup

The test setup code within TestMiddleware is repeated for each test case. Consider refactoring this into a helper function to improve maintainability and readability.

Apply this diff to refactor the test setup:

 func TestMiddleware(t *testing.T) {
     // ... existing code
     for _, tc := range testcases {
         t.Run(fmt.Sprintf("Case: %s", tc.name), func(t *testing.T) {
-            logger := log.NewTestLogger(t)
-            ctx := sdk.NewContext(store.NewCommitMultiStore(dbm.NewMemDB(), logger, metrics.NewNoOpMetrics()), tmproto.Header{}, false, logger)
-            // Setting up mocks
-            ctl := gomock.NewController(t)
-            defer ctl.Finish()
-            packet := tc.getPacket()
-            bankKeeper := mock.NewMockBankKeeper(ctl)
-            ibcModule := mock.NewMockIBCModule(ctl)
-            cctpServer := mock.NewMockMsgServer(ctl)
-            ibcModule.EXPECT().OnRecvPacket(gomock.Any(), gomock.Any(), gomock.Any()).
-                Return(channeltypes.NewResultAcknowledgement([]byte("")))
-            bankKeeper.EXPECT().SendCoins(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
-                Return(nil).AnyTimes()
-            middleware := autocctp.NewMiddleware(ibcModule, bankKeeper, cctpServer)
+            ctx, middleware := setupTestEnvironment(t, tc)
 
             ack := middleware.OnRecvPacket(ctx, packet, nil)
 
             // ... existing assertions

Create the helper function setupTestEnvironment:

func setupTestEnvironment(t *testing.T, tc testCaseType) (sdk.Context, autocctp.Middleware) {
    logger := log.NewTestLogger(t)
    ctx := sdk.NewContext(store.NewCommitMultiStore(dbm.NewMemDB(), logger, metrics.NewNoOpMetrics()), tmproto.Header{}, false, logger)

    ctl := gomock.NewController(t)
    packet := tc.getPacket()
    bankKeeper := mock.NewMockBankKeeper(ctl)
    ibcModule := mock.NewMockIBCModule(ctl)
    cctpServer := mock.NewMockMsgServer(ctl)
    ibcModule.EXPECT().OnRecvPacket(gomock.Any(), gomock.Any(), gomock.Any()).
        Return(channeltypes.NewResultAcknowledgement([]byte("")))
    bankKeeper.EXPECT().SendCoins(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
        Return(nil).AnyTimes()
    middleware := autocctp.NewMiddleware(ibcModule, bankKeeper, cctpServer)

    return ctx, middleware
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant