From f79489e297f2513bba36ca0412537bc0c34300e0 Mon Sep 17 00:00:00 2001
From: akhilkumarpilli <akhilkumar7947@gmail.com>
Date: Tue, 22 Oct 2024 12:33:42 +0530
Subject: [PATCH 1/3] WIP: add x/circuit system tests

---
 tests/systemtests/circuit_test.go | 147 ++++++++++++++++++++++++++++++
 1 file changed, 147 insertions(+)
 create mode 100644 tests/systemtests/circuit_test.go

diff --git a/tests/systemtests/circuit_test.go b/tests/systemtests/circuit_test.go
new file mode 100644
index 000000000000..4e4cc6468fba
--- /dev/null
+++ b/tests/systemtests/circuit_test.go
@@ -0,0 +1,147 @@
+package systemtests
+
+import (
+	"fmt"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+	"github.com/tidwall/gjson"
+)
+
+func TestCircuitCmds(t *testing.T) {
+	// scenario: test circuit commands
+	// given a running chain
+
+	sut.ResetChain(t)
+	require.GreaterOrEqual(t, sut.NodesCount(), 2)
+
+	cli := NewCLIWrapper(t, sut, verbose)
+
+	// get validator addresses
+	superAdmin := cli.GetKeyAddr("node0")
+	require.NotEmpty(t, superAdmin)
+
+	superAdmin2 := cli.GetKeyAddr("node1")
+	require.NotEmpty(t, superAdmin2)
+
+	// short voting period
+	// update expedited voting period to avoid validation error
+	sut.ModifyGenesisJSON(
+		t,
+		SetGovVotingPeriod(t, time.Second*8),
+		SetGovExpeditedVotingPeriod(t, time.Second*7),
+	)
+
+	sut.StartChain(t)
+
+	allMsgsAcc := cli.AddKey("allMsgsAcc")
+	require.NotEmpty(t, allMsgsAcc)
+
+	someMsgsAcc := cli.AddKey("someMsgsAcc")
+	require.NotEmpty(t, someMsgsAcc)
+
+	accountAddr := cli.AddKey("account")
+	require.NotEmpty(t, accountAddr)
+
+	// fund tokens to new created addresses
+	var amount int64 = 100000
+	denom := "stake"
+	rsp := cli.FundAddress(allMsgsAcc, fmt.Sprintf("%d%s", amount, denom))
+	RequireTxSuccess(t, rsp)
+	require.Equal(t, amount, cli.QueryBalance(allMsgsAcc, denom))
+
+	rsp = cli.FundAddress(someMsgsAcc, fmt.Sprintf("%d%s", amount, denom))
+	RequireTxSuccess(t, rsp)
+	require.Equal(t, amount, cli.QueryBalance(someMsgsAcc, denom))
+
+	rsp = cli.FundAddress(accountAddr, fmt.Sprintf("%d%s", amount, denom))
+	RequireTxSuccess(t, rsp)
+	require.Equal(t, amount, cli.QueryBalance(accountAddr, denom))
+
+	// query gov module account address
+	rsp = cli.CustomQuery("q", "auth", "module-account", "gov")
+	govModAddr := gjson.Get(rsp, "account.value.address")
+
+	// create a proposal to add super admin
+	validProposal := fmt.Sprintf(`
+	{
+		"messages": [
+			{
+			"@type": "/cosmos.circuit.v1.MsgAuthorizeCircuitBreaker",
+			"granter": "%s",
+			"grantee": "%s",
+			"permissions": {"level": 3, "limit_type_urls": []}
+			}
+		],
+ 	 	"title": "Params update proposal",
+  		"deposit": "10000000stake",
+  		"summary": "A short summary of my proposal"
+	}`, govModAddr, superAdmin)
+	proposalFile := StoreTempFile(t, []byte(validProposal))
+
+	rsp = cli.RunAndWait("tx", "gov", "submit-proposal", proposalFile.Name(), "--from="+superAdmin)
+	RequireTxSuccess(t, rsp)
+
+	// vote to proposal from two validators
+	rsp = cli.RunAndWait("tx", "gov", "vote", "1", "yes", "--from="+superAdmin)
+	RequireTxSuccess(t, rsp)
+	rsp = cli.RunAndWait("tx", "gov", "vote", "1", "yes", "--from="+superAdmin2)
+	RequireTxSuccess(t, rsp)
+
+	// wait for proposal to pass
+	time.Sleep(time.Second * 8)
+
+	rsp = cli.CustomQuery("q", "circuit", "accounts")
+
+	level := gjson.Get(rsp, fmt.Sprintf("accounts.#(address==%s).permissions.level", superAdmin)).String()
+	require.Equal(t, "LEVEL_SUPER_ADMIN", level)
+
+	authorizeTestCases := []struct {
+		name          string
+		address       string
+		level         int
+		limtTypeURLS  string
+		expPermission string
+	}{
+		{
+			"set new super admin",
+			superAdmin2,
+			3,
+			"",
+			"LEVEL_SUPER_ADMIN",
+		},
+		{
+			"set all msgs level to address",
+			allMsgsAcc,
+			2,
+			"",
+			"LEVEL_ALL_MSGS",
+		},
+		{
+			"set some msgs level to address",
+			someMsgsAcc,
+			1,
+			"/cosmos.bank.v1beta1.MsgSend, /cosmos.bank.v1beta1.MsgMultiSend",
+			"LEVEL_SOME_MSGS",
+		},
+	}
+
+	for _, tc := range authorizeTestCases {
+		t.Run(tc.name, func(t *testing.T) {
+			permissionJSON := fmt.Sprintf(`{"level":%d,"limit_type_urls":[]}`, tc.level)
+			if tc.limtTypeURLS != "" {
+				permissionJSON = fmt.Sprintf(`{"level":%d,"limit_type_urls":["%s"]}`, tc.level, tc.limtTypeURLS)
+			}
+			rsp = cli.RunAndWait("tx", "circuit", "authorize", tc.address, permissionJSON, "--from="+superAdmin)
+			RequireTxSuccess(t, rsp)
+
+			// query account permissions
+			rsp = cli.CustomQuery("q", "circuit", "account", tc.address)
+			require.Equal(t, tc.expPermission, gjson.Get(rsp, "permission.level").String())
+			if tc.limtTypeURLS != "" {
+				require.Equal(t, tc.limtTypeURLS, gjson.Get(rsp, "permission.limit_type_urls.0").String())
+			}
+		})
+	}
+}

