From cabfac77dafa3b4819dfac40c46055a8620e5809 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Fri, 26 Jun 2026 17:22:57 +0500 Subject: [PATCH 01/17] Add more tests Signed-off-by: Abdulbois --- testutil/sample/sample.go | 8 + types/pki/errors_test.go | 31 + .../keeper/device_software_compliance_test.go | 25 +- ...c_query_device_software_compliance_test.go | 17 +- .../keeper/msg_server_invalid_signer_test.go | 50 ++ x/compliance/types/genesis_test.go | 146 +---- x/compliance/types/msg_methods_test.go | 55 ++ x/dclauth/ante/ante_test.go | 46 ++ .../base/keeper/grpc_query_account_test.go | 127 ++++ .../base/keeper/grpc_query_params_test.go | 21 + x/dclauth/keeper/account_extras_test.go | 43 ++ x/dclauth/keeper/account_test.go | 33 +- .../keeper/grpc_query_account_stat_test.go | 10 +- x/dclauth/keeper/grpc_query_account_test.go | 47 +- ...c_query_pending_account_revocation_test.go | 48 +- .../keeper/grpc_query_pending_account_test.go | 47 +- .../grpc_query_rejected_account_test.go | 56 +- .../keeper/grpc_query_revoked_account_test.go | 55 +- .../keeper/msg_server_invalid_signer_test.go | 45 ++ .../keeper/pending_account_revocation_test.go | 26 +- x/dclauth/keeper/pending_account_test.go | 25 +- x/dclauth/keeper/rejected_account_test.go | 22 +- x/dclauth/keeper/revoked_account_test.go | 29 +- x/dclauth/types/account_methods_test.go | 67 ++ x/dclauth/types/genesis_test.go | 154 ++--- ...ssage_propose_add_account_branches_test.go | 60 ++ x/dclauth/types/msg_methods_test.go | 55 ++ .../grpc_query_rejected_upgrade_test.go | 15 +- .../keeper/msg_server_invalid_signer_test.go | 37 ++ x/dclupgrade/keeper/rejected_upgrade_test.go | 21 +- x/dclupgrade/types/genesis_test.go | 77 +-- x/dclupgrade/types/msg_methods_test.go | 51 ++ .../keeper/msg_server_invalid_signer_test.go | 49 ++ x/model/types/errors_test.go | 26 + x/model/types/genesis_test.go | 84 +-- x/model/types/msg_methods_test.go | 57 ++ .../grpc_query_rejected_certificate_test.go | 37 +- .../keeper/msg_server_invalid_signer_test.go | 100 +++ ...tificates_by_subject_key_id_extras_test.go | 30 + x/pki/keeper/noc_ica_certificates_test.go | 22 + x/pki/keeper/rejected_certificate_test.go | 30 +- .../keeper/revoked_root_certificates_test.go | 25 + x/pki/types/genesis_test.go | 610 +++++------------- x/pki/types/msg_methods_test.go | 83 +++ x/validator/genesis_test.go | 89 ++- .../grpc_query_disabled_validator_test.go | 5 - .../grpc_query_last_validator_power_test.go | 30 +- .../keeper/grpc_query_rejected_node_test.go | 78 ++- .../keeper/grpc_query_validator_test.go | 52 +- x/validator/keeper/iterate_test.go | 57 ++ .../keeper/last_validator_power_test.go | 43 +- .../keeper/msg_server_invalid_signer_test.go | 49 ++ x/validator/keeper/process_malicious_test.go | 406 ++++++------ x/validator/types/genesis_test.go | 166 +++-- .../message_create_validator_branches_test.go | 42 ++ x/validator/types/msg_methods_test.go | 60 ++ x/validator/types/validator_methods_test.go | 139 ++++ x/vendorinfo/types/genesis_test.go | 20 +- x/vendorinfo/types/msg_methods_test.go | 49 ++ 59 files changed, 2385 insertions(+), 1602 deletions(-) create mode 100644 types/pki/errors_test.go create mode 100644 x/compliance/keeper/msg_server_invalid_signer_test.go create mode 100644 x/compliance/types/msg_methods_test.go create mode 100644 x/dclauth/ante/ante_test.go create mode 100644 x/dclauth/base/keeper/grpc_query_account_test.go create mode 100644 x/dclauth/base/keeper/grpc_query_params_test.go create mode 100644 x/dclauth/keeper/account_extras_test.go create mode 100644 x/dclauth/keeper/msg_server_invalid_signer_test.go create mode 100644 x/dclauth/types/account_methods_test.go create mode 100644 x/dclauth/types/message_propose_add_account_branches_test.go create mode 100644 x/dclauth/types/msg_methods_test.go create mode 100644 x/dclupgrade/keeper/msg_server_invalid_signer_test.go create mode 100644 x/dclupgrade/types/msg_methods_test.go create mode 100644 x/model/keeper/msg_server_invalid_signer_test.go create mode 100644 x/model/types/errors_test.go create mode 100644 x/model/types/msg_methods_test.go create mode 100644 x/pki/keeper/msg_server_invalid_signer_test.go create mode 100644 x/pki/keeper/noc_certificates_by_subject_key_id_extras_test.go create mode 100644 x/pki/types/msg_methods_test.go create mode 100644 x/validator/keeper/iterate_test.go create mode 100644 x/validator/keeper/msg_server_invalid_signer_test.go create mode 100644 x/validator/types/message_create_validator_branches_test.go create mode 100644 x/validator/types/msg_methods_test.go create mode 100644 x/validator/types/validator_methods_test.go create mode 100644 x/vendorinfo/types/msg_methods_test.go diff --git a/testutil/sample/sample.go b/testutil/sample/sample.go index 0d983aa82..d95705b3d 100644 --- a/testutil/sample/sample.go +++ b/testutil/sample/sample.go @@ -12,3 +12,11 @@ func AccAddress() string { return sdk.AccAddress(addr).String() } + +// ValAddress returns a sample validator operator (valoper) address. +func ValAddress() string { + pk := ed25519.GenPrivKey().PubKey() + addr := pk.Address() + + return sdk.ValAddress(addr).String() +} diff --git a/types/pki/errors_test.go b/types/pki/errors_test.go new file mode 100644 index 000000000..468a6e083 --- /dev/null +++ b/types/pki/errors_test.go @@ -0,0 +1,31 @@ +package types_test + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" + pkitypes "github.com/zigbee-alliance/distributed-compliance-ledger/types/pki" +) + +// TestErrorConstructors exercises the error constructors that build the module's +// typed error surface so each wrapped sentinel is produced at least once. +func TestErrorConstructors(t *testing.T) { + errs := []error{ + pkitypes.NewErrRevokedCertificateDoesNotExist("subj", "skid"), + pkitypes.NewErrProvidedNotNocCertButExistingNoc("subj", "skid"), + pkitypes.NewErrProvidedNotNocCertButRootIsNoc(), + pkitypes.NewErrCRLSignerCertificatePidNotEqualRevocationPointPid(1, 2), + pkitypes.NewErrInvalidAuthorityKeyIDFormat(), + pkitypes.NewErrVidNotFound("vid"), + pkitypes.NewErrPemValuesNotEqual("subj", "skid"), + pkitypes.NewErrUnsupportedOperation("op"), + pkitypes.NewErrInvalidVidFormat("bad"), + pkitypes.NewErrInvalidPidFormat("bad"), + } + for _, err := range errs { + require.Error(t, err) + require.NotEmpty(t, err.Error()) + require.NotNil(t, errors.Unwrap(err)) + } +} diff --git a/x/compliance/keeper/device_software_compliance_test.go b/x/compliance/keeper/device_software_compliance_test.go index b46ad79f0..6557cf896 100644 --- a/x/compliance/keeper/device_software_compliance_test.go +++ b/x/compliance/keeper/device_software_compliance_test.go @@ -1,6 +1,5 @@ package keeper_test -/* import ( "strconv" "testing" @@ -13,9 +12,6 @@ import ( "github.com/zigbee-alliance/distributed-compliance-ledger/x/compliance/types" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func createNDeviceSoftwareCompliance(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.DeviceSoftwareCompliance { items := make([]types.DeviceSoftwareCompliance, n) for i := range items { @@ -23,16 +19,15 @@ func createNDeviceSoftwareCompliance(keeper *keeper.Keeper, ctx sdk.Context, n i keeper.SetDeviceSoftwareCompliance(ctx, &items[i]) } + return items } func TestDeviceSoftwareComplianceGet(t *testing.T) { - keeper, ctx := keepertest.ComplianceKeeper(t) + keeper, ctx := keepertest.ComplianceKeeper(t, nil, nil) items := createNDeviceSoftwareCompliance(keeper, ctx, 10) for _, item := range items { - rst, found := keeper.GetDeviceSoftwareCompliance(ctx, - item.CDCertificateId, - ) + rst, found := keeper.GetDeviceSoftwareCompliance(ctx, item.CDCertificateId) require.True(t, found) require.Equal(t, nullify.Fill(&item), @@ -40,26 +35,22 @@ func TestDeviceSoftwareComplianceGet(t *testing.T) { ) } } + func TestDeviceSoftwareComplianceRemove(t *testing.T) { - keeper, ctx := keepertest.ComplianceKeeper(t) + keeper, ctx := keepertest.ComplianceKeeper(t, nil, nil) items := createNDeviceSoftwareCompliance(keeper, ctx, 10) for _, item := range items { - keeper.RemoveDeviceSoftwareCompliance(ctx, - item.CdCertificateId, - ) - _, found := keeper.GetDeviceSoftwareCompliance(ctx, - item.CdCertificateId, - ) + keeper.RemoveDeviceSoftwareCompliance(ctx, item.CDCertificateId) + _, found := keeper.GetDeviceSoftwareCompliance(ctx, item.CDCertificateId) require.False(t, found) } } func TestDeviceSoftwareComplianceGetAll(t *testing.T) { - keeper, ctx := keepertest.ComplianceKeeper(t) + keeper, ctx := keepertest.ComplianceKeeper(t, nil, nil) items := createNDeviceSoftwareCompliance(keeper, ctx, 10) require.ElementsMatch(t, nullify.Fill(items), nullify.Fill(keeper.GetAllDeviceSoftwareCompliance(ctx)), ) } -*/ diff --git a/x/compliance/keeper/grpc_query_device_software_compliance_test.go b/x/compliance/keeper/grpc_query_device_software_compliance_test.go index 711a2b217..037a2b6e4 100644 --- a/x/compliance/keeper/grpc_query_device_software_compliance_test.go +++ b/x/compliance/keeper/grpc_query_device_software_compliance_test.go @@ -1,6 +1,5 @@ package keeper_test -/* import ( "strconv" "testing" @@ -16,11 +15,8 @@ import ( "github.com/zigbee-alliance/distributed-compliance-ledger/x/compliance/types" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func TestDeviceSoftwareComplianceQuerySingle(t *testing.T) { - keeper, ctx := keepertest.ComplianceKeeper(t) + keeper, ctx := keepertest.ComplianceKeeper(t, nil, nil) wctx := sdk.WrapSDKContext(ctx) msgs := createNDeviceSoftwareCompliance(keeper, ctx, 2) for _, tc := range []struct { @@ -32,23 +28,23 @@ func TestDeviceSoftwareComplianceQuerySingle(t *testing.T) { { desc: "First", request: &types.QueryGetDeviceSoftwareComplianceRequest{ - CdCertificateId: msgs[0].CdCertificateId, + CDCertificateId: msgs[0].CDCertificateId, }, response: &types.QueryGetDeviceSoftwareComplianceResponse{DeviceSoftwareCompliance: msgs[0]}, }, { desc: "Second", request: &types.QueryGetDeviceSoftwareComplianceRequest{ - CdCertificateId: msgs[1].CdCertificateId, + CDCertificateId: msgs[1].CDCertificateId, }, response: &types.QueryGetDeviceSoftwareComplianceResponse{DeviceSoftwareCompliance: msgs[1]}, }, { desc: "KeyNotFound", request: &types.QueryGetDeviceSoftwareComplianceRequest{ - CdCertificateId: strconv.Itoa(100000), + CDCertificateId: strconv.Itoa(100000), }, - err: status.Error(codes.InvalidArgument, "not found"), + err: status.Error(codes.NotFound, "not found"), }, { desc: "InvalidRequest", @@ -71,7 +67,7 @@ func TestDeviceSoftwareComplianceQuerySingle(t *testing.T) { } func TestDeviceSoftwareComplianceQueryPaginated(t *testing.T) { - keeper, ctx := keepertest.ComplianceKeeper(t) + keeper, ctx := keepertest.ComplianceKeeper(t, nil, nil) wctx := sdk.WrapSDKContext(ctx) msgs := createNDeviceSoftwareCompliance(keeper, ctx, 5) @@ -125,4 +121,3 @@ func TestDeviceSoftwareComplianceQueryPaginated(t *testing.T) { require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } -*/ diff --git a/x/compliance/keeper/msg_server_invalid_signer_test.go b/x/compliance/keeper/msg_server_invalid_signer_test.go new file mode 100644 index 000000000..1590807d4 --- /dev/null +++ b/x/compliance/keeper/msg_server_invalid_signer_test.go @@ -0,0 +1,50 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/compliance/keeper" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/compliance/types" +) + +func TestMsgServerInvalidSigner(t *testing.T) { + k, ctx := keepertest.ComplianceKeeper(t, nil, nil) + ms := keeper.NewMsgServerImpl(*k) + wctx := sdk.WrapSDKContext(ctx) + const bad = "invalid-signer-address" + + cases := []struct { + name string + call func() error + }{ + {"CertifyModel", func() error { + _, err := ms.CertifyModel(wctx, &types.MsgCertifyModel{Signer: bad}) + return err + }}, + {"RevokeModel", func() error { + _, err := ms.RevokeModel(wctx, &types.MsgRevokeModel{Signer: bad}) + return err + }}, + {"ProvisionModel", func() error { + _, err := ms.ProvisionModel(wctx, &types.MsgProvisionModel{Signer: bad}) + return err + }}, + {"UpdateComplianceInfo", func() error { + _, err := ms.UpdateComplianceInfo(wctx, &types.MsgUpdateComplianceInfo{Creator: bad}) + return err + }}, + {"DeleteComplianceInfo", func() error { + _, err := ms.DeleteComplianceInfo(wctx, &types.MsgDeleteComplianceInfo{Creator: bad}) + return err + }}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + require.Error(t, tc.call()) + }) + } +} diff --git a/x/compliance/types/genesis_test.go b/x/compliance/types/genesis_test.go index 87d994c8d..cd22ce121 100644 --- a/x/compliance/types/genesis_test.go +++ b/x/compliance/types/genesis_test.go @@ -1,6 +1,5 @@ package types_test -/* import ( "testing" @@ -23,70 +22,25 @@ func TestGenesisState_Validate(t *testing.T) { desc: "valid genesis state", genState: &types.GenesisState{ ComplianceInfoList: []types.ComplianceInfo{ - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - CertificationType: "0", - }, - { - Vid: 1, - Pid: 1, - SoftwareVersion: 1, - CertificationType: "1", - }, + {Vid: 0, Pid: 0, SoftwareVersion: 0, CertificationType: "0"}, + {Vid: 1, Pid: 1, SoftwareVersion: 1, CertificationType: "1"}, }, CertifiedModelList: []types.CertifiedModel{ - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - CertificationType: "0", - }, - { - Vid: 1, - Pid: 1, - SoftwareVersion: 1, - CertificationType: "1", - }, + {Vid: 0, Pid: 0, SoftwareVersion: 0, CertificationType: "0"}, + {Vid: 1, Pid: 1, SoftwareVersion: 1, CertificationType: "1"}, }, RevokedModelList: []types.RevokedModel{ - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - CertificationType: "0", - }, - { - Vid: 1, - Pid: 1, - SoftwareVersion: 1, - CertificationType: "1", - }, + {Vid: 0, Pid: 0, SoftwareVersion: 0, CertificationType: "0"}, + {Vid: 1, Pid: 1, SoftwareVersion: 1, CertificationType: "1"}, }, ProvisionalModelList: []types.ProvisionalModel{ - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - CertificationType: "0", - }, - { - Vid: 1, - Pid: 1, - SoftwareVersion: 1, - CertificationType: "1", - }, + {Vid: 0, Pid: 0, SoftwareVersion: 0, CertificationType: "0"}, + {Vid: 1, Pid: 1, SoftwareVersion: 1, CertificationType: "1"}, }, DeviceSoftwareComplianceList: []types.DeviceSoftwareCompliance{ - { - CdCertificateId: "0", -}, - { - CdCertificateId: "1", -}, -}, -// this line is used by starport scaffolding # types/genesis/validField + {CDCertificateId: "0"}, + {CDCertificateId: "1"}, + }, }, valid: true, }, @@ -94,18 +48,8 @@ func TestGenesisState_Validate(t *testing.T) { desc: "duplicated complianceInfo", genState: &types.GenesisState{ ComplianceInfoList: []types.ComplianceInfo{ - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - CertificationType: "0", - }, - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - CertificationType: "0", - }, + {Vid: 0, Pid: 0, SoftwareVersion: 0, CertificationType: "0"}, + {Vid: 0, Pid: 0, SoftwareVersion: 0, CertificationType: "0"}, }, }, valid: false, @@ -114,18 +58,8 @@ func TestGenesisState_Validate(t *testing.T) { desc: "duplicated certifiedModel", genState: &types.GenesisState{ CertifiedModelList: []types.CertifiedModel{ - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - CertificationType: "0", - }, - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - CertificationType: "0", - }, + {Vid: 0, Pid: 0, SoftwareVersion: 0, CertificationType: "0"}, + {Vid: 0, Pid: 0, SoftwareVersion: 0, CertificationType: "0"}, }, }, valid: false, @@ -134,18 +68,8 @@ func TestGenesisState_Validate(t *testing.T) { desc: "duplicated revokedModel", genState: &types.GenesisState{ RevokedModelList: []types.RevokedModel{ - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - CertificationType: "0", - }, - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - CertificationType: "0", - }, + {Vid: 0, Pid: 0, SoftwareVersion: 0, CertificationType: "0"}, + {Vid: 0, Pid: 0, SoftwareVersion: 0, CertificationType: "0"}, }, }, valid: false, @@ -154,37 +78,22 @@ func TestGenesisState_Validate(t *testing.T) { desc: "duplicated provisionalModel", genState: &types.GenesisState{ ProvisionalModelList: []types.ProvisionalModel{ - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - CertificationType: "0", - }, - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - CertificationType: "0", - }, + {Vid: 0, Pid: 0, SoftwareVersion: 0, CertificationType: "0"}, + {Vid: 0, Pid: 0, SoftwareVersion: 0, CertificationType: "0"}, }, }, valid: false, }, { - desc: "duplicated deviceSoftwareCompliance", - genState: &types.GenesisState{ - DeviceSoftwareComplianceList: []types.DeviceSoftwareCompliance{ - { - CdCertificateId: "0", -}, - { - CdCertificateId: "0", -}, + desc: "duplicated deviceSoftwareCompliance", + genState: &types.GenesisState{ + DeviceSoftwareComplianceList: []types.DeviceSoftwareCompliance{ + {CDCertificateId: "0"}, + {CDCertificateId: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -// this line is used by starport scaffolding # types/genesis/testcase } { t.Run(tc.desc, func(t *testing.T) { err := tc.genState.Validate() @@ -196,4 +105,3 @@ func TestGenesisState_Validate(t *testing.T) { }) } } -*/ diff --git a/x/compliance/types/msg_methods_test.go b/x/compliance/types/msg_methods_test.go new file mode 100644 index 000000000..c1ee69f80 --- /dev/null +++ b/x/compliance/types/msg_methods_test.go @@ -0,0 +1,55 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" +) + +type legacyMsg interface { + Route() string + Type() string + GetSignBytes() []byte + GetSigners() []sdk.AccAddress +} + +func requireMsgMethods(t *testing.T, msg legacyMsg) { + t.Helper() + require.NotEmpty(t, msg.Route()) + require.NotEmpty(t, msg.Type()) + require.NotPanics(t, func() { _ = msg.GetSignBytes() }) + require.Len(t, msg.GetSigners(), 1) +} + +func TestComplianceMsgMethods(t *testing.T) { + addr := sample.AccAddress() + msgs := []legacyMsg{ + &MsgCertifyModel{Signer: addr}, + &MsgRevokeModel{Signer: addr}, + &MsgProvisionModel{Signer: addr}, + &MsgUpdateComplianceInfo{Creator: addr}, + &MsgDeleteComplianceInfo{Creator: addr}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + requireMsgMethods(t, msg) + }) + } +} + +func TestComplianceMsgGetSignersPanicsOnInvalidSigner(t *testing.T) { + msgs := []legacyMsg{ + &MsgCertifyModel{Signer: "invalid"}, + &MsgRevokeModel{Signer: "invalid"}, + &MsgProvisionModel{Signer: "invalid"}, + &MsgUpdateComplianceInfo{Creator: "invalid"}, + &MsgDeleteComplianceInfo{Creator: "invalid"}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + require.Panics(t, func() { _ = msg.GetSigners() }) + }) + } +} diff --git a/x/dclauth/ante/ante_test.go b/x/dclauth/ante/ante_test.go new file mode 100644 index 000000000..cdbfa53a6 --- /dev/null +++ b/x/dclauth/ante/ante_test.go @@ -0,0 +1,46 @@ +package ante_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256r1" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/ante" +) + +func TestDefaultSigVerificationGasConsumer(t *testing.T) { + params := authtypes.DefaultParams() + meter := storetypes.NewInfiniteGasMeter() + + // ED25519 keys are explicitly unsupported. + err := ante.DefaultSigVerificationGasConsumer(meter, + signing.SignatureV2{PubKey: ed25519.GenPrivKey().PubKey()}, params) + require.Error(t, err) + + // secp256k1 is accepted. + err = ante.DefaultSigVerificationGasConsumer(meter, + signing.SignatureV2{PubKey: secp256k1.GenPrivKey().PubKey()}, params) + require.NoError(t, err) + + // secp256r1 is accepted. + r1, genErr := secp256r1.GenPrivKey() + require.NoError(t, genErr) + err = ante.DefaultSigVerificationGasConsumer(meter, + signing.SignatureV2{PubKey: r1.PubKey()}, params) + require.NoError(t, err) + + // Any other (here: nil) public key type is rejected. + err = ante.DefaultSigVerificationGasConsumer(meter, + signing.SignatureV2{PubKey: nil}, params) + require.Error(t, err) +} + +func TestNewAnteHandler_RequiresAccountKeeper(t *testing.T) { + _, err := ante.NewAnteHandler(ante.HandlerOptions{}) + require.Error(t, err) +} diff --git a/x/dclauth/base/keeper/grpc_query_account_test.go b/x/dclauth/base/keeper/grpc_query_account_test.go new file mode 100644 index 000000000..155f5f869 --- /dev/null +++ b/x/dclauth/base/keeper/grpc_query_account_test.go @@ -0,0 +1,127 @@ +package keeper_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + dclauthtypes "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" +) + +// putInterfaceAccount stores an interface-encoded (Any-wrapped) BaseAccount, +// which is the encoding the cosmos-compatible Accounts list query decodes with +// (k.decodeAccount -> UnmarshalInterface). +func putInterfaceAccount(t *testing.T, cdc codec.BinaryCodec, storeKey storetypes.StoreKey, ctx sdk.Context, addr sdk.AccAddress) { + t.Helper() + + ba := authtypes.NewBaseAccountWithAddress(addr) + bz, err := cdc.MarshalInterface(ba) + require.NoError(t, err) + + store := prefix.NewStore(ctx.KVStore(storeKey), dclauthtypes.KeyPrefix(dclauthtypes.AccountKeyPrefix)) + store.Set(dclauthtypes.AccountKey(addr), bz) +} + +func TestAccountsQuery(t *testing.T) { + k, cdc, ctx, storeKey := setupKeeperWithAuthInterfaces(t) + wctx := sdk.WrapSDKContext(ctx) + + addrs := []string{"acc_1____________", "acc_2____________", "acc_3____________"} + for _, a := range addrs { + putInterfaceAccount(t, cdc, storeKey, ctx, sdk.AccAddress(a)) + } + + t.Run("InvalidRequest", func(t *testing.T) { + _, err := k.Accounts(wctx, nil) + require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) + }) + + t.Run("All", func(t *testing.T) { + resp, err := k.Accounts(wctx, &authtypes.QueryAccountsRequest{ + Pagination: &query.PageRequest{CountTotal: true}, + }) + require.NoError(t, err) + require.Len(t, resp.Accounts, len(addrs)) + require.Equal(t, uint64(len(addrs)), resp.Pagination.Total) + }) + + t.Run("Paginated", func(t *testing.T) { + resp, err := k.Accounts(wctx, &authtypes.QueryAccountsRequest{ + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}, + }) + require.NoError(t, err) + require.Len(t, resp.Accounts, 1) + require.Equal(t, uint64(len(addrs)), resp.Pagination.Total) + }) +} + +func TestAccountQuery(t *testing.T) { + k, cdc, ctx, storeKey := setupKeeperWithAuthInterfaces(t) + wctx := sdk.WrapSDKContext(ctx) + + addr := sdk.AccAddress("acc_1____________") + acc := putAccount(t, cdc, storeKey, ctx, addr) + + t.Run("InvalidRequest", func(t *testing.T) { + _, err := k.Account(wctx, nil) + require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) + }) + + t.Run("Found", func(t *testing.T) { + resp, err := k.Account(wctx, &authtypes.QueryAccountRequest{Address: addr.String()}) + require.NoError(t, err) + require.NotNil(t, resp.Account) + require.Equal(t, acc.BaseAccount, resp.Account.GetCachedValue()) + }) + + t.Run("InvalidAddress", func(t *testing.T) { + _, err := k.Account(wctx, &authtypes.QueryAccountRequest{Address: "not-a-bech32-address"}) + require.Error(t, err) + }) + + t.Run("NotFound", func(t *testing.T) { + missing := sdk.AccAddress("acc_missing______") + _, err := k.Account(wctx, &authtypes.QueryAccountRequest{Address: missing.String()}) + require.Error(t, err) + require.Equal(t, codes.NotFound, status.Code(err)) + }) +} + +func TestParamsQuery_InvalidRequest(t *testing.T) { + k, _, ctx, _ := setupKeeperWithAuthInterfaces(t) + wctx := sdk.WrapSDKContext(ctx) + + _, err := k.Params(wctx, nil) + require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "empty request")) +} + +// The remaining query methods are unimplemented passthrough stubs required to +// satisfy the cosmos auth QueryServer interface; exercise them so the wiring is +// covered and stays nil-safe. +func TestAccountQueryStubs(t *testing.T) { + k, _, ctx, _ := setupKeeperWithAuthInterfaces(t) + wctx := sdk.WrapSDKContext(ctx) + + _, err := k.AccountAddressByID(wctx, nil) + require.NoError(t, err) + _, err = k.ModuleAccounts(wctx, nil) + require.NoError(t, err) + _, err = k.ModuleAccountByName(wctx, nil) + require.NoError(t, err) + _, err = k.Bech32Prefix(wctx, nil) + require.NoError(t, err) + _, err = k.AddressBytesToString(wctx, nil) + require.NoError(t, err) + _, err = k.AddressStringToBytes(wctx, nil) + require.NoError(t, err) + _, err = k.AccountInfo(wctx, nil) + require.NoError(t, err) +} diff --git a/x/dclauth/base/keeper/grpc_query_params_test.go b/x/dclauth/base/keeper/grpc_query_params_test.go new file mode 100644 index 000000000..611cd6f15 --- /dev/null +++ b/x/dclauth/base/keeper/grpc_query_params_test.go @@ -0,0 +1,21 @@ +package keeper + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/stretchr/testify/require" +) + +func TestParamsQuery_Success(t *testing.T) { + keeper, ctx := setupKeeperWithParams(t) + wctx := sdk.WrapSDKContext(ctx) + + expected := authtypes.DefaultGenesisState().Params + keeper.SetParams(ctx, expected) + + resp, err := keeper.Params(wctx, &authtypes.QueryParamsRequest{}) + require.NoError(t, err) + require.Equal(t, expected, resp.Params) +} diff --git a/x/dclauth/keeper/account_extras_test.go b/x/dclauth/keeper/account_extras_test.go new file mode 100644 index 000000000..40d53099f --- /dev/null +++ b/x/dclauth/keeper/account_extras_test.go @@ -0,0 +1,43 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/stretchr/testify/require" + keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" + commontypes "github.com/zigbee-alliance/distributed-compliance-ledger/x/common/types" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" +) + +func TestAccountKeeper_AccessHelpers(t *testing.T) { + keeper, ctx := keepertest.DclauthKeeper(t) + + addr := sdk.MustAccAddressFromBech32(sample.AccAddress()) + missing := sdk.MustAccAddressFromBech32(sample.AccAddress()) + + acc := types.NewAccount( + authtypes.NewBaseAccount(addr, nil, 0, 0), + types.AccountRoles{types.Vendor}, nil, nil, 7, + []*commontypes.Uint16Range{{Min: 10, Max: 20}}, + ) + keeper.SetAccountO(ctx, *acc) + + require.NotNil(t, keeper.GetAccount(ctx, addr)) + require.Nil(t, keeper.GetAccount(ctx, missing)) + + require.Nil(t, keeper.GetModuleAddress("any")) + + require.True(t, keeper.HasVendorID(ctx, addr, 7)) + require.False(t, keeper.HasVendorID(ctx, addr, 8)) + require.False(t, keeper.HasVendorID(ctx, missing, 7)) + + require.True(t, keeper.HasRightsToChange(ctx, addr, 15)) + require.False(t, keeper.HasRightsToChange(ctx, addr, 99)) + require.False(t, keeper.HasRightsToChange(ctx, missing, 15)) + + require.Equal(t, 1, keeper.CountAccountsWithRole(ctx, types.Vendor)) + require.Equal(t, 0, keeper.CountAccountsWithRole(ctx, types.Trustee)) +} diff --git a/x/dclauth/keeper/account_test.go b/x/dclauth/keeper/account_test.go index 968a274c5..07469015e 100644 --- a/x/dclauth/keeper/account_test.go +++ b/x/dclauth/keeper/account_test.go @@ -1,28 +1,35 @@ package keeper_test -/* import ( - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/stretchr/testify/require" keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/nullify" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" ) -// Prevent strconv unused error -var _ = strconv.IntSize +// newTestAccount builds a valid DCL Account (with a base account) for the given +// bech32 address. Shared by the dclauth keeper test files in this package. +func newTestAccount(addr string) types.Account { + accAddr := sdk.MustAccAddressFromBech32(addr) + ba := authtypes.NewBaseAccount(accAddr, nil, 0, 0) + + return *types.NewAccount(ba, nil, nil, nil, 0, nil) +} func createNAccount(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.Account { items := make([]types.Account, n) for i := range items { - items[i].Address = strconv.Itoa(i) + items[i] = newTestAccount(sample.AccAddress()) - keeper.SetAccount(ctx, items[i]) + keeper.SetAccountO(ctx, items[i]) } + return items } @@ -30,9 +37,7 @@ func TestAccountGet(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) items := createNAccount(keeper, ctx, 10) for _, item := range items { - rst, found := keeper.GetAccount(ctx, - item.Address, - ) + rst, found := keeper.GetAccountO(ctx, item.GetAddress()) require.True(t, found) require.Equal(t, nullify.Fill(&item), @@ -40,16 +45,13 @@ func TestAccountGet(t *testing.T) { ) } } + func TestAccountRemove(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) items := createNAccount(keeper, ctx, 10) for _, item := range items { - keeper.RemoveAccount(ctx, - item.Address, - ) - _, found := keeper.GetAccount(ctx, - item.Address, - ) + keeper.RemoveAccount(ctx, item.GetAddress()) + _, found := keeper.GetAccountO(ctx, item.GetAddress()) require.False(t, found) } } @@ -62,4 +64,3 @@ func TestAccountGetAll(t *testing.T) { nullify.Fill(keeper.GetAllAccount(ctx)), ) } -*/ diff --git a/x/dclauth/keeper/grpc_query_account_stat_test.go b/x/dclauth/keeper/grpc_query_account_stat_test.go index a355f086e..d9f46e5bd 100644 --- a/x/dclauth/keeper/grpc_query_account_stat_test.go +++ b/x/dclauth/keeper/grpc_query_account_stat_test.go @@ -1,6 +1,5 @@ package keeper_test -/* import ( "testing" @@ -47,4 +46,11 @@ func TestAccountStatQuery(t *testing.T) { }) } } -*/ + +func TestAccountStatQuery_NotFound(t *testing.T) { + keeper, ctx := keepertest.DclauthKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + _, err := keeper.AccountStat(wctx, &types.QueryGetAccountStatRequest{}) + require.ErrorIs(t, err, status.Error(codes.NotFound, "not found")) +} diff --git a/x/dclauth/keeper/grpc_query_account_test.go b/x/dclauth/keeper/grpc_query_account_test.go index 2ce61c7e2..7718a325a 100644 --- a/x/dclauth/keeper/grpc_query_account_test.go +++ b/x/dclauth/keeper/grpc_query_account_test.go @@ -1,8 +1,6 @@ package keeper_test -/* import ( - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -10,14 +8,12 @@ import ( "github.com/stretchr/testify/require" keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/nullify" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func TestAccountQuerySingle(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) wctx := sdk.WrapSDKContext(ctx) @@ -29,25 +25,19 @@ func TestAccountQuerySingle(t *testing.T) { err error }{ { - desc: "First", - request: &types.QueryGetAccountRequest{ - Address: msgs[0].Address, - }, + desc: "First", + request: &types.QueryGetAccountRequest{Address: msgs[0].GetAddress().String()}, response: &types.QueryGetAccountResponse{Account: msgs[0]}, }, { - desc: "Second", - request: &types.QueryGetAccountRequest{ - Address: msgs[1].Address, - }, + desc: "Second", + request: &types.QueryGetAccountRequest{Address: msgs[1].GetAddress().String()}, response: &types.QueryGetAccountResponse{Account: msgs[1]}, }, { - desc: "KeyNotFound", - request: &types.QueryGetAccountRequest{ - Address: strconv.Itoa(100000), - }, - err: status.Error(codes.NotFound, "not found"), + desc: "KeyNotFound", + request: &types.QueryGetAccountRequest{Address: sample.AccAddress()}, + err: status.Error(codes.NotFound, "not found"), }, { desc: "InvalidRequest", @@ -67,6 +57,11 @@ func TestAccountQuerySingle(t *testing.T) { } }) } + + t.Run("InvalidAddress", func(t *testing.T) { + _, err := keeper.Account(wctx, &types.QueryGetAccountRequest{Address: "not-a-bech32-address"}) + require.Error(t, err) + }) } func TestAccountQueryPaginated(t *testing.T) { @@ -90,10 +85,7 @@ func TestAccountQueryPaginated(t *testing.T) { resp, err := keeper.AccountAll(wctx, request(nil, uint64(i), uint64(step), false)) require.NoError(t, err) require.LessOrEqual(t, len(resp.Account), step) - require.Subset(t, - nullify.Fill(msgs), - nullify.Fill(resp.Account), - ) + require.Subset(t, nullify.Fill(msgs), nullify.Fill(resp.Account)) } }) t.Run("ByKey", func(t *testing.T) { @@ -103,10 +95,7 @@ func TestAccountQueryPaginated(t *testing.T) { resp, err := keeper.AccountAll(wctx, request(next, 0, uint64(step), false)) require.NoError(t, err) require.LessOrEqual(t, len(resp.Account), step) - require.Subset(t, - nullify.Fill(msgs), - nullify.Fill(resp.Account), - ) + require.Subset(t, nullify.Fill(msgs), nullify.Fill(resp.Account)) next = resp.Pagination.NextKey } }) @@ -114,14 +103,10 @@ func TestAccountQueryPaginated(t *testing.T) { resp, err := keeper.AccountAll(wctx, request(nil, 0, 0, true)) require.NoError(t, err) require.Equal(t, len(msgs), int(resp.Pagination.Total)) - require.ElementsMatch(t, - nullify.Fill(msgs), - nullify.Fill(resp.Account), - ) + require.ElementsMatch(t, nullify.Fill(msgs), nullify.Fill(resp.Account)) }) t.Run("InvalidRequest", func(t *testing.T) { _, err := keeper.AccountAll(wctx, nil) require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } -*/ diff --git a/x/dclauth/keeper/grpc_query_pending_account_revocation_test.go b/x/dclauth/keeper/grpc_query_pending_account_revocation_test.go index f65605f8c..d8827a25d 100644 --- a/x/dclauth/keeper/grpc_query_pending_account_revocation_test.go +++ b/x/dclauth/keeper/grpc_query_pending_account_revocation_test.go @@ -1,9 +1,6 @@ package keeper_test -/* - import ( - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -11,14 +8,12 @@ import ( "github.com/stretchr/testify/require" keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/nullify" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func TestPendingAccountRevocationQuerySingle(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) wctx := sdk.WrapSDKContext(ctx) @@ -30,25 +25,19 @@ func TestPendingAccountRevocationQuerySingle(t *testing.T) { err error }{ { - desc: "First", - request: &types.QueryGetPendingAccountRevocationRequest{ - Address: msgs[0].Address, - }, + desc: "First", + request: &types.QueryGetPendingAccountRevocationRequest{Address: msgs[0].Address}, response: &types.QueryGetPendingAccountRevocationResponse{PendingAccountRevocation: msgs[0]}, }, { - desc: "Second", - request: &types.QueryGetPendingAccountRevocationRequest{ - Address: msgs[1].Address, - }, + desc: "Second", + request: &types.QueryGetPendingAccountRevocationRequest{Address: msgs[1].Address}, response: &types.QueryGetPendingAccountRevocationResponse{PendingAccountRevocation: msgs[1]}, }, { - desc: "KeyNotFound", - request: &types.QueryGetPendingAccountRevocationRequest{ - Address: strconv.Itoa(100000), - }, - err: status.Error(codes.NotFound, "not found"), + desc: "KeyNotFound", + request: &types.QueryGetPendingAccountRevocationRequest{Address: sample.AccAddress()}, + err: status.Error(codes.NotFound, "not found"), }, { desc: "InvalidRequest", @@ -68,6 +57,11 @@ func TestPendingAccountRevocationQuerySingle(t *testing.T) { } }) } + + t.Run("InvalidAddress", func(t *testing.T) { + _, err := keeper.PendingAccountRevocation(wctx, &types.QueryGetPendingAccountRevocationRequest{Address: "not-a-bech32-address"}) + require.Error(t, err) + }) } func TestPendingAccountRevocationQueryPaginated(t *testing.T) { @@ -91,10 +85,7 @@ func TestPendingAccountRevocationQueryPaginated(t *testing.T) { resp, err := keeper.PendingAccountRevocationAll(wctx, request(nil, uint64(i), uint64(step), false)) require.NoError(t, err) require.LessOrEqual(t, len(resp.PendingAccountRevocation), step) - require.Subset(t, - nullify.Fill(msgs), - nullify.Fill(resp.PendingAccountRevocation), - ) + require.Subset(t, nullify.Fill(msgs), nullify.Fill(resp.PendingAccountRevocation)) } }) t.Run("ByKey", func(t *testing.T) { @@ -104,10 +95,7 @@ func TestPendingAccountRevocationQueryPaginated(t *testing.T) { resp, err := keeper.PendingAccountRevocationAll(wctx, request(next, 0, uint64(step), false)) require.NoError(t, err) require.LessOrEqual(t, len(resp.PendingAccountRevocation), step) - require.Subset(t, - nullify.Fill(msgs), - nullify.Fill(resp.PendingAccountRevocation), - ) + require.Subset(t, nullify.Fill(msgs), nullify.Fill(resp.PendingAccountRevocation)) next = resp.Pagination.NextKey } }) @@ -115,14 +103,10 @@ func TestPendingAccountRevocationQueryPaginated(t *testing.T) { resp, err := keeper.PendingAccountRevocationAll(wctx, request(nil, 0, 0, true)) require.NoError(t, err) require.Equal(t, len(msgs), int(resp.Pagination.Total)) - require.ElementsMatch(t, - nullify.Fill(msgs), - nullify.Fill(resp.PendingAccountRevocation), - ) + require.ElementsMatch(t, nullify.Fill(msgs), nullify.Fill(resp.PendingAccountRevocation)) }) t.Run("InvalidRequest", func(t *testing.T) { _, err := keeper.PendingAccountRevocationAll(wctx, nil) require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } -*/ diff --git a/x/dclauth/keeper/grpc_query_pending_account_test.go b/x/dclauth/keeper/grpc_query_pending_account_test.go index cb249c5f7..0b0bead59 100644 --- a/x/dclauth/keeper/grpc_query_pending_account_test.go +++ b/x/dclauth/keeper/grpc_query_pending_account_test.go @@ -1,8 +1,6 @@ package keeper_test -/* import ( - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -10,14 +8,12 @@ import ( "github.com/stretchr/testify/require" keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/nullify" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func TestPendingAccountQuerySingle(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) wctx := sdk.WrapSDKContext(ctx) @@ -29,25 +25,19 @@ func TestPendingAccountQuerySingle(t *testing.T) { err error }{ { - desc: "First", - request: &types.QueryGetPendingAccountRequest{ - Address: msgs[0].Address, - }, + desc: "First", + request: &types.QueryGetPendingAccountRequest{Address: msgs[0].GetAddress().String()}, response: &types.QueryGetPendingAccountResponse{PendingAccount: msgs[0]}, }, { - desc: "Second", - request: &types.QueryGetPendingAccountRequest{ - Address: msgs[1].Address, - }, + desc: "Second", + request: &types.QueryGetPendingAccountRequest{Address: msgs[1].GetAddress().String()}, response: &types.QueryGetPendingAccountResponse{PendingAccount: msgs[1]}, }, { - desc: "KeyNotFound", - request: &types.QueryGetPendingAccountRequest{ - Address: strconv.Itoa(100000), - }, - err: status.Error(codes.NotFound, "not found"), + desc: "KeyNotFound", + request: &types.QueryGetPendingAccountRequest{Address: sample.AccAddress()}, + err: status.Error(codes.NotFound, "not found"), }, { desc: "InvalidRequest", @@ -67,6 +57,11 @@ func TestPendingAccountQuerySingle(t *testing.T) { } }) } + + t.Run("InvalidAddress", func(t *testing.T) { + _, err := keeper.PendingAccount(wctx, &types.QueryGetPendingAccountRequest{Address: "not-a-bech32-address"}) + require.Error(t, err) + }) } func TestPendingAccountQueryPaginated(t *testing.T) { @@ -90,10 +85,7 @@ func TestPendingAccountQueryPaginated(t *testing.T) { resp, err := keeper.PendingAccountAll(wctx, request(nil, uint64(i), uint64(step), false)) require.NoError(t, err) require.LessOrEqual(t, len(resp.PendingAccount), step) - require.Subset(t, - nullify.Fill(msgs), - nullify.Fill(resp.PendingAccount), - ) + require.Subset(t, nullify.Fill(msgs), nullify.Fill(resp.PendingAccount)) } }) t.Run("ByKey", func(t *testing.T) { @@ -103,10 +95,7 @@ func TestPendingAccountQueryPaginated(t *testing.T) { resp, err := keeper.PendingAccountAll(wctx, request(next, 0, uint64(step), false)) require.NoError(t, err) require.LessOrEqual(t, len(resp.PendingAccount), step) - require.Subset(t, - nullify.Fill(msgs), - nullify.Fill(resp.PendingAccount), - ) + require.Subset(t, nullify.Fill(msgs), nullify.Fill(resp.PendingAccount)) next = resp.Pagination.NextKey } }) @@ -114,14 +103,10 @@ func TestPendingAccountQueryPaginated(t *testing.T) { resp, err := keeper.PendingAccountAll(wctx, request(nil, 0, 0, true)) require.NoError(t, err) require.Equal(t, len(msgs), int(resp.Pagination.Total)) - require.ElementsMatch(t, - nullify.Fill(msgs), - nullify.Fill(resp.PendingAccount), - ) + require.ElementsMatch(t, nullify.Fill(msgs), nullify.Fill(resp.PendingAccount)) }) t.Run("InvalidRequest", func(t *testing.T) { _, err := keeper.PendingAccountAll(wctx, nil) require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } -*/ diff --git a/x/dclauth/keeper/grpc_query_rejected_account_test.go b/x/dclauth/keeper/grpc_query_rejected_account_test.go index c1960772b..60eb4ccde 100644 --- a/x/dclauth/keeper/grpc_query_rejected_account_test.go +++ b/x/dclauth/keeper/grpc_query_rejected_account_test.go @@ -1,24 +1,19 @@ package keeper_test -/* import ( - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/nullify" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) -// Prevent strconv unused error. -var _ = strconv.IntSize - func TestRejectedAccountQuerySingle(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) wctx := sdk.WrapSDKContext(ctx) @@ -30,29 +25,23 @@ func TestRejectedAccountQuerySingle(t *testing.T) { err error }{ { - desc: "First", - request: &types.QueryGetRejectedAccountRequest{ - Address: msgs[0].Address, - }, + desc: "First", + request: &types.QueryGetRejectedAccountRequest{Address: msgs[0].GetAddress().String()}, response: &types.QueryGetRejectedAccountResponse{RejectedAccount: msgs[0]}, }, { - desc: "Second", - request: &types.QueryGetRejectedAccountRequest{ - Address: msgs[1].Address, - }, + desc: "Second", + request: &types.QueryGetRejectedAccountRequest{Address: msgs[1].GetAddress().String()}, response: &types.QueryGetRejectedAccountResponse{RejectedAccount: msgs[1]}, }, { - desc: "KeyNotFound", - request: &types.QueryGetRejectedAccountRequest{ - Address: strconv.Itoa(100000), - }, - err: status.Error(codes.InvalidArgument, "not found"), + desc: "KeyNotFound", + request: &types.QueryGetRejectedAccountRequest{Address: sample.AccAddress()}, + err: status.Error(codes.NotFound, "not found"), }, { desc: "InvalidRequest", - err: status.Error(codes.InvalidArgument, "invalid request"), + err: status.Error(codes.NotFound, "not found"), }, } { t.Run(tc.desc, func(t *testing.T) { @@ -68,6 +57,11 @@ func TestRejectedAccountQuerySingle(t *testing.T) { } }) } + + t.Run("InvalidAddress", func(t *testing.T) { + _, err := keeper.RejectedAccount(wctx, &types.QueryGetRejectedAccountRequest{Address: "not-a-bech32-address"}) + require.Error(t, err) + }) } func TestRejectedAccountQueryPaginated(t *testing.T) { @@ -91,10 +85,7 @@ func TestRejectedAccountQueryPaginated(t *testing.T) { resp, err := keeper.RejectedAccountAll(wctx, request(nil, uint64(i), uint64(step), false)) require.NoError(t, err) require.LessOrEqual(t, len(resp.RejectedAccount), step) - require.Subset(t, - nullify.Fill(msgs), - nullify.Fill(resp.RejectedAccount), - ) + require.Subset(t, nullify.Fill(msgs), nullify.Fill(resp.RejectedAccount)) } }) t.Run("ByKey", func(t *testing.T) { @@ -104,10 +95,7 @@ func TestRejectedAccountQueryPaginated(t *testing.T) { resp, err := keeper.RejectedAccountAll(wctx, request(next, 0, uint64(step), false)) require.NoError(t, err) require.LessOrEqual(t, len(resp.RejectedAccount), step) - require.Subset(t, - nullify.Fill(msgs), - nullify.Fill(resp.RejectedAccount), - ) + require.Subset(t, nullify.Fill(msgs), nullify.Fill(resp.RejectedAccount)) next = resp.Pagination.NextKey } }) @@ -115,14 +103,10 @@ func TestRejectedAccountQueryPaginated(t *testing.T) { resp, err := keeper.RejectedAccountAll(wctx, request(nil, 0, 0, true)) require.NoError(t, err) require.Equal(t, len(msgs), int(resp.Pagination.Total)) - require.ElementsMatch(t, - nullify.Fill(msgs), - nullify.Fill(resp.RejectedAccount), - ) + require.ElementsMatch(t, nullify.Fill(msgs), nullify.Fill(resp.RejectedAccount)) }) t.Run("InvalidRequest", func(t *testing.T) { _, err := keeper.RejectedAccountAll(wctx, nil) - require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) + require.ErrorIs(t, err, status.Error(codes.NotFound, "not found")) }) } -*/ diff --git a/x/dclauth/keeper/grpc_query_revoked_account_test.go b/x/dclauth/keeper/grpc_query_revoked_account_test.go index a450b9f9d..c096d5877 100644 --- a/x/dclauth/keeper/grpc_query_revoked_account_test.go +++ b/x/dclauth/keeper/grpc_query_revoked_account_test.go @@ -1,24 +1,19 @@ package keeper_test -/* import ( - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/nullify" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func TestRevokedAccountQuerySingle(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) wctx := sdk.WrapSDKContext(ctx) @@ -30,25 +25,19 @@ func TestRevokedAccountQuerySingle(t *testing.T) { err error }{ { - desc: "First", - request: &types.QueryGetRevokedAccountRequest{ - Address: msgs[0].Address, - }, + desc: "First", + request: &types.QueryGetRevokedAccountRequest{Address: msgs[0].GetAddress().String()}, response: &types.QueryGetRevokedAccountResponse{RevokedAccount: msgs[0]}, }, { - desc: "Second", - request: &types.QueryGetRevokedAccountRequest{ - Address: msgs[1].Address, - }, + desc: "Second", + request: &types.QueryGetRevokedAccountRequest{Address: msgs[1].GetAddress().String()}, response: &types.QueryGetRevokedAccountResponse{RevokedAccount: msgs[1]}, }, { - desc: "KeyNotFound", - request: &types.QueryGetRevokedAccountRequest{ - Address: strconv.Itoa(100000), - }, - err: status.Error(codes.NotFound, "not found"), + desc: "KeyNotFound", + request: &types.QueryGetRevokedAccountRequest{Address: sample.AccAddress()}, + err: status.Error(codes.NotFound, "not found"), }, { desc: "InvalidRequest", @@ -68,6 +57,11 @@ func TestRevokedAccountQuerySingle(t *testing.T) { } }) } + + t.Run("InvalidAddress", func(t *testing.T) { + _, err := keeper.RevokedAccount(wctx, &types.QueryGetRevokedAccountRequest{Address: "not-a-bech32-address"}) + require.Error(t, err) + }) } func TestRevokedAccountQueryPaginated(t *testing.T) { @@ -91,10 +85,7 @@ func TestRevokedAccountQueryPaginated(t *testing.T) { resp, err := keeper.RevokedAccountAll(wctx, request(nil, uint64(i), uint64(step), false)) require.NoError(t, err) require.LessOrEqual(t, len(resp.RevokedAccount), step) - require.Subset(t, - nullify.Fill(msgs), - nullify.Fill(resp.RevokedAccount), - ) + require.Subset(t, nullify.Fill(msgs), nullify.Fill(resp.RevokedAccount)) } }) t.Run("ByKey", func(t *testing.T) { @@ -104,10 +95,7 @@ func TestRevokedAccountQueryPaginated(t *testing.T) { resp, err := keeper.RevokedAccountAll(wctx, request(next, 0, uint64(step), false)) require.NoError(t, err) require.LessOrEqual(t, len(resp.RevokedAccount), step) - require.Subset(t, - nullify.Fill(msgs), - nullify.Fill(resp.RevokedAccount), - ) + require.Subset(t, nullify.Fill(msgs), nullify.Fill(resp.RevokedAccount)) next = resp.Pagination.NextKey } }) @@ -115,15 +103,10 @@ func TestRevokedAccountQueryPaginated(t *testing.T) { resp, err := keeper.RevokedAccountAll(wctx, request(nil, 0, 0, true)) require.NoError(t, err) require.Equal(t, len(msgs), int(resp.Pagination.Total)) - require.ElementsMatch(t, - nullify.Fill(msgs), - nullify.Fill(resp.RevokedAccount), - ) + require.ElementsMatch(t, nullify.Fill(msgs), nullify.Fill(resp.RevokedAccount)) }) t.Run("InvalidRequest", func(t *testing.T) { _, err := keeper.RevokedAccountAll(wctx, nil) - require.ErrorIs(t, err, status.Error(codes.NotFound, "not found")) + require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } - -*/ diff --git a/x/dclauth/keeper/msg_server_invalid_signer_test.go b/x/dclauth/keeper/msg_server_invalid_signer_test.go new file mode 100644 index 000000000..5fe1b6b72 --- /dev/null +++ b/x/dclauth/keeper/msg_server_invalid_signer_test.go @@ -0,0 +1,45 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" +) + +func TestMsgServerInvalidSigner(t *testing.T) { + ms, ctx := setupMsgServer(t) + const bad = "invalid-signer-address" + + cases := []struct { + name string + call func() error + }{ + {"ProposeAddAccount", func() error { + _, err := ms.ProposeAddAccount(ctx, &types.MsgProposeAddAccount{Signer: bad}) + return err + }}, + {"ApproveAddAccount", func() error { + _, err := ms.ApproveAddAccount(ctx, &types.MsgApproveAddAccount{Signer: bad}) + return err + }}, + {"RejectAddAccount", func() error { + _, err := ms.RejectAddAccount(ctx, &types.MsgRejectAddAccount{Signer: bad}) + return err + }}, + {"ProposeRevokeAccount", func() error { + _, err := ms.ProposeRevokeAccount(ctx, &types.MsgProposeRevokeAccount{Signer: bad}) + return err + }}, + {"ApproveRevokeAccount", func() error { + _, err := ms.ApproveRevokeAccount(ctx, &types.MsgApproveRevokeAccount{Signer: bad}) + return err + }}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + require.Error(t, tc.call()) + }) + } +} diff --git a/x/dclauth/keeper/pending_account_revocation_test.go b/x/dclauth/keeper/pending_account_revocation_test.go index 2682273df..ca37b6884 100644 --- a/x/dclauth/keeper/pending_account_revocation_test.go +++ b/x/dclauth/keeper/pending_account_revocation_test.go @@ -1,29 +1,25 @@ package keeper_test -/* - import ( - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/nullify" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func createNPendingAccountRevocation(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.PendingAccountRevocation { items := make([]types.PendingAccountRevocation, n) for i := range items { - items[i].Address = strconv.Itoa(i) + items[i] = types.PendingAccountRevocation{Address: sample.AccAddress()} keeper.SetPendingAccountRevocation(ctx, items[i]) } + return items } @@ -31,9 +27,8 @@ func TestPendingAccountRevocationGet(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) items := createNPendingAccountRevocation(keeper, ctx, 10) for _, item := range items { - rst, found := keeper.GetPendingAccountRevocation(ctx, - item.Address, - ) + addr := sdk.MustAccAddressFromBech32(item.Address) + rst, found := keeper.GetPendingAccountRevocation(ctx, addr) require.True(t, found) require.Equal(t, nullify.Fill(&item), @@ -41,16 +36,14 @@ func TestPendingAccountRevocationGet(t *testing.T) { ) } } + func TestPendingAccountRevocationRemove(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) items := createNPendingAccountRevocation(keeper, ctx, 10) for _, item := range items { - keeper.RemovePendingAccountRevocation(ctx, - item.Address, - ) - _, found := keeper.GetPendingAccountRevocation(ctx, - item.Address, - ) + addr := sdk.MustAccAddressFromBech32(item.Address) + keeper.RemovePendingAccountRevocation(ctx, addr) + _, found := keeper.GetPendingAccountRevocation(ctx, addr) require.False(t, found) } } @@ -63,4 +56,3 @@ func TestPendingAccountRevocationGetAll(t *testing.T) { nullify.Fill(keeper.GetAllPendingAccountRevocation(ctx)), ) } -*/ diff --git a/x/dclauth/keeper/pending_account_test.go b/x/dclauth/keeper/pending_account_test.go index ec5df97b2..4093be330 100644 --- a/x/dclauth/keeper/pending_account_test.go +++ b/x/dclauth/keeper/pending_account_test.go @@ -1,29 +1,26 @@ package keeper_test -/* - import ( - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/nullify" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func createNPendingAccount(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.PendingAccount { items := make([]types.PendingAccount, n) for i := range items { - items[i].Address = strconv.Itoa(i) + acc := newTestAccount(sample.AccAddress()) + items[i] = types.PendingAccount{Account: &acc} keeper.SetPendingAccount(ctx, items[i]) } + return items } @@ -31,9 +28,7 @@ func TestPendingAccountGet(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) items := createNPendingAccount(keeper, ctx, 10) for _, item := range items { - rst, found := keeper.GetPendingAccount(ctx, - item.Address, - ) + rst, found := keeper.GetPendingAccount(ctx, item.GetAddress()) require.True(t, found) require.Equal(t, nullify.Fill(&item), @@ -41,16 +36,13 @@ func TestPendingAccountGet(t *testing.T) { ) } } + func TestPendingAccountRemove(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) items := createNPendingAccount(keeper, ctx, 10) for _, item := range items { - keeper.RemovePendingAccount(ctx, - item.Address, - ) - _, found := keeper.GetPendingAccount(ctx, - item.Address, - ) + keeper.RemovePendingAccount(ctx, item.GetAddress()) + _, found := keeper.GetPendingAccount(ctx, item.GetAddress()) require.False(t, found) } } @@ -63,4 +55,3 @@ func TestPendingAccountGetAll(t *testing.T) { nullify.Fill(keeper.GetAllPendingAccount(ctx)), ) } -*/ diff --git a/x/dclauth/keeper/rejected_account_test.go b/x/dclauth/keeper/rejected_account_test.go index 270e43661..ea390740b 100644 --- a/x/dclauth/keeper/rejected_account_test.go +++ b/x/dclauth/keeper/rejected_account_test.go @@ -1,25 +1,22 @@ package keeper_test -/* import ( - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/nullify" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" ) -// Prevent strconv unused error. -var _ = strconv.IntSize - func createNRejectedAccount(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.RejectedAccount { items := make([]types.RejectedAccount, n) for i := range items { - items[i].Address = strconv.Itoa(i) + acc := newTestAccount(sample.AccAddress()) + items[i] = types.RejectedAccount{Account: &acc} keeper.SetRejectedAccount(ctx, items[i]) } @@ -31,9 +28,7 @@ func TestRejectedAccountGet(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) items := createNRejectedAccount(keeper, ctx, 10) for _, item := range items { - rst, found := keeper.GetRejectedAccount(ctx, - item.Address, - ) + rst, found := keeper.GetRejectedAccount(ctx, item.GetAddress()) require.True(t, found) require.Equal(t, nullify.Fill(&item), @@ -46,12 +41,8 @@ func TestRejectedAccountRemove(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) items := createNRejectedAccount(keeper, ctx, 10) for _, item := range items { - keeper.RemoveRejectedAccount(ctx, - item.Address, - ) - _, found := keeper.GetRejectedAccount(ctx, - item.Address, - ) + keeper.RemoveRejectedAccount(ctx, item.GetAddress()) + _, found := keeper.GetRejectedAccount(ctx, item.GetAddress()) require.False(t, found) } } @@ -64,4 +55,3 @@ func TestRejectedAccountGetAll(t *testing.T) { nullify.Fill(keeper.GetAllRejectedAccount(ctx)), ) } -*/ diff --git a/x/dclauth/keeper/revoked_account_test.go b/x/dclauth/keeper/revoked_account_test.go index cc805e47e..93149bb85 100644 --- a/x/dclauth/keeper/revoked_account_test.go +++ b/x/dclauth/keeper/revoked_account_test.go @@ -1,43 +1,34 @@ package keeper_test -/* import ( - "fmt" - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/nullify" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func createNRevokedAccount(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.RevokedAccount { items := make([]types.RevokedAccount, n) for i := range items { - items[i].Address = strconv.Itoa(i) + acc := newTestAccount(sample.AccAddress()) + items[i] = types.RevokedAccount{Account: &acc} keeper.SetRevokedAccount(ctx, items[i]) } + return items } func TestRevokedAccountGet(t *testing.T) { - fmt.Print("H!") keeper, ctx := keepertest.DclauthKeeper(t) - fmt.Print("He!") items := createNRevokedAccount(keeper, ctx, 10) - - fmt.Print("Hello!") for _, item := range items { - rst, found := keeper.GetRevokedAccount(ctx, - item.GetAddress(), - ) + rst, found := keeper.GetRevokedAccount(ctx, item.GetAddress()) require.True(t, found) require.Equal(t, nullify.Fill(&item), @@ -45,16 +36,13 @@ func TestRevokedAccountGet(t *testing.T) { ) } } + func TestRevokedAccountRemove(t *testing.T) { keeper, ctx := keepertest.DclauthKeeper(t) items := createNRevokedAccount(keeper, ctx, 10) for _, item := range items { - keeper.RemoveRevokedAccount(ctx, - item.GetAddress(), - ) - _, found := keeper.GetRevokedAccount(ctx, - item.GetAddress(), - ) + keeper.RemoveRevokedAccount(ctx, item.GetAddress()) + _, found := keeper.GetRevokedAccount(ctx, item.GetAddress()) require.False(t, found) } } @@ -67,4 +55,3 @@ func TestRevokedAccountGetAll(t *testing.T) { nullify.Fill(keeper.GetAllRevokedAccount(ctx)), ) } -*/ diff --git a/x/dclauth/types/account_methods_test.go b/x/dclauth/types/account_methods_test.go new file mode 100644 index 000000000..3c7e7f2fa --- /dev/null +++ b/x/dclauth/types/account_methods_test.go @@ -0,0 +1,67 @@ +package types_test + +import ( + "testing" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" + commontypes "github.com/zigbee-alliance/distributed-compliance-ledger/x/common/types" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" +) + +func TestAccountRole_Validate(t *testing.T) { + require.NoError(t, types.Vendor.Validate()) + require.NoError(t, types.Trustee.Validate()) + require.Error(t, types.AccountRole("NotARole").Validate()) +} + +func baseAccount(t *testing.T) *authtypes.BaseAccount { + t.Helper() + addr := sample.AccAddress() + + return &authtypes.BaseAccount{Address: addr} +} + +func TestAccount_Validate(t *testing.T) { + t.Run("valid non-vendor", func(t *testing.T) { + acc := types.NewAccount(baseAccount(t), types.AccountRoles{types.Trustee}, nil, nil, 0, nil) + require.NoError(t, acc.Validate()) + }) + t.Run("valid vendor with vendorID", func(t *testing.T) { + acc := types.NewAccount(baseAccount(t), types.AccountRoles{types.Vendor}, nil, nil, 1, nil) + require.NoError(t, acc.Validate()) + }) + t.Run("invalid role", func(t *testing.T) { + acc := types.NewAccount(baseAccount(t), types.AccountRoles{types.AccountRole("Bogus")}, nil, nil, 0, nil) + require.Error(t, acc.Validate()) + }) + t.Run("vendor without vendorID", func(t *testing.T) { + acc := types.NewAccount(baseAccount(t), types.AccountRoles{types.Vendor}, nil, nil, 0, nil) + require.Error(t, acc.Validate()) + }) +} + +func TestAccount_RoleHelpers(t *testing.T) { + acc := types.NewAccount(baseAccount(t), types.AccountRoles{types.Vendor}, nil, nil, 1, nil) + require.True(t, acc.HasRole(types.Vendor)) + require.False(t, acc.HasRole(types.Trustee)) + require.True(t, acc.HasOnlyVendorRole(types.Vendor)) + + multi := types.NewAccount(baseAccount(t), types.AccountRoles{types.Vendor, types.Trustee}, nil, nil, 1, nil) + require.False(t, multi.HasOnlyVendorRole(types.Vendor)) +} + +func TestAccount_HasRightsToChange(t *testing.T) { + // No products -> rights to change anything. + noProducts := types.NewAccount(baseAccount(t), types.AccountRoles{types.Vendor}, nil, nil, 1, nil) + require.True(t, noProducts.HasRightsToChange(123)) + + scoped := types.NewAccount(baseAccount(t), types.AccountRoles{types.Vendor}, nil, nil, 1, + []*commontypes.Uint16Range{{Min: 10, Max: 20}}) + require.True(t, scoped.HasRightsToChange(10)) // min boundary + require.True(t, scoped.HasRightsToChange(20)) // max boundary + require.True(t, scoped.HasRightsToChange(15)) // inside + require.False(t, scoped.HasRightsToChange(9)) // below + require.False(t, scoped.HasRightsToChange(21)) // above +} diff --git a/x/dclauth/types/genesis_test.go b/x/dclauth/types/genesis_test.go index 04772de6c..1076bfaaf 100644 --- a/x/dclauth/types/genesis_test.go +++ b/x/dclauth/types/genesis_test.go @@ -1,15 +1,22 @@ package types_test -/* - import ( "testing" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" ) +func testAccount(address string) *types.Account { + return &types.Account{BaseAccount: &authtypes.BaseAccount{Address: address}} +} + func TestGenesisState_Validate(t *testing.T) { + addr1 := sample.AccAddress() + addr2 := sample.AccAddress() + for _, tc := range []struct { desc string genState *types.GenesisState @@ -24,62 +31,50 @@ func TestGenesisState_Validate(t *testing.T) { desc: "valid genesis state", genState: &types.GenesisState{ AccountList: []types.Account{ - { - Address: "0", - }, - { - Address: "1", - }, + *testAccount(addr1), + *testAccount(addr2), }, PendingAccountList: []types.PendingAccount{ - { - Address: "0", - }, - { - Address: "1", - }, + {Account: testAccount(addr1)}, + {Account: testAccount(addr2)}, }, PendingAccountRevocationList: []types.PendingAccountRevocation{ - { - Address: "0", - }, - { - Address: "1", - }, - }, - AccountStat: &types.AccountStat{ - Number: 94, + {Address: addr1}, + {Address: addr2}, }, RevokedAccountList: []types.RevokedAccount{ - { - Address: "0", -}, - { - Address: "1", -}, -}, -RejectedAccountList: []types.RejectedAccount{ - { - Address: "0", -}, - { - Address: "1", -}, -}, -// this line is used by starport scaffolding # types/genesis/validField + {Account: testAccount(addr1)}, + {Account: testAccount(addr2)}, + }, + RejectedAccountList: []types.RejectedAccount{ + {Account: testAccount(addr1)}, + {Account: testAccount(addr2)}, + }, }, valid: true, }, + { + desc: "invalid account address", + genState: &types.GenesisState{ + AccountList: []types.Account{*testAccount("invalid-address")}, + }, + valid: false, + }, { desc: "duplicated account", genState: &types.GenesisState{ AccountList: []types.Account{ - { - Address: "0", - }, - { - Address: "0", - }, + *testAccount(addr1), + *testAccount(addr1), + }, + }, + valid: false, + }, + { + desc: "invalid pendingAccount address", + genState: &types.GenesisState{ + PendingAccountList: []types.PendingAccount{ + {Account: testAccount("invalid-address")}, }, }, valid: false, @@ -88,12 +83,17 @@ RejectedAccountList: []types.RejectedAccount{ desc: "duplicated pendingAccount", genState: &types.GenesisState{ PendingAccountList: []types.PendingAccount{ - { - Address: "0", - }, - { - Address: "0", - }, + {Account: testAccount(addr1)}, + {Account: testAccount(addr1)}, + }, + }, + valid: false, + }, + { + desc: "invalid pendingAccountRevocation address", + genState: &types.GenesisState{ + PendingAccountRevocationList: []types.PendingAccountRevocation{ + {Address: "invalid-address"}, }, }, valid: false, @@ -102,45 +102,32 @@ RejectedAccountList: []types.RejectedAccount{ desc: "duplicated pendingAccountRevocation", genState: &types.GenesisState{ PendingAccountRevocationList: []types.PendingAccountRevocation{ - { - Address: "0", - }, - { - Address: "0", - }, + {Address: addr1}, + {Address: addr1}, }, }, valid: false, }, { - desc: "duplicated revokedAccount", - genState: &types.GenesisState{ - RevokedAccountList: []types.RevokedAccount{ - { - Address: "0", -}, - { - Address: "0", -}, + desc: "duplicated revokedAccount", + genState: &types.GenesisState{ + RevokedAccountList: []types.RevokedAccount{ + {Account: testAccount(addr1)}, + {Account: testAccount(addr1)}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated rejectedAccount", - genState: &types.GenesisState{ - RejectedAccountList: []types.RejectedAccount{ - { - Address: "0", -}, - { - Address: "0", -}, + { + desc: "duplicated rejectedAccount", + genState: &types.GenesisState{ + RejectedAccountList: []types.RejectedAccount{ + {Account: testAccount(addr1)}, + {Account: testAccount(addr1)}, + }, + }, + valid: false, }, - }, - valid: false, -}, -// this line is used by starport scaffolding # types/genesis/testcase } { t.Run(tc.desc, func(t *testing.T) { err := tc.genState.Validate() @@ -152,4 +139,3 @@ RejectedAccountList: []types.RejectedAccount{ }) } } -*/ diff --git a/x/dclauth/types/message_propose_add_account_branches_test.go b/x/dclauth/types/message_propose_add_account_branches_test.go new file mode 100644 index 000000000..a94af254e --- /dev/null +++ b/x/dclauth/types/message_propose_add_account_branches_test.go @@ -0,0 +1,60 @@ +package types_test + +import ( + "testing" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" +) + +func TestMsgProposeAddAccount_ValidateBasicBranches(t *testing.T) { + pk := ed25519.GenPrivKey().PubKey() + accAddr := sdk.AccAddress(pk.Address()) + pkAny, err := codectypes.NewAnyWithValue(pk) + require.NoError(t, err) + badAny, err := codectypes.NewAnyWithValue(&types.MsgProposeAddAccount{}) + require.NoError(t, err) + signer := sample.AccAddress() + + base := func() types.MsgProposeAddAccount { + return types.MsgProposeAddAccount{ + Address: accAddr.String(), + Signer: signer, + PubKey: pkAny, + Roles: types.AccountRoles{types.Vendor}, + VendorID: 1, + } + } + + mutate := func(f func(*types.MsgProposeAddAccount)) types.MsgProposeAddAccount { + m := base() + f(&m) + + return m + } + + for _, tc := range []struct { + name string + msg types.MsgProposeAddAccount + }{ + {"empty address", mutate(func(m *types.MsgProposeAddAccount) { m.Address = "" })}, + {"invalid address", mutate(func(m *types.MsgProposeAddAccount) { m.Address = "invalid" })}, + {"empty signer", mutate(func(m *types.MsgProposeAddAccount) { m.Signer = "" })}, + {"invalid signer", mutate(func(m *types.MsgProposeAddAccount) { m.Signer = "invalid" })}, + {"nil pubkey", mutate(func(m *types.MsgProposeAddAccount) { m.PubKey = nil })}, + {"non-pubkey any", mutate(func(m *types.MsgProposeAddAccount) { m.PubKey = badAny })}, + {"pubkey/address mismatch", mutate(func(m *types.MsgProposeAddAccount) { m.Address = sample.AccAddress() })}, + {"no roles", mutate(func(m *types.MsgProposeAddAccount) { m.Roles = nil })}, + {"invalid role", mutate(func(m *types.MsgProposeAddAccount) { m.Roles = types.AccountRoles{types.AccountRole("bogus")} })}, + {"vendor without vendorID", mutate(func(m *types.MsgProposeAddAccount) { m.VendorID = 0 })}, + } { + t.Run(tc.name, func(t *testing.T) { + msg := tc.msg + require.Error(t, msg.ValidateBasic()) + }) + } +} diff --git a/x/dclauth/types/msg_methods_test.go b/x/dclauth/types/msg_methods_test.go new file mode 100644 index 000000000..648992cdf --- /dev/null +++ b/x/dclauth/types/msg_methods_test.go @@ -0,0 +1,55 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" +) + +type legacyMsg interface { + Route() string + Type() string + GetSignBytes() []byte + GetSigners() []sdk.AccAddress +} + +func requireMsgMethods(t *testing.T, msg legacyMsg) { + t.Helper() + require.NotEmpty(t, msg.Route()) + require.NotEmpty(t, msg.Type()) + require.NotPanics(t, func() { _ = msg.GetSignBytes() }) + require.Len(t, msg.GetSigners(), 1) +} + +func TestDclauthMsgMethods(t *testing.T) { + signer := sample.AccAddress() + msgs := []legacyMsg{ + &MsgApproveAddAccount{Signer: signer}, + &MsgApproveRevokeAccount{Signer: signer}, + &MsgProposeAddAccount{Signer: signer}, + &MsgProposeRevokeAccount{Signer: signer}, + &MsgRejectAddAccount{Signer: signer}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + requireMsgMethods(t, msg) + }) + } +} + +func TestDclauthMsgGetSignersPanicsOnInvalidSigner(t *testing.T) { + msgs := []legacyMsg{ + &MsgApproveAddAccount{Signer: "invalid"}, + &MsgApproveRevokeAccount{Signer: "invalid"}, + &MsgProposeAddAccount{Signer: "invalid"}, + &MsgProposeRevokeAccount{Signer: "invalid"}, + &MsgRejectAddAccount{Signer: "invalid"}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + require.Panics(t, func() { _ = msg.GetSigners() }) + }) + } +} diff --git a/x/dclupgrade/keeper/grpc_query_rejected_upgrade_test.go b/x/dclupgrade/keeper/grpc_query_rejected_upgrade_test.go index c4a746587..4110704d7 100644 --- a/x/dclupgrade/keeper/grpc_query_rejected_upgrade_test.go +++ b/x/dclupgrade/keeper/grpc_query_rejected_upgrade_test.go @@ -1,6 +1,5 @@ package keeper_test -/* import ( "strconv" "testing" @@ -16,11 +15,8 @@ import ( "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclupgrade/types" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func TestRejectedUpgradeQuerySingle(t *testing.T) { - keeper, ctx := keepertest.DclupgradeKeeper(t) + keeper, ctx := keepertest.DclupgradeKeeper(t, nil, nil) wctx := sdk.WrapSDKContext(ctx) msgs := createNRejectedUpgrade(keeper, ctx, 2) for _, tc := range []struct { @@ -32,14 +28,14 @@ func TestRejectedUpgradeQuerySingle(t *testing.T) { { desc: "First", request: &types.QueryGetRejectedUpgradeRequest{ - Name: msgs[0].Name, + Name: msgs[0].Plan.Name, }, response: &types.QueryGetRejectedUpgradeResponse{RejectedUpgrade: msgs[0]}, }, { desc: "Second", request: &types.QueryGetRejectedUpgradeRequest{ - Name: msgs[1].Name, + Name: msgs[1].Plan.Name, }, response: &types.QueryGetRejectedUpgradeResponse{RejectedUpgrade: msgs[1]}, }, @@ -48,7 +44,7 @@ func TestRejectedUpgradeQuerySingle(t *testing.T) { request: &types.QueryGetRejectedUpgradeRequest{ Name: strconv.Itoa(100000), }, - err: status.Error(codes.InvalidArgument, "not found"), + err: status.Error(codes.NotFound, "not found"), }, { desc: "InvalidRequest", @@ -71,7 +67,7 @@ func TestRejectedUpgradeQuerySingle(t *testing.T) { } func TestRejectedUpgradeQueryPaginated(t *testing.T) { - keeper, ctx := keepertest.DclupgradeKeeper(t) + keeper, ctx := keepertest.DclupgradeKeeper(t, nil, nil) wctx := sdk.WrapSDKContext(ctx) msgs := createNRejectedUpgrade(keeper, ctx, 5) @@ -125,4 +121,3 @@ func TestRejectedUpgradeQueryPaginated(t *testing.T) { require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } -*/ diff --git a/x/dclupgrade/keeper/msg_server_invalid_signer_test.go b/x/dclupgrade/keeper/msg_server_invalid_signer_test.go new file mode 100644 index 000000000..87e335781 --- /dev/null +++ b/x/dclupgrade/keeper/msg_server_invalid_signer_test.go @@ -0,0 +1,37 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclupgrade/types" +) + +func TestMsgServerInvalidSigner(t *testing.T) { + ms, ctx := setupMsgServer(t) + const bad = "invalid-signer-address" + + cases := []struct { + name string + call func() error + }{ + {"ProposeUpgrade", func() error { + _, err := ms.ProposeUpgrade(ctx, &types.MsgProposeUpgrade{Creator: bad}) + return err + }}, + {"ApproveUpgrade", func() error { + _, err := ms.ApproveUpgrade(ctx, &types.MsgApproveUpgrade{Creator: bad}) + return err + }}, + {"RejectUpgrade", func() error { + _, err := ms.RejectUpgrade(ctx, &types.MsgRejectUpgrade{Creator: bad}) + return err + }}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + require.Error(t, tc.call()) + }) + } +} diff --git a/x/dclupgrade/keeper/rejected_upgrade_test.go b/x/dclupgrade/keeper/rejected_upgrade_test.go index 8a5365940..76d427701 100644 --- a/x/dclupgrade/keeper/rejected_upgrade_test.go +++ b/x/dclupgrade/keeper/rejected_upgrade_test.go @@ -1,6 +1,5 @@ package keeper_test -/* import ( "strconv" "testing" @@ -13,25 +12,23 @@ import ( "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclupgrade/types" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func createNRejectedUpgrade(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.RejectedUpgrade { items := make([]types.RejectedUpgrade, n) for i := range items { - items[i].Name = strconv.Itoa(i) + items[i].Plan.Name = strconv.Itoa(i) keeper.SetRejectedUpgrade(ctx, items[i]) } + return items } func TestRejectedUpgradeGet(t *testing.T) { - keeper, ctx := keepertest.DclupgradeKeeper(t) + keeper, ctx := keepertest.DclupgradeKeeper(t, nil, nil) items := createNRejectedUpgrade(keeper, ctx, 10) for _, item := range items { rst, found := keeper.GetRejectedUpgrade(ctx, - item.Name, + item.Plan.Name, ) require.True(t, found) require.Equal(t, @@ -40,26 +37,26 @@ func TestRejectedUpgradeGet(t *testing.T) { ) } } + func TestRejectedUpgradeRemove(t *testing.T) { - keeper, ctx := keepertest.DclupgradeKeeper(t) + keeper, ctx := keepertest.DclupgradeKeeper(t, nil, nil) items := createNRejectedUpgrade(keeper, ctx, 10) for _, item := range items { keeper.RemoveRejectedUpgrade(ctx, - item.Name, + item.Plan.Name, ) _, found := keeper.GetRejectedUpgrade(ctx, - item.Name, + item.Plan.Name, ) require.False(t, found) } } func TestRejectedUpgradeGetAll(t *testing.T) { - keeper, ctx := keepertest.DclupgradeKeeper(t) + keeper, ctx := keepertest.DclupgradeKeeper(t, nil, nil) items := createNRejectedUpgrade(keeper, ctx, 10) require.ElementsMatch(t, nullify.Fill(items), nullify.Fill(keeper.GetAllRejectedUpgrade(ctx)), ) } -*/ diff --git a/x/dclupgrade/types/genesis_test.go b/x/dclupgrade/types/genesis_test.go index 4c1b5f5f4..5e3c98ef0 100644 --- a/x/dclupgrade/types/genesis_test.go +++ b/x/dclupgrade/types/genesis_test.go @@ -1,9 +1,9 @@ package types_test -/* import ( "testing" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/stretchr/testify/require" "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclupgrade/types" ) @@ -23,42 +23,17 @@ func TestGenesisState_Validate(t *testing.T) { desc: "valid genesis state", genState: &types.GenesisState{ ProposedUpgradeList: []types.ProposedUpgrade{ - { - Plan: types.Plan{ - Name: "0", - }, - }, - { - Plan: types.Plan{ - Name: "1", - }, - }, + {Plan: upgradetypes.Plan{Name: "0"}}, + {Plan: upgradetypes.Plan{Name: "1"}}, }, ApprovedUpgradeList: []types.ApprovedUpgrade{ - { - Plan: types.Plan{ - Name: "0", - }, - }, - { - Plan: types.Plan{ - Name: "1", - }, - }, + {Plan: upgradetypes.Plan{Name: "0"}}, + {Plan: upgradetypes.Plan{Name: "1"}}, }, RejectedUpgradeList: []types.RejectedUpgrade{ - { - Plan: types.Plan{ - Name: "0", - }, - }, - { - Plan: types.Plan{ - Name: "1", - }, - }, + {Plan: upgradetypes.Plan{Name: "0"}}, + {Plan: upgradetypes.Plan{Name: "1"}}, }, - // this line is used by starport scaffolding # types/genesis/validField }, valid: true, }, @@ -66,16 +41,8 @@ func TestGenesisState_Validate(t *testing.T) { desc: "duplicated proposedUpgrade", genState: &types.GenesisState{ ProposedUpgradeList: []types.ProposedUpgrade{ - { - Plan: types.Plan{ - Name: "0", - }, - }, - { - Plan: types.Plan{ - Name: "0", - }, - }, + {Plan: upgradetypes.Plan{Name: "0"}}, + {Plan: upgradetypes.Plan{Name: "0"}}, }, }, valid: false, @@ -84,16 +51,8 @@ func TestGenesisState_Validate(t *testing.T) { desc: "duplicated approvedUpgrade", genState: &types.GenesisState{ ApprovedUpgradeList: []types.ApprovedUpgrade{ - { - Plan: types.Plan{ - Name: "0", - }, - }, - { - Plan: types.Plan{ - Name: "0", - }, - }, + {Plan: upgradetypes.Plan{Name: "0"}}, + {Plan: upgradetypes.Plan{Name: "0"}}, }, }, valid: false, @@ -102,21 +61,12 @@ func TestGenesisState_Validate(t *testing.T) { desc: "duplicated rejectedUpgrade", genState: &types.GenesisState{ RejectedUpgradeList: []types.RejectedUpgrade{ - { - Plan: types.Plan{ - Name: "0", - }, - }, - { - Plan: types.Plan{ - Name: "1", - }, - }, + {Plan: upgradetypes.Plan{Name: "0"}}, + {Plan: upgradetypes.Plan{Name: "0"}}, }, }, valid: false, }, - // this line is used by starport scaffolding # types/genesis/testcase } { t.Run(tc.desc, func(t *testing.T) { err := tc.genState.Validate() @@ -128,4 +78,3 @@ func TestGenesisState_Validate(t *testing.T) { }) } } -*/ diff --git a/x/dclupgrade/types/msg_methods_test.go b/x/dclupgrade/types/msg_methods_test.go new file mode 100644 index 000000000..cdb141c5b --- /dev/null +++ b/x/dclupgrade/types/msg_methods_test.go @@ -0,0 +1,51 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" +) + +type legacyMsg interface { + Route() string + Type() string + GetSignBytes() []byte + GetSigners() []sdk.AccAddress +} + +func requireMsgMethods(t *testing.T, msg legacyMsg) { + t.Helper() + require.NotEmpty(t, msg.Route()) + require.NotEmpty(t, msg.Type()) + require.NotPanics(t, func() { _ = msg.GetSignBytes() }) + require.Len(t, msg.GetSigners(), 1) +} + +func TestDclupgradeMsgMethods(t *testing.T) { + creator := sample.AccAddress() + msgs := []legacyMsg{ + &MsgApproveUpgrade{Creator: creator}, + &MsgProposeUpgrade{Creator: creator}, + &MsgRejectUpgrade{Creator: creator}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + requireMsgMethods(t, msg) + }) + } +} + +func TestDclupgradeMsgGetSignersPanicsOnInvalidSigner(t *testing.T) { + msgs := []legacyMsg{ + &MsgApproveUpgrade{Creator: "invalid"}, + &MsgProposeUpgrade{Creator: "invalid"}, + &MsgRejectUpgrade{Creator: "invalid"}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + require.Panics(t, func() { _ = msg.GetSigners() }) + }) + } +} diff --git a/x/model/keeper/msg_server_invalid_signer_test.go b/x/model/keeper/msg_server_invalid_signer_test.go new file mode 100644 index 000000000..62a7a3471 --- /dev/null +++ b/x/model/keeper/msg_server_invalid_signer_test.go @@ -0,0 +1,49 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/model/types" +) + +func TestMsgServerInvalidSigner(t *testing.T) { + ms, ctx := setupMsgServer(t) + const bad = "invalid-signer-address" + + cases := []struct { + name string + call func() error + }{ + {"CreateModel", func() error { + _, err := ms.CreateModel(ctx, &types.MsgCreateModel{Creator: bad}) + return err + }}, + {"UpdateModel", func() error { + _, err := ms.UpdateModel(ctx, &types.MsgUpdateModel{Creator: bad}) + return err + }}, + {"DeleteModel", func() error { + _, err := ms.DeleteModel(ctx, &types.MsgDeleteModel{Creator: bad}) + return err + }}, + {"CreateModelVersion", func() error { + _, err := ms.CreateModelVersion(ctx, &types.MsgCreateModelVersion{Creator: bad}) + return err + }}, + {"UpdateModelVersion", func() error { + _, err := ms.UpdateModelVersion(ctx, &types.MsgUpdateModelVersion{Creator: bad}) + return err + }}, + {"DeleteModelVersion", func() error { + _, err := ms.DeleteModelVersion(ctx, &types.MsgDeleteModelVersion{Creator: bad}) + return err + }}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + require.Error(t, tc.call()) + }) + } +} diff --git a/x/model/types/errors_test.go b/x/model/types/errors_test.go new file mode 100644 index 000000000..1a71dd2d9 --- /dev/null +++ b/x/model/types/errors_test.go @@ -0,0 +1,26 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/model/types" +) + +func TestErrorConstructors(t *testing.T) { + errs := []error{ + types.NewErrVendorProductsDoNotExist(1), + types.NewErrSoftwareVersionStringInvalid("v"), + types.NewErrFirmwareInformationInvalid("fw"), + types.NewErrCDVersionNumberInvalid(1), + types.NewErrOtaURLInvalid("url"), + types.NewErrReleaseNotesURLInvalid("url"), + types.NewErrNoModelVersionsExist(1, 2), + types.NewErrModelVersionAlreadyExists(1, 2, 3), + types.NewErrorOtaURLNotProvidedButOtherOtaFieldsProvided(), + } + for _, err := range errs { + require.Error(t, err) + require.NotEmpty(t, err.Error()) + } +} diff --git a/x/model/types/genesis_test.go b/x/model/types/genesis_test.go index 116c69343..b3325bc75 100644 --- a/x/model/types/genesis_test.go +++ b/x/model/types/genesis_test.go @@ -1,6 +1,5 @@ package types_test -/* import ( "testing" @@ -23,46 +22,21 @@ func TestGenesisState_Validate(t *testing.T) { desc: "valid genesis state", genState: &types.GenesisState{ VendorProductsList: []types.VendorProducts{ - { - Vid: 0, - }, - { - Vid: 1, - }, + {Vid: 0}, + {Vid: 1}, }, ModelList: []types.Model{ - { - Vid: 0, - Pid: 0, - }, - { - Vid: 1, - Pid: 1, - }, + {Vid: 0, Pid: 0}, + {Vid: 1, Pid: 1}, }, ModelVersionList: []types.ModelVersion{ - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - }, - { - Vid: 1, - Pid: 1, - SoftwareVersion: 1, - }, + {Vid: 0, Pid: 0, SoftwareVersion: 0}, + {Vid: 1, Pid: 1, SoftwareVersion: 1}, }, ModelVersionsList: []types.ModelVersions{ - { - Vid: 0, - Pid: 0, - }, - { - Vid: 1, - Pid: 1, - }, + {Vid: 0, Pid: 0}, + {Vid: 1, Pid: 1}, }, - // this line is used by starport scaffolding # types/genesis/validField }, valid: true, }, @@ -70,12 +44,8 @@ func TestGenesisState_Validate(t *testing.T) { desc: "duplicated vendorProducts", genState: &types.GenesisState{ VendorProductsList: []types.VendorProducts{ - { - Vid: 0, - }, - { - Vid: 0, - }, + {Vid: 0}, + {Vid: 0}, }, }, valid: false, @@ -84,14 +54,8 @@ func TestGenesisState_Validate(t *testing.T) { desc: "duplicated model", genState: &types.GenesisState{ ModelList: []types.Model{ - { - Vid: 0, - Pid: 0, - }, - { - Vid: 0, - Pid: 0, - }, + {Vid: 0, Pid: 0}, + {Vid: 0, Pid: 0}, }, }, valid: false, @@ -100,16 +64,8 @@ func TestGenesisState_Validate(t *testing.T) { desc: "duplicated modelVersion", genState: &types.GenesisState{ ModelVersionList: []types.ModelVersion{ - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - }, - { - Vid: 0, - Pid: 0, - SoftwareVersion: 0, - }, + {Vid: 0, Pid: 0, SoftwareVersion: 0}, + {Vid: 0, Pid: 0, SoftwareVersion: 0}, }, }, valid: false, @@ -118,19 +74,12 @@ func TestGenesisState_Validate(t *testing.T) { desc: "duplicated modelVersions", genState: &types.GenesisState{ ModelVersionsList: []types.ModelVersions{ - { - Vid: 0, - Pid: 0, - }, - { - Vid: 0, - Pid: 0, - }, + {Vid: 0, Pid: 0}, + {Vid: 0, Pid: 0}, }, }, valid: false, }, - // this line is used by starport scaffolding # types/genesis/testcase } { t.Run(tc.desc, func(t *testing.T) { err := tc.genState.Validate() @@ -142,4 +91,3 @@ func TestGenesisState_Validate(t *testing.T) { }) } } -*/ diff --git a/x/model/types/msg_methods_test.go b/x/model/types/msg_methods_test.go new file mode 100644 index 000000000..4d0f818fd --- /dev/null +++ b/x/model/types/msg_methods_test.go @@ -0,0 +1,57 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" +) + +type legacyMsg interface { + Route() string + Type() string + GetSignBytes() []byte + GetSigners() []sdk.AccAddress +} + +func requireMsgMethods(t *testing.T, msg legacyMsg) { + t.Helper() + require.NotEmpty(t, msg.Route()) + require.NotEmpty(t, msg.Type()) + require.NotPanics(t, func() { _ = msg.GetSignBytes() }) + require.Len(t, msg.GetSigners(), 1) +} + +func TestModelMsgMethods(t *testing.T) { + creator := sample.AccAddress() + msgs := []legacyMsg{ + &MsgCreateModel{Creator: creator}, + &MsgUpdateModel{Creator: creator}, + &MsgDeleteModel{Creator: creator}, + &MsgCreateModelVersion{Creator: creator}, + &MsgUpdateModelVersion{Creator: creator}, + &MsgDeleteModelVersion{Creator: creator}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + requireMsgMethods(t, msg) + }) + } +} + +func TestModelMsgGetSignersPanicsOnInvalidSigner(t *testing.T) { + msgs := []legacyMsg{ + &MsgCreateModel{Creator: "invalid"}, + &MsgUpdateModel{Creator: "invalid"}, + &MsgDeleteModel{Creator: "invalid"}, + &MsgCreateModelVersion{Creator: "invalid"}, + &MsgUpdateModelVersion{Creator: "invalid"}, + &MsgDeleteModelVersion{Creator: "invalid"}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + require.Panics(t, func() { _ = msg.GetSigners() }) + }) + } +} diff --git a/x/pki/keeper/grpc_query_rejected_certificate_test.go b/x/pki/keeper/grpc_query_rejected_certificate_test.go index 031626500..43bdecc38 100644 --- a/x/pki/keeper/grpc_query_rejected_certificate_test.go +++ b/x/pki/keeper/grpc_query_rejected_certificate_test.go @@ -1,6 +1,5 @@ package keeper_test -/* import ( "strconv" "testing" @@ -16,46 +15,43 @@ import ( "github.com/zigbee-alliance/distributed-compliance-ledger/x/pki/types" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func TestRejectedCertificateQuerySingle(t *testing.T) { - keeper, ctx := keepertest.PkiKeeper(t) + keeper, ctx := keepertest.PkiKeeper(t, nil) wctx := sdk.WrapSDKContext(ctx) msgs := createNRejectedCertificate(keeper, ctx, 2) for _, tc := range []struct { desc string - request *types.QueryGetRejectedCertificateRequest - response *types.QueryGetRejectedCertificateResponse + request *types.QueryGetRejectedCertificatesRequest + response *types.QueryGetRejectedCertificatesResponse err error }{ { desc: "First", - request: &types.QueryGetRejectedCertificateRequest{ + request: &types.QueryGetRejectedCertificatesRequest{ Subject: msgs[0].Subject, - SubjectKeyID: msgs[0].SubjectKeyID, + SubjectKeyId: msgs[0].SubjectKeyId, }, - response: &types.QueryGetRejectedCertificateResponse{RejectedCertificate: msgs[0]}, + response: &types.QueryGetRejectedCertificatesResponse{RejectedCertificate: msgs[0]}, }, { desc: "Second", - request: &types.QueryGetRejectedCertificateRequest{ + request: &types.QueryGetRejectedCertificatesRequest{ Subject: msgs[1].Subject, - SubjectKeyID: msgs[1].SubjectKeyID, + SubjectKeyId: msgs[1].SubjectKeyId, }, - response: &types.QueryGetRejectedCertificateResponse{RejectedCertificate: msgs[1]}, + response: &types.QueryGetRejectedCertificatesResponse{RejectedCertificate: msgs[1]}, }, { desc: "KeyNotFound", - request: &types.QueryGetRejectedCertificateRequest{ + request: &types.QueryGetRejectedCertificatesRequest{ Subject: strconv.Itoa(100000), - SubjectKeyID: strconv.Itoa(100000), + SubjectKeyId: strconv.Itoa(100000), }, - err: status.Error(codes.InvalidArgument, "not found"), + err: status.Error(codes.NotFound, "not found"), }, { desc: "InvalidRequest", - err: status.Error(codes.InvalidArgument, "invalid request"), + err: status.Error(codes.NotFound, "invalid request"), }, } { t.Run(tc.desc, func(t *testing.T) { @@ -74,12 +70,12 @@ func TestRejectedCertificateQuerySingle(t *testing.T) { } func TestRejectedCertificateQueryPaginated(t *testing.T) { - keeper, ctx := keepertest.PkiKeeper(t) + keeper, ctx := keepertest.PkiKeeper(t, nil) wctx := sdk.WrapSDKContext(ctx) msgs := createNRejectedCertificate(keeper, ctx, 5) - request := func(next []byte, offset, limit uint64, total bool) *types.QueryAllRejectedCertificateRequest { - return &types.QueryAllRejectedCertificateRequest{ + request := func(next []byte, offset, limit uint64, total bool) *types.QueryAllRejectedCertificatesRequest { + return &types.QueryAllRejectedCertificatesRequest{ Pagination: &query.PageRequest{ Key: next, Offset: offset, @@ -128,4 +124,3 @@ func TestRejectedCertificateQueryPaginated(t *testing.T) { require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } -*/ diff --git a/x/pki/keeper/msg_server_invalid_signer_test.go b/x/pki/keeper/msg_server_invalid_signer_test.go new file mode 100644 index 000000000..593dcfd89 --- /dev/null +++ b/x/pki/keeper/msg_server_invalid_signer_test.go @@ -0,0 +1,100 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/pki/types" +) + +// TestMsgServerInvalidSigner exercises the invalid-signer-address error branch +// that fronts every pki transaction handler. The address is parsed before any +// keeper dependency is touched, so a nil dclauth keeper is fine here. +func TestMsgServerInvalidSigner(t *testing.T) { + ms, ctx := setupMsgServer(t) + const bad = "invalid-signer-address" + + cases := []struct { + name string + call func() error + }{ + {"AddX509Cert", func() error { + _, err := ms.AddX509Cert(ctx, &types.MsgAddX509Cert{Signer: bad}) + return err + }}, + {"AddNocX509RootCert", func() error { + _, err := ms.AddNocX509RootCert(ctx, &types.MsgAddNocX509RootCert{Signer: bad}) + return err + }}, + {"AddNocX509IcaCert", func() error { + _, err := ms.AddNocX509IcaCert(ctx, &types.MsgAddNocX509IcaCert{Signer: bad}) + return err + }}, + {"AddPkiRevocationDistributionPoint", func() error { + _, err := ms.AddPkiRevocationDistributionPoint(ctx, &types.MsgAddPkiRevocationDistributionPoint{Signer: bad}) + return err + }}, + {"UpdatePkiRevocationDistributionPoint", func() error { + _, err := ms.UpdatePkiRevocationDistributionPoint(ctx, &types.MsgUpdatePkiRevocationDistributionPoint{Signer: bad}) + return err + }}, + {"DeletePkiRevocationDistributionPoint", func() error { + _, err := ms.DeletePkiRevocationDistributionPoint(ctx, &types.MsgDeletePkiRevocationDistributionPoint{Signer: bad}) + return err + }}, + {"ApproveAddX509RootCert", func() error { + _, err := ms.ApproveAddX509RootCert(ctx, &types.MsgApproveAddX509RootCert{Signer: bad}) + return err + }}, + {"ApproveRevokeX509RootCert", func() error { + _, err := ms.ApproveRevokeX509RootCert(ctx, &types.MsgApproveRevokeX509RootCert{Signer: bad}) + return err + }}, + {"AssignVid", func() error { + _, err := ms.AssignVid(ctx, &types.MsgAssignVid{Signer: bad}) + return err + }}, + {"ProposeAddX509RootCert", func() error { + _, err := ms.ProposeAddX509RootCert(ctx, &types.MsgProposeAddX509RootCert{Signer: bad}) + return err + }}, + {"ProposeRevokeX509RootCert", func() error { + _, err := ms.ProposeRevokeX509RootCert(ctx, &types.MsgProposeRevokeX509RootCert{Signer: bad}) + return err + }}, + {"RejectAddX509RootCert", func() error { + _, err := ms.RejectAddX509RootCert(ctx, &types.MsgRejectAddX509RootCert{Signer: bad}) + return err + }}, + {"RemoveX509Cert", func() error { + _, err := ms.RemoveX509Cert(ctx, &types.MsgRemoveX509Cert{Signer: bad}) + return err + }}, + {"RemoveNocX509RootCert", func() error { + _, err := ms.RemoveNocX509RootCert(ctx, &types.MsgRemoveNocX509RootCert{Signer: bad}) + return err + }}, + {"RemoveNocX509IcaCert", func() error { + _, err := ms.RemoveNocX509IcaCert(ctx, &types.MsgRemoveNocX509IcaCert{Signer: bad}) + return err + }}, + {"RevokeX509Cert", func() error { + _, err := ms.RevokeX509Cert(ctx, &types.MsgRevokeX509Cert{Signer: bad}) + return err + }}, + {"RevokeNocX509RootCert", func() error { + _, err := ms.RevokeNocX509RootCert(ctx, &types.MsgRevokeNocX509RootCert{Signer: bad}) + return err + }}, + {"RevokeNocX509IcaCert", func() error { + _, err := ms.RevokeNocX509IcaCert(ctx, &types.MsgRevokeNocX509IcaCert{Signer: bad}) + return err + }}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + require.Error(t, tc.call()) + }) + } +} diff --git a/x/pki/keeper/noc_certificates_by_subject_key_id_extras_test.go b/x/pki/keeper/noc_certificates_by_subject_key_id_extras_test.go new file mode 100644 index 000000000..7bd9c7139 --- /dev/null +++ b/x/pki/keeper/noc_certificates_by_subject_key_id_extras_test.go @@ -0,0 +1,30 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/pki/types" +) + +func TestAddGetRemoveNocCertificatesBySubjectKeyID(t *testing.T) { + keeper, ctx := keepertest.PkiKeeper(t, nil) + + keeper.AddNocCertificatesBySubjectKeyID(ctx, types.NocCertificates{ + SubjectKeyId: "skid", + Certs: []*types.Certificate{ + {Subject: "s1", SubjectKeyId: "skid"}, + }, + }) + + got, found := keeper.GetNocCertificatesBySubjectKeyID(ctx, "skid") + require.True(t, found) + require.Len(t, got.Certs, 1) + require.Equal(t, "s1", got.Certs[0].Subject) + + keeper.RemoveNocCertificatesBySubjectAndSubjectKeyID(ctx, "s1", "skid") + + _, found = keeper.GetNocCertificatesBySubjectKeyID(ctx, "skid") + require.False(t, found) +} diff --git a/x/pki/keeper/noc_ica_certificates_test.go b/x/pki/keeper/noc_ica_certificates_test.go index f3d333c01..b3c51b2b5 100644 --- a/x/pki/keeper/noc_ica_certificates_test.go +++ b/x/pki/keeper/noc_ica_certificates_test.go @@ -63,3 +63,25 @@ func TestNocIcaCertificatesGetAll(t *testing.T) { nullify.Fill(keeper.GetAllNocIcaCertificates(ctx)), ) } + +func TestGetNocIcaCertificatesBySubjectAndSKID(t *testing.T) { + keeper, ctx := keepertest.PkiKeeper(t, nil) + keeper.SetNocIcaCertificates(ctx, types.NocIcaCertificates{ + Vid: 1, + Certs: []*types.Certificate{ + {Subject: "s1", SubjectKeyId: "k1"}, + {Subject: "s2", SubjectKeyId: "k2"}, + }, + }) + + res, found := keeper.GetNocIcaCertificatesBySubjectAndSKID(ctx, 1, "s1", "k1") + require.True(t, found) + require.Len(t, res.Certs, 1) + require.Equal(t, "s1", res.Certs[0].Subject) + + _, found = keeper.GetNocIcaCertificatesBySubjectAndSKID(ctx, 1, "nope", "nope") + require.False(t, found) + + _, found = keeper.GetNocIcaCertificatesBySubjectAndSKID(ctx, 99, "s1", "k1") + require.False(t, found) +} diff --git a/x/pki/keeper/rejected_certificate_test.go b/x/pki/keeper/rejected_certificate_test.go index ff8d0d7bd..79d3e8665 100644 --- a/x/pki/keeper/rejected_certificate_test.go +++ b/x/pki/keeper/rejected_certificate_test.go @@ -1,6 +1,5 @@ package keeper_test -/* import ( "strconv" "testing" @@ -13,28 +12,23 @@ import ( "github.com/zigbee-alliance/distributed-compliance-ledger/x/pki/types" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func createNRejectedCertificate(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.RejectedCertificate { items := make([]types.RejectedCertificate, n) for i := range items { items[i].Subject = strconv.Itoa(i) - items[i].SubjectKeyID = strconv.Itoa(i) + items[i].SubjectKeyId = strconv.Itoa(i) keeper.SetRejectedCertificate(ctx, items[i]) } + return items } func TestRejectedCertificateGet(t *testing.T) { - keeper, ctx := keepertest.PkiKeeper(t) + keeper, ctx := keepertest.PkiKeeper(t, nil) items := createNRejectedCertificate(keeper, ctx, 10) for _, item := range items { - rst, found := keeper.GetRejectedCertificate(ctx, - item.Subject, - item.SubjectKeyID, - ) + rst, found := keeper.GetRejectedCertificate(ctx, item.Subject, item.SubjectKeyId) require.True(t, found) require.Equal(t, nullify.Fill(&item), @@ -42,28 +36,22 @@ func TestRejectedCertificateGet(t *testing.T) { ) } } + func TestRejectedCertificateRemove(t *testing.T) { - keeper, ctx := keepertest.PkiKeeper(t) + keeper, ctx := keepertest.PkiKeeper(t, nil) items := createNRejectedCertificate(keeper, ctx, 10) for _, item := range items { - keeper.RemoveRejectedCertificate(ctx, - item.Subject, - item.SubjectKeyID, - ) - _, found := keeper.GetRejectedCertificate(ctx, - item.Subject, - item.SubjectKeyID, - ) + keeper.RemoveRejectedCertificate(ctx, item.Subject, item.SubjectKeyId) + _, found := keeper.GetRejectedCertificate(ctx, item.Subject, item.SubjectKeyId) require.False(t, found) } } func TestRejectedCertificateGetAll(t *testing.T) { - keeper, ctx := keepertest.PkiKeeper(t) + keeper, ctx := keepertest.PkiKeeper(t, nil) items := createNRejectedCertificate(keeper, ctx, 10) require.ElementsMatch(t, nullify.Fill(items), nullify.Fill(keeper.GetAllRejectedCertificate(ctx)), ) } -*/ diff --git a/x/pki/keeper/revoked_root_certificates_test.go b/x/pki/keeper/revoked_root_certificates_test.go index 653c25676..576adea4a 100644 --- a/x/pki/keeper/revoked_root_certificates_test.go +++ b/x/pki/keeper/revoked_root_certificates_test.go @@ -36,3 +36,28 @@ func TestRevokedRootCertificatesRemove(t *testing.T) { _, found := keeper.GetRevokedRootCertificates(ctx) require.False(t, found) } + +func TestRevokedRootCertificateAddRemove(t *testing.T) { + keeper, ctx := keepertest.PkiKeeper(t, nil) + + certID := types.CertificateIdentifier{Subject: "subj", SubjectKeyId: "skid"} + other := types.CertificateIdentifier{Subject: "subj2", SubjectKeyId: "skid2"} + + keeper.AddRevokedRootCertificate(ctx, certID) + keeper.AddRevokedRootCertificate(ctx, certID) // duplicate is ignored + keeper.AddRevokedRootCertificate(ctx, other) + + stored, found := keeper.GetRevokedRootCertificates(ctx) + require.True(t, found) + require.Len(t, stored.Certs, 2) + + // Removing an absent identifier is a no-op. + keeper.RemoveRevokedRootCertificate(ctx, types.CertificateIdentifier{Subject: "nope", SubjectKeyId: "nope"}) + stored, _ = keeper.GetRevokedRootCertificates(ctx) + require.Len(t, stored.Certs, 2) + + keeper.RemoveRevokedRootCertificate(ctx, certID) + stored, _ = keeper.GetRevokedRootCertificates(ctx) + require.Len(t, stored.Certs, 1) + require.Equal(t, other, *stored.Certs[0]) +} diff --git a/x/pki/types/genesis_test.go b/x/pki/types/genesis_test.go index a19a0df8b..ad0d63c58 100644 --- a/x/pki/types/genesis_test.go +++ b/x/pki/types/genesis_test.go @@ -1,6 +1,5 @@ package types_test -/* import ( "testing" @@ -23,218 +22,29 @@ func TestGenesisState_Validate(t *testing.T) { desc: "valid genesis state", genState: &types.GenesisState{ ApprovedCertificatesList: []types.ApprovedCertificates{ - { - Subject: "0", - SubjectKeyID: "0", - }, - { - Subject: "1", - SubjectKeyID: "1", - }, + {Subject: "0", SubjectKeyId: "0"}, + {Subject: "1", SubjectKeyId: "1"}, }, ProposedCertificateList: []types.ProposedCertificate{ - { - Subject: "0", - SubjectKeyID: "0", - }, - { - Subject: "1", - SubjectKeyID: "1", - }, + {Subject: "0", SubjectKeyId: "0"}, + {Subject: "1", SubjectKeyId: "1"}, }, ChildCertificatesList: []types.ChildCertificates{ - { - Issuer: "0", - AuthorityKeyId: "0", - }, - { - Issuer: "1", - AuthorityKeyId: "1", - }, - }, - ProposedCertificateRevocationList: []types.ProposedCertificateRevocation{ - { - Subject: "0", - SubjectKeyID: "0", - }, - { - Subject: "1", - SubjectKeyID: "1", - }, + {Issuer: "0", AuthorityKeyId: "0"}, + {Issuer: "1", AuthorityKeyId: "1"}, }, RevokedCertificatesList: []types.RevokedCertificates{ - { - Subject: "0", - SubjectKeyID: "0", - }, - { - Subject: "1", - SubjectKeyID: "1", - }, + {Subject: "0", SubjectKeyId: "0"}, + {Subject: "1", SubjectKeyId: "1"}, }, UniqueCertificateList: []types.UniqueCertificate{ - { - Issuer: "0", - SerialNumber: "0", - }, - { - Issuer: "1", - SerialNumber: "1", - }, - }, - ApprovedRootCertificates: &types.ApprovedRootCertificates{ - Certs: []*types.CertificateIdentifier{}, + {Issuer: "0", SerialNumber: "0"}, + {Issuer: "1", SerialNumber: "1"}, }, - RevokedRootCertificates: &types.RevokedRootCertificates{ - Certs: []*types.CertificateIdentifier{}, - }, - ApprovedCertificatesBySubjectList: []types.ApprovedCertificatesBySubject{ - { - Subject: "0", - }, - { - Subject: "1", - }, + NocRootCertificatesList: []types.NocRootCertificates{ + {Vid: 0}, + {Vid: 1}, }, - RejectedCertificateList: []types.RejectedCertificate{ - { - Subject: "0", - SubjectKeyID: "0", - }, - { - Subject: "1", - SubjectKeyID: "1", - }, - }, - PKIRevocationDistributionPointList: []types.PKIRevocationDistributionPoint{ - { - Vid: 0, -Label: "0", -IssuerSubjectKeyID: "0", -}, - { - Vid: 1, -Label: "1", -IssuerSubjectKeyID: "1", -}, -}, -PkiRevocationDistributionPointsByIssuerSubjectKeyIDList: []types.PkiRevocationDistributionPointsByIssuerSubjectKeyID{ - { - IssuerSubjectKeyID: "0", -}, - { - IssuerSubjectKeyID: "1", -}, -}, -NocRootCertificatesList: []types.NocRootCertificates{ - { - Vid: 0, -}, - { - Vid: 1, -}, -}, -NocCertificatesList: []types.NocCertificates{ - { - Vid: 0, -}, - { - Vid: 1, -}, -}, -RevokedNocRootCertificatesList: []types.RevokedNocRootCertificates{ - { - Subject: "0", -SubjectKeyID: "0", -}, - { - Subject: "1", -SubjectKeyID: "1", -}, -}, -NocCertificatesByVidAndSkidList: []types.NocCertificatesByVidAndSkid{ - { - Vid: 0, -SubjectKeyID: "0", -}, - { - Vid: 1, -SubjectKeyID: "1", -}, -}, -NocCertificatesBySubjectKeyIDList: []types.NocCertificatesBySubjectKeyID{ - { - Vid: 0, -SubjectKeyID: "0", -}, - { - Vid: 1, -SubjectKeyID: "1", -}, -}, -NocCertificatesList: []types.NocCertificates{ - { - Subject: "0", -SubjectKeyID: "0", -}, - { - Subject: "1", -SubjectKeyID: "1", -}, -}, -NocCertificatesBySubjectList: []types.NocCertificatesBySubject{ - { - Subject: "0", -}, - { - Subject: "1", -}, -}, -CertificatesList: []types.AllCertificates{ - { - Subject: "0", -SubjectKeyID: "0", -}, - { - Subject: "1", -SubjectKeyID: "1", -}, -}, -RevokedNocIcaCertificatesList: []types.RevokedNocIcaCertificates{ - { - Subject: "0", -SubjectKeyID: "0", -}, - { - Subject: "1", -SubjectKeyID: "1", -}, -}, -AllCertificatesBySubjectList: []types.AllCertificatesBySubject{ - { - Subject: "0", -}, - { - Subject: "1", -}, -}, -AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyId{ - { - SubjectKeyId: "0", -}, - { - SubjectKeyId: "1", -}, -}, -AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyID{ - { - SubjectKeyId: "0", -}, - { - SubjectKeyId: "1", -}, -}, -// this line is used by starport scaffolding # types/genesis/validField }, valid: true, }, @@ -242,14 +52,8 @@ AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyID{ desc: "duplicated approvedCertificates", genState: &types.GenesisState{ ApprovedCertificatesList: []types.ApprovedCertificates{ - { - Subject: "0", - SubjectKeyID: "0", - }, - { - Subject: "0", - SubjectKeyID: "0", - }, + {Subject: "0", SubjectKeyId: "0"}, + {Subject: "0", SubjectKeyId: "0"}, }, }, valid: false, @@ -258,14 +62,8 @@ AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyID{ desc: "duplicated proposedCertificate", genState: &types.GenesisState{ ProposedCertificateList: []types.ProposedCertificate{ - { - Subject: "0", - SubjectKeyID: "0", - }, - { - Subject: "0", - SubjectKeyID: "0", - }, + {Subject: "0", SubjectKeyId: "0"}, + {Subject: "0", SubjectKeyId: "0"}, }, }, valid: false, @@ -274,14 +72,8 @@ AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyID{ desc: "duplicated childCertificates", genState: &types.GenesisState{ ChildCertificatesList: []types.ChildCertificates{ - { - Issuer: "0", - AuthorityKeyId: "0", - }, - { - Issuer: "0", - AuthorityKeyId: "0", - }, + {Issuer: "0", AuthorityKeyId: "0"}, + {Issuer: "0", AuthorityKeyId: "0"}, }, }, valid: false, @@ -290,14 +82,8 @@ AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyID{ desc: "duplicated proposedCertificateRevocation", genState: &types.GenesisState{ ProposedCertificateRevocationList: []types.ProposedCertificateRevocation{ - { - Subject: "0", - SubjectKeyID: "0", - }, - { - Subject: "0", - SubjectKeyID: "0", - }, + {Subject: "0", SubjectKeyId: "0", SerialNumber: "0"}, + {Subject: "0", SubjectKeyId: "0", SerialNumber: "0"}, }, }, valid: false, @@ -306,14 +92,8 @@ AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyID{ desc: "duplicated revokedCertificates", genState: &types.GenesisState{ RevokedCertificatesList: []types.RevokedCertificates{ - { - Subject: "0", - SubjectKeyID: "0", - }, - { - Subject: "0", - SubjectKeyID: "0", - }, + {Subject: "0", SubjectKeyId: "0"}, + {Subject: "0", SubjectKeyId: "0"}, }, }, valid: false, @@ -322,14 +102,8 @@ AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyID{ desc: "duplicated uniqueCertificate", genState: &types.GenesisState{ UniqueCertificateList: []types.UniqueCertificate{ - { - Issuer: "0", - SerialNumber: "0", - }, - { - Issuer: "0", - SerialNumber: "0", - }, + {Issuer: "0", SerialNumber: "0"}, + {Issuer: "0", SerialNumber: "0"}, }, }, valid: false, @@ -338,12 +112,8 @@ AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyID{ desc: "duplicated approvedCertificatesBySubject", genState: &types.GenesisState{ ApprovedCertificatesBySubjectList: []types.ApprovedCertificatesBySubject{ - { - Subject: "0", - }, - { - Subject: "0", - }, + {Subject: "0"}, + {Subject: "0"}, }, }, valid: false, @@ -352,231 +122,142 @@ AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyID{ desc: "duplicated rejectedCertificate", genState: &types.GenesisState{ RejectedCertificateList: []types.RejectedCertificate{ - { - Subject: "0", - SubjectKeyID: "0", - }, - { - Subject: "0", - SubjectKeyID: "0", - }, + {Subject: "0", SubjectKeyId: "0"}, + {Subject: "0", SubjectKeyId: "0"}, }, }, valid: false, }, { - desc: "duplicated pKIRevocationDistributionPoint", - genState: &types.GenesisState{ - PKIRevocationDistributionPointList: []types.PKIRevocationDistributionPoint{ - { - Vid: 0, -Label: "0", -IssuerSubjectKeyID: "0", -}, - { - Vid: 0, -Label: "0", -IssuerSubjectKeyID: "0", -}, - }, - }, - valid: false, -}, -{ - desc: "duplicated pkiRevocationDistributionPointsByIssuerSubjectKeyID", - genState: &types.GenesisState{ - PkiRevocationDistributionPointsByIssuerSubjectKeyIDList: []types.PkiRevocationDistributionPointsByIssuerSubjectKeyID{ - { - IssuerSubjectKeyID: "0", -}, - { - IssuerSubjectKeyID: "0", -}, + desc: "duplicated pkiRevocationDistributionPoint", + genState: &types.GenesisState{ + PkiRevocationDistributionPointList: []types.PkiRevocationDistributionPoint{ + {Vid: 0, Label: "0", IssuerSubjectKeyID: "0"}, + {Vid: 0, Label: "0", IssuerSubjectKeyID: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated nocRootCertificates", - genState: &types.GenesisState{ - NocRootCertificatesList: []types.NocRootCertificates{ - { - Vid: 0, -}, - { - Vid: 0, -}, + { + desc: "duplicated pkiRevocationDistributionPointsByIssuerSubjectKeyID", + genState: &types.GenesisState{ + PkiRevocationDistributionPointsByIssuerSubjectKeyIDList: []types.PkiRevocationDistributionPointsByIssuerSubjectKeyID{ + {IssuerSubjectKeyID: "0"}, + {IssuerSubjectKeyID: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated nocCertificates", - genState: &types.GenesisState{ - NocCertificatesList: []types.NocCertificates{ - { - Vid: 0, -}, - { - Vid: 0, -}, + { + desc: "duplicated approvedCertificatesBySubjectKeyId", + genState: &types.GenesisState{ + ApprovedCertificatesBySubjectKeyIdList: []types.ApprovedCertificatesBySubjectKeyId{ + {SubjectKeyId: "0"}, + {SubjectKeyId: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated revokedNocRootCertificates", - genState: &types.GenesisState{ - RevokedNocRootCertificatesList: []types.RevokedNocRootCertificates{ - { - Subject: "0", -SubjectKeyID: "0", -}, - { - Subject: "0", -SubjectKeyID: "0", -}, + { + desc: "duplicated nocRootCertificates", + genState: &types.GenesisState{ + NocRootCertificatesList: []types.NocRootCertificates{ + {Vid: 0}, + {Vid: 0}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated nocCertificatesByVidAndSkid", - genState: &types.GenesisState{ - NocCertificatesByVidAndSkidList: []types.NocCertificatesByVidAndSkid{ - { - Vid: 0, -SubjectKeyID: "0", -}, - { - Vid: 0, -SubjectKeyID: "0", -}, + { + desc: "duplicated nocIcaCertificates", + genState: &types.GenesisState{ + NocIcaCertificatesList: []types.NocIcaCertificates{ + {Vid: 0}, + {Vid: 0}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated nocCertificatesBySubjectKeyId", - genState: &types.GenesisState{ - NocCertificatesBySubjectKeyIDList: []types.NocCertificatesBySubjectKeyID{ - { - Vid: 0, -SubjectKeyID: "0", -}, - { - Vid: 0, -SubjectKeyID: "0", -}, + { + desc: "duplicated revokedNocRootCertificates", + genState: &types.GenesisState{ + RevokedNocRootCertificatesList: []types.RevokedNocRootCertificates{ + {Subject: "0", SubjectKeyId: "0"}, + {Subject: "0", SubjectKeyId: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated nocCertificates", - genState: &types.GenesisState{ - NocCertificatesList: []types.NocCertificates{ - { - Subject: "0", -SubjectKeyID: "0", -}, - { - Subject: "0", -SubjectKeyID: "0", -}, + { + desc: "duplicated nocCertificatesByVidAndSkid", + genState: &types.GenesisState{ + NocCertificatesByVidAndSkidList: []types.NocCertificatesByVidAndSkid{ + {Vid: 0, SubjectKeyId: "0"}, + {Vid: 0, SubjectKeyId: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated nocCertificatesBySubject", - genState: &types.GenesisState{ - NocCertificatesBySubjectList: []types.NocCertificatesBySubject{ - { - Subject: "0", -}, - { - Subject: "0", -}, + { + desc: "duplicated nocCertificatesBySubjectKeyId", + genState: &types.GenesisState{ + NocCertificatesBySubjectKeyIDList: []types.NocCertificatesBySubjectKeyID{ + {SubjectKeyId: "0"}, + {SubjectKeyId: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated certificates", - genState: &types.GenesisState{ - CertificatesList: []types.AllCertificates{ - { - Subject: "0", -SubjectKeyID: "0", -}, - { - Subject: "0", -SubjectKeyID: "0", -}, + { + desc: "duplicated nocCertificatesBySubject", + genState: &types.GenesisState{ + NocCertificatesBySubjectList: []types.NocCertificatesBySubject{ + {Subject: "0"}, + {Subject: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated revokedNocIcaCertificates", - genState: &types.GenesisState{ - RevokedNocIcaCertificatesList: []types.RevokedNocIcaCertificates{ - { - Subject: "0", -SubjectKeyID: "0", -}, - { - Subject: "0", -SubjectKeyID: "0", -}, + { + desc: "duplicated certificates", + genState: &types.GenesisState{ + CertificatesList: []types.AllCertificates{ + {Subject: "0", SubjectKeyId: "0"}, + {Subject: "0", SubjectKeyId: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated allCertificatesBySubject", - genState: &types.GenesisState{ - AllCertificatesBySubjectList: []types.AllCertificatesBySubject{ - { - Subject: "0", -}, - { - Subject: "0", -}, + { + desc: "duplicated revokedNocIcaCertificates", + genState: &types.GenesisState{ + RevokedNocIcaCertificatesList: []types.RevokedNocIcaCertificates{ + {Subject: "0", SubjectKeyId: "0"}, + {Subject: "0", SubjectKeyId: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated allCertificatesBySubjectKeyId", - genState: &types.GenesisState{ - AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyId{ - { - SubjectKeyId: "0", -}, - { - SubjectKeyId: "0", -}, + { + desc: "duplicated allCertificatesBySubject", + genState: &types.GenesisState{ + AllCertificatesBySubjectList: []types.AllCertificatesBySubject{ + {Subject: "0"}, + {Subject: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated allCertificatesBySubjectKeyId", - genState: &types.GenesisState{ - AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyId{ - { - SubjectKeyId: "0", -}, - { - SubjectKeyId: "0", -}, + { + desc: "duplicated allCertificatesBySubjectKeyId", + genState: &types.GenesisState{ + AllCertificatesBySubjectKeyIdList: []types.AllCertificatesBySubjectKeyId{ + {SubjectKeyId: "0"}, + {SubjectKeyId: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -// this line is used by starport scaffolding # types/genesis/testcase } { t.Run(tc.desc, func(t *testing.T) { err := tc.genState.Validate() @@ -588,4 +269,3 @@ SubjectKeyID: "0", }) } } -*/ diff --git a/x/pki/types/msg_methods_test.go b/x/pki/types/msg_methods_test.go new file mode 100644 index 000000000..ae07cc372 --- /dev/null +++ b/x/pki/types/msg_methods_test.go @@ -0,0 +1,83 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" +) + +// legacyMsg captures the standard cosmos sdk.LegacyMsg boilerplate implemented +// by every transaction message in this module. +type legacyMsg interface { + Route() string + Type() string + GetSignBytes() []byte + GetSigners() []sdk.AccAddress +} + +func requireMsgMethods(t *testing.T, msg legacyMsg) { + t.Helper() + require.NotEmpty(t, msg.Route()) + require.NotEmpty(t, msg.Type()) + require.NotPanics(t, func() { _ = msg.GetSignBytes() }) + require.Len(t, msg.GetSigners(), 1) +} + +func TestPkiMsgMethods(t *testing.T) { + signer := sample.AccAddress() + msgs := []legacyMsg{ + &MsgAddNocX509IcaCert{Signer: signer}, + &MsgAddNocX509RootCert{Signer: signer}, + &MsgAddPkiRevocationDistributionPoint{Signer: signer}, + &MsgAddX509Cert{Signer: signer}, + &MsgApproveAddX509RootCert{Signer: signer}, + &MsgApproveRevokeX509RootCert{Signer: signer}, + &MsgAssignVid{Signer: signer}, + &MsgDeletePkiRevocationDistributionPoint{Signer: signer}, + &MsgProposeAddX509RootCert{Signer: signer}, + &MsgProposeRevokeX509RootCert{Signer: signer}, + &MsgRejectAddX509RootCert{Signer: signer}, + &MsgRemoveNocX509IcaCert{Signer: signer}, + &MsgRemoveNocX509RootCert{Signer: signer}, + &MsgRemoveX509Cert{Signer: signer}, + &MsgRevokeNocX509IcaCert{Signer: signer}, + &MsgRevokeNocX509RootCert{Signer: signer}, + &MsgRevokeX509Cert{Signer: signer}, + &MsgUpdatePkiRevocationDistributionPoint{Signer: signer}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + requireMsgMethods(t, msg) + }) + } +} + +func TestPkiMsgGetSignersPanicsOnInvalidSigner(t *testing.T) { + msgs := []legacyMsg{ + &MsgAddNocX509IcaCert{Signer: "invalid"}, + &MsgAddNocX509RootCert{Signer: "invalid"}, + &MsgAddPkiRevocationDistributionPoint{Signer: "invalid"}, + &MsgAddX509Cert{Signer: "invalid"}, + &MsgApproveAddX509RootCert{Signer: "invalid"}, + &MsgApproveRevokeX509RootCert{Signer: "invalid"}, + &MsgAssignVid{Signer: "invalid"}, + &MsgDeletePkiRevocationDistributionPoint{Signer: "invalid"}, + &MsgProposeAddX509RootCert{Signer: "invalid"}, + &MsgProposeRevokeX509RootCert{Signer: "invalid"}, + &MsgRejectAddX509RootCert{Signer: "invalid"}, + &MsgRemoveNocX509IcaCert{Signer: "invalid"}, + &MsgRemoveNocX509RootCert{Signer: "invalid"}, + &MsgRemoveX509Cert{Signer: "invalid"}, + &MsgRevokeNocX509IcaCert{Signer: "invalid"}, + &MsgRevokeNocX509RootCert{Signer: "invalid"}, + &MsgRevokeX509Cert{Signer: "invalid"}, + &MsgUpdatePkiRevocationDistributionPoint{Signer: "invalid"}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + require.Panics(t, func() { _ = msg.GetSigners() }) + }) + } +} diff --git a/x/validator/genesis_test.go b/x/validator/genesis_test.go index 7ff94f656..82944284c 100644 --- a/x/validator/genesis_test.go +++ b/x/validator/genesis_test.go @@ -1,72 +1,57 @@ package validator_test -/* TODO issue 99 import ( "testing" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" + testkeeper "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator" "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" ) func TestGenesis(t *testing.T) { + setup := testkeeper.Setup(t) + + pk1 := ed25519.GenPrivKey().PubKey() + pk2 := ed25519.GenPrivKey().PubKey() + v1, err := types.NewValidator(sdk.ValAddress(pk1.Address()), pk1, types.NewDescription("v1", "", "", "")) + require.NoError(t, err) + v2, err := types.NewValidator(sdk.ValAddress(pk2.Address()), pk2, types.NewDescription("v2", "", "", "")) + require.NoError(t, err) + genesisState := types.GenesisState{ - ValidatorList: []types.Validator{ - { - Owner: "0", - }, - { - Owner: "1", - }, - }, + ValidatorList: []types.Validator{v1, v2}, LastValidatorPowerList: []types.LastValidatorPower{ - { - Owner: "0", - }, - { - Owner: "1", - }, + types.NewLastValidatorPower(v1.GetOwner()), + types.NewLastValidatorPower(v2.GetOwner()), }, ProposedDisableValidatorList: []types.ProposedDisableValidator{ - { - Address: "0", -}, - { - Address: "1", -}, - }, - DisabledValidatorList: []types.DisabledValidator{ - { - Address: "0", -}, - { - Address: "1", -}, - }, - RejectedNodeList: []types.RejectedNode{ - { - Owner: "0", -}, - { - Owner: "1", -}, - }, - // this line is used by starport scaffolding # genesis/test/state + {Address: "a"}, + {Address: "b"}, + }, + DisabledValidatorList: []types.DisabledValidator{ + {Address: "c"}, + {Address: "d"}, + }, + RejectedValidatorList: []types.RejectedDisableValidator{ + {Address: sample.ValAddress()}, + }, } - k, ctx := keepertest.ValidatorKeeper(t, nil) - validator.InitGenesis(ctx, *k, genesisState) - got := validator.ExportGenesis(ctx, *k) + validator.InitGenesis(setup.Ctx, setup.ValidatorKeeper, genesisState) + + got := validator.ExportGenesis(setup.Ctx, setup.ValidatorKeeper) require.NotNil(t, got) + require.Len(t, got.ValidatorList, 2) + require.Len(t, got.LastValidatorPowerList, 2) + require.Len(t, got.ProposedDisableValidatorList, 2) + require.Len(t, got.DisabledValidatorList, 2) + require.Len(t, got.RejectedValidatorList, 1) - require.Len(t, got.ValidatorList, len(genesisState.ValidatorList)) - require.Subset(t, genesisState.ValidatorList, got.ValidatorList) - require.Len(t, got.LastValidatorPowerList, len(genesisState.LastValidatorPowerList)) - require.Subset(t, genesisState.LastValidatorPowerList, got.LastValidatorPowerList) - require.ElementsMatch(t, genesisState.ProposedDisableValidatorList, got.ProposedDisableValidatorList) -require.ElementsMatch(t, genesisState.DisabledValidatorList, got.DisabledValidatorList) -require.ElementsMatch(t, genesisState.RejectedNodeList, got.RejectedNodeList) -// this line is used by starport scaffolding # genesis/test/assert + // WriteValidators iterates the active set; covered regardless of result. + _, err = validator.WriteValidators(setup.Ctx, setup.ValidatorKeeper) + require.NoError(t, err) } -*/ diff --git a/x/validator/keeper/grpc_query_disabled_validator_test.go b/x/validator/keeper/grpc_query_disabled_validator_test.go index e31781337..1ffe5fd2e 100644 --- a/x/validator/keeper/grpc_query_disabled_validator_test.go +++ b/x/validator/keeper/grpc_query_disabled_validator_test.go @@ -1,6 +1,5 @@ package keeper_test -/* import ( "strconv" "testing" @@ -16,9 +15,6 @@ import ( "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" ) -// Prevent strconv unused error. -var _ = strconv.IntSize - func TestDisabledValidatorQuerySingle(t *testing.T) { keeper, ctx := keepertest.ValidatorKeeper(t, nil) wctx := sdk.WrapSDKContext(ctx) @@ -125,4 +121,3 @@ func TestDisabledValidatorQueryPaginated(t *testing.T) { require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } -*/ diff --git a/x/validator/keeper/grpc_query_last_validator_power_test.go b/x/validator/keeper/grpc_query_last_validator_power_test.go index 8c161b6b5..169a542ac 100644 --- a/x/validator/keeper/grpc_query_last_validator_power_test.go +++ b/x/validator/keeper/grpc_query_last_validator_power_test.go @@ -1,25 +1,20 @@ package keeper_test -/* FIXME issue 99 - import ( - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" "github.com/stretchr/testify/require" keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func TestLastValidatorPowerQuerySingle(t *testing.T) { - keeper, ctx := keepertest.ValidatorKeeper(t) + keeper, ctx := keepertest.ValidatorKeeper(t, nil) wctx := sdk.WrapSDKContext(ctx) msgs := createNLastValidatorPower(keeper, ctx, 2) for _, tc := range []struct { @@ -45,10 +40,17 @@ func TestLastValidatorPowerQuerySingle(t *testing.T) { { desc: "KeyNotFound", request: &types.QueryGetLastValidatorPowerRequest{ - Owner: strconv.Itoa(100000), + Owner: sample.ValAddress(), }, err: status.Error(codes.NotFound, "not found"), }, + { + desc: "EmptyAddress", + request: &types.QueryGetLastValidatorPowerRequest{ + Owner: "", + }, + err: status.Error(codes.InvalidArgument, "validator address cannot be empty"), + }, { desc: "InvalidRequest", err: status.Error(codes.InvalidArgument, "invalid request"), @@ -59,14 +61,22 @@ func TestLastValidatorPowerQuerySingle(t *testing.T) { if tc.err != nil { require.ErrorIs(t, err, tc.err) } else { + require.NoError(t, err) require.Equal(t, tc.response, response) } }) } + + t.Run("InvalidAddress", func(t *testing.T) { + _, err := keeper.LastValidatorPower(wctx, &types.QueryGetLastValidatorPowerRequest{ + Owner: "not-a-bech32-address", + }) + require.Error(t, err) + }) } func TestLastValidatorPowerQueryPaginated(t *testing.T) { - keeper, ctx := keepertest.ValidatorKeeper(t) + keeper, ctx := keepertest.ValidatorKeeper(t, nil) wctx := sdk.WrapSDKContext(ctx) msgs := createNLastValidatorPower(keeper, ctx, 5) @@ -104,10 +114,10 @@ func TestLastValidatorPowerQueryPaginated(t *testing.T) { resp, err := keeper.LastValidatorPowerAll(wctx, request(nil, 0, 0, true)) require.NoError(t, err) require.Equal(t, len(msgs), int(resp.Pagination.Total)) + require.ElementsMatch(t, msgs, resp.LastValidatorPower) }) t.Run("InvalidRequest", func(t *testing.T) { _, err := keeper.LastValidatorPowerAll(wctx, nil) require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } -*/ diff --git a/x/validator/keeper/grpc_query_rejected_node_test.go b/x/validator/keeper/grpc_query_rejected_node_test.go index 95cc0152c..b9ed0c89f 100644 --- a/x/validator/keeper/grpc_query_rejected_node_test.go +++ b/x/validator/keeper/grpc_query_rejected_node_test.go @@ -1,8 +1,6 @@ package keeper_test -/* import ( - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -13,42 +11,55 @@ import ( keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/nullify" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" ) -// Prevent strconv unused error -var _ = strconv.IntSize +// createNRejectedDisableValidator stores n rejected-disable-validator records. +// SetRejectedNode keys by the (bech32 valoper) Address, silently skipping +// records with an invalid address, so valid sample addresses are required. +func createNRejectedDisableValidator(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.RejectedDisableValidator { + items := make([]types.RejectedDisableValidator, n) + for i := range items { + items[i].Address = sample.ValAddress() -func TestRejectedNodeQuerySingle(t *testing.T) { - keeper, ctx := keepertest.ValidatorKeeper(t) + keeper.SetRejectedNode(ctx, items[i]) + } + + return items +} + +func TestRejectedDisableValidatorQuerySingle(t *testing.T) { + keeper, ctx := keepertest.ValidatorKeeper(t, nil) wctx := sdk.WrapSDKContext(ctx) - msgs := createNRejectedNode(keeper, ctx, 2) + msgs := createNRejectedDisableValidator(keeper, ctx, 2) for _, tc := range []struct { desc string - request *types.QueryGetRejectedNodeRequest - response *types.QueryGetRejectedNodeResponse + request *types.QueryGetRejectedDisableValidatorRequest + response *types.QueryGetRejectedDisableValidatorResponse err error }{ { desc: "First", - request: &types.QueryGetRejectedNodeRequest{ - Owner: msgs[0].Owner, + request: &types.QueryGetRejectedDisableValidatorRequest{ + Owner: msgs[0].Address, }, - response: &types.QueryGetRejectedNodeResponse{RejectedNode: msgs[0]}, + response: &types.QueryGetRejectedDisableValidatorResponse{RejectedValidator: msgs[0]}, }, { desc: "Second", - request: &types.QueryGetRejectedNodeRequest{ - Owner: msgs[1].Owner, + request: &types.QueryGetRejectedDisableValidatorRequest{ + Owner: msgs[1].Address, }, - response: &types.QueryGetRejectedNodeResponse{RejectedNode: msgs[1]}, + response: &types.QueryGetRejectedDisableValidatorResponse{RejectedValidator: msgs[1]}, }, { desc: "KeyNotFound", - request: &types.QueryGetRejectedNodeRequest{ - Owner: strconv.Itoa(100000), + request: &types.QueryGetRejectedDisableValidatorRequest{ + Owner: sample.ValAddress(), }, - err: status.Error(codes.InvalidArgument, "not found"), + err: status.Error(codes.NotFound, "not found"), }, { desc: "InvalidRequest", @@ -56,7 +67,7 @@ func TestRejectedNodeQuerySingle(t *testing.T) { }, } { t.Run(tc.desc, func(t *testing.T) { - response, err := keeper.RejectedNode(wctx, tc.request) + response, err := keeper.RejectedDisableValidator(wctx, tc.request) if tc.err != nil { require.ErrorIs(t, err, tc.err) } else { @@ -70,13 +81,13 @@ func TestRejectedNodeQuerySingle(t *testing.T) { } } -func TestRejectedNodeQueryPaginated(t *testing.T) { - keeper, ctx := keepertest.ValidatorKeeper(t) +func TestRejectedDisableValidatorQueryPaginated(t *testing.T) { + keeper, ctx := keepertest.ValidatorKeeper(t, nil) wctx := sdk.WrapSDKContext(ctx) - msgs := createNRejectedNode(keeper, ctx, 5) + msgs := createNRejectedDisableValidator(keeper, ctx, 5) - request := func(next []byte, offset, limit uint64, total bool) *types.QueryAllRejectedNodeRequest { - return &types.QueryAllRejectedNodeRequest{ + request := func(next []byte, offset, limit uint64, total bool) *types.QueryAllRejectedDisableValidatorRequest { + return &types.QueryAllRejectedDisableValidatorRequest{ Pagination: &query.PageRequest{ Key: next, Offset: offset, @@ -88,12 +99,12 @@ func TestRejectedNodeQueryPaginated(t *testing.T) { t.Run("ByOffset", func(t *testing.T) { step := 2 for i := 0; i < len(msgs); i += step { - resp, err := keeper.RejectedNodeAll(wctx, request(nil, uint64(i), uint64(step), false)) + resp, err := keeper.RejectedDisableValidatorAll(wctx, request(nil, uint64(i), uint64(step), false)) require.NoError(t, err) - require.LessOrEqual(t, len(resp.RejectedNode), step) + require.LessOrEqual(t, len(resp.RejectedValidator), step) require.Subset(t, nullify.Fill(msgs), - nullify.Fill(resp.RejectedNode), + nullify.Fill(resp.RejectedValidator), ) } }) @@ -101,28 +112,27 @@ func TestRejectedNodeQueryPaginated(t *testing.T) { step := 2 var next []byte for i := 0; i < len(msgs); i += step { - resp, err := keeper.RejectedNodeAll(wctx, request(next, 0, uint64(step), false)) + resp, err := keeper.RejectedDisableValidatorAll(wctx, request(next, 0, uint64(step), false)) require.NoError(t, err) - require.LessOrEqual(t, len(resp.RejectedNode), step) + require.LessOrEqual(t, len(resp.RejectedValidator), step) require.Subset(t, nullify.Fill(msgs), - nullify.Fill(resp.RejectedNode), + nullify.Fill(resp.RejectedValidator), ) next = resp.Pagination.NextKey } }) t.Run("Total", func(t *testing.T) { - resp, err := keeper.RejectedNodeAll(wctx, request(nil, 0, 0, true)) + resp, err := keeper.RejectedDisableValidatorAll(wctx, request(nil, 0, 0, true)) require.NoError(t, err) require.Equal(t, len(msgs), int(resp.Pagination.Total)) require.ElementsMatch(t, nullify.Fill(msgs), - nullify.Fill(resp.RejectedNode), + nullify.Fill(resp.RejectedValidator), ) }) t.Run("InvalidRequest", func(t *testing.T) { - _, err := keeper.RejectedNodeAll(wctx, nil) + _, err := keeper.RejectedDisableValidatorAll(wctx, nil) require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } -*/ diff --git a/x/validator/keeper/grpc_query_validator_test.go b/x/validator/keeper/grpc_query_validator_test.go index 5d676e703..c0d79b19c 100644 --- a/x/validator/keeper/grpc_query_validator_test.go +++ b/x/validator/keeper/grpc_query_validator_test.go @@ -1,25 +1,46 @@ package keeper_test -/* FIXME issue 99 - import ( "strconv" "testing" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" "github.com/stretchr/testify/require" keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) -// Prevent strconv unused error -var _ = strconv.IntSize +// createNValidator stores n validators and returns them in the canonical +// (unmarshalled) form produced by the keeper, so that direct equality with +// query responses holds despite the cached pub key in the proto Any field. +func createNValidator(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.Validator { + items := make([]types.Validator, n) + for i := range items { + pk := ed25519.GenPrivKey().PubKey() + validator, err := types.NewValidator( + sdk.ValAddress(pk.Address()), + pk, + types.Description{Moniker: strconv.Itoa(i)}, + ) + if err != nil { + panic(err) + } + + keeper.SetValidator(ctx, validator) + stored, _ := keeper.GetValidator(ctx, validator.GetOwner()) + items[i] = stored + } + + return items +} func TestValidatorQuerySingle(t *testing.T) { - keeper, ctx := keepertest.ValidatorKeeper(t) + keeper, ctx := keepertest.ValidatorKeeper(t, nil) wctx := sdk.WrapSDKContext(ctx) msgs := createNValidator(keeper, ctx, 2) for _, tc := range []struct { @@ -45,10 +66,17 @@ func TestValidatorQuerySingle(t *testing.T) { { desc: "KeyNotFound", request: &types.QueryGetValidatorRequest{ - Owner: strconv.Itoa(100000), + Owner: sdk.ValAddress(ed25519.GenPrivKey().PubKey().Address()).String(), }, err: status.Error(codes.NotFound, "not found"), }, + { + desc: "EmptyAddress", + request: &types.QueryGetValidatorRequest{ + Owner: "", + }, + err: status.Error(codes.InvalidArgument, "validator address cannot be empty"), + }, { desc: "InvalidRequest", err: status.Error(codes.InvalidArgument, "invalid request"), @@ -59,14 +87,22 @@ func TestValidatorQuerySingle(t *testing.T) { if tc.err != nil { require.ErrorIs(t, err, tc.err) } else { + require.NoError(t, err) require.Equal(t, tc.response, response) } }) } + + t.Run("InvalidAddress", func(t *testing.T) { + _, err := keeper.Validator(wctx, &types.QueryGetValidatorRequest{ + Owner: "not-a-bech32-address", + }) + require.Error(t, err) + }) } func TestValidatorQueryPaginated(t *testing.T) { - keeper, ctx := keepertest.ValidatorKeeper(t) + keeper, ctx := keepertest.ValidatorKeeper(t, nil) wctx := sdk.WrapSDKContext(ctx) msgs := createNValidator(keeper, ctx, 5) @@ -104,10 +140,10 @@ func TestValidatorQueryPaginated(t *testing.T) { resp, err := keeper.ValidatorAll(wctx, request(nil, 0, 0, true)) require.NoError(t, err) require.Equal(t, len(msgs), int(resp.Pagination.Total)) + require.ElementsMatch(t, msgs, resp.Validator) }) t.Run("InvalidRequest", func(t *testing.T) { _, err := keeper.ValidatorAll(wctx, nil) require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } -*/ diff --git a/x/validator/keeper/iterate_test.go b/x/validator/keeper/iterate_test.go new file mode 100644 index 000000000..397c33229 --- /dev/null +++ b/x/validator/keeper/iterate_test.go @@ -0,0 +1,57 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + testkeeper "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" +) + +func TestValidatorIterationAndRemove(t *testing.T) { + setup := testkeeper.Setup(t) + k := setup.ValidatorKeeper + ctx := setup.Ctx + + v1, v2 := testkeeper.StoreTwoValidators(setup) + require.NoError(t, k.SetValidatorByConsAddr(ctx, v1)) + require.NoError(t, k.SetValidatorByConsAddr(ctx, v2)) + k.SetLastValidatorPower(ctx, types.NewLastValidatorPower(v1.GetOwner())) + k.SetLastValidatorPower(ctx, types.NewLastValidatorPower(v2.GetOwner())) + + require.Len(t, k.GetAllValidator(ctx), 2) + require.Len(t, k.GetAllLastValidators(ctx), 2) + + var iterated int + k.IterateValidators(ctx, func(types.Validator) bool { + iterated++ + + return false + }) + require.Equal(t, 2, iterated) + + // IterateValidators with early stop. + var stopped int + k.IterateValidators(ctx, func(types.Validator) bool { + stopped++ + + return true + }) + require.Equal(t, 1, stopped) + + // RemoveValidator requires the cached consensus pub key, which the + // StoreTwoValidators fixtures carry. + k.RemoveValidator(ctx, v1.GetOwner()) + _, found := k.GetValidator(ctx, v1.GetOwner()) + require.False(t, found) + + // Removing a non-existent validator is a no-op. + k.RemoveValidator(ctx, v1.GetOwner()) +} + +func TestSetLastValidatorPower_PanicsOnInvalidOwner(t *testing.T) { + k, ctx := testkeeper.ValidatorKeeper(t, nil) + require.Panics(t, func() { + k.SetLastValidatorPower(ctx, types.LastValidatorPower{Owner: "invalid-address"}) + }) +} diff --git a/x/validator/keeper/last_validator_power_test.go b/x/validator/keeper/last_validator_power_test.go index 659168418..d8cc66895 100644 --- a/x/validator/keeper/last_validator_power_test.go +++ b/x/validator/keeper/last_validator_power_test.go @@ -1,59 +1,62 @@ package keeper_test -/* FIXME issue 99 - import ( - "strconv" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" ) -// Prevent strconv unused error -var _ = strconv.IntSize - func createNLastValidatorPower(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.LastValidatorPower { items := make([]types.LastValidatorPower, n) for i := range items { - items[i].Owner = strconv.Itoa(i) + items[i].Owner = sample.ValAddress() + items[i].Power = int32(i) keeper.SetLastValidatorPower(ctx, items[i]) } + return items } func TestLastValidatorPowerGet(t *testing.T) { - keeper, ctx := keepertest.ValidatorKeeper(t) + keeper, ctx := keepertest.ValidatorKeeper(t, nil) items := createNLastValidatorPower(keeper, ctx, 10) for _, item := range items { - rst, found := keeper.GetLastValidatorPower(ctx, - item.Owner, - ) + owner, err := sdk.ValAddressFromBech32(item.Owner) + require.NoError(t, err) + + rst, found := keeper.GetLastValidatorPower(ctx, owner) require.True(t, found) require.Equal(t, item, rst) } } + func TestLastValidatorPowerRemove(t *testing.T) { - keeper, ctx := keepertest.ValidatorKeeper(t) + keeper, ctx := keepertest.ValidatorKeeper(t, nil) items := createNLastValidatorPower(keeper, ctx, 10) for _, item := range items { - keeper.RemoveLastValidatorPower(ctx, - item.Owner, - ) - _, found := keeper.GetLastValidatorPower(ctx, - item.Owner, - ) + owner, err := sdk.ValAddressFromBech32(item.Owner) + require.NoError(t, err) + + keeper.RemoveLastValidatorPower(ctx, owner) + _, found := keeper.GetLastValidatorPower(ctx, owner) require.False(t, found) } } func TestLastValidatorPowerGetAll(t *testing.T) { - keeper, ctx := keepertest.ValidatorKeeper(t) + keeper, ctx := keepertest.ValidatorKeeper(t, nil) items := createNLastValidatorPower(keeper, ctx, 10) require.ElementsMatch(t, items, keeper.GetAllLastValidatorPower(ctx)) } -*/ + +func TestLastValidatorPowerCount(t *testing.T) { + keeper, ctx := keepertest.ValidatorKeeper(t, nil) + items := createNLastValidatorPower(keeper, ctx, 10) + require.Equal(t, len(items), keeper.CountLastValidators(ctx)) +} diff --git a/x/validator/keeper/msg_server_invalid_signer_test.go b/x/validator/keeper/msg_server_invalid_signer_test.go new file mode 100644 index 000000000..7cdbeb3e8 --- /dev/null +++ b/x/validator/keeper/msg_server_invalid_signer_test.go @@ -0,0 +1,49 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" +) + +func TestMsgServerInvalidSigner(t *testing.T) { + ms, ctx := setupMsgServer(t) + const bad = "invalid-signer-address" + + cases := []struct { + name string + call func() error + }{ + {"CreateValidator", func() error { + _, err := ms.CreateValidator(ctx, &types.MsgCreateValidator{Signer: bad}) + return err + }}, + {"DisableValidator", func() error { + _, err := ms.DisableValidator(ctx, &types.MsgDisableValidator{Creator: bad}) + return err + }}, + {"EnableValidator", func() error { + _, err := ms.EnableValidator(ctx, &types.MsgEnableValidator{Creator: bad}) + return err + }}, + {"ApproveDisableValidator", func() error { + _, err := ms.ApproveDisableValidator(ctx, &types.MsgApproveDisableValidator{Creator: bad}) + return err + }}, + {"ProposeDisableValidator", func() error { + _, err := ms.ProposeDisableValidator(ctx, &types.MsgProposeDisableValidator{Creator: bad}) + return err + }}, + {"RejectDisableValidator", func() error { + _, err := ms.RejectDisableValidator(ctx, &types.MsgRejectDisableValidator{Creator: bad}) + return err + }}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + require.Error(t, tc.call()) + }) + } +} diff --git a/x/validator/keeper/process_malicious_test.go b/x/validator/keeper/process_malicious_test.go index 8704bc8a4..c114dde5d 100644 --- a/x/validator/keeper/process_malicious_test.go +++ b/x/validator/keeper/process_malicious_test.go @@ -1,222 +1,188 @@ package keeper_test -// import ( -// "testing" -// "time" - -// // evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" -// evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" -// slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" -// "github.com/stretchr/testify/require" - -// // abci "github.com/cometbft/cometbft/abci/types" -// tmproto "github.com/cometbft/cometbft/proto/tendermint/types" -// testkeeper "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" -// "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" -// ) - -// func TestProcessMalicious_HandleJailUnjail(t *testing.T) { -// setup := testkeeper.Setup(t) - -// // create validator -// validator := testkeeper.DefaultValidator() -// setup.ValidatorKeeper.SetValidator(setup.Ctx, validator) -// _ = setup.ValidatorKeeper.SetValidatorByConsAddr(setup.Ctx, validator) - -// // check it is not slashed -// receivedValidator, _ := setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) -// require.False(t, receivedValidator.Jailed) -// require.Equal(t, types.Power, receivedValidator.Power) - -// // Jail/Slash -// setup.ValidatorKeeper.Jail(setup.Ctx, validator, "some reason") - -// // check validator is slashed -// receivedValidator, _ = setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) -// require.True(t, receivedValidator.Jailed) -// require.Equal(t, types.ZeroPower, receivedValidator.Power) -// require.Equal(t, "some reason", receivedValidator.JailedReason) - -// // Unjail/unslash -// setup.ValidatorKeeper.Unjail(setup.Ctx, validator) - -// // check validator is not slashed -// receivedValidator, _ = setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) -// require.False(t, receivedValidator.Jailed) -// require.Equal(t, types.Power, receivedValidator.Power) -// require.Equal(t, "", receivedValidator.JailedReason) -// } - -// func TestProcessMalicious_HandleDoubleSign(t *testing.T) { -// setup := testkeeper.Setup(t) - -// // create validator -// validator := testkeeper.DefaultValidator() -// setup.ValidatorKeeper.SetValidator(setup.Ctx, validator) -// _ = setup.ValidatorKeeper.SetValidatorByConsAddr(setup.Ctx, validator) - -// // check it is not slashed -// receivedValidator, _ := setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) -// require.False(t, receivedValidator.Jailed) -// require.Equal(t, types.Power, receivedValidator.Power) - -// timestamp := time.Now().UTC() - -// // imitate double sign for validator -// setup.Ctx = setup.Ctx.WithBlockHeader(tmproto.Header{ -// Time: timestamp.Add(time.Second * time.Duration(5)), -// }) -// validatorConsAddr, _ := validator.GetConsAddr() -// evidence := evidencetypes.Equivocation{ -// Height: 1, -// Time: timestamp, -// Power: int64(types.Power), -// ConsensusAddress: validatorConsAddr.String(), -// } - -// setup.ValidatorKeeper.HandleDoubleSign(setup.Ctx, &evidence) - -// // check validator is slashed -// receivedValidator, _ = setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) -// require.True(t, receivedValidator.Jailed) -// require.Equal(t, types.ZeroPower, receivedValidator.Power) - -// events := setup.Ctx.EventManager().Events().ToABCIEvents() -// require.Equal(t, 1, len(events)) -// require.Equal(t, slashingtypes.EventTypeSlash, events[0].Type) -// } - -// func TestProcessMalicious_HandleDoubleSign_ForOutdated(t *testing.T) { -// setup := testkeeper.Setup(t) - -// // create validator -// validator := testkeeper.DefaultValidator() -// setup.ValidatorKeeper.SetValidator(setup.Ctx, validator) -// _ = setup.ValidatorKeeper.SetValidatorByConsAddr(setup.Ctx, validator) - -// // check it is not slashed -// receivedValidator, _ := setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) -// require.False(t, receivedValidator.Jailed) -// require.Equal(t, types.Power, receivedValidator.Power) - -// timestamp := time.Now().UTC() -// initialHeight := int64(1) - -// // imitate double sign for validator with outdated timestamp AND block -// maxEvidenceAge := time.Duration(1000) -// maxNumBlocks := int64(20) -// cp := abci.ConsensusParams{ -// Evidence: &tmproto.EvidenceParams{ -// MaxAgeDuration: maxEvidenceAge, -// MaxAgeNumBlocks: maxNumBlocks, -// }, -// } -// setup.Ctx = setup.Ctx.WithConsensusParams(&cp) -// setup.Ctx = setup.Ctx.WithBlockHeader(tmproto.Header{ -// Time: timestamp.Add(maxEvidenceAge + 2*time.Second), -// Height: maxNumBlocks + initialHeight + 1, -// }) - -// validatorConsAddr, _ := validator.GetConsAddr() -// evidence := evidencetypes.Equivocation{ -// Height: initialHeight, -// Time: timestamp, -// Power: int64(types.Power), -// ConsensusAddress: validatorConsAddr.String(), -// } - -// setup.ValidatorKeeper.HandleDoubleSign(setup.Ctx, &evidence) - -// // check validator is not slashed -// receivedValidator, _ = setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) -// require.False(t, receivedValidator.Jailed) -// require.Equal(t, types.Power, receivedValidator.Power) -// } - -// func TestProcessMalicious_HandleDoubleSign_ForNotOutdatedBlock(t *testing.T) { -// setup := testkeeper.Setup(t) - -// // create validator -// validator := testkeeper.DefaultValidator() -// setup.ValidatorKeeper.SetValidator(setup.Ctx, validator) -// _ = setup.ValidatorKeeper.SetValidatorByConsAddr(setup.Ctx, validator) - -// // check it is not slashed -// receivedValidator, _ := setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) -// require.False(t, receivedValidator.Jailed) -// require.Equal(t, types.Power, receivedValidator.Power) - -// timestamp := time.Now().UTC() -// initialHeight := int64(1) - -// // imitate double sign for validator with outdated timestamp and not block -// maxEvidenceAge := time.Duration(1000) -// cp := abci.ConsensusParams{ -// Evidence: &tmproto.EvidenceParams{ -// MaxAgeDuration: maxEvidenceAge, -// }, -// } -// setup.Ctx = setup.Ctx.WithConsensusParams(&cp) -// setup.Ctx = setup.Ctx.WithBlockHeader(tmproto.Header{ -// Time: timestamp.Add(maxEvidenceAge + 2*time.Second), -// }) - -// validatorConsAddr, _ := validator.GetConsAddr() -// evidence := evidencetypes.Equivocation{ -// Height: initialHeight, -// Time: timestamp, -// Power: int64(types.Power), -// ConsensusAddress: validatorConsAddr.String(), -// } - -// setup.ValidatorKeeper.HandleDoubleSign(setup.Ctx, &evidence) - -// // check validator is slashed -// receivedValidator, _ = setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) -// require.True(t, receivedValidator.Jailed) -// require.Equal(t, types.ZeroPower, receivedValidator.Power) -// } - -// func TestProcessMalicious_HandleDoubleSign_ForNotOutdatedAge(t *testing.T) { -// setup := testkeeper.Setup(t) - -// // create validator -// validator := testkeeper.DefaultValidator() -// setup.ValidatorKeeper.SetValidator(setup.Ctx, validator) -// _ = setup.ValidatorKeeper.SetValidatorByConsAddr(setup.Ctx, validator) - -// // check it is not slashed -// receivedValidator, _ := setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) -// require.False(t, receivedValidator.Jailed) -// require.Equal(t, types.Power, receivedValidator.Power) - -// timestamp := time.Now().UTC() -// initialHeight := int64(1) - -// // imitate double sign for validator with outdated block and not timestamp -// maxNumBlocks := int64(20) -// cp := abci.ConsensusParams{ -// Evidence: &tmproto.EvidenceParams{ -// MaxAgeNumBlocks: maxNumBlocks, -// }, -// } -// setup.Ctx = setup.Ctx.WithConsensusParams(&cp) -// setup.Ctx = setup.Ctx.WithBlockHeader(tmproto.Header{ -// Height: maxNumBlocks + initialHeight + 1, -// }) - -// validatorConsAddr, _ := validator.GetConsAddr() -// evidence := evidencetypes.Equivocation{ -// Height: initialHeight, -// Time: timestamp, -// Power: int64(types.Power), -// ConsensusAddress: validatorConsAddr.String(), -// } - -// setup.ValidatorKeeper.HandleDoubleSign(setup.Ctx, &evidence) - -// // check validator is slashed -// receivedValidator, _ = setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) -// require.True(t, receivedValidator.Jailed) -// require.Equal(t, types.ZeroPower, receivedValidator.Power) -// } +import ( + "testing" + "time" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/stretchr/testify/require" + testkeeper "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" +) + +func TestProcessMalicious_HandleJailUnjail(t *testing.T) { + setup := testkeeper.Setup(t) + + validator := testkeeper.DefaultValidator() + setup.ValidatorKeeper.SetValidator(setup.Ctx, validator) + _ = setup.ValidatorKeeper.SetValidatorByConsAddr(setup.Ctx, validator) + + receivedValidator, _ := setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) + require.False(t, receivedValidator.Jailed) + require.Equal(t, types.Power, receivedValidator.Power) + + setup.ValidatorKeeper.Jail(setup.Ctx, validator, "some reason") + + receivedValidator, _ = setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) + require.True(t, receivedValidator.Jailed) + require.Equal(t, types.ZeroPower, receivedValidator.Power) + require.Equal(t, "some reason", receivedValidator.JailedReason) + + setup.ValidatorKeeper.Unjail(setup.Ctx, validator) + + receivedValidator, _ = setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) + require.False(t, receivedValidator.Jailed) + require.Equal(t, types.Power, receivedValidator.Power) + require.Equal(t, "", receivedValidator.JailedReason) +} + +func TestProcessMalicious_HandleDoubleSign(t *testing.T) { + setup := testkeeper.Setup(t) + + validator := testkeeper.DefaultValidator() + setup.ValidatorKeeper.SetValidator(setup.Ctx, validator) + _ = setup.ValidatorKeeper.SetValidatorByConsAddr(setup.Ctx, validator) + + receivedValidator, _ := setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) + require.False(t, receivedValidator.Jailed) + require.Equal(t, types.Power, receivedValidator.Power) + + timestamp := time.Now().UTC() + + setup.Ctx = setup.Ctx.WithBlockHeader(tmproto.Header{ + Time: timestamp.Add(time.Second * time.Duration(5)), + }) + validatorConsAddr, _ := validator.GetConsAddr() + evidence := evidencetypes.Equivocation{ + Height: 1, + Time: timestamp, + Power: int64(types.Power), + ConsensusAddress: validatorConsAddr.String(), + } + + setup.ValidatorKeeper.HandleDoubleSign(setup.Ctx, &evidence) + + receivedValidator, _ = setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) + require.True(t, receivedValidator.Jailed) + require.Equal(t, types.ZeroPower, receivedValidator.Power) + + events := setup.Ctx.EventManager().Events().ToABCIEvents() + require.Equal(t, 1, len(events)) + require.Equal(t, slashingtypes.EventTypeSlash, events[0].Type) +} + +func TestProcessMalicious_HandleDoubleSign_ForOutdated(t *testing.T) { + setup := testkeeper.Setup(t) + + validator := testkeeper.DefaultValidator() + setup.ValidatorKeeper.SetValidator(setup.Ctx, validator) + _ = setup.ValidatorKeeper.SetValidatorByConsAddr(setup.Ctx, validator) + + timestamp := time.Now().UTC() + initialHeight := int64(1) + + // imitate double sign for validator with outdated timestamp AND block + maxEvidenceAge := time.Duration(1000) + maxNumBlocks := int64(20) + cp := tmproto.ConsensusParams{ + Evidence: &tmproto.EvidenceParams{ + MaxAgeDuration: maxEvidenceAge, + MaxAgeNumBlocks: maxNumBlocks, + }, + } + setup.Ctx = setup.Ctx.WithConsensusParams(&cp) + setup.Ctx = setup.Ctx.WithBlockHeader(tmproto.Header{ + Time: timestamp.Add(maxEvidenceAge + 2*time.Second), + Height: maxNumBlocks + initialHeight + 1, + }) + + validatorConsAddr, _ := validator.GetConsAddr() + evidence := evidencetypes.Equivocation{ + Height: initialHeight, + Time: timestamp, + Power: int64(types.Power), + ConsensusAddress: validatorConsAddr.String(), + } + + setup.ValidatorKeeper.HandleDoubleSign(setup.Ctx, &evidence) + + receivedValidator, _ := setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) + require.False(t, receivedValidator.Jailed) + require.Equal(t, types.Power, receivedValidator.Power) +} + +func TestProcessMalicious_HandleDoubleSign_ForNotOutdatedBlock(t *testing.T) { + setup := testkeeper.Setup(t) + + validator := testkeeper.DefaultValidator() + setup.ValidatorKeeper.SetValidator(setup.Ctx, validator) + _ = setup.ValidatorKeeper.SetValidatorByConsAddr(setup.Ctx, validator) + + timestamp := time.Now().UTC() + initialHeight := int64(1) + + // outdated timestamp but NOT block -> still slashed (both must exceed) + maxEvidenceAge := time.Duration(1000) + cp := tmproto.ConsensusParams{ + Evidence: &tmproto.EvidenceParams{ + MaxAgeDuration: maxEvidenceAge, + }, + } + setup.Ctx = setup.Ctx.WithConsensusParams(&cp) + setup.Ctx = setup.Ctx.WithBlockHeader(tmproto.Header{ + Time: timestamp.Add(maxEvidenceAge + 2*time.Second), + }) + + validatorConsAddr, _ := validator.GetConsAddr() + evidence := evidencetypes.Equivocation{ + Height: initialHeight, + Time: timestamp, + Power: int64(types.Power), + ConsensusAddress: validatorConsAddr.String(), + } + + setup.ValidatorKeeper.HandleDoubleSign(setup.Ctx, &evidence) + + receivedValidator, _ := setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) + require.True(t, receivedValidator.Jailed) + require.Equal(t, types.ZeroPower, receivedValidator.Power) +} + +func TestProcessMalicious_HandleDoubleSign_ForNotOutdatedAge(t *testing.T) { + setup := testkeeper.Setup(t) + + validator := testkeeper.DefaultValidator() + setup.ValidatorKeeper.SetValidator(setup.Ctx, validator) + _ = setup.ValidatorKeeper.SetValidatorByConsAddr(setup.Ctx, validator) + + timestamp := time.Now().UTC() + initialHeight := int64(1) + + // outdated block but NOT timestamp -> still slashed (both must exceed) + maxNumBlocks := int64(20) + cp := tmproto.ConsensusParams{ + Evidence: &tmproto.EvidenceParams{ + MaxAgeNumBlocks: maxNumBlocks, + }, + } + setup.Ctx = setup.Ctx.WithConsensusParams(&cp) + setup.Ctx = setup.Ctx.WithBlockHeader(tmproto.Header{ + Height: maxNumBlocks + initialHeight + 1, + }) + + validatorConsAddr, _ := validator.GetConsAddr() + evidence := evidencetypes.Equivocation{ + Height: initialHeight, + Time: timestamp, + Power: int64(types.Power), + ConsensusAddress: validatorConsAddr.String(), + } + + setup.ValidatorKeeper.HandleDoubleSign(setup.Ctx, &evidence) + + receivedValidator, _ := setup.ValidatorKeeper.GetValidator(setup.Ctx, validator.GetOwner()) + require.True(t, receivedValidator.Jailed) + require.Equal(t, types.ZeroPower, receivedValidator.Power) +} diff --git a/x/validator/types/genesis_test.go b/x/validator/types/genesis_test.go index 4386c98a5..0edae6a72 100644 --- a/x/validator/types/genesis_test.go +++ b/x/validator/types/genesis_test.go @@ -1,15 +1,17 @@ package types_test -/* FIXME issue 99 - import ( "testing" "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" ) func TestGenesisState_Validate(t *testing.T) { + valAddr1 := sample.ValAddress() + valAddr2 := sample.ValAddress() + for _, tc := range []struct { desc string genState *types.GenesisState @@ -24,59 +26,52 @@ func TestGenesisState_Validate(t *testing.T) { desc: "valid genesis state", genState: &types.GenesisState{ ValidatorList: []types.Validator{ - { - Owner: "0", - }, - { - Owner: "1", - }, + {Owner: valAddr1}, + {Owner: valAddr2}, }, LastValidatorPowerList: []types.LastValidatorPower{ - { - Owner: "0", - }, - { - Owner: "1", - }, + {Owner: valAddr1}, + {Owner: valAddr2}, }, ProposedDisableValidatorList: []types.ProposedDisableValidator{ - { - Address: "0", -}, - { - Address: "1", -}, -}, -DisabledValidatorList: []types.DisabledValidator{ - { - Address: "0", -}, - { - Address: "1", -}, -}, -RejectedNodeList: []types.RejectedNode{ - { - Owner: "0", -}, - { - Owner: "1", -}, -}, -// this line is used by starport scaffolding # types/genesis/validField + {Address: "0"}, + {Address: "1"}, + }, + DisabledValidatorList: []types.DisabledValidator{ + {Address: "0"}, + {Address: "1"}, + }, + RejectedValidatorList: []types.RejectedDisableValidator{ + {Address: valAddr1}, + {Address: valAddr2}, + }, }, valid: true, }, + { + desc: "invalid validator owner", + genState: &types.GenesisState{ + ValidatorList: []types.Validator{ + {Owner: "invalid-address"}, + }, + }, + valid: false, + }, { desc: "duplicated validator", genState: &types.GenesisState{ ValidatorList: []types.Validator{ - { - Owner: "0", - }, - { - Owner: "0", - }, + {Owner: valAddr1}, + {Owner: valAddr1}, + }, + }, + valid: false, + }, + { + desc: "invalid lastValidatorPower owner", + genState: &types.GenesisState{ + LastValidatorPowerList: []types.LastValidatorPower{ + {Owner: "invalid-address"}, }, }, valid: false, @@ -85,59 +80,51 @@ RejectedNodeList: []types.RejectedNode{ desc: "duplicated lastValidatorPower", genState: &types.GenesisState{ LastValidatorPowerList: []types.LastValidatorPower{ - { - Owner: "0", - }, - { - Owner: "0", - }, + {Owner: valAddr1}, + {Owner: valAddr1}, + }, + }, + valid: false, + }, + { + desc: "duplicated proposedDisableValidator", + genState: &types.GenesisState{ + ProposedDisableValidatorList: []types.ProposedDisableValidator{ + {Address: "0"}, + {Address: "0"}, }, }, valid: false, }, { - desc: "duplicated proposedDisableValidator", - genState: &types.GenesisState{ - ProposedDisableValidatorList: []types.ProposedDisableValidator{ - { - Address: "0", -}, - { - Address: "0", -}, + desc: "duplicated disabledValidator", + genState: &types.GenesisState{ + DisabledValidatorList: []types.DisabledValidator{ + {Address: "0"}, + {Address: "0"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated disabledValidator", - genState: &types.GenesisState{ - DisabledValidatorList: []types.DisabledValidator{ - { - Address: "0", -}, - { - Address: "0", -}, + { + desc: "invalid rejectedValidator address", + genState: &types.GenesisState{ + RejectedValidatorList: []types.RejectedDisableValidator{ + {Address: "invalid-address"}, + }, + }, + valid: false, }, - }, - valid: false, -}, -{ - desc: "duplicated rejectedNode", - genState: &types.GenesisState{ - RejectedNodeList: []types.RejectedNode{ - { - Owner: "0", -}, - { - Owner: "0", -}, + { + desc: "duplicated rejectedValidator", + genState: &types.GenesisState{ + RejectedValidatorList: []types.RejectedDisableValidator{ + {Address: valAddr1}, + {Address: valAddr1}, + }, + }, + valid: false, }, - }, - valid: false, -}, -// this line is used by starport scaffolding # types/genesis/testcase } { t.Run(tc.desc, func(t *testing.T) { err := tc.genState.Validate() @@ -149,4 +136,3 @@ RejectedNodeList: []types.RejectedNode{ }) } } -*/ diff --git a/x/validator/types/message_create_validator_branches_test.go b/x/validator/types/message_create_validator_branches_test.go new file mode 100644 index 000000000..ccf79b6fb --- /dev/null +++ b/x/validator/types/message_create_validator_branches_test.go @@ -0,0 +1,42 @@ +package types_test + +import ( + "strings" + "testing" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" +) + +func TestMsgCreateValidator_ValidateBasicBranches(t *testing.T) { + goodPk, err := codectypes.NewAnyWithValue(ed25519.GenPrivKey().PubKey()) + require.NoError(t, err) + badPk, err := codectypes.NewAnyWithValue(&types.Validator{Owner: "x"}) + require.NoError(t, err) + + signer := sample.ValAddress() + goodDesc := types.NewDescription("moniker", "identity", "website", "details") + + for _, tc := range []struct { + name string + msg types.MsgCreateValidator + }{ + {"invalid signer", types.MsgCreateValidator{Signer: "invalid"}}, + {"nil pubkey", types.MsgCreateValidator{Signer: signer, PubKey: nil, Description: goodDesc}}, + {"non-pubkey any", types.MsgCreateValidator{Signer: signer, PubKey: badPk, Description: goodDesc}}, + {"empty description", types.MsgCreateValidator{Signer: signer, PubKey: goodPk}}, + {"invalid description", types.MsgCreateValidator{ + Signer: signer, + PubKey: goodPk, + Description: types.NewDescription(strings.Repeat("a", types.MaxNameLength+1), "", "", ""), + }}, + } { + t.Run(tc.name, func(t *testing.T) { + msg := tc.msg + require.Error(t, msg.ValidateBasic()) + }) + } +} diff --git a/x/validator/types/msg_methods_test.go b/x/validator/types/msg_methods_test.go new file mode 100644 index 000000000..000b55c8a --- /dev/null +++ b/x/validator/types/msg_methods_test.go @@ -0,0 +1,60 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" +) + +type legacyMsg interface { + Route() string + Type() string + GetSignBytes() []byte + GetSigners() []sdk.AccAddress +} + +func requireMsgMethods(t *testing.T, msg legacyMsg) { + t.Helper() + require.NotEmpty(t, msg.Route()) + require.NotEmpty(t, msg.Type()) + require.NotPanics(t, func() { _ = msg.GetSignBytes() }) + require.Len(t, msg.GetSigners(), 1) +} + +func TestValidatorMsgMethods(t *testing.T) { + acc := sample.AccAddress() + val := sample.ValAddress() + msgs := []legacyMsg{ + // signer is a validator operator (valoper) address + &MsgCreateValidator{Signer: val}, + &MsgDisableValidator{Creator: val}, + &MsgEnableValidator{Creator: val}, + // signer is a regular account address + &MsgApproveDisableValidator{Creator: acc}, + &MsgProposeDisableValidator{Creator: acc}, + &MsgRejectDisableValidator{Creator: acc}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + requireMsgMethods(t, msg) + }) + } +} + +func TestValidatorMsgGetSignersPanicsOnInvalidSigner(t *testing.T) { + msgs := []legacyMsg{ + &MsgCreateValidator{Signer: "invalid"}, + &MsgDisableValidator{Creator: "invalid"}, + &MsgEnableValidator{Creator: "invalid"}, + &MsgApproveDisableValidator{Creator: "invalid"}, + &MsgProposeDisableValidator{Creator: "invalid"}, + &MsgRejectDisableValidator{Creator: "invalid"}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + require.Panics(t, func() { _ = msg.GetSigners() }) + }) + } +} diff --git a/x/validator/types/validator_methods_test.go b/x/validator/types/validator_methods_test.go new file mode 100644 index 000000000..a5e3071fd --- /dev/null +++ b/x/validator/types/validator_methods_test.go @@ -0,0 +1,139 @@ +package types_test + +import ( + "strings" + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" +) + +func newTestValidator(t *testing.T) (types.Validator, sdk.ValAddress) { + t.Helper() + pk := ed25519.GenPrivKey().PubKey() + owner := sdk.ValAddress(pk.Address()) + v, err := types.NewValidator(owner, pk, types.NewDescription("moniker", "identity", "website", "details")) + require.NoError(t, err) + + return v, owner +} + +func TestValidator_Accessors(t *testing.T) { + v, owner := newTestValidator(t) + + require.Equal(t, types.Power, v.GetPower()) + require.Equal(t, "moniker", v.GetMoniker()) + require.False(t, v.IsJailed()) + require.Equal(t, owner, v.GetOwner()) + + consAddr, err := v.GetConsAddress() + require.NoError(t, err) + require.NotEmpty(t, consAddr) + + consPubKey, err := v.GetConsPubKey() + require.NoError(t, err) + require.NotNil(t, consPubKey) + + consAddr2, err := v.GetConsAddr() + require.NoError(t, err) + require.Equal(t, consAddr, consAddr2) + + _, err = v.TmConsPublicKey() + require.NoError(t, err) + + require.NotPanics(t, func() { _ = v.ABCIValidatorUpdate() }) + require.NotPanics(t, func() { _ = v.ABCIValidatorUpdateZero() }) + require.NotEmpty(t, v.String()) + + registry := codectypes.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(registry) + require.NoError(t, v.UnpackInterfaces(registry)) +} + +func TestValidator_GetOwner(t *testing.T) { + require.Nil(t, types.Validator{Owner: ""}.GetOwner()) + require.Panics(t, func() { _ = types.Validator{Owner: "invalid-address"}.GetOwner() }) +} + +func TestValidator_ConsKeyErrors(t *testing.T) { + // PubKey Any whose cached value is not a cryptotypes.PubKey. + bad := types.Validator{PubKey: mustNonPubKeyAny(t)} + _, err := bad.GetConsAddress() + require.Error(t, err) + _, err = bad.GetConsPubKey() + require.Error(t, err) + _, err = bad.GetConsAddr() + require.Error(t, err) + _, err = bad.TmConsPublicKey() + require.Error(t, err) +} + +func mustNonPubKeyAny(t *testing.T) *codectypes.Any { + t.Helper() + // Use a proto message that is NOT a cryptotypes.PubKey. + a, err := codectypes.NewAnyWithValue(&types.Validator{Owner: "x"}) + require.NoError(t, err) + + return a +} + +func TestValidator_MarshalRoundtrip(t *testing.T) { + v, _ := newTestValidator(t) + registry := codectypes.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(registry) + cdc := codec.NewProtoCodec(registry) + + bz := types.MustMarshalValidator(cdc, &v) + require.NotEmpty(t, bz) + + got := types.MustUnmarshalValidator(cdc, bz) + require.Equal(t, v.Owner, got.Owner) + + got2, err := types.UnmarshalValidator(cdc, bz) + require.NoError(t, err) + require.Equal(t, v.Owner, got2.Owner) +} + +func TestDescription_Validate(t *testing.T) { + for _, tc := range []struct { + name string + desc types.Description + valid bool + }{ + {"valid", types.NewDescription("m", "i", "w", "d"), true}, + {"empty moniker", types.NewDescription("", "i", "w", "d"), false}, + {"moniker too long", types.NewDescription(strings.Repeat("a", types.MaxNameLength+1), "", "", ""), false}, + {"identity too long", types.NewDescription("m", strings.Repeat("a", types.MaxIdentityLength+1), "", ""), false}, + {"website too long", types.NewDescription("m", "", strings.Repeat("a", types.MaxWebsiteLength+1), ""), false}, + {"details too long", types.NewDescription("m", "", "", strings.Repeat("a", types.MaxDetailsLength+1)), false}, + } { + t.Run(tc.name, func(t *testing.T) { + err := tc.desc.Validate() + if tc.valid { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } + require.NotEmpty(t, types.NewDescription("m", "", "", "").String()) +} + +func TestLastValidatorPower_Methods(t *testing.T) { + addr, err := sdk.ValAddressFromBech32(sample.ValAddress()) + require.NoError(t, err) + + lvp := types.NewLastValidatorPower(addr) + require.Equal(t, addr, lvp.GetOwner()) + require.Equal(t, types.Power, lvp.GetPower()) + require.NotEmpty(t, lvp.String()) + + require.Nil(t, types.LastValidatorPower{Owner: ""}.GetOwner()) + require.Panics(t, func() { _ = types.LastValidatorPower{Owner: "invalid"}.GetOwner() }) +} diff --git a/x/vendorinfo/types/genesis_test.go b/x/vendorinfo/types/genesis_test.go index d3a9ba3e5..0ce120878 100644 --- a/x/vendorinfo/types/genesis_test.go +++ b/x/vendorinfo/types/genesis_test.go @@ -1,6 +1,5 @@ package types_test -/* import ( "testing" @@ -23,14 +22,9 @@ func TestGenesisState_Validate(t *testing.T) { desc: "valid genesis state", genState: &types.GenesisState{ VendorInfoList: []types.VendorInfo{ - { - VendorID: 0, - }, - { - VendorID: 1, - }, + {VendorID: 0}, + {VendorID: 1}, }, - // this line is used by starport scaffolding # types/genesis/validField }, valid: true, }, @@ -38,17 +32,12 @@ func TestGenesisState_Validate(t *testing.T) { desc: "duplicated vendorInfo", genState: &types.GenesisState{ VendorInfoList: []types.VendorInfo{ - { - VendorID: 0, - }, - { - VendorID: 0, - }, + {VendorID: 0}, + {VendorID: 0}, }, }, valid: false, }, - // this line is used by starport scaffolding # types/genesis/testcase } { t.Run(tc.desc, func(t *testing.T) { err := tc.genState.Validate() @@ -60,4 +49,3 @@ func TestGenesisState_Validate(t *testing.T) { }) } } -*/ diff --git a/x/vendorinfo/types/msg_methods_test.go b/x/vendorinfo/types/msg_methods_test.go new file mode 100644 index 000000000..fc7ee123b --- /dev/null +++ b/x/vendorinfo/types/msg_methods_test.go @@ -0,0 +1,49 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" +) + +type legacyMsg interface { + Route() string + Type() string + GetSignBytes() []byte + GetSigners() []sdk.AccAddress +} + +func requireMsgMethods(t *testing.T, msg legacyMsg) { + t.Helper() + require.NotEmpty(t, msg.Route()) + require.NotEmpty(t, msg.Type()) + require.NotPanics(t, func() { _ = msg.GetSignBytes() }) + require.Len(t, msg.GetSigners(), 1) +} + +func TestVendorinfoMsgMethods(t *testing.T) { + creator := sample.AccAddress() + msgs := []legacyMsg{ + &MsgCreateVendorInfo{Creator: creator}, + &MsgUpdateVendorInfo{Creator: creator}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + requireMsgMethods(t, msg) + }) + } +} + +func TestVendorinfoMsgGetSignersPanicsOnInvalidSigner(t *testing.T) { + msgs := []legacyMsg{ + &MsgCreateVendorInfo{Creator: "invalid"}, + &MsgUpdateVendorInfo{Creator: "invalid"}, + } + for _, msg := range msgs { + t.Run(msg.Type(), func(t *testing.T) { + require.Panics(t, func() { _ = msg.GetSigners() }) + }) + } +} From 53c72ed1406879f2a4d1c2f723415f5a0d070b4f Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Fri, 26 Jun 2026 17:54:33 +0500 Subject: [PATCH 02/17] Add extensive negative test cases and remove unused code linters Signed-off-by: Abdulbois --- .../keeper/msg_server_invalid_signer_test.go | 5 + .../keeper/msg_server_invalid_signer_test.go | 5 + x/dclauth/keeper/msg_server_test.go | 1 - .../keeper/msg_server_invalid_signer_test.go | 3 + x/dclupgrade/keeper/msg_server_test.go | 1 - .../keeper/msg_server_invalid_signer_test.go | 6 + x/model/keeper/msg_server_test.go | 1 - .../keeper/msg_server_invalid_signer_test.go | 18 ++ x/pki/keeper/msg_server_test.go | 1 - x/pki/x509/x509_negative_test.go | 229 ++++++++++++++++++ .../keeper/msg_server_invalid_signer_test.go | 6 + x/validator/keeper/msg_server_test.go | 1 - 12 files changed, 272 insertions(+), 5 deletions(-) create mode 100644 x/pki/x509/x509_negative_test.go diff --git a/x/compliance/keeper/msg_server_invalid_signer_test.go b/x/compliance/keeper/msg_server_invalid_signer_test.go index 1590807d4..22f80aafd 100644 --- a/x/compliance/keeper/msg_server_invalid_signer_test.go +++ b/x/compliance/keeper/msg_server_invalid_signer_test.go @@ -22,22 +22,27 @@ func TestMsgServerInvalidSigner(t *testing.T) { }{ {"CertifyModel", func() error { _, err := ms.CertifyModel(wctx, &types.MsgCertifyModel{Signer: bad}) + return err }}, {"RevokeModel", func() error { _, err := ms.RevokeModel(wctx, &types.MsgRevokeModel{Signer: bad}) + return err }}, {"ProvisionModel", func() error { _, err := ms.ProvisionModel(wctx, &types.MsgProvisionModel{Signer: bad}) + return err }}, {"UpdateComplianceInfo", func() error { _, err := ms.UpdateComplianceInfo(wctx, &types.MsgUpdateComplianceInfo{Creator: bad}) + return err }}, {"DeleteComplianceInfo", func() error { _, err := ms.DeleteComplianceInfo(wctx, &types.MsgDeleteComplianceInfo{Creator: bad}) + return err }}, } diff --git a/x/dclauth/keeper/msg_server_invalid_signer_test.go b/x/dclauth/keeper/msg_server_invalid_signer_test.go index 5fe1b6b72..fb95dbfcd 100644 --- a/x/dclauth/keeper/msg_server_invalid_signer_test.go +++ b/x/dclauth/keeper/msg_server_invalid_signer_test.go @@ -17,22 +17,27 @@ func TestMsgServerInvalidSigner(t *testing.T) { }{ {"ProposeAddAccount", func() error { _, err := ms.ProposeAddAccount(ctx, &types.MsgProposeAddAccount{Signer: bad}) + return err }}, {"ApproveAddAccount", func() error { _, err := ms.ApproveAddAccount(ctx, &types.MsgApproveAddAccount{Signer: bad}) + return err }}, {"RejectAddAccount", func() error { _, err := ms.RejectAddAccount(ctx, &types.MsgRejectAddAccount{Signer: bad}) + return err }}, {"ProposeRevokeAccount", func() error { _, err := ms.ProposeRevokeAccount(ctx, &types.MsgProposeRevokeAccount{Signer: bad}) + return err }}, {"ApproveRevokeAccount", func() error { _, err := ms.ApproveRevokeAccount(ctx, &types.MsgApproveRevokeAccount{Signer: bad}) + return err }}, } diff --git a/x/dclauth/keeper/msg_server_test.go b/x/dclauth/keeper/msg_server_test.go index 9cfe72671..ea0f39864 100644 --- a/x/dclauth/keeper/msg_server_test.go +++ b/x/dclauth/keeper/msg_server_test.go @@ -10,7 +10,6 @@ import ( "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" ) -//nolint:unused func setupMsgServer(tb testing.TB) (types.MsgServer, context.Context) { tb.Helper() k, ctx := keepertest.DclauthKeeper(tb) diff --git a/x/dclupgrade/keeper/msg_server_invalid_signer_test.go b/x/dclupgrade/keeper/msg_server_invalid_signer_test.go index 87e335781..575aa4452 100644 --- a/x/dclupgrade/keeper/msg_server_invalid_signer_test.go +++ b/x/dclupgrade/keeper/msg_server_invalid_signer_test.go @@ -17,14 +17,17 @@ func TestMsgServerInvalidSigner(t *testing.T) { }{ {"ProposeUpgrade", func() error { _, err := ms.ProposeUpgrade(ctx, &types.MsgProposeUpgrade{Creator: bad}) + return err }}, {"ApproveUpgrade", func() error { _, err := ms.ApproveUpgrade(ctx, &types.MsgApproveUpgrade{Creator: bad}) + return err }}, {"RejectUpgrade", func() error { _, err := ms.RejectUpgrade(ctx, &types.MsgRejectUpgrade{Creator: bad}) + return err }}, } diff --git a/x/dclupgrade/keeper/msg_server_test.go b/x/dclupgrade/keeper/msg_server_test.go index e7fded3f4..369a414d9 100644 --- a/x/dclupgrade/keeper/msg_server_test.go +++ b/x/dclupgrade/keeper/msg_server_test.go @@ -10,7 +10,6 @@ import ( "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclupgrade/types" ) -//nolint:unused func setupMsgServer(tb testing.TB) (types.MsgServer, context.Context) { tb.Helper() k, ctx := keepertest.DclupgradeKeeper(tb, nil, nil) diff --git a/x/model/keeper/msg_server_invalid_signer_test.go b/x/model/keeper/msg_server_invalid_signer_test.go index 62a7a3471..459e5bcde 100644 --- a/x/model/keeper/msg_server_invalid_signer_test.go +++ b/x/model/keeper/msg_server_invalid_signer_test.go @@ -17,26 +17,32 @@ func TestMsgServerInvalidSigner(t *testing.T) { }{ {"CreateModel", func() error { _, err := ms.CreateModel(ctx, &types.MsgCreateModel{Creator: bad}) + return err }}, {"UpdateModel", func() error { _, err := ms.UpdateModel(ctx, &types.MsgUpdateModel{Creator: bad}) + return err }}, {"DeleteModel", func() error { _, err := ms.DeleteModel(ctx, &types.MsgDeleteModel{Creator: bad}) + return err }}, {"CreateModelVersion", func() error { _, err := ms.CreateModelVersion(ctx, &types.MsgCreateModelVersion{Creator: bad}) + return err }}, {"UpdateModelVersion", func() error { _, err := ms.UpdateModelVersion(ctx, &types.MsgUpdateModelVersion{Creator: bad}) + return err }}, {"DeleteModelVersion", func() error { _, err := ms.DeleteModelVersion(ctx, &types.MsgDeleteModelVersion{Creator: bad}) + return err }}, } diff --git a/x/model/keeper/msg_server_test.go b/x/model/keeper/msg_server_test.go index d5c7ffd22..bf7ed4e53 100644 --- a/x/model/keeper/msg_server_test.go +++ b/x/model/keeper/msg_server_test.go @@ -10,7 +10,6 @@ import ( "github.com/zigbee-alliance/distributed-compliance-ledger/x/model/types" ) -//nolint:unused func setupMsgServer(tb testing.TB) (types.MsgServer, context.Context) { tb.Helper() k, ctx := keepertest.ModelKeeper(tb, nil, nil) diff --git a/x/pki/keeper/msg_server_invalid_signer_test.go b/x/pki/keeper/msg_server_invalid_signer_test.go index 593dcfd89..7159d2d98 100644 --- a/x/pki/keeper/msg_server_invalid_signer_test.go +++ b/x/pki/keeper/msg_server_invalid_signer_test.go @@ -20,74 +20,92 @@ func TestMsgServerInvalidSigner(t *testing.T) { }{ {"AddX509Cert", func() error { _, err := ms.AddX509Cert(ctx, &types.MsgAddX509Cert{Signer: bad}) + return err }}, {"AddNocX509RootCert", func() error { _, err := ms.AddNocX509RootCert(ctx, &types.MsgAddNocX509RootCert{Signer: bad}) + return err }}, {"AddNocX509IcaCert", func() error { _, err := ms.AddNocX509IcaCert(ctx, &types.MsgAddNocX509IcaCert{Signer: bad}) + return err }}, {"AddPkiRevocationDistributionPoint", func() error { _, err := ms.AddPkiRevocationDistributionPoint(ctx, &types.MsgAddPkiRevocationDistributionPoint{Signer: bad}) + return err }}, {"UpdatePkiRevocationDistributionPoint", func() error { _, err := ms.UpdatePkiRevocationDistributionPoint(ctx, &types.MsgUpdatePkiRevocationDistributionPoint{Signer: bad}) + return err }}, {"DeletePkiRevocationDistributionPoint", func() error { _, err := ms.DeletePkiRevocationDistributionPoint(ctx, &types.MsgDeletePkiRevocationDistributionPoint{Signer: bad}) + return err }}, {"ApproveAddX509RootCert", func() error { _, err := ms.ApproveAddX509RootCert(ctx, &types.MsgApproveAddX509RootCert{Signer: bad}) + return err }}, {"ApproveRevokeX509RootCert", func() error { _, err := ms.ApproveRevokeX509RootCert(ctx, &types.MsgApproveRevokeX509RootCert{Signer: bad}) + return err }}, {"AssignVid", func() error { _, err := ms.AssignVid(ctx, &types.MsgAssignVid{Signer: bad}) + return err }}, {"ProposeAddX509RootCert", func() error { _, err := ms.ProposeAddX509RootCert(ctx, &types.MsgProposeAddX509RootCert{Signer: bad}) + return err }}, {"ProposeRevokeX509RootCert", func() error { _, err := ms.ProposeRevokeX509RootCert(ctx, &types.MsgProposeRevokeX509RootCert{Signer: bad}) + return err }}, {"RejectAddX509RootCert", func() error { _, err := ms.RejectAddX509RootCert(ctx, &types.MsgRejectAddX509RootCert{Signer: bad}) + return err }}, {"RemoveX509Cert", func() error { _, err := ms.RemoveX509Cert(ctx, &types.MsgRemoveX509Cert{Signer: bad}) + return err }}, {"RemoveNocX509RootCert", func() error { _, err := ms.RemoveNocX509RootCert(ctx, &types.MsgRemoveNocX509RootCert{Signer: bad}) + return err }}, {"RemoveNocX509IcaCert", func() error { _, err := ms.RemoveNocX509IcaCert(ctx, &types.MsgRemoveNocX509IcaCert{Signer: bad}) + return err }}, {"RevokeX509Cert", func() error { _, err := ms.RevokeX509Cert(ctx, &types.MsgRevokeX509Cert{Signer: bad}) + return err }}, {"RevokeNocX509RootCert", func() error { _, err := ms.RevokeNocX509RootCert(ctx, &types.MsgRevokeNocX509RootCert{Signer: bad}) + return err }}, {"RevokeNocX509IcaCert", func() error { _, err := ms.RevokeNocX509IcaCert(ctx, &types.MsgRevokeNocX509IcaCert{Signer: bad}) + return err }}, } diff --git a/x/pki/keeper/msg_server_test.go b/x/pki/keeper/msg_server_test.go index 528a05540..a3122d836 100644 --- a/x/pki/keeper/msg_server_test.go +++ b/x/pki/keeper/msg_server_test.go @@ -10,7 +10,6 @@ import ( "github.com/zigbee-alliance/distributed-compliance-ledger/x/pki/types" ) -//nolint:unused func setupMsgServer(tb testing.TB) (types.MsgServer, context.Context) { tb.Helper() k, ctx := keepertest.PkiKeeper(tb, nil) diff --git a/x/pki/x509/x509_negative_test.go b/x/pki/x509/x509_negative_test.go new file mode 100644 index 000000000..6361aa5e3 --- /dev/null +++ b/x/pki/x509/x509_negative_test.go @@ -0,0 +1,229 @@ +//nolint:testpackage +package x509 + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "testing" + "time" + + "github.com/stretchr/testify/require" + testconstants "github.com/zigbee-alliance/distributed-compliance-ledger/integration_tests/constants" +) + +// decodeStd decodes a PEM fixture and returns the underlying crypto/x509 cert +// so individual parsed fields can be mutated to exercise one profile rule at a +// time (the validators read the parsed struct, not the signature). +func decodeStd(t *testing.T, pemCert string) *x509.Certificate { + t.Helper() + c, err := DecodeX509Certificate(pemCert) + require.NoError(t, err) + + return c.Certificate +} + +func removeExt(cert *x509.Certificate, oid asn1.ObjectIdentifier) { + out := cert.Extensions[:0] + for _, e := range cert.Extensions { + if !e.Id.Equal(oid) { + out = append(out, e) + } + } + cert.Extensions = out +} + +func setExtCriticalFlag(cert *x509.Certificate, oid asn1.ObjectIdentifier, critical bool) { + for i := range cert.Extensions { + if cert.Extensions[i].Id.Equal(oid) { + cert.Extensions[i].Critical = critical + + return + } + } +} + +func TestVerifyCAExtensions_NegativeBranches(t *testing.T) { + t.Run("KeyUsage absent", func(t *testing.T) { + cert := decodeStd(t, testconstants.PAACertWithNumericVid) + removeExt(cert, OIDKeyUsage) + require.Error(t, VerifyCAExtensions(cert)) + }) + t.Run("SubjectKeyId wrong length", func(t *testing.T) { + cert := decodeStd(t, testconstants.PAACertWithNumericVid) + cert.SubjectKeyId = []byte{0x01, 0x02, 0x03} + require.Error(t, VerifyCAExtensions(cert)) + }) + t.Run("non-self-signed AuthorityKeyId wrong length", func(t *testing.T) { + cert := decodeStd(t, testconstants.IntermediateCertPem) // non-self-signed CA + cert.AuthorityKeyId = []byte{0x01, 0x02, 0x03} + require.Error(t, VerifyCAExtensions(cert)) + }) +} + +func TestVerifyEndEntityExtensions_NegativeBranches(t *testing.T) { + base := func(t *testing.T) *x509.Certificate { + return decodeStd(t, testconstants.MatterDACShaped) + } + t.Run("BasicConstraints not critical", func(t *testing.T) { + cert := base(t) + setExtCriticalFlag(cert, OIDBasicConstraints, false) + require.Error(t, verifyEndEntityExtensions(cert, "DAC")) + }) + t.Run("KeyUsage absent", func(t *testing.T) { + cert := base(t) + removeExt(cert, OIDKeyUsage) + require.Error(t, verifyEndEntityExtensions(cert, "DAC")) + }) + t.Run("KeyUsage not critical", func(t *testing.T) { + cert := base(t) + setExtCriticalFlag(cert, OIDKeyUsage, false) + require.Error(t, verifyEndEntityExtensions(cert, "DAC")) + }) + t.Run("SubjectKeyId wrong length", func(t *testing.T) { + cert := base(t) + cert.SubjectKeyId = []byte{0x01} + require.Error(t, verifyEndEntityExtensions(cert, "DAC")) + }) + t.Run("AuthorityKeyId wrong length", func(t *testing.T) { + cert := base(t) + cert.AuthorityKeyId = []byte{0x01} + require.Error(t, verifyEndEntityExtensions(cert, "DAC")) + }) +} + +func TestVerifyDAChainNonRoot_CAExtensionsFail(t *testing.T) { + cert := decodeStd(t, testconstants.PAICertWithNumericVid) // cA=TRUE + removeExt(cert, OIDKeyUsage) + require.Error(t, VerifyDAChainNonRoot(cert)) +} + +func TestVerifyPAAPathLen_PresentNotOne(t *testing.T) { + cert := decodeStd(t, testconstants.PAACertWithNumericVid) + cert.MaxPathLen = 2 + cert.MaxPathLenZero = false + require.Error(t, VerifyPAAPathLen(cert)) +} + +func TestVerifyECDSAP256SHA256_WrongCurve(t *testing.T) { + cert := decodeStd(t, testconstants.PAACertWithNumericVid) + pub, ok := cert.PublicKey.(*ecdsa.PublicKey) + require.True(t, ok) + pub.Curve = elliptic.P224() + cert.PublicKey = pub + require.Error(t, VerifyECDSAP256SHA256(cert)) +} + +func TestVerifyVidPidConsistency_Branches(t *testing.T) { + // parent VID malformed + require.Error(t, VerifyVidPidConsistency("vid=0x1", "vid=0xZZ")) + // child VID differs from parent VID + require.Error(t, VerifyVidPidConsistency("vid=0x2", "vid=0x1")) + // parent PID malformed + require.Error(t, VerifyVidPidConsistency("pid=0x1", "pid=0xZZ")) + // child PID differs from parent PID + require.Error(t, VerifyVidPidConsistency("pid=0x2", "pid=0x1")) + // happy path: parent carries neither -> no constraint + require.NoError(t, VerifyVidPidConsistency("vid=0x2,pid=0x5", "CN=parent")) +} + +func TestFormatOID_Branches(t *testing.T) { + const vidOID = "1.3.6.1.4.1.37244.2.1" + + // value already in readable form (no '#') -> returned untouched + out, err := FormatOID(vidOID+"=0x6006", vidOID, "vid") + require.NoError(t, err) + require.Equal(t, vidOID+"=0x6006", out) // no '#': left as-is + + // non-hex DER + _, err = FormatOID(vidOID+"=#ZZ", vidOID, "vid") + require.Error(t, err) + + // DER too short (<2 bytes) + _, err = FormatOID(vidOID+"=#13", vidOID, "vid") + require.Error(t, err) + + // wrong DER tag (0x05 != PrintableString/UTF8String) + _, err = FormatOID(vidOID+"=#0500", vidOID, "vid") + require.Error(t, err) + + // DER length exceeds remaining bytes + _, err = FormatOID(vidOID+"=#1305", vidOID, "vid") + require.Error(t, err) +} + +func TestGetIntValueFromSubject_BadHex(t *testing.T) { + _, err := GetVidFromSubject("vid=0xZZ") + require.Error(t, err) +} + +func TestToSubjectAsText_MalformedVID(t *testing.T) { + _, err := ToSubjectAsText("1.3.6.1.4.1.37244.2.1=#ZZ") + require.Error(t, err) +} + +func TestCertificatePEMsEqual_InvalidPEM(t *testing.T) { + require.False(t, CertificatePEMsEqual("not a pem", testconstants.RootCertPem)) + require.True(t, CertificatePEMsEqual(testconstants.RootCertPem, testconstants.RootCertPem)) +} + +func TestDecodeX509Certificate_BadDER(t *testing.T) { + const badDER = "-----BEGIN CERTIFICATE-----\nAQID\n-----END CERTIFICATE-----\n" + _, err := DecodeX509Certificate(badDER) + require.Error(t, err) +} + +func TestVerifyVVSCSignature_Branches(t *testing.T) { + leaf, err := DecodeX509Certificate(testconstants.LeafCertPem) + require.NoError(t, err) + intermediate, err := DecodeX509Certificate(testconstants.IntermediateCertPem) + require.NoError(t, err) + + // not yet valid + future := leaf.Certificate.NotBefore.Add(-1 * time.Hour) + require.Error(t, leaf.VerifyVVSCSignature(intermediate, future)) + + // expired + expired := leaf.Certificate.NotAfter.Add(1 * time.Hour) + require.Error(t, leaf.VerifyVVSCSignature(intermediate, expired)) + + // signature mismatch: wrong parent (RootCertPem did not sign the leaf) + root, err := DecodeX509Certificate(testconstants.RootCertPem) + require.NoError(t, err) + within := leaf.Certificate.NotBefore.Add(time.Hour) + require.Error(t, leaf.VerifyVVSCSignature(root, within)) +} + +func TestVerifyVVSCExtensions_Wrapper(t *testing.T) { + // Valid DAC-shaped cert satisfies the shared end-entity profile. + require.NoError(t, VerifyVVSCExtensions(decodeStd(t, testconstants.MatterDACShaped))) +} + +func TestVerifyPAIPathLen_Absent(t *testing.T) { + // A PAA has no pathLenConstraint=0, so the PAI rule rejects it. + cert := decodeStd(t, testconstants.PAACertWithNumericVid) + cert.MaxPathLenZero = false + require.Error(t, VerifyPAIPathLen(cert)) +} + +func TestVerifyVidPidConsistency_ChildVidMalformed(t *testing.T) { + // parent carries a valid VID, child VID is malformed + require.Error(t, VerifyVidPidConsistency("vid=0xZZ", "vid=0x1")) + // parent carries a valid PID, child PID is malformed + require.Error(t, VerifyVidPidConsistency("pid=0xZZ", "pid=0x1")) +} + +func TestVerifyVVSCSignature_Success(t *testing.T) { + leaf, err := DecodeX509Certificate(testconstants.LeafCertPem) + require.NoError(t, err) + intermediate, err := DecodeX509Certificate(testconstants.IntermediateCertPem) + require.NoError(t, err) + + within := leaf.Certificate.NotBefore.Add(time.Hour) + require.NoError(t, leaf.VerifyVVSCSignature(intermediate, within)) +} + +// ensure pkix import is used to keep the import meaningful if other refs change. +var _ = pkix.Name{} diff --git a/x/validator/keeper/msg_server_invalid_signer_test.go b/x/validator/keeper/msg_server_invalid_signer_test.go index 7cdbeb3e8..410ff5cec 100644 --- a/x/validator/keeper/msg_server_invalid_signer_test.go +++ b/x/validator/keeper/msg_server_invalid_signer_test.go @@ -17,26 +17,32 @@ func TestMsgServerInvalidSigner(t *testing.T) { }{ {"CreateValidator", func() error { _, err := ms.CreateValidator(ctx, &types.MsgCreateValidator{Signer: bad}) + return err }}, {"DisableValidator", func() error { _, err := ms.DisableValidator(ctx, &types.MsgDisableValidator{Creator: bad}) + return err }}, {"EnableValidator", func() error { _, err := ms.EnableValidator(ctx, &types.MsgEnableValidator{Creator: bad}) + return err }}, {"ApproveDisableValidator", func() error { _, err := ms.ApproveDisableValidator(ctx, &types.MsgApproveDisableValidator{Creator: bad}) + return err }}, {"ProposeDisableValidator", func() error { _, err := ms.ProposeDisableValidator(ctx, &types.MsgProposeDisableValidator{Creator: bad}) + return err }}, {"RejectDisableValidator", func() error { _, err := ms.RejectDisableValidator(ctx, &types.MsgRejectDisableValidator{Creator: bad}) + return err }}, } diff --git a/x/validator/keeper/msg_server_test.go b/x/validator/keeper/msg_server_test.go index d5bfb5203..5cb8ca0ee 100644 --- a/x/validator/keeper/msg_server_test.go +++ b/x/validator/keeper/msg_server_test.go @@ -10,7 +10,6 @@ import ( "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" ) -//nolint:deadcode,unused func setupMsgServer(tb testing.TB) (types.MsgServer, context.Context) { tb.Helper() k, ctx := keepertest.ValidatorKeeper(tb, nil) From c7f8615805b3980b76f84ddd67a080d0177f25b5 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Fri, 26 Jun 2026 18:09:01 +0500 Subject: [PATCH 03/17] Fix lint Signed-off-by: Abdulbois --- x/pki/x509/x509_negative_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x/pki/x509/x509_negative_test.go b/x/pki/x509/x509_negative_test.go index 6361aa5e3..3d72e8ce8 100644 --- a/x/pki/x509/x509_negative_test.go +++ b/x/pki/x509/x509_negative_test.go @@ -65,6 +65,8 @@ func TestVerifyCAExtensions_NegativeBranches(t *testing.T) { func TestVerifyEndEntityExtensions_NegativeBranches(t *testing.T) { base := func(t *testing.T) *x509.Certificate { + t.Helper() + return decodeStd(t, testconstants.MatterDACShaped) } t.Run("BasicConstraints not critical", func(t *testing.T) { From 971ccd10407e296f417fb37e672245b37e7e5786 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Sat, 27 Jun 2026 13:18:46 +0500 Subject: [PATCH 04/17] Expand test cases for URL liveness and CLI commands, add list upgrade offline tests, and clean up unused code. Signed-off-by: Abdulbois --- utils/cli/url_liveness_test.go | 41 +++ utils/cli/utils_test.go | 75 ++++ x/dclgenutil/client/cli/init_test.go | 341 +++++++----------- .../client/cli/query_upgrade_list_test.go | 76 ++++ 4 files changed, 317 insertions(+), 216 deletions(-) create mode 100644 utils/cli/utils_test.go create mode 100644 x/dclupgrade/client/cli/query_upgrade_list_test.go diff --git a/utils/cli/url_liveness_test.go b/utils/cli/url_liveness_test.go index ddf353a6b..595835188 100644 --- a/utils/cli/url_liveness_test.go +++ b/utils/cli/url_liveness_test.go @@ -35,14 +35,23 @@ func TestIsLiveURL(t *testing.T) { want bool }{ {"200 OK", http.StatusOK, true}, + {"201 created", http.StatusCreated, true}, + {"204 no content", http.StatusNoContent, true}, + {"299 highest 2xx", 299, true}, {"301 redirect", http.StatusMovedPermanently, true}, + {"302 found", http.StatusFound, true}, + {"308 permanent redirect", http.StatusPermanentRedirect, true}, + {"399 highest 3xx", 399, true}, {"401 unauthorized", http.StatusUnauthorized, true}, {"403 forbidden", http.StatusForbidden, true}, {"405 method not allowed", http.StatusMethodNotAllowed, true}, {"451 unavailable for legal reasons", http.StatusUnavailableForLegalReasons, true}, + {"400 bad request", http.StatusBadRequest, false}, {"404 not found", http.StatusNotFound, false}, + {"429 too many requests", http.StatusTooManyRequests, false}, {"500 internal server error", http.StatusInternalServerError, false}, {"502 bad gateway", http.StatusBadGateway, false}, + {"503 service unavailable", http.StatusServiceUnavailable, false}, } for _, tt := range tests { @@ -68,6 +77,38 @@ func TestIsLiveURLUnreachable(t *testing.T) { require.False(t, IsLiveURL(u.String())) } +// TestIsLiveURLRequestCreationError covers the path where the HTTP request +// cannot even be constructed (e.g. the URL is malformed), so no network call +// is attempted and the URL is treated as not live. +func TestIsLiveURLRequestCreationError(t *testing.T) { + tests := []struct { + name string + url string + }{ + {"invalid control character", "http://example.com/\x7f"}, + {"missing protocol scheme", ":no-scheme"}, + {"control character in host", "http://exa\x00mple.com"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.False(t, IsLiveURL(tt.url)) + }) + } +} + +// TestIsLiveURLAllowed4XXNotMutated guards against the allow-list of 4XX status +// codes being changed unexpectedly, since IsLiveURL relies on it for the +// allowed-status path. +func TestIsLiveURLAllowed4XXNotMutated(t *testing.T) { + require.ElementsMatch(t, []int{ + http.StatusUnauthorized, + http.StatusForbidden, + http.StatusUnavailableForLegalReasons, + http.StatusMethodNotAllowed, + }, allowed4XXStatusCodes) +} + func TestCheckURLsForLiveness(t *testing.T) { okSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) diff --git a/utils/cli/utils_test.go b/utils/cli/utils_test.go new file mode 100644 index 000000000..d16f358b9 --- /dev/null +++ b/utils/cli/utils_test.go @@ -0,0 +1,75 @@ +// Copyright 2020 DSR Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestReadFromFileExistingFile(t *testing.T) { + const content = "certificate-content\nmultiple-lines\n" + + path := filepath.Join(t.TempDir(), "cert.pem") + require.NoError(t, os.WriteFile(path, []byte(content), 0o600)) + + got, err := ReadFromFile(path) + require.NoError(t, err) + require.Equal(t, content, got) +} + +func TestReadFromFileEmptyFile(t *testing.T) { + path := filepath.Join(t.TempDir(), "empty.pem") + require.NoError(t, os.WriteFile(path, []byte{}, 0o600)) + + got, err := ReadFromFile(path) + require.NoError(t, err) + require.Equal(t, "", got) +} + +// TestReadFromFileNotAPath covers the branch where the target does not exist on +// disk and is therefore returned verbatim (e.g. a PEM string passed inline). +func TestReadFromFileNotAPath(t *testing.T) { + tests := []struct { + name string + target string + }{ + {"inline pem", "-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"}, + {"plain string", "not-a-file"}, + {"empty string", ""}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ReadFromFile(tt.target) + require.NoError(t, err) + require.Equal(t, tt.target, got) + }) + } +} + +// TestReadFromFileReadError covers the branch where os.Stat succeeds but the +// subsequent read fails. Pointing at a directory triggers a read error while +// still passing the existence check. +func TestReadFromFileReadError(t *testing.T) { + dir := t.TempDir() + + got, err := ReadFromFile(dir) + require.Error(t, err) + require.Equal(t, "", got) +} diff --git a/x/dclgenutil/client/cli/init_test.go b/x/dclgenutil/client/cli/init_test.go index 13a6f4097..fc709c2fe 100644 --- a/x/dclgenutil/client/cli/init_test.go +++ b/x/dclgenutil/client/cli/init_test.go @@ -1,295 +1,204 @@ package cli_test -/* TODO issue #197 - import ( - "bytes" "context" "fmt" "io" "os" + "path/filepath" + "strings" "testing" - "time" + tmlog "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/server/mock" - "github.com/cosmos/cosmos-sdk/testutil" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/genutil" - genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" "github.com/cosmos/cosmos-sdk/x/staking" "github.com/spf13/viper" "github.com/stretchr/testify/require" - abci_server "github.com/cometbft/cometbft/abci/server" - "github.com/cometbft/cometbft/libs/cli" - "github.com/cometbft/cometbft/libs/log" + + dclgenutilcli "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclgenutil/client/cli" ) +// validMnemonic is a well-formed bip39 mnemonic used to exercise the --recover path. +const validMnemonic = "decide praise business actor peasant farm drastic weather extend front hurt later song give verb rhythm worry fun pond reform school tumble august one" + var testMbm = module.NewBasicManager( staking.AppModuleBasic{}, genutil.AppModuleBasic{}, ) -func TestInitCmd(t *testing.T) { - tests := []struct { - name string - flags func(dir string) []string - shouldErr bool - err error - }{ - { - name: "happy path", - flags: func(dir string) []string { - return []string{ - "appnode-test", - } - }, - shouldErr: false, - err: nil, - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - home := t.TempDir() - logger := log.NewNopLogger() - cfg, err := genutiltest.CreateDefaultTendermintConfig(home) - require.NoError(t, err) - - serverCtx := server.NewContext(viper.New(), cfg, logger) - interfaceRegistry := types.NewInterfaceRegistry() - marshaler := codec.NewProtoCodec(interfaceRegistry) - clientCtx := client.Context{}. - WithCodec(marshaler). - WithLegacyAmino(makeCodec()). - WithHomeDir(home) - - ctx := context.Background() - ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) - ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) - - cmd := genutilcli.InitCmd(testMbm, home) - cmd.SetArgs( - tt.flags(home), - ) - - if tt.shouldErr { - err := cmd.ExecuteContext(ctx) - require.EqualError(t, err, tt.err.Error()) - } else { - require.NoError(t, cmd.ExecuteContext(ctx)) - } - }) - } - -} +// initTestSetup wires up the client/server contexts the InitCmd RunE closure +// pulls out of the command context. chainID, when non-empty, is set on the +// client context to exercise the chain-id resolution branch. +func initTestSetup(t *testing.T, chainID string) (context.Context, string) { + t.Helper() -func TestInitRecover(t *testing.T) { home := t.TempDir() - logger := log.NewNopLogger() cfg, err := genutiltest.CreateDefaultTendermintConfig(home) require.NoError(t, err) - serverCtx := server.NewContext(viper.New(), cfg, logger) + serverCtx := server.NewContext(viper.New(), cfg, tmlog.NewNopLogger()) + interfaceRegistry := types.NewInterfaceRegistry() marshaler := codec.NewProtoCodec(interfaceRegistry) clientCtx := client.Context{}. WithCodec(marshaler). - WithLegacyAmino(makeCodec()). WithHomeDir(home) + if chainID != "" { + clientCtx = clientCtx.WithChainID(chainID) + } ctx := context.Background() ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) - cmd := genutilcli.InitCmd(testMbm, home) - mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + return ctx, home +} - cmd.SetArgs([]string{ - "appnode-test", - fmt.Sprintf("--%s=true", genutilcli.FlagRecover), - }) +// TestInitCmdDefaultChainID covers the happy path where no chain-id is provided, +// so a random one is generated. +func TestInitCmdDefaultChainID(t *testing.T) { + ctx, home := initTestSetup(t, "") + + cmd := dclgenutilcli.InitCmd(testMbm, home) + cmd.SetArgs([]string{"appnode-test"}) - // use valid mnemonic and complete recovery key generation successfully - mockIn.Reset("decide praise business actor peasant farm drastic weather extend front hurt later song give verb rhythm worry fun pond reform school tumble august one\n") require.NoError(t, cmd.ExecuteContext(ctx)) + require.FileExists(t, filepath.Join(home, "config", "genesis.json")) + require.FileExists(t, filepath.Join(home, "config", "config.toml")) } -func TestInitStakingBondDenom(t *testing.T) { - home := t.TempDir() - logger := log.NewNopLogger() - cfg, err := genutiltest.CreateDefaultTendermintConfig(home) - require.NoError(t, err) +// TestInitCmdChainIDFromClientCtx covers the branch where the chain-id is taken +// from the client context. +func TestInitCmdChainIDFromClientCtx(t *testing.T) { + ctx, home := initTestSetup(t, "dclchain-1") - serverCtx := server.NewContext(viper.New(), cfg, logger) - interfaceRegistry := types.NewInterfaceRegistry() - marshaler := codec.NewProtoCodec(interfaceRegistry) - clientCtx := client.Context{}. - WithCodec(marshaler). - WithLegacyAmino(makeCodec()). - WithHomeDir(home) + cmd := dclgenutilcli.InitCmd(testMbm, home) + cmd.SetArgs([]string{"appnode-test"}) - ctx := context.Background() - ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) - ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) + require.NoError(t, cmd.ExecuteContext(ctx)) + + genFile := filepath.Join(home, "config", "genesis.json") + content, err := os.ReadFile(genFile) + require.NoError(t, err) + require.Contains(t, string(content), "dclchain-1") +} - cmd := genutilcli.InitCmd(testMbm, home) +// TestInitCmdChainIDFromFlag covers the branch where the chain-id is taken from +// the --chain-id flag, which takes precedence over the client context value. +func TestInitCmdChainIDFromFlag(t *testing.T) { + ctx, home := initTestSetup(t, "ignored-chain") + cmd := dclgenutilcli.InitCmd(testMbm, home) cmd.SetArgs([]string{ "appnode-test", - fmt.Sprintf("--%s=%s", cli.HomeFlag, home), - fmt.Sprintf("--%s=testtoken", genutilcli.FlagStakingBondDenom), + fmt.Sprintf("--%s=flag-chain-9", flags.FlagChainID), }) + require.NoError(t, cmd.ExecuteContext(ctx)) -} -func TestEmptyState(t *testing.T) { - home := t.TempDir() - logger := log.NewNopLogger() - cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + content, err := os.ReadFile(filepath.Join(home, "config", "genesis.json")) require.NoError(t, err) + require.Contains(t, string(content), "flag-chain-9") + require.NotContains(t, string(content), "ignored-chain") +} - serverCtx := server.NewContext(viper.New(), cfg, logger) - interfaceRegistry := types.NewInterfaceRegistry() - marshaler := codec.NewProtoCodec(interfaceRegistry) - clientCtx := client.Context{}. - WithCodec(marshaler). - WithLegacyAmino(makeCodec()). - WithHomeDir(home) - - ctx := context.Background() - ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) - ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) - - cmd := genutilcli.InitCmd(testMbm, home) - cmd.SetArgs([]string{"appnode-test", fmt.Sprintf("--%s=%s", cli.HomeFlag, home)}) - - require.NoError(t, cmd.ExecuteContext(ctx)) +// TestInitCmdRecoverValidMnemonic covers the --recover path with a valid mnemonic. +func TestInitCmdRecoverValidMnemonic(t *testing.T) { + ctx, home := initTestSetup(t, "") - old := os.Stdout - r, w, _ := os.Pipe() - os.Stdout = w + cmd := dclgenutilcli.InitCmd(testMbm, home) + cmd.SetIn(strings.NewReader(validMnemonic + "\n")) + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + cmd.SetArgs([]string{ + "appnode-test", + fmt.Sprintf("--%s=true", dclgenutilcli.FlagRecover), + }) - cmd = server.ExportCmd(nil, home) - cmd.SetArgs([]string{fmt.Sprintf("--%s=%s", cli.HomeFlag, home)}) require.NoError(t, cmd.ExecuteContext(ctx)) - - outC := make(chan string) - go func() { - var buf bytes.Buffer - io.Copy(&buf, r) - outC <- buf.String() - }() - - w.Close() - os.Stdout = old - out := <-outC - - require.Contains(t, out, "genesis_time") - require.Contains(t, out, "chain_id") - require.Contains(t, out, "consensus_params") - require.Contains(t, out, "app_hash") - require.Contains(t, out, "app_state") + require.FileExists(t, filepath.Join(home, "config", "genesis.json")) } -func TestStartStandAlone(t *testing.T) { - home := t.TempDir() - logger := log.NewNopLogger() - interfaceRegistry := types.NewInterfaceRegistry() - marshaler := codec.NewProtoCodec(interfaceRegistry) - err := genutiltest.ExecInitCmd(testMbm, home, marshaler) - require.NoError(t, err) - - app, err := mock.NewApp(home, logger) - require.NoError(t, err) - - svrAddr, _, err := server.FreeTCPAddr() - require.NoError(t, err) - - svr, err := abci_server.NewServer(svrAddr, "socket", app) - require.NoError(t, err, "error creating listener") +// TestInitCmdRecoverInvalidMnemonic covers the --recover path where the supplied +// mnemonic is invalid and the command errors out. +func TestInitCmdRecoverInvalidMnemonic(t *testing.T) { + ctx, home := initTestSetup(t, "") - svr.SetLogger(logger.With("module", "abci-server")) - err = svr.Start() - require.NoError(t, err) + cmd := dclgenutilcli.InitCmd(testMbm, home) + cmd.SetIn(strings.NewReader("not a valid mnemonic\n")) + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + cmd.SetArgs([]string{ + "appnode-test", + fmt.Sprintf("--%s=true", dclgenutilcli.FlagRecover), + }) - timer := time.NewTimer(time.Duration(2) * time.Second) - for range timer.C { - err = svr.Stop() - require.NoError(t, err) - break - } + err := cmd.ExecuteContext(ctx) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid mnemonic") } -func TestInitNodeValidatorFiles(t *testing.T) { - home := t.TempDir() - cfg, err := genutiltest.CreateDefaultTendermintConfig(home) - require.NoError(t, err) +// TestInitCmdGenesisExists covers the guard that refuses to overwrite an +// existing genesis.json when --overwrite is not set. +func TestInitCmdGenesisExists(t *testing.T) { + ctx, home := initTestSetup(t, "") - nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(cfg) - require.NoError(t, err) + cmd := dclgenutilcli.InitCmd(testMbm, home) + cmd.SetArgs([]string{"appnode-test"}) + require.NoError(t, cmd.ExecuteContext(ctx)) - require.NotEqual(t, "", nodeID) - require.NotEqual(t, 0, len(valPubKey.Bytes())) + // Second run without --overwrite must fail because genesis.json now exists. + cmd = dclgenutilcli.InitCmd(testMbm, home) + cmd.SetArgs([]string{"appnode-test"}) + err := cmd.ExecuteContext(ctx) + require.Error(t, err) + require.Contains(t, err.Error(), "genesis.json file already exists") } -func TestInitConfig(t *testing.T) { - home := t.TempDir() - logger := log.NewNopLogger() - cfg, err := genutiltest.CreateDefaultTendermintConfig(home) - require.NoError(t, err) - - serverCtx := server.NewContext(viper.New(), cfg, logger) - interfaceRegistry := types.NewInterfaceRegistry() - marshaler := codec.NewProtoCodec(interfaceRegistry) - clientCtx := client.Context{}. - WithCodec(marshaler). - WithLegacyAmino(makeCodec()). - WithChainID("foo"). // add chain-id to clientCtx - WithHomeDir(home) - - ctx := context.Background() - ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) - ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) - - cmd := genutilcli.InitCmd(testMbm, home) - cmd.SetArgs([]string{"testnode"}) +// TestInitCmdOverwrite covers the --overwrite path, which reads the existing +// genesis doc from file before re-exporting it. +func TestInitCmdOverwrite(t *testing.T) { + ctx, home := initTestSetup(t, "") + cmd := dclgenutilcli.InitCmd(testMbm, home) + cmd.SetArgs([]string{"appnode-test"}) require.NoError(t, cmd.ExecuteContext(ctx)) - old := os.Stdout - r, w, _ := os.Pipe() - os.Stdout = w - - cmd = server.ExportCmd(nil, home) + cmd = dclgenutilcli.InitCmd(testMbm, home) + cmd.SetArgs([]string{ + "appnode-test", + fmt.Sprintf("--%s=true", dclgenutilcli.FlagOverwrite), + }) require.NoError(t, cmd.ExecuteContext(ctx)) + require.FileExists(t, filepath.Join(home, "config", "genesis.json")) +} - outC := make(chan string) - go func() { - var buf bytes.Buffer - io.Copy(&buf, r) - outC <- buf.String() - }() +// TestInitCmdOverwriteCorruptGenesis covers the path where an existing genesis +// file is present but cannot be parsed, so reading the genesis doc fails. +func TestInitCmdOverwriteCorruptGenesis(t *testing.T) { + ctx, home := initTestSetup(t, "") - w.Close() - os.Stdout = old - out := <-outC + cmd := dclgenutilcli.InitCmd(testMbm, home) + cmd.SetArgs([]string{"appnode-test"}) + require.NoError(t, cmd.ExecuteContext(ctx)) - require.Contains(t, out, "\"chain_id\": \"foo\"") -} + // Corrupt the genesis file so GenesisDocFromFile fails on the next run. + genFile := filepath.Join(home, "config", "genesis.json") + require.NoError(t, os.WriteFile(genFile, []byte("{ not valid json"), 0o600)) -// custom tx codec -func makeCodec() *codec.LegacyAmino { - var cdc = codec.NewLegacyAmino() - sdk.RegisterLegacyAminoCodec(cdc) - cryptocodec.RegisterCrypto(cdc) - return cdc + cmd = dclgenutilcli.InitCmd(testMbm, home) + cmd.SetArgs([]string{ + "appnode-test", + fmt.Sprintf("--%s=true", dclgenutilcli.FlagOverwrite), + }) + err := cmd.ExecuteContext(ctx) + require.Error(t, err) + require.Contains(t, err.Error(), "Failed to read genesis doc from file") } -*/ diff --git a/x/dclupgrade/client/cli/query_upgrade_list_test.go b/x/dclupgrade/client/cli/query_upgrade_list_test.go new file mode 100644 index 000000000..dab2222a6 --- /dev/null +++ b/x/dclupgrade/client/cli/query_upgrade_list_test.go @@ -0,0 +1,76 @@ +package cli_test + +import ( + "context" + "io" + "testing" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/spf13/cobra" + "github.com/stretchr/testify/require" + + dclupgradecli "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclupgrade/client/cli" +) + +// listUpgradeCmds maps each list command constructor to a readable name so the +// query-list RunE closures can be exercised with a single table of cases. +var listUpgradeCmds = map[string]func() *cobra.Command{ + "proposed": dclupgradecli.CmdListProposedUpgrade, + "approved": dclupgradecli.CmdListApprovedUpgrade, + "rejected": dclupgradecli.CmdListRejectedUpgrade, +} + +// offlineQueryCtx returns a command context carrying a client context that has +// no RPC node configured. Executing a list query against it drives the RunE +// closure through GetClientQueryContext, ReadPageRequest, the query-client +// construction and the (failing) gRPC call without needing a running network. +func offlineQueryCtx() context.Context { + interfaceRegistry := codectypes.NewInterfaceRegistry() + clientCtx := client.Context{}. + WithCodec(codec.NewProtoCodec(interfaceRegistry)). + WithInterfaceRegistry(interfaceRegistry) + + return context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) +} + +// execListCmd runs a list command with an empty --node so the client context +// stays offline (no real network connection is attempted) plus any extra args. +func execListCmd(cmd *cobra.Command, extraArgs ...string) error { + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + cmd.SetArgs(append([]string{"--" + flags.FlagNode + "="}, extraArgs...)) + + return cmd.ExecuteContext(offlineQueryCtx()) +} + +// TestCmdListUpgradeOffline covers the happy-path branches of the list RunE +// closures up to the query call, which fails because no RPC node is configured. +func TestCmdListUpgradeOffline(t *testing.T) { + for name, newCmd := range listUpgradeCmds { + name, newCmd := name, newCmd + t.Run(name, func(t *testing.T) { + err := execListCmd(newCmd()) + require.Error(t, err) + require.Contains(t, err.Error(), "no RPC client") + }) + } +} + +// TestCmdListUpgradePaginationError covers the ReadPageRequest error branch: +// --page and --offset cannot be used together. +func TestCmdListUpgradePaginationError(t *testing.T) { + for name, newCmd := range listUpgradeCmds { + name, newCmd := name, newCmd + t.Run(name, func(t *testing.T) { + err := execListCmd(newCmd(), + "--"+flags.FlagPage+"=2", + "--"+flags.FlagOffset+"=1", + ) + require.Error(t, err) + require.Contains(t, err.Error(), "page and offset cannot be used together") + }) + } +} From 19bf4e49ab5437325c2f04c2a9abae2e564f6999 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Sat, 27 Jun 2026 13:28:43 +0500 Subject: [PATCH 05/17] Improve coverage for URL liveness by merging tests from non-dev build into coverage report. Signed-off-by: Abdulbois --- .github/workflows/verify.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 32b85b7ad..059cce0a8 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -113,7 +113,16 @@ jobs: set -euo pipefail # shellcheck disable=SC2046 go test -tags=dev -json -v $(go list ./... | grep -v '/integration_tests') -coverprofile=./cover.out -covermode=set -coverpkg=./... 2>&1 | tee /tmp/gotest.log | gotestfmt - go test -v utils/cli/url_liveness.go utils/cli/url_liveness_test.go + # url_liveness.go gates its HTTP logic behind the dev build flag, so it is + # unreachable (and its !dev-tagged test is excluded) in the -tags=dev run + # above. Re-run the package without the dev tag to collect real coverage for + # the liveness checks, then merge it into cover.out so it shows up in the + # coverage report. The non-dev package build reports the same canonical file + # paths as the run above, so gocovmerge unions the two cleanly. + go test -covermode=set -coverprofile=./cover_url_liveness.out -coverpkg=./utils/cli/... ./utils/cli/ + go install github.com/wadey/gocovmerge@b5bfa59ec0adc420475f97f89b58045c721d761c + gocovmerge ./cover.out ./cover_url_liveness.out > ./cover_merged.out + mv ./cover_merged.out ./cover.out env: GH_TOKEN: ${{ secrets.GH_TOKEN }} GOCOVER_MODE: set From 6ca6639933280ce73063e3ae3cdac351807459f7 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Sat, 27 Jun 2026 13:38:28 +0500 Subject: [PATCH 06/17] Refactor test coverage workflow: extract unit test script, increase coverage threshold to 90%, and update dependencies. Signed-off-by: Abdulbois --- .github/.testcoverage.yml | 2 +- .github/workflows/run_unit_tests.sh | 26 ++++++++++++++++++++++++++ .github/workflows/verify.yml | 16 +--------------- go.mod | 1 + 4 files changed, 29 insertions(+), 16 deletions(-) create mode 100755 .github/workflows/run_unit_tests.sh diff --git a/.github/.testcoverage.yml b/.github/.testcoverage.yml index 8f3e013b2..a58833674 100644 --- a/.github/.testcoverage.yml +++ b/.github/.testcoverage.yml @@ -2,5 +2,5 @@ profile: 'cover.out' threshold: - total: 50 + total: 90 file: 0 \ No newline at end of file diff --git a/.github/workflows/run_unit_tests.sh b/.github/workflows/run_unit_tests.sh new file mode 100755 index 000000000..3bfe8871b --- /dev/null +++ b/.github/workflows/run_unit_tests.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Runs the Go unit tests with coverage and writes the merged profile to cover.out. +# +# The main suite runs with -tags=dev. url_liveness.go gates its HTTP logic behind +# that dev flag (and its !dev-tagged test is excluded), so it is unreachable in the +# dev run. We re-run the utils/cli package without the dev tag to collect real +# coverage for the liveness checks and merge it in, so it shows up in the coverage +# report. The non-dev package build reports the same canonical file paths as the +# dev run, so gocovmerge unions the two cleanly. + +set -euo pipefail + +GOCOVMERGE_VERSION="b5bfa59ec0adc420475f97f89b58045c721d761c" + +# shellcheck disable=SC2046 +go test -tags=dev -json -v $(go list ./... | grep -v '/integration_tests') \ + -coverprofile=./cover.out -covermode=set -coverpkg=./... 2>&1 \ + | tee /tmp/gotest.log | gotestfmt + +go test -covermode=set -coverprofile=./cover_url_liveness.out \ + -coverpkg=./utils/cli/... ./utils/cli/ + +go install "github.com/wadey/gocovmerge@${GOCOVMERGE_VERSION}" +gocovmerge ./cover.out ./cover_url_liveness.out > ./cover_merged.out +mv ./cover_merged.out ./cover.out diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 059cce0a8..687d1f0ec 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -109,20 +109,7 @@ jobs: ${{ runner.os }}-go- - uses: haveyoudebuggedit/gotestfmt-action@v2 - name: Run tests - run: | - set -euo pipefail - # shellcheck disable=SC2046 - go test -tags=dev -json -v $(go list ./... | grep -v '/integration_tests') -coverprofile=./cover.out -covermode=set -coverpkg=./... 2>&1 | tee /tmp/gotest.log | gotestfmt - # url_liveness.go gates its HTTP logic behind the dev build flag, so it is - # unreachable (and its !dev-tagged test is excluded) in the -tags=dev run - # above. Re-run the package without the dev tag to collect real coverage for - # the liveness checks, then merge it into cover.out so it shows up in the - # coverage report. The non-dev package build reports the same canonical file - # paths as the run above, so gocovmerge unions the two cleanly. - go test -covermode=set -coverprofile=./cover_url_liveness.out -coverpkg=./utils/cli/... ./utils/cli/ - go install github.com/wadey/gocovmerge@b5bfa59ec0adc420475f97f89b58045c721d761c - gocovmerge ./cover.out ./cover_url_liveness.out > ./cover_merged.out - mv ./cover_merged.out ./cover.out + run: .github/workflows/run_unit_tests.sh env: GH_TOKEN: ${{ secrets.GH_TOKEN }} GOCOVER_MODE: set @@ -220,7 +207,6 @@ jobs: run: .github/workflows/filter_coverage.sh - name: Report test coverage uses: vladopajic/go-test-coverage@v2 - continue-on-error: true with: config: ./.github/.testcoverage.yml git-token: ${{ github.ref_name == 'master' && secrets.GITHUB_TOKEN || '' }} diff --git a/go.mod b/go.mod index 57df48a75..20cede5fb 100644 --- a/go.mod +++ b/go.mod @@ -62,6 +62,7 @@ require ( github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chzyer/readline v1.5.1 // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/cockroachdb/errors v1.10.0 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect From 784ee2c65ececef499865af211b8de59af3693c6 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Sat, 27 Jun 2026 13:48:58 +0500 Subject: [PATCH 07/17] Enable JSON output and append logs for CLI test coverage in workflow script. Signed-off-by: Abdulbois --- .github/workflows/run_unit_tests.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_unit_tests.sh b/.github/workflows/run_unit_tests.sh index 3bfe8871b..1acdd6bea 100755 --- a/.github/workflows/run_unit_tests.sh +++ b/.github/workflows/run_unit_tests.sh @@ -18,8 +18,9 @@ go test -tags=dev -json -v $(go list ./... | grep -v '/integration_tests') \ -coverprofile=./cover.out -covermode=set -coverpkg=./... 2>&1 \ | tee /tmp/gotest.log | gotestfmt -go test -covermode=set -coverprofile=./cover_url_liveness.out \ - -coverpkg=./utils/cli/... ./utils/cli/ +go test -json -v -covermode=set -coverprofile=./cover_url_liveness.out \ + -coverpkg=./utils/cli/... ./utils/cli/ 2>&1 \ + | tee -a /tmp/gotest.log | gotestfmt go install "github.com/wadey/gocovmerge@${GOCOVMERGE_VERSION}" gocovmerge ./cover.out ./cover_url_liveness.out > ./cover_merged.out From dba86a40cedcd0f569cc845964c7c28e3010e18c Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Sat, 27 Jun 2026 15:36:16 +0500 Subject: [PATCH 08/17] Expand test coverage for `UpdatePkiRevocationDistributionPoint` handler with non-CRL `DataFields`. Signed-off-by: Abdulbois --- x/pki/tests/handler_update_revocation_test.go | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/x/pki/tests/handler_update_revocation_test.go b/x/pki/tests/handler_update_revocation_test.go index 691839fb2..57d86e913 100644 --- a/x/pki/tests/handler_update_revocation_test.go +++ b/x/pki/tests/handler_update_revocation_test.go @@ -890,3 +890,55 @@ func TestHandler_UpdatePkiRevocationDistributionPoint_CrlSignerCertificateField( }) } } + +// TestHandler_UpdatePkiRevocationDistributionPoint_NonCRL_DataFields covers the +// data-field update branches (DataURL/DataFileSize/DataDigest/DataDigestType). +// Those fields can only be set when the revocation point is NOT of CRL type +// (otherwise the handler rejects them), but only the CRL type can be added +// through the normal flow. The point is therefore seeded as CRL and then +// switched to a non-CRL type directly in the keeper before the update. +func TestHandler_UpdatePkiRevocationDistributionPoint_NonCRL_DataFields(t *testing.T) { + const nonCRLRevocationType = uint32(2) + + setup := utils.Setup(t) + + vendorAcc := utils.GenerateAccAddress() + addedRevocation := createAddRevocationMessageWithPAACertWithNumericVid(vendorAcc.String()) + setup.AddAccount(vendorAcc, []dclauthtypes.AccountRole{dclauthtypes.Vendor}, addedRevocation.Vid) + + utils.ProposeAndApproveRootCertificateByOptions(setup, setup.Trustee1, utils.CreatePAACertWithNumericVidOptions()) + + _, err := setup.Handler(setup.Ctx, addedRevocation) + require.NoError(t, err) + + // Switch the stored point to a non-CRL type so the update is allowed to carry + // data fields. This bypasses the add-time validation, which only permits CRL. + point, isFound := setup.Keeper.GetPkiRevocationDistributionPoint( + setup.Ctx, addedRevocation.Vid, addedRevocation.Label, addedRevocation.IssuerSubjectKeyID) + require.True(t, isFound) + point.RevocationType = nonCRLRevocationType + setup.Keeper.SetPkiRevocationDistributionPoint(setup.Ctx, point) + setup.Keeper.UpdatePkiRevocationDistributionPointBySubjectKeyID(setup.Ctx, point) + + updateRevocation := &types.MsgUpdatePkiRevocationDistributionPoint{ + Signer: vendorAcc.String(), + Vid: addedRevocation.Vid, + Label: addedRevocation.Label, + IssuerSubjectKeyID: addedRevocation.IssuerSubjectKeyID, + DataURL: addedRevocation.DataURL + "/data", + DataFileSize: uint64(123), + DataDigest: testconstants.DataDigest, + DataDigestType: uint32(1), + SchemaVersion: 0, + } + _, err = setup.Handler(setup.Ctx, updateRevocation) + require.NoError(t, err) + + updatedPoint, isFound := setup.Keeper.GetPkiRevocationDistributionPoint( + setup.Ctx, addedRevocation.Vid, addedRevocation.Label, addedRevocation.IssuerSubjectKeyID) + require.True(t, isFound) + require.Equal(t, updateRevocation.DataURL, updatedPoint.DataURL) + require.Equal(t, updateRevocation.DataFileSize, updatedPoint.DataFileSize) + require.Equal(t, updateRevocation.DataDigest, updatedPoint.DataDigest) + require.Equal(t, updateRevocation.DataDigestType, updatedPoint.DataDigestType) +} From 0c4d058dfce531ec2da89aeea433bd9153fddc7f Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Sat, 27 Jun 2026 15:42:13 +0500 Subject: [PATCH 09/17] Expand test coverage for `CmdListCertificates` and `CmdListAllCertificatesBySubject` with offline and error scenarios. Signed-off-by: Abdulbois --- .../cli/query_all_certificates_list_test.go | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 x/pki/client/cli/query_all_certificates_list_test.go diff --git a/x/pki/client/cli/query_all_certificates_list_test.go b/x/pki/client/cli/query_all_certificates_list_test.go new file mode 100644 index 000000000..3670f3b26 --- /dev/null +++ b/x/pki/client/cli/query_all_certificates_list_test.go @@ -0,0 +1,69 @@ +package cli_test + +import ( + "context" + "io" + "testing" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/spf13/cobra" + "github.com/stretchr/testify/require" + + pkicli "github.com/zigbee-alliance/distributed-compliance-ledger/x/pki/client/cli" +) + +// offlineQueryCtx returns a command context carrying a client context with no +// RPC node configured. Executing a query against it drives the RunE closure +// through its happy-path branches up to the (failing) query call, without +// requiring a running network. +func offlineQueryCtx() context.Context { + interfaceRegistry := codectypes.NewInterfaceRegistry() + clientCtx := client.Context{}. + WithCodec(codec.NewProtoCodec(interfaceRegistry)). + WithInterfaceRegistry(interfaceRegistry) + + return context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) +} + +// execCmdOffline runs a command with an empty --node so the client context stays +// offline (no real network connection is attempted) plus any extra args. +func execCmdOffline(cmd *cobra.Command, extraArgs ...string) error { + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + cmd.SetArgs(append([]string{"--" + flags.FlagNode + "="}, extraArgs...)) + + return cmd.ExecuteContext(offlineQueryCtx()) +} + +// TestCmdListCertificatesOffline covers the happy-path branches of the +// CmdListCertificates RunE closure up to the gRPC query call, which fails +// because no RPC node is configured. +func TestCmdListCertificatesOffline(t *testing.T) { + err := execCmdOffline(pkicli.CmdListCertificates()) + require.Error(t, err) + require.Contains(t, err.Error(), "no RPC client") +} + +// TestCmdListCertificatesPaginationError covers the ReadPageRequest error branch: +// --page and --offset cannot be used together. +func TestCmdListCertificatesPaginationError(t *testing.T) { + err := execCmdOffline(pkicli.CmdListCertificates(), + "--"+flags.FlagPage+"=2", + "--"+flags.FlagOffset+"=1", + ) + require.Error(t, err) + require.Contains(t, err.Error(), "page and offset cannot be used together") +} + +// TestCmdListAllCertificatesBySubjectOffline covers the CmdListAllCertificatesBySubject +// RunE closure up to the QueryWithProof store query, which fails offline. +func TestCmdListAllCertificatesBySubjectOffline(t *testing.T) { + err := execCmdOffline(pkicli.CmdListAllCertificatesBySubject(), + "--"+pkicli.FlagSubject+"=test-subject", + ) + require.Error(t, err) + require.Contains(t, err.Error(), "no RPC client") +} From 82b05e55090dee9452b7f846129b5116b76954d6 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Mon, 29 Jun 2026 14:20:27 +0500 Subject: [PATCH 10/17] Expand test coverage for `MsgProposeUpgrade` and `AddX509Cert` handlers with extensive negative scenarios. Signed-off-by: Abdulbois --- .../types/message_propose_upgrade_test.go | 178 ++++++++++++++++++ x/pki/tests/handler_add_pai_cert_test.go | 68 +++++++ 2 files changed, 246 insertions(+) diff --git a/x/dclupgrade/types/message_propose_upgrade_test.go b/x/dclupgrade/types/message_propose_upgrade_test.go index fba142b0f..ce8e56207 100644 --- a/x/dclupgrade/types/message_propose_upgrade_test.go +++ b/x/dclupgrade/types/message_propose_upgrade_test.go @@ -164,6 +164,40 @@ func TestMsgProposeUpgrade_ValidateBasic(t *testing.T) { } } +func TestNewMsgProposeUpgrade(t *testing.T) { + creator := sample.AccAddress() + plan := Plan{ + Name: testconstants.UpgradePlanName, + Height: testconstants.UpgradePlanHeight, + Info: testconstants.UpgradePlanInfo, + } + + msg := NewMsgProposeUpgrade(creator, plan, testconstants.Info) + require.Equal(t, creator, msg.Creator) + require.Equal(t, plan, msg.Plan) + require.Equal(t, testconstants.Info, msg.Info) + require.NotZero(t, msg.Time) +} + +func TestMsgProposeUpgrade_ValidateBasicCLI_InvalidCreator(t *testing.T) { + // Invalid creator address is rejected before the live GitHub-API call, + // so this exercises ValidateBasicCLI without any network access. + msg := MsgProposeUpgrade{ + Creator: "invalid_address", + Plan: Plan{ + Name: testconstants.UpgradePlanName, + Height: testconstants.UpgradePlanHeight, + Info: testconstants.UpgradePlanInfo, + }, + Info: testconstants.Info, + Time: testconstants.Time, + } + + err := msg.ValidateBasicCLI() + require.Error(t, err) + require.ErrorIs(t, err, sdkerrors.ErrInvalidAddress) +} + func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { var expectedResponse string @@ -268,6 +302,102 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { }, err: sdkerrors.ErrJSONUnmarshal, }, + { + // valid JSON but the single binary targets an unsupported platform. + name: "unsupported os platform (valid json)", + expected: testconstants.UpgradeGitAPIJSONResponse, + msg: MsgProposeUpgrade{ + Creator: sample.AccAddress(), + Plan: Plan{ + Name: testconstants.UpgradePlanName, + Height: testconstants.UpgradePlanHeight, + Info: "{\"binaries\":{\"windows/amd64\":\"https://github.com/zigbee-alliance/distributed-compliance-ledger/releases/download/v1.4.4/dcld?checksum=sha256:abc\"}}", + }, + Info: testconstants.Info, + Time: testconstants.Time, + }, + err: sdkerrors.ErrJSONUnmarshal, + }, + { + // URL has the required "?checksum=" separator but too few path segments. + name: "binary url too short", + expected: testconstants.UpgradeGitAPIJSONResponse, + msg: MsgProposeUpgrade{ + Creator: sample.AccAddress(), + Plan: Plan{ + Name: testconstants.UpgradePlanName, + Height: testconstants.UpgradePlanHeight, + Info: "{\"binaries\":{\"linux/amd64\":\"http://x?checksum=sha256:abc\"}}", + }, + Info: testconstants.Info, + Time: testconstants.Time, + }, + err: sdkerrors.ErrInvalidRequest, + }, + { + // git tag parsed from the URL does not contain the plan name. + name: "plan name not in binary url tag", + expected: testconstants.UpgradeGitAPIJSONResponse, + msg: MsgProposeUpgrade{ + Creator: sample.AccAddress(), + Plan: Plan{ + Name: "v9.9.9", + Height: testconstants.UpgradePlanHeight, + Info: testconstants.UpgradePlanInfo, + }, + Info: testconstants.Info, + Time: testconstants.Time, + }, + err: sdkerrors.ErrInvalidRequest, + }, + { + // server returns a body that is not valid JSON. + name: "git api response not json", + expected: "this is not json", + msg: MsgProposeUpgrade{ + Creator: sample.AccAddress(), + Plan: Plan{ + Name: testconstants.UpgradePlanName, + Height: testconstants.UpgradePlanHeight, + Info: testconstants.UpgradePlanInfo, + }, + Info: testconstants.Info, + Time: testconstants.Time, + }, + err: sdkerrors.ErrJSONUnmarshal, + }, + { + // server returns valid JSON without the "assets" field. + name: "git api response without assets", + expected: "{\"message\":\"Not Found\"}", + msg: MsgProposeUpgrade{ + Creator: sample.AccAddress(), + Plan: Plan{ + Name: testconstants.UpgradePlanName, + Height: testconstants.UpgradePlanHeight, + Info: testconstants.UpgradePlanInfo, + }, + Info: testconstants.Info, + Time: testconstants.Time, + }, + err: sdkerrors.ErrJSONUnmarshal, + }, + { + // assets present but none match the expected binary. + name: "git api response with no matching asset", + expected: "{\"assets\":[{\"name\": \"other\", \"state\": \"uploaded\", \"digest\": null, \"browser_download_url\":\"https://example.com/other\"}]}", + msg: MsgProposeUpgrade{ + Creator: sample.AccAddress(), + Plan: Plan{ + Name: testconstants.UpgradePlanName, + Height: testconstants.UpgradePlanHeight, + Info: testconstants.UpgradePlanInfo, + }, + Info: testconstants.Info, + Time: testconstants.Time, + }, + err: sdkerrors.ErrInvalidRequest, + }, } positiveTests := []struct { @@ -275,6 +405,21 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { expected string msg MsgProposeUpgrade }{ + { + // empty Plan.Info short-circuits with no error and no network call. + name: "empty plan info", + expected: testconstants.UpgradeGitAPIJSONResponse, + msg: MsgProposeUpgrade{ + Creator: sample.AccAddress(), + Plan: Plan{ + Name: testconstants.UpgradePlanName, + Height: testconstants.UpgradePlanHeight, + Info: "", + }, + Info: testconstants.Info, + Time: testconstants.Time, + }, + }, { name: "valid binary file without checksum", expected: "{\"assets\":[{\"name\": \"dcld\", \"state\": \"uploaded\", \"digest\": null, \"browser_download_url\":\"" + testconstants.UpgradeBrowserDownloadURL + "\"}]}", @@ -334,4 +479,37 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { require.ErrorIs(t, err, tt.err) }) } + + t.Run("git token header is set when GH_TOKEN present", func(t *testing.T) { + t.Setenv("GH_TOKEN", "test-token") + expectedResponse = testconstants.UpgradeGitAPIJSONResponse + msg := MsgProposeUpgrade{ + Creator: sample.AccAddress(), + Plan: Plan{ + Name: testconstants.UpgradePlanName, + Height: testconstants.UpgradePlanHeight, + Info: testconstants.UpgradePlanInfo, + }, + Info: testconstants.Info, + Time: testconstants.Time, + } + require.NoError(t, ValidateBinaries(&msg, svr.URL)) + }) + + t.Run("git api request fails", func(t *testing.T) { + msg := MsgProposeUpgrade{ + Creator: sample.AccAddress(), + Plan: Plan{ + Name: testconstants.UpgradePlanName, + Height: testconstants.UpgradePlanHeight, + Info: testconstants.UpgradePlanInfo, + }, + Info: testconstants.Info, + Time: testconstants.Time, + } + // Unroutable address forces client.Do to fail before any response. + err := ValidateBinaries(&msg, "http://127.0.0.1:1") + require.Error(t, err) + require.ErrorIs(t, err, sdkerrors.ErrInvalidRequest) + }) } diff --git a/x/pki/tests/handler_add_pai_cert_test.go b/x/pki/tests/handler_add_pai_cert_test.go index acd74d5fd..76c8b4c51 100644 --- a/x/pki/tests/handler_add_pai_cert_test.go +++ b/x/pki/tests/handler_add_pai_cert_test.go @@ -392,6 +392,74 @@ func TestHandler_AddDaIntermediateCert_RootIsNoc(t *testing.T) { require.Contains(t, err.Error(), "PAI: BasicConstraints pathLenConstraint SHALL be present and set to 0") } +func TestHandler_AddDaIntermediateCert_RootWithMalformedSubject(t *testing.T) { + // The stored root certificate keeps a valid PEM (so the chain still builds), + // but its SubjectAsText is tampered so ensureVidMatches rejects it. A real + // parsed certificate never reaches these branches, so they are driven by + // seeding the store directly. + cases := []struct { + name string + subjectAsText string + err error + }{ + { + // matter-vid OID present with invalid DER -> ToSubjectAsText fails to decode. + name: "RootSubjectDerMalformed", + subjectAsText: "1.3.6.1.4.1.37244.2.1=#ZZ", + err: pkitypes.ErrInvalidCertificate, + }, + { + // vid value is not valid hex -> GetVidFromSubject fails. + name: "RootVidNotHex", + subjectAsText: "vid=0xZZ", + err: pkitypes.ErrInvalidVidFormat, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + setup := utils.Setup(t) + + // store root certificate with a tampered SubjectAsText + rootCertificate := utils.RootDaCertificate(setup.Trustee1) + rootCertificate.SubjectAsText = tc.subjectAsText + setup.Keeper.AddAllCertificate(setup.Ctx, rootCertificate) + + // add intermediate certificate + testIntermediateCertificate := utils.IntermediateDaCertificate(setup.Vendor1) + addX509Cert := types.NewMsgAddX509Cert( + setup.Vendor1.String(), + testIntermediateCertificate.PemCert, + testconstants.CertSchemaVersion) + _, err := setup.Handler(setup.Ctx, addX509Cert) + require.ErrorIs(t, err, tc.err) + }) + } +} + +func TestHandler_AddDaLeafCert_ImmediateIssuerWithMalformedSubject(t *testing.T) { + setup := utils.Setup(t) + + // store root certificate (clean) so ensureVidMatches passes against the root + rootCertificate := utils.RootDaCertificate(setup.Trustee1) + setup.Keeper.AddAllCertificate(setup.Ctx, rootCertificate) + + // store the intermediate (the leaf's immediate issuer) with a valid PEM but a + // tampered SubjectAsText, which verifyImmediateIssuerVidPid later rejects. + intermediateCertificate := utils.IntermediateDaCertificate(setup.Vendor1) + intermediateCertificate.SubjectAsText = "1.3.6.1.4.1.37244.2.1=#ZZ" + setup.Keeper.AddAllCertificate(setup.Ctx, intermediateCertificate) + + // add leaf certificate; its immediate issuer is the tampered intermediate + leafCertificate := utils.LeafCertificate(setup.Vendor1) + addX509Cert := types.NewMsgAddX509Cert( + setup.Vendor1.String(), + leafCertificate.PemCert, + testconstants.CertSchemaVersion) + _, err := setup.Handler(setup.Ctx, addX509Cert) + require.ErrorIs(t, err, pkitypes.ErrInvalidCertificate) +} + func TestHandler_AddDaIntermediateCert_ForAbsentDirectParentCert(t *testing.T) { setup := utils.Setup(t) From 9033522dc57c18fcb5806005f4c31a8962ca97cf Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Mon, 29 Jun 2026 15:22:29 +0500 Subject: [PATCH 11/17] Expand test coverage for PKI handlers with additional edge and negative scenarios, including invalid certificates, self-signed CRLs, and mismatched model versions. Signed-off-by: Abdulbois --- x/compliance/handler_provision_model_test.go | 41 +++++++++ x/pki/tests/handler_propose_paa_cert_test.go | 42 +++++++++ x/pki/tests/handler_update_revocation_test.go | 88 +++++++++++++++++++ 3 files changed, 171 insertions(+) diff --git a/x/compliance/handler_provision_model_test.go b/x/compliance/handler_provision_model_test.go index 4f414967f..4735dbca8 100644 --- a/x/compliance/handler_provision_model_test.go +++ b/x/compliance/handler_provision_model_test.go @@ -6,6 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" testconstants "github.com/zigbee-alliance/distributed-compliance-ledger/integration_tests/constants" "github.com/zigbee-alliance/distributed-compliance-ledger/x/compliance/types" @@ -198,3 +199,43 @@ func TestHandler_SchemaVersion_ProvisionModel_StampsCurrentOnCreate(t *testing.T provisionalModel, _ := queryProvisionalModel(setup, vid, pid, softwareVersion, certificationType) require.Equal(t, provisionalModel.CurrentSchemaVersion(), provisionalModel.SchemaVersion) } + +// Covers the default switch arm: existing compliance info whose status is none of +// provisional/certified/revoked. +func TestHandler_ProvisionModel_ExistingInfoUnknownStatus(t *testing.T) { + setup, vid, pid, softwareVersion, softwareVersionString, certificationType := setupProvisionModel(t) + + setup.Keeper.SetComplianceInfo(setup.Ctx, &types.ComplianceInfo{ + Vid: vid, + Pid: pid, + SoftwareVersion: softwareVersion, + CertificationType: certificationType, + SoftwareVersionCertificationStatus: 99, // not provisional/certified/revoked + }) + + _, err := setup.provisionModel(vid, pid, softwareVersion, softwareVersionString, certificationType, setup.CertificationCenter) + require.ErrorIs(t, err, types.ErrComplianceInfoAlreadyExist) +} + +// Covers the branch where the stored model version's software version string does +// not match the one in the message. +func TestHandler_ProvisionModel_ModelVersionStringMismatch(t *testing.T) { + setup, vid, pid, softwareVersion, _, certificationType := setupProvisionModel(t) + + _, err := setup.provisionModel(vid, pid, softwareVersion, "different-version-string", certificationType, setup.CertificationCenter) + require.ErrorIs(t, err, types.ErrModelVersionStringDoesNotMatch) +} + +// Covers the branch where the stored model version's CD version number does not +// match the one in the message (software version string still matches). +func TestHandler_ProvisionModel_CdVersionNumberMismatch(t *testing.T) { + setup := setup(t) + vid, pid, softwareVersion := testconstants.Vid, testconstants.Pid, testconstants.SoftwareVersion + + modelVersion := newModelVersion(vid, pid, softwareVersion, testconstants.SoftwareVersionString) + modelVersion.CdVersionNumber = testconstants.CdVersionNumber + 1 + setup.ModelKeeper.On("GetModelVersion", mock.Anything, vid, pid, softwareVersion).Return(*modelVersion, true) + + _, err := setup.provisionModel(vid, pid, softwareVersion, testconstants.SoftwareVersionString, types.ZigbeeCertificationType, setup.CertificationCenter) + require.ErrorIs(t, err, types.ErrModelVersionStringDoesNotMatch) +} diff --git a/x/pki/tests/handler_propose_paa_cert_test.go b/x/pki/tests/handler_propose_paa_cert_test.go index 2f037cfcf..d7d295969 100644 --- a/x/pki/tests/handler_propose_paa_cert_test.go +++ b/x/pki/tests/handler_propose_paa_cert_test.go @@ -308,6 +308,48 @@ func TestHandler_ProposeAddDaRootCert_CertificateAlreadyExists(t *testing.T) { // require.True(t, pkitypes.ErrInappropriateCertificateType.Is(err)) // } +func TestHandler_ProposeAddDaRootCert_ExistingCertIsNotRoot(t *testing.T) { + setup := utils.Setup(t) + + // seed an approved NON-root certificate sharing the proposed root's + // subject/SKID (different serial so the uniqueness check passes). + existing := utils.RootDaCertificate(setup.Trustee1) + existing.SerialNumber = utils.SerialNumber + existing.IsRoot = false + utils.AddMokedDaCertificate(setup, existing) + + // propose the self-signed root with the same subject/SKID + proposeAddX509RootCert := types.NewMsgProposeAddX509RootCert( + setup.Trustee1.String(), + testconstants.RootCertPem, + testconstants.Info, + testconstants.Vid, + testconstants.CertSchemaVersion) + _, err := setup.Handler(setup.Ctx, proposeAddX509RootCert) + require.ErrorIs(t, err, sdkerrors.ErrUnauthorized) +} + +func TestHandler_ProposeAddDaRootCert_ExistingCertIsNoc(t *testing.T) { + setup := utils.Setup(t) + + // seed an approved NOC root certificate sharing the proposed root's + // subject/SKID (different serial so the uniqueness check passes). + existing := utils.RootDaCertificate(setup.Trustee1) + existing.SerialNumber = utils.SerialNumber + existing.IsRoot = true + existing.CertificateType = types.CertificateType_OperationalPKI + utils.AddMokedDaCertificate(setup, existing) + + proposeAddX509RootCert := types.NewMsgProposeAddX509RootCert( + setup.Trustee1.String(), + testconstants.RootCertPem, + testconstants.Info, + testconstants.Vid, + testconstants.CertSchemaVersion) + _, err := setup.Handler(setup.Ctx, proposeAddX509RootCert) + require.ErrorIs(t, err, pkitypes.ErrInappropriateCertificateType) +} + func TestHandler_ProposeAddDaRootCert_ForDifferentSigner(t *testing.T) { setup := utils.Setup(t) diff --git a/x/pki/tests/handler_update_revocation_test.go b/x/pki/tests/handler_update_revocation_test.go index 57d86e913..9b1ec6c4b 100644 --- a/x/pki/tests/handler_update_revocation_test.go +++ b/x/pki/tests/handler_update_revocation_test.go @@ -267,6 +267,58 @@ func TestHandler_UpdatePkiRevocationDistributionPoint_NegativeCases(t *testing.T }, err: pkitypes.ErrCertNotChainedBack, }, + { + // PAA path: the new CRL signer certificate is not a parseable/valid cert. + name: "PAANewCertificateInvalid", + vendorAccVid: testconstants.PAACertWithNumericVidVid, + rootCertOptions: utils.CreatePAACertWithNumericVidOptions(), + addRevocation: createAddRevocationMessageWithPAACertWithNumericVid(vendorAcc.String()), + updatedRevocation: &types.MsgUpdatePkiRevocationDistributionPoint{ + Signer: vendorAcc.String(), + Vid: testconstants.PAACertWithNumericVidVid, + CrlSignerCertificate: testconstants.StubCertPem, + Label: label, + DataURL: testconstants.DataURL, + IssuerSubjectKeyID: testconstants.SubjectKeyIDWithoutColons, + SchemaVersion: 0, + }, + err: pkitypes.ErrInvalidCertificate, + }, + { + // PAI path: the new CRL signer certificate is not a parseable/valid cert. + name: "PAINewCertificateInvalid", + vendorAccVid: testconstants.PAICertWithNumericPidVidVid, + rootCertOptions: utils.CreatePAACertWithNumericVidOptions(), + addRevocation: createAddRevocationMessageWithPAICertWithNumericVidPid(vendorAcc.String()), + updatedRevocation: &types.MsgUpdatePkiRevocationDistributionPoint{ + Signer: vendorAcc.String(), + Vid: testconstants.PAICertWithNumericPidVidVid, + CrlSignerCertificate: testconstants.StubCertPem, + Label: label, + DataURL: testconstants.DataURL, + IssuerSubjectKeyID: testconstants.SubjectKeyIDWithoutColons, + SchemaVersion: 0, + }, + err: pkitypes.ErrInvalidCertificate, + }, + { + // PAI path: the new CRL signer certificate is self-signed, which is + // invalid for a non-root distribution point. + name: "PAINewCertificateSelfSigned", + vendorAccVid: testconstants.PAICertWithNumericPidVidVid, + rootCertOptions: utils.CreatePAACertWithNumericVidOptions(), + addRevocation: createAddRevocationMessageWithPAICertWithNumericVidPid(vendorAcc.String()), + updatedRevocation: &types.MsgUpdatePkiRevocationDistributionPoint{ + Signer: vendorAcc.String(), + Vid: testconstants.PAICertWithNumericPidVidVid, + CrlSignerCertificate: testconstants.PAACertWithNumericVid, + Label: label, + DataURL: testconstants.DataURL, + IssuerSubjectKeyID: testconstants.SubjectKeyIDWithoutColons, + SchemaVersion: 0, + }, + err: pkitypes.ErrNonRootCertificateSelfSigned, + }, } for _, tc := range cases { @@ -942,3 +994,39 @@ func TestHandler_UpdatePkiRevocationDistributionPoint_NonCRL_DataFields(t *testi require.Equal(t, updateRevocation.DataDigest, updatedPoint.DataDigest) require.Equal(t, updateRevocation.DataDigestType, updatedPoint.DataDigestType) } + +func TestHandler_UpdatePkiRevocationDistributionPoint_StoredCertificateUndecodable(t *testing.T) { + setup := utils.Setup(t) + + vendorAcc := utils.GenerateAccAddress() + setup.AddAccount(vendorAcc, []dclauthtypes.AccountRole{dclauthtypes.Vendor}, testconstants.PAACertWithNumericVidVid) + + // propose and approve root certificate + rootCert := utils.RootDaCertificateWithNumericVid(setup.Trustee1) + utils.ProposeAndApproveRootCertificate(setup, setup.Trustee1, rootCert) + + // create a valid revocation point + addRevocation := createAddRevocationMessageWithPAACertWithNumericVid(vendorAcc.String()) + _, err := setup.Handler(setup.Ctx, addRevocation) + require.NoError(t, err) + + // corrupt the stored CRL signer certificate so it can no longer be decoded + point, found := setup.Keeper.GetPkiRevocationDistributionPoint( + setup.Ctx, addRevocation.Vid, addRevocation.Label, addRevocation.IssuerSubjectKeyID) + require.True(t, found) + point.CrlSignerCertificate = "not a certificate" + setup.Keeper.SetPkiRevocationDistributionPoint(setup.Ctx, point) + + // updating with a new certificate now fails while decoding the *old* stored cert + update := &types.MsgUpdatePkiRevocationDistributionPoint{ + Signer: vendorAcc.String(), + Vid: addRevocation.Vid, + CrlSignerCertificate: testconstants.PAACertWithNumericVid, + Label: addRevocation.Label, + DataURL: testconstants.DataURL, + IssuerSubjectKeyID: addRevocation.IssuerSubjectKeyID, + SchemaVersion: 0, + } + _, err = setup.Handler(setup.Ctx, update) + require.ErrorIs(t, err, pkitypes.ErrInvalidCertificate) +} From d554e04109e712bd696c27586d8f2f6f8d2a7535 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Mon, 29 Jun 2026 18:30:57 +0500 Subject: [PATCH 12/17] Clean-up Signed-off-by: Abdulbois --- types/pki/errors_test.go | 31 --- ...ssage_propose_add_account_branches_test.go | 60 ----- .../types/message_propose_add_account_test.go | 51 ++++ x/model/types/errors_test.go | 26 -- ...tificates_by_subject_key_id_extras_test.go | 30 --- ...noc_certificates_by_subject_key_id_test.go | 21 ++ x/pki/x509/x509_negative_test.go | 231 ------------------ x/pki/x509/x509_test.go | 211 ++++++++++++++++ .../message_create_validator_branches_test.go | 42 ---- .../types/message_create_validator_test.go | 58 ++--- 10 files changed, 312 insertions(+), 449 deletions(-) delete mode 100644 types/pki/errors_test.go delete mode 100644 x/dclauth/types/message_propose_add_account_branches_test.go delete mode 100644 x/model/types/errors_test.go delete mode 100644 x/pki/keeper/noc_certificates_by_subject_key_id_extras_test.go delete mode 100644 x/pki/x509/x509_negative_test.go delete mode 100644 x/validator/types/message_create_validator_branches_test.go diff --git a/types/pki/errors_test.go b/types/pki/errors_test.go deleted file mode 100644 index 468a6e083..000000000 --- a/types/pki/errors_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package types_test - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/require" - pkitypes "github.com/zigbee-alliance/distributed-compliance-ledger/types/pki" -) - -// TestErrorConstructors exercises the error constructors that build the module's -// typed error surface so each wrapped sentinel is produced at least once. -func TestErrorConstructors(t *testing.T) { - errs := []error{ - pkitypes.NewErrRevokedCertificateDoesNotExist("subj", "skid"), - pkitypes.NewErrProvidedNotNocCertButExistingNoc("subj", "skid"), - pkitypes.NewErrProvidedNotNocCertButRootIsNoc(), - pkitypes.NewErrCRLSignerCertificatePidNotEqualRevocationPointPid(1, 2), - pkitypes.NewErrInvalidAuthorityKeyIDFormat(), - pkitypes.NewErrVidNotFound("vid"), - pkitypes.NewErrPemValuesNotEqual("subj", "skid"), - pkitypes.NewErrUnsupportedOperation("op"), - pkitypes.NewErrInvalidVidFormat("bad"), - pkitypes.NewErrInvalidPidFormat("bad"), - } - for _, err := range errs { - require.Error(t, err) - require.NotEmpty(t, err.Error()) - require.NotNil(t, errors.Unwrap(err)) - } -} diff --git a/x/dclauth/types/message_propose_add_account_branches_test.go b/x/dclauth/types/message_propose_add_account_branches_test.go deleted file mode 100644 index a94af254e..000000000 --- a/x/dclauth/types/message_propose_add_account_branches_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package types_test - -import ( - "testing" - - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" - "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" - "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types" -) - -func TestMsgProposeAddAccount_ValidateBasicBranches(t *testing.T) { - pk := ed25519.GenPrivKey().PubKey() - accAddr := sdk.AccAddress(pk.Address()) - pkAny, err := codectypes.NewAnyWithValue(pk) - require.NoError(t, err) - badAny, err := codectypes.NewAnyWithValue(&types.MsgProposeAddAccount{}) - require.NoError(t, err) - signer := sample.AccAddress() - - base := func() types.MsgProposeAddAccount { - return types.MsgProposeAddAccount{ - Address: accAddr.String(), - Signer: signer, - PubKey: pkAny, - Roles: types.AccountRoles{types.Vendor}, - VendorID: 1, - } - } - - mutate := func(f func(*types.MsgProposeAddAccount)) types.MsgProposeAddAccount { - m := base() - f(&m) - - return m - } - - for _, tc := range []struct { - name string - msg types.MsgProposeAddAccount - }{ - {"empty address", mutate(func(m *types.MsgProposeAddAccount) { m.Address = "" })}, - {"invalid address", mutate(func(m *types.MsgProposeAddAccount) { m.Address = "invalid" })}, - {"empty signer", mutate(func(m *types.MsgProposeAddAccount) { m.Signer = "" })}, - {"invalid signer", mutate(func(m *types.MsgProposeAddAccount) { m.Signer = "invalid" })}, - {"nil pubkey", mutate(func(m *types.MsgProposeAddAccount) { m.PubKey = nil })}, - {"non-pubkey any", mutate(func(m *types.MsgProposeAddAccount) { m.PubKey = badAny })}, - {"pubkey/address mismatch", mutate(func(m *types.MsgProposeAddAccount) { m.Address = sample.AccAddress() })}, - {"no roles", mutate(func(m *types.MsgProposeAddAccount) { m.Roles = nil })}, - {"invalid role", mutate(func(m *types.MsgProposeAddAccount) { m.Roles = types.AccountRoles{types.AccountRole("bogus")} })}, - {"vendor without vendorID", mutate(func(m *types.MsgProposeAddAccount) { m.VendorID = 0 })}, - } { - t.Run(tc.name, func(t *testing.T) { - msg := tc.msg - require.Error(t, msg.ValidateBasic()) - }) - } -} diff --git a/x/dclauth/types/message_propose_add_account_test.go b/x/dclauth/types/message_propose_add_account_test.go index d2db8d83f..8e8fa3c88 100644 --- a/x/dclauth/types/message_propose_add_account_test.go +++ b/x/dclauth/types/message_propose_add_account_test.go @@ -6,11 +6,14 @@ import ( "testing" "cosmossdk.io/errors" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/stretchr/testify/require" testconstants "github.com/zigbee-alliance/distributed-compliance-ledger/integration_tests/constants" + "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" validator "github.com/zigbee-alliance/distributed-compliance-ledger/utils/validator" "github.com/zigbee-alliance/distributed-compliance-ledger/x/common/types" ) @@ -192,3 +195,51 @@ func TestMsgProposeAddAccountGetSignBytes(t *testing.T) { require.Equal(t, expected, string(msg.GetSignBytes())) } + +func TestMsgProposeAddAccount_ValidateBasicBranches(t *testing.T) { + pk := ed25519.GenPrivKey().PubKey() + accAddr := sdk.AccAddress(pk.Address()) + pkAny, err := codectypes.NewAnyWithValue(pk) + require.NoError(t, err) + badAny, err := codectypes.NewAnyWithValue(&MsgProposeAddAccount{}) + require.NoError(t, err) + signer := sample.AccAddress() + + base := func() MsgProposeAddAccount { + return MsgProposeAddAccount{ + Address: accAddr.String(), + Signer: signer, + PubKey: pkAny, + Roles: AccountRoles{Vendor}, + VendorID: 1, + } + } + + mutate := func(f func(*MsgProposeAddAccount)) MsgProposeAddAccount { + m := base() + f(&m) + + return m + } + + for _, tc := range []struct { + name string + msg MsgProposeAddAccount + }{ + {"empty address", mutate(func(m *MsgProposeAddAccount) { m.Address = "" })}, + {"invalid address", mutate(func(m *MsgProposeAddAccount) { m.Address = "invalid" })}, + {"empty signer", mutate(func(m *MsgProposeAddAccount) { m.Signer = "" })}, + {"invalid signer", mutate(func(m *MsgProposeAddAccount) { m.Signer = "invalid" })}, + {"nil pubkey", mutate(func(m *MsgProposeAddAccount) { m.PubKey = nil })}, + {"non-pubkey any", mutate(func(m *MsgProposeAddAccount) { m.PubKey = badAny })}, + {"pubkey/address mismatch", mutate(func(m *MsgProposeAddAccount) { m.Address = sample.AccAddress() })}, + {"no roles", mutate(func(m *MsgProposeAddAccount) { m.Roles = nil })}, + {"invalid role", mutate(func(m *MsgProposeAddAccount) { m.Roles = AccountRoles{"bogus"} })}, + {"vendor without vendorID", mutate(func(m *MsgProposeAddAccount) { m.VendorID = 0 })}, + } { + t.Run(tc.name, func(t *testing.T) { + msg := tc.msg + require.Error(t, msg.ValidateBasic()) + }) + } +} diff --git a/x/model/types/errors_test.go b/x/model/types/errors_test.go deleted file mode 100644 index 1a71dd2d9..000000000 --- a/x/model/types/errors_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package types_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/zigbee-alliance/distributed-compliance-ledger/x/model/types" -) - -func TestErrorConstructors(t *testing.T) { - errs := []error{ - types.NewErrVendorProductsDoNotExist(1), - types.NewErrSoftwareVersionStringInvalid("v"), - types.NewErrFirmwareInformationInvalid("fw"), - types.NewErrCDVersionNumberInvalid(1), - types.NewErrOtaURLInvalid("url"), - types.NewErrReleaseNotesURLInvalid("url"), - types.NewErrNoModelVersionsExist(1, 2), - types.NewErrModelVersionAlreadyExists(1, 2, 3), - types.NewErrorOtaURLNotProvidedButOtherOtaFieldsProvided(), - } - for _, err := range errs { - require.Error(t, err) - require.NotEmpty(t, err.Error()) - } -} diff --git a/x/pki/keeper/noc_certificates_by_subject_key_id_extras_test.go b/x/pki/keeper/noc_certificates_by_subject_key_id_extras_test.go deleted file mode 100644 index 7bd9c7139..000000000 --- a/x/pki/keeper/noc_certificates_by_subject_key_id_extras_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package keeper_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" - "github.com/zigbee-alliance/distributed-compliance-ledger/x/pki/types" -) - -func TestAddGetRemoveNocCertificatesBySubjectKeyID(t *testing.T) { - keeper, ctx := keepertest.PkiKeeper(t, nil) - - keeper.AddNocCertificatesBySubjectKeyID(ctx, types.NocCertificates{ - SubjectKeyId: "skid", - Certs: []*types.Certificate{ - {Subject: "s1", SubjectKeyId: "skid"}, - }, - }) - - got, found := keeper.GetNocCertificatesBySubjectKeyID(ctx, "skid") - require.True(t, found) - require.Len(t, got.Certs, 1) - require.Equal(t, "s1", got.Certs[0].Subject) - - keeper.RemoveNocCertificatesBySubjectAndSubjectKeyID(ctx, "s1", "skid") - - _, found = keeper.GetNocCertificatesBySubjectKeyID(ctx, "skid") - require.False(t, found) -} diff --git a/x/pki/keeper/noc_certificates_by_subject_key_id_test.go b/x/pki/keeper/noc_certificates_by_subject_key_id_test.go index 7acd0f343..76095bc9f 100644 --- a/x/pki/keeper/noc_certificates_by_subject_key_id_test.go +++ b/x/pki/keeper/noc_certificates_by_subject_key_id_test.go @@ -62,3 +62,24 @@ func TestNocCertificatesBySubjectKeyIdGetAll(t *testing.T) { nullify.Fill(keeper.GetAllNocCertificatesBySubjectKeyID(ctx)), ) } + +func TestAddGetRemoveNocCertificatesBySubjectKeyID(t *testing.T) { + keeper, ctx := keepertest.PkiKeeper(t, nil) + + keeper.AddNocCertificatesBySubjectKeyID(ctx, types.NocCertificates{ + SubjectKeyId: "skid", + Certs: []*types.Certificate{ + {Subject: "s1", SubjectKeyId: "skid"}, + }, + }) + + got, found := keeper.GetNocCertificatesBySubjectKeyID(ctx, "skid") + require.True(t, found) + require.Len(t, got.Certs, 1) + require.Equal(t, "s1", got.Certs[0].Subject) + + keeper.RemoveNocCertificatesBySubjectAndSubjectKeyID(ctx, "s1", "skid") + + _, found = keeper.GetNocCertificatesBySubjectKeyID(ctx, "skid") + require.False(t, found) +} diff --git a/x/pki/x509/x509_negative_test.go b/x/pki/x509/x509_negative_test.go deleted file mode 100644 index 3d72e8ce8..000000000 --- a/x/pki/x509/x509_negative_test.go +++ /dev/null @@ -1,231 +0,0 @@ -//nolint:testpackage -package x509 - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "testing" - "time" - - "github.com/stretchr/testify/require" - testconstants "github.com/zigbee-alliance/distributed-compliance-ledger/integration_tests/constants" -) - -// decodeStd decodes a PEM fixture and returns the underlying crypto/x509 cert -// so individual parsed fields can be mutated to exercise one profile rule at a -// time (the validators read the parsed struct, not the signature). -func decodeStd(t *testing.T, pemCert string) *x509.Certificate { - t.Helper() - c, err := DecodeX509Certificate(pemCert) - require.NoError(t, err) - - return c.Certificate -} - -func removeExt(cert *x509.Certificate, oid asn1.ObjectIdentifier) { - out := cert.Extensions[:0] - for _, e := range cert.Extensions { - if !e.Id.Equal(oid) { - out = append(out, e) - } - } - cert.Extensions = out -} - -func setExtCriticalFlag(cert *x509.Certificate, oid asn1.ObjectIdentifier, critical bool) { - for i := range cert.Extensions { - if cert.Extensions[i].Id.Equal(oid) { - cert.Extensions[i].Critical = critical - - return - } - } -} - -func TestVerifyCAExtensions_NegativeBranches(t *testing.T) { - t.Run("KeyUsage absent", func(t *testing.T) { - cert := decodeStd(t, testconstants.PAACertWithNumericVid) - removeExt(cert, OIDKeyUsage) - require.Error(t, VerifyCAExtensions(cert)) - }) - t.Run("SubjectKeyId wrong length", func(t *testing.T) { - cert := decodeStd(t, testconstants.PAACertWithNumericVid) - cert.SubjectKeyId = []byte{0x01, 0x02, 0x03} - require.Error(t, VerifyCAExtensions(cert)) - }) - t.Run("non-self-signed AuthorityKeyId wrong length", func(t *testing.T) { - cert := decodeStd(t, testconstants.IntermediateCertPem) // non-self-signed CA - cert.AuthorityKeyId = []byte{0x01, 0x02, 0x03} - require.Error(t, VerifyCAExtensions(cert)) - }) -} - -func TestVerifyEndEntityExtensions_NegativeBranches(t *testing.T) { - base := func(t *testing.T) *x509.Certificate { - t.Helper() - - return decodeStd(t, testconstants.MatterDACShaped) - } - t.Run("BasicConstraints not critical", func(t *testing.T) { - cert := base(t) - setExtCriticalFlag(cert, OIDBasicConstraints, false) - require.Error(t, verifyEndEntityExtensions(cert, "DAC")) - }) - t.Run("KeyUsage absent", func(t *testing.T) { - cert := base(t) - removeExt(cert, OIDKeyUsage) - require.Error(t, verifyEndEntityExtensions(cert, "DAC")) - }) - t.Run("KeyUsage not critical", func(t *testing.T) { - cert := base(t) - setExtCriticalFlag(cert, OIDKeyUsage, false) - require.Error(t, verifyEndEntityExtensions(cert, "DAC")) - }) - t.Run("SubjectKeyId wrong length", func(t *testing.T) { - cert := base(t) - cert.SubjectKeyId = []byte{0x01} - require.Error(t, verifyEndEntityExtensions(cert, "DAC")) - }) - t.Run("AuthorityKeyId wrong length", func(t *testing.T) { - cert := base(t) - cert.AuthorityKeyId = []byte{0x01} - require.Error(t, verifyEndEntityExtensions(cert, "DAC")) - }) -} - -func TestVerifyDAChainNonRoot_CAExtensionsFail(t *testing.T) { - cert := decodeStd(t, testconstants.PAICertWithNumericVid) // cA=TRUE - removeExt(cert, OIDKeyUsage) - require.Error(t, VerifyDAChainNonRoot(cert)) -} - -func TestVerifyPAAPathLen_PresentNotOne(t *testing.T) { - cert := decodeStd(t, testconstants.PAACertWithNumericVid) - cert.MaxPathLen = 2 - cert.MaxPathLenZero = false - require.Error(t, VerifyPAAPathLen(cert)) -} - -func TestVerifyECDSAP256SHA256_WrongCurve(t *testing.T) { - cert := decodeStd(t, testconstants.PAACertWithNumericVid) - pub, ok := cert.PublicKey.(*ecdsa.PublicKey) - require.True(t, ok) - pub.Curve = elliptic.P224() - cert.PublicKey = pub - require.Error(t, VerifyECDSAP256SHA256(cert)) -} - -func TestVerifyVidPidConsistency_Branches(t *testing.T) { - // parent VID malformed - require.Error(t, VerifyVidPidConsistency("vid=0x1", "vid=0xZZ")) - // child VID differs from parent VID - require.Error(t, VerifyVidPidConsistency("vid=0x2", "vid=0x1")) - // parent PID malformed - require.Error(t, VerifyVidPidConsistency("pid=0x1", "pid=0xZZ")) - // child PID differs from parent PID - require.Error(t, VerifyVidPidConsistency("pid=0x2", "pid=0x1")) - // happy path: parent carries neither -> no constraint - require.NoError(t, VerifyVidPidConsistency("vid=0x2,pid=0x5", "CN=parent")) -} - -func TestFormatOID_Branches(t *testing.T) { - const vidOID = "1.3.6.1.4.1.37244.2.1" - - // value already in readable form (no '#') -> returned untouched - out, err := FormatOID(vidOID+"=0x6006", vidOID, "vid") - require.NoError(t, err) - require.Equal(t, vidOID+"=0x6006", out) // no '#': left as-is - - // non-hex DER - _, err = FormatOID(vidOID+"=#ZZ", vidOID, "vid") - require.Error(t, err) - - // DER too short (<2 bytes) - _, err = FormatOID(vidOID+"=#13", vidOID, "vid") - require.Error(t, err) - - // wrong DER tag (0x05 != PrintableString/UTF8String) - _, err = FormatOID(vidOID+"=#0500", vidOID, "vid") - require.Error(t, err) - - // DER length exceeds remaining bytes - _, err = FormatOID(vidOID+"=#1305", vidOID, "vid") - require.Error(t, err) -} - -func TestGetIntValueFromSubject_BadHex(t *testing.T) { - _, err := GetVidFromSubject("vid=0xZZ") - require.Error(t, err) -} - -func TestToSubjectAsText_MalformedVID(t *testing.T) { - _, err := ToSubjectAsText("1.3.6.1.4.1.37244.2.1=#ZZ") - require.Error(t, err) -} - -func TestCertificatePEMsEqual_InvalidPEM(t *testing.T) { - require.False(t, CertificatePEMsEqual("not a pem", testconstants.RootCertPem)) - require.True(t, CertificatePEMsEqual(testconstants.RootCertPem, testconstants.RootCertPem)) -} - -func TestDecodeX509Certificate_BadDER(t *testing.T) { - const badDER = "-----BEGIN CERTIFICATE-----\nAQID\n-----END CERTIFICATE-----\n" - _, err := DecodeX509Certificate(badDER) - require.Error(t, err) -} - -func TestVerifyVVSCSignature_Branches(t *testing.T) { - leaf, err := DecodeX509Certificate(testconstants.LeafCertPem) - require.NoError(t, err) - intermediate, err := DecodeX509Certificate(testconstants.IntermediateCertPem) - require.NoError(t, err) - - // not yet valid - future := leaf.Certificate.NotBefore.Add(-1 * time.Hour) - require.Error(t, leaf.VerifyVVSCSignature(intermediate, future)) - - // expired - expired := leaf.Certificate.NotAfter.Add(1 * time.Hour) - require.Error(t, leaf.VerifyVVSCSignature(intermediate, expired)) - - // signature mismatch: wrong parent (RootCertPem did not sign the leaf) - root, err := DecodeX509Certificate(testconstants.RootCertPem) - require.NoError(t, err) - within := leaf.Certificate.NotBefore.Add(time.Hour) - require.Error(t, leaf.VerifyVVSCSignature(root, within)) -} - -func TestVerifyVVSCExtensions_Wrapper(t *testing.T) { - // Valid DAC-shaped cert satisfies the shared end-entity profile. - require.NoError(t, VerifyVVSCExtensions(decodeStd(t, testconstants.MatterDACShaped))) -} - -func TestVerifyPAIPathLen_Absent(t *testing.T) { - // A PAA has no pathLenConstraint=0, so the PAI rule rejects it. - cert := decodeStd(t, testconstants.PAACertWithNumericVid) - cert.MaxPathLenZero = false - require.Error(t, VerifyPAIPathLen(cert)) -} - -func TestVerifyVidPidConsistency_ChildVidMalformed(t *testing.T) { - // parent carries a valid VID, child VID is malformed - require.Error(t, VerifyVidPidConsistency("vid=0xZZ", "vid=0x1")) - // parent carries a valid PID, child PID is malformed - require.Error(t, VerifyVidPidConsistency("pid=0xZZ", "pid=0x1")) -} - -func TestVerifyVVSCSignature_Success(t *testing.T) { - leaf, err := DecodeX509Certificate(testconstants.LeafCertPem) - require.NoError(t, err) - intermediate, err := DecodeX509Certificate(testconstants.IntermediateCertPem) - require.NoError(t, err) - - within := leaf.Certificate.NotBefore.Add(time.Hour) - require.NoError(t, leaf.VerifyVVSCSignature(intermediate, within)) -} - -// ensure pkix import is used to keep the import meaningful if other refs change. -var _ = pkix.Name{} diff --git a/x/pki/x509/x509_test.go b/x/pki/x509/x509_test.go index 1d067a325..0ab5c613a 100644 --- a/x/pki/x509/x509_test.go +++ b/x/pki/x509/x509_test.go @@ -16,6 +16,8 @@ package x509 import ( + "crypto/ecdsa" + "crypto/elliptic" x509std "crypto/x509" "crypto/x509/pkix" "encoding/asn1" @@ -930,3 +932,212 @@ func Test_VerifyNoPIDInSubject(t *testing.T) { require.ErrorIs(t, err, pkitypes.ErrNotEmptyPid) }) } + +// decodeStd decodes a PEM fixture and returns the underlying crypto/x509 cert +// so individual parsed fields can be mutated to exercise one profile rule at a +// time (the validators read the parsed struct, not the signature). +func decodeStd(t *testing.T, pemCert string) *x509std.Certificate { + t.Helper() + c, err := DecodeX509Certificate(pemCert) + require.NoError(t, err) + + return c.Certificate +} + +func removeExt(cert *x509std.Certificate, oid asn1.ObjectIdentifier) { + out := cert.Extensions[:0] + for _, e := range cert.Extensions { + if !e.Id.Equal(oid) { + out = append(out, e) + } + } + cert.Extensions = out +} + +func setExtCriticalFlag(cert *x509std.Certificate, oid asn1.ObjectIdentifier, critical bool) { + for i := range cert.Extensions { + if cert.Extensions[i].Id.Equal(oid) { + cert.Extensions[i].Critical = critical + + return + } + } +} + +func TestVerifyCAExtensions_NegativeBranches(t *testing.T) { + t.Run("KeyUsage absent", func(t *testing.T) { + cert := decodeStd(t, testconstants.PAACertWithNumericVid) + removeExt(cert, OIDKeyUsage) + require.Error(t, VerifyCAExtensions(cert)) + }) + t.Run("SubjectKeyId wrong length", func(t *testing.T) { + cert := decodeStd(t, testconstants.PAACertWithNumericVid) + cert.SubjectKeyId = []byte{0x01, 0x02, 0x03} + require.Error(t, VerifyCAExtensions(cert)) + }) + t.Run("non-self-signed AuthorityKeyId wrong length", func(t *testing.T) { + cert := decodeStd(t, testconstants.IntermediateCertPem) // non-self-signed CA + cert.AuthorityKeyId = []byte{0x01, 0x02, 0x03} + require.Error(t, VerifyCAExtensions(cert)) + }) +} + +func TestVerifyEndEntityExtensions_NegativeBranches(t *testing.T) { + base := func(t *testing.T) *x509std.Certificate { + t.Helper() + + return decodeStd(t, testconstants.MatterDACShaped) + } + t.Run("BasicConstraints not critical", func(t *testing.T) { + cert := base(t) + setExtCriticalFlag(cert, OIDBasicConstraints, false) + require.Error(t, verifyEndEntityExtensions(cert, "DAC")) + }) + t.Run("KeyUsage absent", func(t *testing.T) { + cert := base(t) + removeExt(cert, OIDKeyUsage) + require.Error(t, verifyEndEntityExtensions(cert, "DAC")) + }) + t.Run("KeyUsage not critical", func(t *testing.T) { + cert := base(t) + setExtCriticalFlag(cert, OIDKeyUsage, false) + require.Error(t, verifyEndEntityExtensions(cert, "DAC")) + }) + t.Run("SubjectKeyId wrong length", func(t *testing.T) { + cert := base(t) + cert.SubjectKeyId = []byte{0x01} + require.Error(t, verifyEndEntityExtensions(cert, "DAC")) + }) + t.Run("AuthorityKeyId wrong length", func(t *testing.T) { + cert := base(t) + cert.AuthorityKeyId = []byte{0x01} + require.Error(t, verifyEndEntityExtensions(cert, "DAC")) + }) +} + +func TestVerifyDAChainNonRoot_CAExtensionsFail(t *testing.T) { + cert := decodeStd(t, testconstants.PAICertWithNumericVid) // cA=TRUE + removeExt(cert, OIDKeyUsage) + require.Error(t, VerifyDAChainNonRoot(cert)) +} + +func TestVerifyPAAPathLen_PresentNotOne(t *testing.T) { + cert := decodeStd(t, testconstants.PAACertWithNumericVid) + cert.MaxPathLen = 2 + cert.MaxPathLenZero = false + require.Error(t, VerifyPAAPathLen(cert)) +} + +func TestVerifyECDSAP256SHA256_WrongCurve(t *testing.T) { + cert := decodeStd(t, testconstants.PAACertWithNumericVid) + pub, ok := cert.PublicKey.(*ecdsa.PublicKey) + require.True(t, ok) + pub.Curve = elliptic.P224() + cert.PublicKey = pub + require.Error(t, VerifyECDSAP256SHA256(cert)) +} + +func TestVerifyVidPidConsistency_Branches(t *testing.T) { + // parent VID malformed + require.Error(t, VerifyVidPidConsistency("vid=0x1", "vid=0xZZ")) + // parent PID malformed + require.Error(t, VerifyVidPidConsistency("pid=0x1", "pid=0xZZ")) + // happy path: parent carries neither -> no constraint + require.NoError(t, VerifyVidPidConsistency("vid=0x2,pid=0x5", "CN=parent")) +} + +func TestFormatOID_Branches(t *testing.T) { + const vidOID = "1.3.6.1.4.1.37244.2.1" + + // value already in readable form (no '#') -> returned untouched + out, err := FormatOID(vidOID+"=0x6006", vidOID, "vid") + require.NoError(t, err) + require.Equal(t, vidOID+"=0x6006", out) // no '#': left as-is + + // non-hex DER + _, err = FormatOID(vidOID+"=#ZZ", vidOID, "vid") + require.Error(t, err) + + // DER too short (<2 bytes) + _, err = FormatOID(vidOID+"=#13", vidOID, "vid") + require.Error(t, err) + + // wrong DER tag (0x05 != PrintableString/UTF8String) + _, err = FormatOID(vidOID+"=#0500", vidOID, "vid") + require.Error(t, err) + + // DER length exceeds remaining bytes + _, err = FormatOID(vidOID+"=#1305", vidOID, "vid") + require.Error(t, err) +} + +func TestGetIntValueFromSubject_BadHex(t *testing.T) { + _, err := GetVidFromSubject("vid=0xZZ") + require.Error(t, err) +} + +func TestToSubjectAsText_MalformedVID(t *testing.T) { + _, err := ToSubjectAsText("1.3.6.1.4.1.37244.2.1=#ZZ") + require.Error(t, err) +} + +func TestCertificatePEMsEqual_InvalidPEM(t *testing.T) { + require.False(t, CertificatePEMsEqual("not a pem", testconstants.RootCertPem)) + require.True(t, CertificatePEMsEqual(testconstants.RootCertPem, testconstants.RootCertPem)) +} + +func TestDecodeX509Certificate_BadDER(t *testing.T) { + const badDER = "-----BEGIN CERTIFICATE-----\nAQID\n-----END CERTIFICATE-----\n" + _, err := DecodeX509Certificate(badDER) + require.Error(t, err) +} + +func TestVerifyVVSCSignature_Branches(t *testing.T) { + leaf, err := DecodeX509Certificate(testconstants.LeafCertPem) + require.NoError(t, err) + intermediate, err := DecodeX509Certificate(testconstants.IntermediateCertPem) + require.NoError(t, err) + + // not yet valid + future := leaf.Certificate.NotBefore.Add(-1 * time.Hour) + require.Error(t, leaf.VerifyVVSCSignature(intermediate, future)) + + // expired + expired := leaf.Certificate.NotAfter.Add(1 * time.Hour) + require.Error(t, leaf.VerifyVVSCSignature(intermediate, expired)) + + // signature mismatch: wrong parent (RootCertPem did not sign the leaf) + root, err := DecodeX509Certificate(testconstants.RootCertPem) + require.NoError(t, err) + within := leaf.Certificate.NotBefore.Add(time.Hour) + require.Error(t, leaf.VerifyVVSCSignature(root, within)) +} + +func TestVerifyVVSCExtensions_Wrapper(t *testing.T) { + // Valid DAC-shaped cert satisfies the shared end-entity profile. + require.NoError(t, VerifyVVSCExtensions(decodeStd(t, testconstants.MatterDACShaped))) +} + +func TestVerifyPAIPathLen_Absent(t *testing.T) { + // A PAA has no pathLenConstraint=0, so the PAI rule rejects it. + cert := decodeStd(t, testconstants.PAACertWithNumericVid) + cert.MaxPathLenZero = false + require.Error(t, VerifyPAIPathLen(cert)) +} + +func TestVerifyVidPidConsistency_ChildVidMalformed(t *testing.T) { + // parent carries a valid VID, child VID is malformed + require.Error(t, VerifyVidPidConsistency("vid=0xZZ", "vid=0x1")) + // parent carries a valid PID, child PID is malformed + require.Error(t, VerifyVidPidConsistency("pid=0xZZ", "pid=0x1")) +} + +func TestVerifyVVSCSignature_Success(t *testing.T) { + leaf, err := DecodeX509Certificate(testconstants.LeafCertPem) + require.NoError(t, err) + intermediate, err := DecodeX509Certificate(testconstants.IntermediateCertPem) + require.NoError(t, err) + + within := leaf.Certificate.NotBefore.Add(time.Hour) + require.NoError(t, leaf.VerifyVVSCSignature(intermediate, within)) +} diff --git a/x/validator/types/message_create_validator_branches_test.go b/x/validator/types/message_create_validator_branches_test.go deleted file mode 100644 index ccf79b6fb..000000000 --- a/x/validator/types/message_create_validator_branches_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package types_test - -import ( - "strings" - "testing" - - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - "github.com/stretchr/testify/require" - "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" - "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" -) - -func TestMsgCreateValidator_ValidateBasicBranches(t *testing.T) { - goodPk, err := codectypes.NewAnyWithValue(ed25519.GenPrivKey().PubKey()) - require.NoError(t, err) - badPk, err := codectypes.NewAnyWithValue(&types.Validator{Owner: "x"}) - require.NoError(t, err) - - signer := sample.ValAddress() - goodDesc := types.NewDescription("moniker", "identity", "website", "details") - - for _, tc := range []struct { - name string - msg types.MsgCreateValidator - }{ - {"invalid signer", types.MsgCreateValidator{Signer: "invalid"}}, - {"nil pubkey", types.MsgCreateValidator{Signer: signer, PubKey: nil, Description: goodDesc}}, - {"non-pubkey any", types.MsgCreateValidator{Signer: signer, PubKey: badPk, Description: goodDesc}}, - {"empty description", types.MsgCreateValidator{Signer: signer, PubKey: goodPk}}, - {"invalid description", types.MsgCreateValidator{ - Signer: signer, - PubKey: goodPk, - Description: types.NewDescription(strings.Repeat("a", types.MaxNameLength+1), "", "", ""), - }}, - } { - t.Run(tc.name, func(t *testing.T) { - msg := tc.msg - require.Error(t, msg.ValidateBasic()) - }) - } -} diff --git a/x/validator/types/message_create_validator_test.go b/x/validator/types/message_create_validator_test.go index cb77dac79..ccf79b6fb 100644 --- a/x/validator/types/message_create_validator_test.go +++ b/x/validator/types/message_create_validator_test.go @@ -1,42 +1,42 @@ -package types +package types_test -/* TODO issue 99 import ( + "strings" "testing" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/stretchr/testify/require" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/sample" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types" ) -func TestMsgCreateValidator_ValidateBasic(t *testing.T) { - tests := []struct { +func TestMsgCreateValidator_ValidateBasicBranches(t *testing.T) { + goodPk, err := codectypes.NewAnyWithValue(ed25519.GenPrivKey().PubKey()) + require.NoError(t, err) + badPk, err := codectypes.NewAnyWithValue(&types.Validator{Owner: "x"}) + require.NoError(t, err) + + signer := sample.ValAddress() + goodDesc := types.NewDescription("moniker", "identity", "website", "details") + + for _, tc := range []struct { name string - msg MsgCreateValidator - err error + msg types.MsgCreateValidator }{ - { - name: "invalid address", - msg: MsgCreateValidator{ - Signer: "invalid_address", - }, - err: sdkerrors.ErrInvalidAddress, - }, { - name: "valid address", - msg: MsgCreateValidator{ - Signer: sample.AccAddress(), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := tt.msg.ValidateBasic() - if tt.err != nil { - require.ErrorIs(t, err, tt.err) - return - } - require.NoError(t, err) + {"invalid signer", types.MsgCreateValidator{Signer: "invalid"}}, + {"nil pubkey", types.MsgCreateValidator{Signer: signer, PubKey: nil, Description: goodDesc}}, + {"non-pubkey any", types.MsgCreateValidator{Signer: signer, PubKey: badPk, Description: goodDesc}}, + {"empty description", types.MsgCreateValidator{Signer: signer, PubKey: goodPk}}, + {"invalid description", types.MsgCreateValidator{ + Signer: signer, + PubKey: goodPk, + Description: types.NewDescription(strings.Repeat("a", types.MaxNameLength+1), "", "", ""), + }}, + } { + t.Run(tc.name, func(t *testing.T) { + msg := tc.msg + require.Error(t, msg.ValidateBasic()) }) } } -*/ From d4a16b3c8631f9211fb1e430df259559911f85cf Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Tue, 30 Jun 2026 16:07:49 +0500 Subject: [PATCH 13/17] Expand test coverage for `MsgProposeUpgrade`, `MsgAssignVid`, `AddX509Cert`, and validator handlers with additional edge and negative scenarios. Signed-off-by: Abdulbois --- x/dclupgrade/handler_test.go | 24 +++++ x/pki/tests/handler_add_pai_cert_test.go | 70 ++++++++++++++ x/pki/tests/handler_assign_vid_test.go | 111 +++++++++++++++++++++++ x/validator/handler_test.go | 106 ++++++++++++++++++++++ 4 files changed, 311 insertions(+) diff --git a/x/dclupgrade/handler_test.go b/x/dclupgrade/handler_test.go index 57607ec38..b65937c81 100644 --- a/x/dclupgrade/handler_test.go +++ b/x/dclupgrade/handler_test.go @@ -98,6 +98,30 @@ func TestHandler_ProposedUpgradeExists(t *testing.T) { require.True(t, types.ErrProposedUpgradeAlreadyExists.Is(err)) } +func TestHandler_ProposeUpgrade_SingleApproverApprovesImmediately(t *testing.T) { + setup := Setup(t) + + trusteeAccAddress := testdata.GenerateAccAddress() + setup.AddAccount(trusteeAccAddress, []dclauthtypes.AccountRole{dclauthtypes.Trustee}) + + // Only one upgrade approver exists, so the required approvals count is 1 and + // the proposal is approved (and the upgrade scheduled) immediately instead of + // being stored as a pending proposed upgrade. + setup.DclauthKeeper.On("CountAccountsWithRole", mock.Anything, dclauthtypes.Trustee).Return(1) + + msgProposeUpgrade := NewMsgProposeUpgrade(trusteeAccAddress) + setup.UpgradeKeeper.On("ScheduleUpgrade", mock.Anything, msgProposeUpgrade.Plan).Return(nil) + + _, err := setup.Handler(setup.Ctx, msgProposeUpgrade) + require.NoError(t, err) + + // the upgrade is approved, not left as a pending proposal + _, found := setup.Keeper.GetApprovedUpgrade(setup.Ctx, msgProposeUpgrade.Plan.Name) + require.True(t, found) + _, found = setup.Keeper.GetProposedUpgrade(setup.Ctx, msgProposeUpgrade.Plan.Name) + require.False(t, found) +} + func TestHandler_ProposeAndRejectUpgrade(t *testing.T) { setup := Setup(t) diff --git a/x/pki/tests/handler_add_pai_cert_test.go b/x/pki/tests/handler_add_pai_cert_test.go index 76c8b4c51..b4f92ef38 100644 --- a/x/pki/tests/handler_add_pai_cert_test.go +++ b/x/pki/tests/handler_add_pai_cert_test.go @@ -392,6 +392,76 @@ func TestHandler_AddDaIntermediateCert_RootIsNoc(t *testing.T) { require.Contains(t, err.Error(), "PAI: BasicConstraints pathLenConstraint SHALL be present and set to 0") } +func TestHandler_AddDaIntermediateCert_ExistingCertUnauthorizedIssuer(t *testing.T) { + setup := utils.Setup(t) + + // seed an existing non-root certificate with the same subject/SKID as the new + // intermediate but a different issuer, so the issuer/authorityKeyID match + // check against existing certificates fails. + existing := types.Certificate{ + Subject: testconstants.IntermediateSubject, + SubjectKeyId: testconstants.IntermediateSubjectKeyID, + Issuer: "CN=different-issuer", + AuthorityKeyId: "AA:BB:CC", + SerialNumber: "99999", + IsRoot: false, + CertificateType: types.CertificateType_DeviceAttestationPKI, + PemCert: testconstants.IntermediateCertPem, + } + utils.AddMokedDaCertificate(setup, existing) + + addX509Cert := types.NewMsgAddX509Cert( + setup.Vendor1.String(), + testconstants.IntermediateCertPem, + testconstants.CertSchemaVersion) + _, err := setup.Handler(setup.Ctx, addX509Cert) + require.ErrorIs(t, err, sdkerrors.ErrUnauthorized) +} + +func TestHandler_AddDaIntermediateCert_ExistingCertIsNoc(t *testing.T) { + setup := utils.Setup(t) + + // seed an existing NOC certificate with the same subject/SKID AND matching + // issuer/authorityKeyID as the new intermediate, so the "existing cert is + // NOC" check fires. + existing := types.Certificate{ + Subject: testconstants.IntermediateSubject, + SubjectKeyId: testconstants.IntermediateSubjectKeyID, + Issuer: testconstants.IntermediateIssuer, + AuthorityKeyId: testconstants.IntermediateAuthorityKeyID, + SerialNumber: "99999", + IsRoot: false, + CertificateType: types.CertificateType_OperationalPKI, + PemCert: testconstants.IntermediateCertPem, + } + utils.AddMokedDaCertificate(setup, existing) + + addX509Cert := types.NewMsgAddX509Cert( + setup.Vendor1.String(), + testconstants.IntermediateCertPem, + testconstants.CertSchemaVersion) + _, err := setup.Handler(setup.Ctx, addX509Cert) + require.ErrorIs(t, err, pkitypes.ErrInappropriateCertificateType) +} + +func TestHandler_AddDaIntermediateCert_RootIsNocTyped(t *testing.T) { + setup := utils.Setup(t) + + // seed a valid DA root (so the chain builds) but mark it as a NOC certificate + // type, so the "root must not be NOC" check fires. + rootCertificate := utils.RootDaCertificate(setup.Trustee1) + rootCertificate.CertificateType = types.CertificateType_OperationalPKI + utils.AddMokedDaCertificate(setup, rootCertificate) + + testIntermediateCertificate := utils.IntermediateDaCertificate(setup.Vendor1) + addX509Cert := types.NewMsgAddX509Cert( + setup.Vendor1.String(), + testIntermediateCertificate.PemCert, + testconstants.CertSchemaVersion) + _, err := setup.Handler(setup.Ctx, addX509Cert) + require.ErrorIs(t, err, pkitypes.ErrInappropriateCertificateType) +} + func TestHandler_AddDaIntermediateCert_RootWithMalformedSubject(t *testing.T) { // The stored root certificate keeps a valid PEM (so the chain still builds), // but its SubjectAsText is tampered so ensureVidMatches rejects it. A real diff --git a/x/pki/tests/handler_assign_vid_test.go b/x/pki/tests/handler_assign_vid_test.go index 841ead23e..1c2de362b 100644 --- a/x/pki/tests/handler_assign_vid_test.go +++ b/x/pki/tests/handler_assign_vid_test.go @@ -172,6 +172,117 @@ func TestHandler_AssignVid_CertificateAlreadyHasVid(t *testing.T) { require.ErrorIs(t, err, pkitypes.ErrNotEmptyVid) } +func TestHandler_AssignVid_ForNocRootCertificate(t *testing.T) { + setup := utils.Setup(t) + + vendorAcc := setup.CreateVendorAdminAccount(0) + + // seed a NOC-typed root certificate directly + cert := utils.RootDaCertificate(setup.Trustee1) + cert.Vid = 0 + cert.CertificateType = types.CertificateType_OperationalPKI + utils.AddMokedDaCertificate(setup, cert) + + assignVid := types.MsgAssignVid{ + Signer: vendorAcc.String(), + Subject: cert.Subject, + SubjectKeyId: cert.SubjectKeyId, + Vid: testconstants.Vid, + } + + _, err := setup.Handler(setup.Ctx, &assignVid) + require.ErrorIs(t, err, pkitypes.ErrInappropriateCertificateType) +} + +func TestHandler_AssignVid_CertificateWithMalformedSubjectVid(t *testing.T) { + setup := utils.Setup(t) + + vendorAcc := setup.CreateVendorAdminAccount(0) + + // seed a root certificate whose SubjectAsText carries a non-hex vid value, so + // parsing the VID out of the subject fails. + cert := utils.RootDaCertificate(setup.Trustee1) + cert.Vid = 0 + cert.SubjectAsText = "vid=0xZZ" + utils.AddMokedDaCertificate(setup, cert) + + assignVid := types.MsgAssignVid{ + Signer: vendorAcc.String(), + Subject: cert.Subject, + SubjectKeyId: cert.SubjectKeyId, + Vid: testconstants.Vid, + } + + _, err := setup.Handler(setup.Ctx, &assignVid) + require.ErrorIs(t, err, pkitypes.ErrInvalidCertificate) +} + +// The following three cases drive the defensive "index missing" branches by +// seeding the certificate into only a subset of the indexes the handler walks. + +func TestHandler_AssignVid_MissingAllCertificatesBySubjectKeyID(t *testing.T) { + setup := utils.Setup(t) + + vendorAcc := setup.CreateVendorAdminAccount(0) + + cert := utils.RootDaCertificate(setup.Trustee1) + cert.Vid = 0 + setup.Keeper.AddAllCertificate(setup.Ctx, cert) // only the global list + + assignVid := types.MsgAssignVid{ + Signer: vendorAcc.String(), + Subject: cert.Subject, + SubjectKeyId: cert.SubjectKeyId, + Vid: testconstants.Vid, + } + + _, err := setup.Handler(setup.Ctx, &assignVid) + require.ErrorIs(t, err, pkitypes.ErrCertificateDoesNotExist) +} + +func TestHandler_AssignVid_MissingApprovedCertificates(t *testing.T) { + setup := utils.Setup(t) + + vendorAcc := setup.CreateVendorAdminAccount(0) + + cert := utils.RootDaCertificate(setup.Trustee1) + cert.Vid = 0 + setup.Keeper.AddAllCertificate(setup.Ctx, cert) + setup.Keeper.AddAllCertificateBySubjectKeyID(setup.Ctx, cert) + + assignVid := types.MsgAssignVid{ + Signer: vendorAcc.String(), + Subject: cert.Subject, + SubjectKeyId: cert.SubjectKeyId, + Vid: testconstants.Vid, + } + + _, err := setup.Handler(setup.Ctx, &assignVid) + require.ErrorIs(t, err, pkitypes.ErrCertificateDoesNotExist) +} + +func TestHandler_AssignVid_MissingApprovedCertificatesBySubjectKeyID(t *testing.T) { + setup := utils.Setup(t) + + vendorAcc := setup.CreateVendorAdminAccount(0) + + cert := utils.RootDaCertificate(setup.Trustee1) + cert.Vid = 0 + setup.Keeper.AddAllCertificate(setup.Ctx, cert) + setup.Keeper.AddAllCertificateBySubjectKeyID(setup.Ctx, cert) + setup.Keeper.AddApprovedCertificate(setup.Ctx, cert) + + assignVid := types.MsgAssignVid{ + Signer: vendorAcc.String(), + Subject: cert.Subject, + SubjectKeyId: cert.SubjectKeyId, + Vid: testconstants.Vid, + } + + _, err := setup.Handler(setup.Ctx, &assignVid) + require.ErrorIs(t, err, pkitypes.ErrCertificateDoesNotExist) +} + func TestHandler_AssignVid_MessageVidAndCertificateVidNotEqual(t *testing.T) { setup := utils.Setup(t) diff --git a/x/validator/handler_test.go b/x/validator/handler_test.go index f67d87706..0b76f3e4f 100644 --- a/x/validator/handler_test.go +++ b/x/validator/handler_test.go @@ -16,10 +16,12 @@ package validator import ( + "strings" "testing" // cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types". + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -173,6 +175,57 @@ func TestHandler_CreateValidator_TwiceForSameValidatorAddress(t *testing.T) { require.ErrorIs(t, err, sdkstakingtypes.ErrValidatorPubKeyExists) } +func TestHandler_CreateValidator_PoolIsFull(t *testing.T) { + setup := Setup(t) + + // fill the active set up to the maximum number of nodes + for i := 0; i < types.MaxNodes; i++ { + _, _, addr := testdata.KeyTestPubAddr() + setup.ValidatorKeeper.SetLastValidatorPower(setup.Ctx, types.NewLastValidatorPower(sdk.ValAddress(addr))) + } + + msgCreateValidator, err := types.NewMsgCreateValidator( + sdk.ValAddress(testconstants.Address1), + testconstants.ValidatorPubKey1, + &types.Description{Moniker: testconstants.ProductName}, + ) + require.NoError(t, err) + + _, err = setup.Handler(setup.Ctx, msgCreateValidator) + require.ErrorIs(t, err, types.PoolIsFull) +} + +func TestHandler_CreateValidator_PubKeyNotCryptoPubKey(t *testing.T) { + setup := Setup(t) + + // an Any whose cached value is a proto message that is not a cryptotypes.PubKey + badPk, err := codectypes.NewAnyWithValue(&types.Validator{Owner: "x"}) + require.NoError(t, err) + + msgCreateValidator := &types.MsgCreateValidator{ + Signer: sdk.ValAddress(testconstants.Address1).String(), + PubKey: badPk, + Description: types.Description{Moniker: testconstants.ProductName}, + } + + _, err = setup.Handler(setup.Ctx, msgCreateValidator) + require.ErrorIs(t, err, sdkerrors.ErrInvalidType) +} + +func TestHandler_CreateValidator_InvalidDescription(t *testing.T) { + setup := Setup(t) + + msgCreateValidator, err := types.NewMsgCreateValidator( + sdk.ValAddress(testconstants.Address1), + testconstants.ValidatorPubKey1, + &types.Description{Moniker: strings.Repeat("a", types.MaxNameLength+1)}, + ) + require.NoError(t, err) + + _, err = setup.Handler(setup.Ctx, msgCreateValidator) + require.Error(t, err) +} + func TestHandler_CreateValidator_TwiceForSameValidatorOwner(t *testing.T) { setup := Setup(t) @@ -675,6 +728,59 @@ func TestHandler_DisabledValidatorAlreadyExistsPropose(t *testing.T) { // require.ErrorIs(t, err, types.ErrDisabledValidatorAlreadytExists) // } +func TestHandler_DisableValidator_ByNotNodeAdmin(t *testing.T) { + setup := Setup(t) + + // ValidatorAddress1 has a validator but no node-admin account, so the role + // check fails. + valAddress, err := sdk.ValAddressFromBech32(testconstants.ValidatorAddress1) + require.NoError(t, err) + + ba := authtypes.NewBaseAccount(sdk.AccAddress(valAddress), testconstants.PubKey2, 0, 0) + account := dclauthtypes.NewAccount(ba, + dclauthtypes.AccountRoles{dclauthtypes.Trustee}, nil, nil, testconstants.VendorID2, testconstants.ProductIDsEmpty) + setup.DclauthKeeper.SetAccount(setup.Ctx, account) + + msgDisableValidator := types.NewMsgDisableValidator(valAddress) + _, err = setup.Handler(setup.Ctx, msgDisableValidator) + require.ErrorIs(t, err, sdkerrors.ErrUnauthorized) +} + +func TestHandler_DisableValidator_ValidatorNotFound(t *testing.T) { + setup := Setup(t) + + // a node admin whose validator does not exist + valAddress := sdk.ValAddress(testconstants.Address2) + ba := authtypes.NewBaseAccount(testconstants.Address2, testconstants.PubKey2, 0, 0) + account := dclauthtypes.NewAccount(ba, + dclauthtypes.AccountRoles{dclauthtypes.NodeAdmin}, nil, nil, testconstants.VendorID2, testconstants.ProductIDsEmpty) + setup.DclauthKeeper.SetAccount(setup.Ctx, account) + + msgDisableValidator := types.NewMsgDisableValidator(valAddress) + _, err := setup.Handler(setup.Ctx, msgDisableValidator) + require.ErrorIs(t, err, sdkstakingtypes.ErrNoValidatorFound) +} + +func TestHandler_DisableValidator_AlreadyDisabled(t *testing.T) { + setup := Setup(t) + + valAddress, err := sdk.ValAddressFromBech32(testconstants.ValidatorAddress1) + require.NoError(t, err) + + ba := authtypes.NewBaseAccount(sdk.AccAddress(valAddress), testconstants.PubKey2, 0, 0) + account := dclauthtypes.NewAccount(ba, + dclauthtypes.AccountRoles{dclauthtypes.NodeAdmin}, nil, nil, testconstants.VendorID2, testconstants.ProductIDsEmpty) + setup.DclauthKeeper.SetAccount(setup.Ctx, account) + + msgDisableValidator := types.NewMsgDisableValidator(valAddress) + _, err = setup.Handler(setup.Ctx, msgDisableValidator) + require.NoError(t, err) + + // disabling again must fail because the disabled validator already exists + _, err = setup.Handler(setup.Ctx, msgDisableValidator) + require.ErrorIs(t, err, types.ErrDisabledValidatorAlreadytExists) +} + func TestHandler_OwnerNodeAdminCanDisabledValidator(t *testing.T) { setup := Setup(t) From fe60b10bc7feaf805eff5affc34cbd71f712f30f Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Tue, 30 Jun 2026 18:52:05 +0500 Subject: [PATCH 14/17] Expand test coverage for `VerifyVVSCCertificate` with edge and negative scenarios involving invalid parent certificates. Signed-off-by: Abdulbois --- x/pki/keeper/all_certificates_test.go | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/x/pki/keeper/all_certificates_test.go b/x/pki/keeper/all_certificates_test.go index 3aefb1207..aa2fe8953 100644 --- a/x/pki/keeper/all_certificates_test.go +++ b/x/pki/keeper/all_certificates_test.go @@ -6,9 +6,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + testconstants "github.com/zigbee-alliance/distributed-compliance-ledger/integration_tests/constants" keepertest "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/keeper" "github.com/zigbee-alliance/distributed-compliance-ledger/testutil/nullify" + pkitypes "github.com/zigbee-alliance/distributed-compliance-ledger/types/pki" "github.com/zigbee-alliance/distributed-compliance-ledger/x/pki/keeper" + "github.com/zigbee-alliance/distributed-compliance-ledger/x/pki/tests/utils" "github.com/zigbee-alliance/distributed-compliance-ledger/x/pki/types" ) @@ -66,3 +69,59 @@ func TestCertificatesGetAll(t *testing.T) { nullify.Fill(keeper.GetAllAllCertificates(ctx)), ) } + +func TestVerifyVVSCCertificate_ParentNotVidSigner(t *testing.T) { + setup := utils.Setup(t) + + // parent exists at the ICA's issuer/AKI but is not a VID-signer cert, so the + // VVSC walker skips it and no chain can be built. + parent := types.Certificate{ + Subject: testconstants.VvscIcaCert1Issuer, + SubjectKeyId: testconstants.VvscIcaCert1AuthorityKeyID, + CertificateType: types.CertificateType_OperationalPKI, + PemCert: testconstants.VvscRootCert1, + } + setup.Keeper.AddAllCertificate(setup.Ctx, parent) + + addMsg := types.NewMsgAddNocX509IcaCert( + setup.Vendor1.String(), testconstants.VvscIcaCert1, testconstants.CertSchemaVersion, true) + _, err := setup.Handler(setup.Ctx, addMsg) + require.ErrorIs(t, err, pkitypes.ErrInvalidCertificate) +} + +func TestVerifyVVSCCertificate_ParentUndecodable(t *testing.T) { + setup := utils.Setup(t) + + // parent is a VID-signer entry but its stored PEM cannot be decoded. + parent := types.Certificate{ + Subject: testconstants.VvscIcaCert1Issuer, + SubjectKeyId: testconstants.VvscIcaCert1AuthorityKeyID, + CertificateType: types.CertificateType_VIDSignerPKI, + PemCert: "not a certificate", + } + setup.Keeper.AddAllCertificate(setup.Ctx, parent) + + addMsg := types.NewMsgAddNocX509IcaCert( + setup.Vendor1.String(), testconstants.VvscIcaCert1, testconstants.CertSchemaVersion, true) + _, err := setup.Handler(setup.Ctx, addMsg) + require.ErrorIs(t, err, pkitypes.ErrInvalidCertificate) +} + +func TestVerifyVVSCCertificate_ParentSignatureMismatch(t *testing.T) { + setup := utils.Setup(t) + + // parent is a valid VID-signer cert but did not sign the ICA, so the VVSC + // signature check fails and no chain can be built. + parent := types.Certificate{ + Subject: testconstants.VvscIcaCert1Issuer, + SubjectKeyId: testconstants.VvscIcaCert1AuthorityKeyID, + CertificateType: types.CertificateType_VIDSignerPKI, + PemCert: testconstants.VvscRootCert2, + } + setup.Keeper.AddAllCertificate(setup.Ctx, parent) + + addMsg := types.NewMsgAddNocX509IcaCert( + setup.Vendor1.String(), testconstants.VvscIcaCert1, testconstants.CertSchemaVersion, true) + _, err := setup.Handler(setup.Ctx, addMsg) + require.ErrorIs(t, err, pkitypes.ErrInvalidCertificate) +} From 4f270c34ed871ac3c70e7c628a45905e29cb9bc1 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Mon, 6 Jul 2026 14:40:27 +0500 Subject: [PATCH 15/17] Update error handling for unauthorized certificate issuers in PKI handlers and tests Signed-off-by: Abdulbois --- types/pki/errors.go | 3 ++- x/pki/tests/handler_add_noc_ica_cert_test.go | 6 +++--- x/pki/tests/handler_add_noc_root_cert_test.go | 2 +- x/pki/tests/handler_add_pai_cert_test.go | 2 +- x/pki/tests/handler_propose_paa_cert_test.go | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/types/pki/errors.go b/types/pki/errors.go index 450928aaf..961333f8c 100644 --- a/types/pki/errors.go +++ b/types/pki/errors.go @@ -55,6 +55,7 @@ var ( ErrCRLSignerCertificateInvalidVersion = errors.Register(ModuleName, 443, "invalid CRLSignerCertificate version") ErrCertVidNotEqualToIssuerVid = errors.Register(ModuleName, 444, "certificate's vid is not equal to issuer's vid") ErrCertPidNotEqualToIssuerPid = errors.Register(ModuleName, 445, "certificate's pid is not equal to issuer's pid") + ErrUnauthorizedCertIssuer = errors.Register(ModuleName, 446, "unauthorized certificate issuer") ) func NewErrUnauthorizedRole(transactionName string, requiredRole types.AccountRole) error { @@ -192,7 +193,7 @@ func NewErrNonRootCertificateSelfSigned() error { } func NewErrUnauthorizedCertIssuer(subject string, subjectKeyID string) error { - return errors.Wrapf(sdkerrors.ErrUnauthorized, + return errors.Wrapf(ErrUnauthorizedCertIssuer, "Issuer and authorityKeyID of new certificate with subject=%v and subjectKeyID=%v "+ "must be the same as ones of existing certificates with the same subject and subjectKeyID", subject, subjectKeyID) diff --git a/x/pki/tests/handler_add_noc_ica_cert_test.go b/x/pki/tests/handler_add_noc_ica_cert_test.go index b7ee35f4c..2ca04ff56 100644 --- a/x/pki/tests/handler_add_noc_ica_cert_test.go +++ b/x/pki/tests/handler_add_noc_ica_cert_test.go @@ -518,7 +518,7 @@ func runIntermediateCertificateExistCases(t *testing.T, fx intermediateCertifica IsRoot: true, Vid: testconstants.Vid, }, - err: sdkerrors.ErrUnauthorized, + err: pkitypes.ErrUnauthorizedCertIssuer, }, { name: "ExistingWithDifferentIssuer", @@ -532,7 +532,7 @@ func runIntermediateCertificateExistCases(t *testing.T, fx intermediateCertifica IsRoot: false, Vid: testconstants.Vid, }, - err: sdkerrors.ErrUnauthorized, + err: pkitypes.ErrUnauthorizedCertIssuer, }, { name: "ExistingWithDifferentAuthorityKeyId", @@ -546,7 +546,7 @@ func runIntermediateCertificateExistCases(t *testing.T, fx intermediateCertifica IsRoot: false, Vid: testconstants.Vid, }, - err: sdkerrors.ErrUnauthorized, + err: pkitypes.ErrUnauthorizedCertIssuer, }, { name: "ExistingNotNocCert", diff --git a/x/pki/tests/handler_add_noc_root_cert_test.go b/x/pki/tests/handler_add_noc_root_cert_test.go index 20798e8e7..b80ffafd2 100644 --- a/x/pki/tests/handler_add_noc_root_cert_test.go +++ b/x/pki/tests/handler_add_noc_root_cert_test.go @@ -416,7 +416,7 @@ func runCertificateExistCases(t *testing.T, base certificateExistCase) { IsRoot: false, Vid: testconstants.Vid, }, - err: sdkerrors.ErrUnauthorized, + err: pkitypes.ErrUnauthorizedCertIssuer, }, { name: "ExistingNotNocCert", diff --git a/x/pki/tests/handler_add_pai_cert_test.go b/x/pki/tests/handler_add_pai_cert_test.go index b4f92ef38..feecc70cc 100644 --- a/x/pki/tests/handler_add_pai_cert_test.go +++ b/x/pki/tests/handler_add_pai_cert_test.go @@ -415,7 +415,7 @@ func TestHandler_AddDaIntermediateCert_ExistingCertUnauthorizedIssuer(t *testing testconstants.IntermediateCertPem, testconstants.CertSchemaVersion) _, err := setup.Handler(setup.Ctx, addX509Cert) - require.ErrorIs(t, err, sdkerrors.ErrUnauthorized) + require.ErrorIs(t, err, pkitypes.ErrUnauthorizedCertIssuer) } func TestHandler_AddDaIntermediateCert_ExistingCertIsNoc(t *testing.T) { diff --git a/x/pki/tests/handler_propose_paa_cert_test.go b/x/pki/tests/handler_propose_paa_cert_test.go index d7d295969..8ddc0d2a0 100644 --- a/x/pki/tests/handler_propose_paa_cert_test.go +++ b/x/pki/tests/handler_propose_paa_cert_test.go @@ -326,7 +326,7 @@ func TestHandler_ProposeAddDaRootCert_ExistingCertIsNotRoot(t *testing.T) { testconstants.Vid, testconstants.CertSchemaVersion) _, err := setup.Handler(setup.Ctx, proposeAddX509RootCert) - require.ErrorIs(t, err, sdkerrors.ErrUnauthorized) + require.ErrorIs(t, err, pkitypes.ErrUnauthorizedCertIssuer) } func TestHandler_ProposeAddDaRootCert_ExistingCertIsNoc(t *testing.T) { From a2405a8d27e8df72ec2985ea3de1aae4f5c0a9b6 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Mon, 6 Jul 2026 14:40:36 +0500 Subject: [PATCH 16/17] Remove redundant test `TestHandler_UpdatePkiRevocationDistributionPoint_NonCRL_DataFields` and improve assertions for `VVSC chain verification failed` and handler responses in PKI, validator, and upgrade tests. Signed-off-by: Abdulbois --- .../types/message_propose_upgrade_test.go | 92 ++++++++++--------- x/pki/keeper/all_certificates_test.go | 3 + x/pki/tests/handler_update_revocation_test.go | 52 ----------- x/validator/keeper/iterate_test.go | 7 +- 4 files changed, 57 insertions(+), 97 deletions(-) diff --git a/x/dclupgrade/types/message_propose_upgrade_test.go b/x/dclupgrade/types/message_propose_upgrade_test.go index ce8e56207..a0ed32646 100644 --- a/x/dclupgrade/types/message_propose_upgrade_test.go +++ b/x/dclupgrade/types/message_propose_upgrade_test.go @@ -199,22 +199,25 @@ func TestMsgProposeUpgrade_ValidateBasicCLI_InvalidCreator(t *testing.T) { } func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { - var expectedResponse string + var mockGitAPIResponse string + + var lastAuthHeader string svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, _ = fmt.Fprint(w, expectedResponse) + lastAuthHeader = r.Header.Get("Authorization") + _, _ = fmt.Fprint(w, mockGitAPIResponse) })) defer svr.Close() negativeTests := []struct { - name string - expected string - msg MsgProposeUpgrade - err error + name string + gitAPIResponse string + msg MsgProposeUpgrade + err error }{ { - name: "invalid binary format 1", - expected: testconstants.UpgradeGitAPIJSONResponse, + name: "invalid binary format 1", + gitAPIResponse: testconstants.UpgradeGitAPIJSONResponse, msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -228,8 +231,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { err: sdkerrors.ErrJSONUnmarshal, }, { - name: "invalid binary format 2", - expected: testconstants.UpgradeGitAPIJSONResponse, + name: "invalid binary format 2", + gitAPIResponse: testconstants.UpgradeGitAPIJSONResponse, msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -243,8 +246,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { err: sdkerrors.ErrJSONUnmarshal, }, { - name: "invalid binary format 3", - expected: testconstants.UpgradeGitAPIJSONResponse, + name: "invalid binary format 3", + gitAPIResponse: testconstants.UpgradeGitAPIJSONResponse, msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -258,8 +261,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { err: sdkerrors.ErrJSONUnmarshal, }, { - name: "lots of binary files", - expected: testconstants.UpgradeGitAPIJSONResponse, + name: "lots of binary files", + gitAPIResponse: testconstants.UpgradeGitAPIJSONResponse, msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -273,8 +276,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { err: sdkerrors.ErrJSONUnmarshal, }, { - name: "unsupported os platform", - expected: testconstants.UpgradeGitAPIJSONResponse, + name: "unsupported os platform", + gitAPIResponse: testconstants.UpgradeGitAPIJSONResponse, msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -288,8 +291,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { err: sdkerrors.ErrJSONUnmarshal, }, { - name: "no binary files", - expected: testconstants.UpgradeGitAPIJSONResponse, + name: "no binary files", + gitAPIResponse: testconstants.UpgradeGitAPIJSONResponse, msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -304,8 +307,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { }, { // valid JSON but the single binary targets an unsupported platform. - name: "unsupported os platform (valid json)", - expected: testconstants.UpgradeGitAPIJSONResponse, + name: "unsupported os platform (valid json)", + gitAPIResponse: testconstants.UpgradeGitAPIJSONResponse, msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -320,8 +323,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { }, { // URL has the required "?checksum=" separator but too few path segments. - name: "binary url too short", - expected: testconstants.UpgradeGitAPIJSONResponse, + name: "binary url too short", + gitAPIResponse: testconstants.UpgradeGitAPIJSONResponse, msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -336,8 +339,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { }, { // git tag parsed from the URL does not contain the plan name. - name: "plan name not in binary url tag", - expected: testconstants.UpgradeGitAPIJSONResponse, + name: "plan name not in binary url tag", + gitAPIResponse: testconstants.UpgradeGitAPIJSONResponse, msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -352,8 +355,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { }, { // server returns a body that is not valid JSON. - name: "git api response not json", - expected: "this is not json", + name: "git api response not json", + gitAPIResponse: "this is not json", msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -368,8 +371,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { }, { // server returns valid JSON without the "assets" field. - name: "git api response without assets", - expected: "{\"message\":\"Not Found\"}", + name: "git api response without assets", + gitAPIResponse: "{\"message\":\"Not Found\"}", msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -384,8 +387,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { }, { // assets present but none match the expected binary. - name: "git api response with no matching asset", - expected: "{\"assets\":[{\"name\": \"other\", \"state\": \"uploaded\", \"digest\": null, \"browser_download_url\":\"https://example.com/other\"}]}", + name: "git api response with no matching asset", + gitAPIResponse: "{\"assets\":[{\"name\": \"other\", \"state\": \"uploaded\", \"digest\": null, \"browser_download_url\":\"https://example.com/other\"}]}", msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -401,14 +404,14 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { } positiveTests := []struct { - name string - expected string - msg MsgProposeUpgrade + name string + gitAPIResponse string + msg MsgProposeUpgrade }{ { // empty Plan.Info short-circuits with no error and no network call. - name: "empty plan info", - expected: testconstants.UpgradeGitAPIJSONResponse, + name: "empty plan info", + gitAPIResponse: testconstants.UpgradeGitAPIJSONResponse, msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -421,8 +424,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { }, }, { - name: "valid binary file without checksum", - expected: "{\"assets\":[{\"name\": \"dcld\", \"state\": \"uploaded\", \"digest\": null, \"browser_download_url\":\"" + testconstants.UpgradeBrowserDownloadURL + "\"}]}", + name: "valid binary file without checksum", + gitAPIResponse: "{\"assets\":[{\"name\": \"dcld\", \"state\": \"uploaded\", \"digest\": null, \"browser_download_url\":\"" + testconstants.UpgradeBrowserDownloadURL + "\"}]}", msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -435,8 +438,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { }, }, { - name: "valid binary file with checksum", - expected: testconstants.UpgradeGitAPIJSONResponse, + name: "valid binary file with checksum", + gitAPIResponse: testconstants.UpgradeGitAPIJSONResponse, msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -449,8 +452,8 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { }, }, { - name: "valid binary file for a dev release", - expected: "{\"assets\":[{\"name\": \"dcld\", \"state\": \"uploaded\", \"digest\": \"sha256:f5c1120790319c9c4aefbfbc08a0bb1f91e848e3cd77cf3590a46d637e70cfad\", \"browser_download_url\":\"" + "https://github.com/zigbee-alliance/distributed-compliance-ledger/releases/download/v1.5.2-0.dev.1/dcld" + "\"}]}", + name: "valid binary file for a dev release", + gitAPIResponse: "{\"assets\":[{\"name\": \"dcld\", \"state\": \"uploaded\", \"digest\": \"sha256:f5c1120790319c9c4aefbfbc08a0bb1f91e848e3cd77cf3590a46d637e70cfad\", \"browser_download_url\":\"" + "https://github.com/zigbee-alliance/distributed-compliance-ledger/releases/download/v1.5.2-0.dev.1/dcld" + "\"}]}", msg: MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -465,7 +468,7 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { } for _, tt := range positiveTests { t.Run(tt.name, func(t *testing.T) { - expectedResponse = tt.expected + mockGitAPIResponse = tt.gitAPIResponse err := ValidateBinaries(&tt.msg, svr.URL) require.NoError(t, err) }) @@ -473,7 +476,7 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { for _, tt := range negativeTests { t.Run(tt.name, func(t *testing.T) { - expectedResponse = tt.expected + mockGitAPIResponse = tt.gitAPIResponse err := ValidateBinaries(&tt.msg, svr.URL) require.Error(t, err) require.ErrorIs(t, err, tt.err) @@ -482,7 +485,7 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { t.Run("git token header is set when GH_TOKEN present", func(t *testing.T) { t.Setenv("GH_TOKEN", "test-token") - expectedResponse = testconstants.UpgradeGitAPIJSONResponse + mockGitAPIResponse = testconstants.UpgradeGitAPIJSONResponse msg := MsgProposeUpgrade{ Creator: sample.AccAddress(), Plan: Plan{ @@ -494,6 +497,7 @@ func TestMsgProposeUpgrade_ValidateBinaries(t *testing.T) { Time: testconstants.Time, } require.NoError(t, ValidateBinaries(&msg, svr.URL)) + require.Equal(t, "token test-token", lastAuthHeader) }) t.Run("git api request fails", func(t *testing.T) { diff --git a/x/pki/keeper/all_certificates_test.go b/x/pki/keeper/all_certificates_test.go index aa2fe8953..21446566a 100644 --- a/x/pki/keeper/all_certificates_test.go +++ b/x/pki/keeper/all_certificates_test.go @@ -87,6 +87,7 @@ func TestVerifyVVSCCertificate_ParentNotVidSigner(t *testing.T) { setup.Vendor1.String(), testconstants.VvscIcaCert1, testconstants.CertSchemaVersion, true) _, err := setup.Handler(setup.Ctx, addMsg) require.ErrorIs(t, err, pkitypes.ErrInvalidCertificate) + require.ErrorContains(t, err, "VVSC chain verification failed") } func TestVerifyVVSCCertificate_ParentUndecodable(t *testing.T) { @@ -105,6 +106,7 @@ func TestVerifyVVSCCertificate_ParentUndecodable(t *testing.T) { setup.Vendor1.String(), testconstants.VvscIcaCert1, testconstants.CertSchemaVersion, true) _, err := setup.Handler(setup.Ctx, addMsg) require.ErrorIs(t, err, pkitypes.ErrInvalidCertificate) + require.ErrorContains(t, err, "VVSC chain verification failed") } func TestVerifyVVSCCertificate_ParentSignatureMismatch(t *testing.T) { @@ -124,4 +126,5 @@ func TestVerifyVVSCCertificate_ParentSignatureMismatch(t *testing.T) { setup.Vendor1.String(), testconstants.VvscIcaCert1, testconstants.CertSchemaVersion, true) _, err := setup.Handler(setup.Ctx, addMsg) require.ErrorIs(t, err, pkitypes.ErrInvalidCertificate) + require.ErrorContains(t, err, "VVSC chain verification failed") } diff --git a/x/pki/tests/handler_update_revocation_test.go b/x/pki/tests/handler_update_revocation_test.go index 9b1ec6c4b..b4cc087f5 100644 --- a/x/pki/tests/handler_update_revocation_test.go +++ b/x/pki/tests/handler_update_revocation_test.go @@ -943,58 +943,6 @@ func TestHandler_UpdatePkiRevocationDistributionPoint_CrlSignerCertificateField( } } -// TestHandler_UpdatePkiRevocationDistributionPoint_NonCRL_DataFields covers the -// data-field update branches (DataURL/DataFileSize/DataDigest/DataDigestType). -// Those fields can only be set when the revocation point is NOT of CRL type -// (otherwise the handler rejects them), but only the CRL type can be added -// through the normal flow. The point is therefore seeded as CRL and then -// switched to a non-CRL type directly in the keeper before the update. -func TestHandler_UpdatePkiRevocationDistributionPoint_NonCRL_DataFields(t *testing.T) { - const nonCRLRevocationType = uint32(2) - - setup := utils.Setup(t) - - vendorAcc := utils.GenerateAccAddress() - addedRevocation := createAddRevocationMessageWithPAACertWithNumericVid(vendorAcc.String()) - setup.AddAccount(vendorAcc, []dclauthtypes.AccountRole{dclauthtypes.Vendor}, addedRevocation.Vid) - - utils.ProposeAndApproveRootCertificateByOptions(setup, setup.Trustee1, utils.CreatePAACertWithNumericVidOptions()) - - _, err := setup.Handler(setup.Ctx, addedRevocation) - require.NoError(t, err) - - // Switch the stored point to a non-CRL type so the update is allowed to carry - // data fields. This bypasses the add-time validation, which only permits CRL. - point, isFound := setup.Keeper.GetPkiRevocationDistributionPoint( - setup.Ctx, addedRevocation.Vid, addedRevocation.Label, addedRevocation.IssuerSubjectKeyID) - require.True(t, isFound) - point.RevocationType = nonCRLRevocationType - setup.Keeper.SetPkiRevocationDistributionPoint(setup.Ctx, point) - setup.Keeper.UpdatePkiRevocationDistributionPointBySubjectKeyID(setup.Ctx, point) - - updateRevocation := &types.MsgUpdatePkiRevocationDistributionPoint{ - Signer: vendorAcc.String(), - Vid: addedRevocation.Vid, - Label: addedRevocation.Label, - IssuerSubjectKeyID: addedRevocation.IssuerSubjectKeyID, - DataURL: addedRevocation.DataURL + "/data", - DataFileSize: uint64(123), - DataDigest: testconstants.DataDigest, - DataDigestType: uint32(1), - SchemaVersion: 0, - } - _, err = setup.Handler(setup.Ctx, updateRevocation) - require.NoError(t, err) - - updatedPoint, isFound := setup.Keeper.GetPkiRevocationDistributionPoint( - setup.Ctx, addedRevocation.Vid, addedRevocation.Label, addedRevocation.IssuerSubjectKeyID) - require.True(t, isFound) - require.Equal(t, updateRevocation.DataURL, updatedPoint.DataURL) - require.Equal(t, updateRevocation.DataFileSize, updatedPoint.DataFileSize) - require.Equal(t, updateRevocation.DataDigest, updatedPoint.DataDigest) - require.Equal(t, updateRevocation.DataDigestType, updatedPoint.DataDigestType) -} - func TestHandler_UpdatePkiRevocationDistributionPoint_StoredCertificateUndecodable(t *testing.T) { setup := utils.Setup(t) diff --git a/x/validator/keeper/iterate_test.go b/x/validator/keeper/iterate_test.go index 397c33229..da3596020 100644 --- a/x/validator/keeper/iterate_test.go +++ b/x/validator/keeper/iterate_test.go @@ -45,8 +45,13 @@ func TestValidatorIterationAndRemove(t *testing.T) { _, found := k.GetValidator(ctx, v1.GetOwner()) require.False(t, found) - // Removing a non-existent validator is a no-op. + // Removing a non-existent validator is a no-op: v1 stays absent and v2 is untouched. k.RemoveValidator(ctx, v1.GetOwner()) + _, found = k.GetValidator(ctx, v1.GetOwner()) + require.False(t, found) + _, found = k.GetValidator(ctx, v2.GetOwner()) + require.True(t, found) + require.Len(t, k.GetAllValidator(ctx), 1) } func TestSetLastValidatorPower_PanicsOnInvalidOwner(t *testing.T) { From f01969f0b08c45a02df29c24dcee73b9bb985461 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Mon, 6 Jul 2026 15:25:25 +0500 Subject: [PATCH 17/17] Introduce `ErrVVSCChainVerificationFailed` for specific error handling and update related tests and messages Signed-off-by: Abdulbois --- types/pki/errors.go | 7 +++++++ x/pki/keeper/all_certificates.go | 4 +--- x/pki/keeper/all_certificates_test.go | 9 +++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/types/pki/errors.go b/types/pki/errors.go index 961333f8c..093bfbce8 100644 --- a/types/pki/errors.go +++ b/types/pki/errors.go @@ -56,6 +56,7 @@ var ( ErrCertVidNotEqualToIssuerVid = errors.Register(ModuleName, 444, "certificate's vid is not equal to issuer's vid") ErrCertPidNotEqualToIssuerPid = errors.Register(ModuleName, 445, "certificate's pid is not equal to issuer's pid") ErrUnauthorizedCertIssuer = errors.Register(ModuleName, 446, "unauthorized certificate issuer") + ErrVVSCChainVerificationFailed = errors.Register(ModuleName, 447, "VVSC chain verification failed") ) func NewErrUnauthorizedRole(transactionName string, requiredRole types.AccountRole) error { @@ -153,6 +154,12 @@ func NewErrInvalidCertificate(e interface{}) error { e) } +func NewErrVVSCChainVerificationFailed(subject string, subjectKeyID string) error { + return errors.Wrapf(ErrVVSCChainVerificationFailed, + "VVSC chain verification failed for subject=%v subjectKeyID=%v", + subject, subjectKeyID) +} + func NewErrInvalidDataDigestType(dataDigestType uint32, allowedDataDigestTypes []uint32) error { return errors.Wrapf(ErrInvalidDataDigestType, "Invalid DataDigestType: %d. Supported types are: %v", dataDigestType, allowedDataDigestTypes) diff --git a/x/pki/keeper/all_certificates.go b/x/pki/keeper/all_certificates.go index 6ddcd6a58..c035d02e7 100644 --- a/x/pki/keeper/all_certificates.go +++ b/x/pki/keeper/all_certificates.go @@ -200,9 +200,7 @@ func (k Keeper) verifyVVSCCertificate(ctx sdk.Context, } } - return nil, pkitypes.NewErrInvalidCertificate( - fmt.Sprintf("VVSC chain verification failed for subject=%v subjectKeyID=%v", - x509Certificate.Subject, x509Certificate.SubjectKeyID)) + return nil, pkitypes.NewErrVVSCChainVerificationFailed(x509Certificate.Subject, x509Certificate.SubjectKeyID) } // Tries to build a valid certificate chain for the given certificate. diff --git a/x/pki/keeper/all_certificates_test.go b/x/pki/keeper/all_certificates_test.go index 21446566a..72dc55bea 100644 --- a/x/pki/keeper/all_certificates_test.go +++ b/x/pki/keeper/all_certificates_test.go @@ -86,8 +86,7 @@ func TestVerifyVVSCCertificate_ParentNotVidSigner(t *testing.T) { addMsg := types.NewMsgAddNocX509IcaCert( setup.Vendor1.String(), testconstants.VvscIcaCert1, testconstants.CertSchemaVersion, true) _, err := setup.Handler(setup.Ctx, addMsg) - require.ErrorIs(t, err, pkitypes.ErrInvalidCertificate) - require.ErrorContains(t, err, "VVSC chain verification failed") + require.ErrorIs(t, err, pkitypes.ErrVVSCChainVerificationFailed) } func TestVerifyVVSCCertificate_ParentUndecodable(t *testing.T) { @@ -105,8 +104,7 @@ func TestVerifyVVSCCertificate_ParentUndecodable(t *testing.T) { addMsg := types.NewMsgAddNocX509IcaCert( setup.Vendor1.String(), testconstants.VvscIcaCert1, testconstants.CertSchemaVersion, true) _, err := setup.Handler(setup.Ctx, addMsg) - require.ErrorIs(t, err, pkitypes.ErrInvalidCertificate) - require.ErrorContains(t, err, "VVSC chain verification failed") + require.ErrorIs(t, err, pkitypes.ErrVVSCChainVerificationFailed) } func TestVerifyVVSCCertificate_ParentSignatureMismatch(t *testing.T) { @@ -125,6 +123,5 @@ func TestVerifyVVSCCertificate_ParentSignatureMismatch(t *testing.T) { addMsg := types.NewMsgAddNocX509IcaCert( setup.Vendor1.String(), testconstants.VvscIcaCert1, testconstants.CertSchemaVersion, true) _, err := setup.Handler(setup.Ctx, addMsg) - require.ErrorIs(t, err, pkitypes.ErrInvalidCertificate) - require.ErrorContains(t, err, "VVSC chain verification failed") + require.ErrorIs(t, err, pkitypes.ErrVVSCChainVerificationFailed) }