From 228fb8c450bd448d0e08a1e356e9b29c4f0e3677 Mon Sep 17 00:00:00 2001
From: akhilkumarpilli <akhilkumar7947@gmail.com>
Date: Tue, 22 Oct 2024 16:10:31 +0530
Subject: [PATCH 2/3] done system tests

---
 tests/systemtests/circuit_test.go | 128 ++++++++++++++++++++++++++----
 x/circuit/autocli.go              |   2 +-
 2 files changed, 113 insertions(+), 17 deletions(-)

diff --git a/tests/systemtests/circuit_test.go b/tests/systemtests/circuit_test.go
index 4e4cc6468fba..1fd3e8d0b746 100644
--- a/tests/systemtests/circuit_test.go
+++ b/tests/systemtests/circuit_test.go
@@ -1,7 +1,11 @@
+//go:build system_test
+
 package systemtests
 
 import (
+	"encoding/json"
 	"fmt"
+	"strings"
 	"testing"
 	"time"
 
@@ -9,7 +13,9 @@ import (
 	"github.com/tidwall/gjson"
 )
 
-func TestCircuitCmds(t *testing.T) {
+var someMsgs = []string{"/cosmos.bank.v1beta1.MsgSend", "/cosmos.bank.v1beta1.MsgMultiSend"}
+
+func TestCircuitCommands(t *testing.T) {
 	// scenario: test circuit commands
 	// given a running chain
 
@@ -41,9 +47,6 @@ func TestCircuitCmds(t *testing.T) {
 	someMsgsAcc := cli.AddKey("someMsgsAcc")
 	require.NotEmpty(t, someMsgsAcc)
 
-	accountAddr := cli.AddKey("account")
-	require.NotEmpty(t, accountAddr)
-
 	// fund tokens to new created addresses
 	var amount int64 = 100000
 	denom := "stake"
@@ -55,10 +58,6 @@ func TestCircuitCmds(t *testing.T) {
 	RequireTxSuccess(t, rsp)
 	require.Equal(t, amount, cli.QueryBalance(someMsgsAcc, denom))
 
-	rsp = cli.FundAddress(accountAddr, fmt.Sprintf("%d%s", amount, denom))
-	RequireTxSuccess(t, rsp)
-	require.Equal(t, amount, cli.QueryBalance(accountAddr, denom))
-
 	// query gov module account address
 	rsp = cli.CustomQuery("q", "auth", "module-account", "gov")
 	govModAddr := gjson.Get(rsp, "account.value.address")
@@ -101,28 +100,28 @@ func TestCircuitCmds(t *testing.T) {
 		name          string
 		address       string
 		level         int
-		limtTypeURLS  string
+		limtTypeURLS  []string
 		expPermission string
 	}{
 		{
 			"set new super admin",
 			superAdmin2,
 			3,
-			"",
+			[]string{},
 			"LEVEL_SUPER_ADMIN",
 		},
 		{
 			"set all msgs level to address",
 			allMsgsAcc,
 			2,
-			"",
+			[]string{},
 			"LEVEL_ALL_MSGS",
 		},
 		{
 			"set some msgs level to address",
 			someMsgsAcc,
 			1,
-			"/cosmos.bank.v1beta1.MsgSend, /cosmos.bank.v1beta1.MsgMultiSend",
+			someMsgs,
 			"LEVEL_SOME_MSGS",
 		},
 	}
@@ -130,8 +129,8 @@ func TestCircuitCmds(t *testing.T) {
 	for _, tc := range authorizeTestCases {
 		t.Run(tc.name, func(t *testing.T) {
 			permissionJSON := fmt.Sprintf(`{"level":%d,"limit_type_urls":[]}`, tc.level)
-			if tc.limtTypeURLS != "" {
-				permissionJSON = fmt.Sprintf(`{"level":%d,"limit_type_urls":["%s"]}`, tc.level, tc.limtTypeURLS)
+			if len(tc.limtTypeURLS) != 0 {
+				permissionJSON = fmt.Sprintf(`{"level":%d,"limit_type_urls":["%s"]}`, tc.level, strings.Join(tc.limtTypeURLS[:], `","`))
 			}
 			rsp = cli.RunAndWait("tx", "circuit", "authorize", tc.address, permissionJSON, "--from="+superAdmin)
 			RequireTxSuccess(t, rsp)
@@ -139,8 +138,105 @@ func TestCircuitCmds(t *testing.T) {
 			// query account permissions
 			rsp = cli.CustomQuery("q", "circuit", "account", tc.address)
 			require.Equal(t, tc.expPermission, gjson.Get(rsp, "permission.level").String())
-			if tc.limtTypeURLS != "" {
-				require.Equal(t, tc.limtTypeURLS, gjson.Get(rsp, "permission.limit_type_urls.0").String())
+			if len(tc.limtTypeURLS) != 0 {
+				listStr := gjson.Get(rsp, "permission.limit_type_urls").String()
+
+				// convert string to array
+				var msgsList []string
+				require.NoError(t, json.Unmarshal([]byte(listStr), &msgsList))
+
+				require.EqualValues(t, tc.limtTypeURLS, msgsList)
+			}
+		})
+	}
+
+	// test disable tx command
+	testCircuitTxCommand(t, cli, "disable", superAdmin, superAdmin2, allMsgsAcc, someMsgsAcc)
+
+	// test reset tx command
+	testCircuitTxCommand(t, cli, "reset", superAdmin, superAdmin2, allMsgsAcc, someMsgsAcc)
+}
+
+func testCircuitTxCommand(t *testing.T, cli *CLIWrapper, txType, superAdmin, superAdmin2, allMsgsAcc, someMsgsAcc string) {
+	t.Helper()
+
+	disableTestCases := []struct {
+		name        string
+		fromAddr    string
+		disableMsgs []string
+		executeTxs  [][]string
+	}{
+		{
+			txType + " msgs with super admin",
+			superAdmin,
+			[]string{"/cosmos.gov.v1.MsgVote"},
+			[][]string{
+				{
+					"tx", "gov", "vote", "3", "yes", "--from=" + superAdmin,
+				},
+			},
+		},
+		{
+			txType + " msgs with all msgs level address",
+			allMsgsAcc,
+			[]string{"/cosmos.gov.v1.MsgDeposit"},
+			[][]string{
+				{
+					"tx", "gov", "deposit", "3", "1000stake", "--from=" + allMsgsAcc,
+				},
+			},
+		},
+		{
+			txType + " msgs with some msgs level address",
+			someMsgsAcc,
+			someMsgs,
+			[][]string{
+				{
+					"tx", "bank", "send", superAdmin, someMsgsAcc, "10000stake",
+				},
+				{
+					"tx", "bank", "multi-send", superAdmin, someMsgsAcc, superAdmin2, "10000stake", "--from=" + superAdmin,
+				},
+			},
+		},
+	}
+
+	for _, tc := range disableTestCases {
+		t.Run(tc.name, func(t *testing.T) {
+			cmd := []string{"tx", "circuit", txType, "--from=" + tc.fromAddr}
+			cmd = append(cmd, tc.disableMsgs...)
+			rsp := cli.RunAndWait(cmd...)
+			RequireTxSuccess(t, rsp)
+
+			// execute given type transaction
+			rsp = cli.CustomQuery("q", "circuit", "disabled-list")
+			var list []string
+			if rsp != "{}" {
+				listStr := gjson.Get(rsp, "disabled_list").String()
+
+				// convert string to array
+				require.NoError(t, json.Unmarshal([]byte(listStr), &list))
+			}
+			for _, msg := range tc.disableMsgs {
+				if txType == "disable" {
+					require.Contains(t, list, msg)
+				} else {
+					require.NotContains(t, list, msg)
+				}
+			}
+
+			// test given msg transaction to confirm
+			for _, tx := range tc.executeTxs {
+				tx = append(tx, "--fees=2stake")
+				rsp = cli.RunCommandWithArgs(cli.withTXFlags(tx...)...)
+				if txType == "disable" {
+					RequireTxFailure(t, rsp)
+					require.Contains(t, gjson.Get(rsp, "raw_log").String(), "tx type not allowed")
+				} else {
+					RequireTxSuccess(t, rsp)
+				}
+				// wait for sometime to avoid sequence error
+				time.Sleep(time.Second * 2)
 			}
 		})
 	}
diff --git a/x/circuit/autocli.go b/x/circuit/autocli.go
index e9cc67701cf7..a1cb8ada74e6 100644
--- a/x/circuit/autocli.go
+++ b/x/circuit/autocli.go
@@ -43,7 +43,7 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
 "SOME_MSGS" =     1,
 "ALL_MSGS" =      2,
 "SUPER_ADMIN" =   3,`,
-					Example: fmt.Sprintf(`%s tx circuit authorize [address] '{"level":1,"limit_type_urls":["/cosmos.bank.v1beta1.MsgSend, /cosmos.bank.v1beta1.MsgMultiSend"]}'"`, version.AppName),
+					Example: fmt.Sprintf(`%s tx circuit authorize [address] '{"level":1,"limit_type_urls":["/cosmos.bank.v1beta1.MsgSend", "/cosmos.bank.v1beta1.MsgMultiSend"]}'"`, version.AppName),
 					PositionalArgs: []*autocliv1.PositionalArgDescriptor{
 						{ProtoField: "grantee"},
 						{ProtoField: "permissions"}, // TODO(@julienrbrt) Support flattening msg for setting each field as a positional arg

From 0a52dab6931b0459b0b954e15b95d21c8a666b8b Mon Sep 17 00:00:00 2001
From: akhilkumarpilli <akhilkumar7947@gmail.com>
Date: Thu, 24 Oct 2024 11:02:26 +0530
Subject: [PATCH 3/3] address coderabbitai comments

---
 tests/systemtests/circuit_test.go | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/tests/systemtests/circuit_test.go b/tests/systemtests/circuit_test.go
index 1fd3e8d0b746..e6b660e22eca 100644
--- a/tests/systemtests/circuit_test.go
+++ b/tests/systemtests/circuit_test.go
@@ -100,7 +100,7 @@ func TestCircuitCommands(t *testing.T) {
 		name          string
 		address       string
 		level         int
-		limtTypeURLS  []string
+		limtTypeURLs  []string
 		expPermission string
 	}{
 		{
@@ -129,8 +129,8 @@ func TestCircuitCommands(t *testing.T) {
 	for _, tc := range authorizeTestCases {
 		t.Run(tc.name, func(t *testing.T) {
 			permissionJSON := fmt.Sprintf(`{"level":%d,"limit_type_urls":[]}`, tc.level)
-			if len(tc.limtTypeURLS) != 0 {
-				permissionJSON = fmt.Sprintf(`{"level":%d,"limit_type_urls":["%s"]}`, tc.level, strings.Join(tc.limtTypeURLS[:], `","`))
+			if len(tc.limtTypeURLs) != 0 {
+				permissionJSON = fmt.Sprintf(`{"level":%d,"limit_type_urls":["%s"]}`, tc.level, strings.Join(tc.limtTypeURLs[:], `","`))
 			}
 			rsp = cli.RunAndWait("tx", "circuit", "authorize", tc.address, permissionJSON, "--from="+superAdmin)
 			RequireTxSuccess(t, rsp)
@@ -138,14 +138,14 @@ func TestCircuitCommands(t *testing.T) {
 			// query account permissions
 			rsp = cli.CustomQuery("q", "circuit", "account", tc.address)
 			require.Equal(t, tc.expPermission, gjson.Get(rsp, "permission.level").String())
-			if len(tc.limtTypeURLS) != 0 {
+			if len(tc.limtTypeURLs) != 0 {
 				listStr := gjson.Get(rsp, "permission.limit_type_urls").String()
 
 				// convert string to array
 				var msgsList []string
 				require.NoError(t, json.Unmarshal([]byte(listStr), &msgsList))
 
-				require.EqualValues(t, tc.limtTypeURLS, msgsList)
+				require.EqualValues(t, tc.limtTypeURLs, msgsList)
 			}
 		})
 	}
@@ -211,11 +211,11 @@ func testCircuitTxCommand(t *testing.T, cli *CLIWrapper, txType, superAdmin, sup
 			// execute given type transaction
 			rsp = cli.CustomQuery("q", "circuit", "disabled-list")
 			var list []string
-			if rsp != "{}" {
-				listStr := gjson.Get(rsp, "disabled_list").String()
+			if gjson.Get(rsp, "disabled_list").Exists() {
+				listJSON := gjson.Get(rsp, "disabled_list").Raw
 
 				// convert string to array
-				require.NoError(t, json.Unmarshal([]byte(listStr), &list))
+				require.NoError(t, json.Unmarshal([]byte(listJSON), &list))
 			}
 			for _, msg := range tc.disableMsgs {
 				if txType == "disable" {