Mutation Testing: Missing Test Coverage (~184 genuine gaps from 225 mutations) #75
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Mutation Testing: Missing Test Coverage
Ran
cargo-mutantsagainst the server workspace. 225 mutations were not caught by existing tests. After manual review, ~184 are genuine test gaps and ~41 are false positives (infrastructure/display code).True Findings — sorted by priority
🔴 Critical
arbiter-server/src/evm/mod.rs:146— replaceEngine::vet_transactionwithOk(())— Policy engine never invoked; all transactions pass regardless of policyarbiter-server/src/evm/mod.rs:169— delete!invet_transaction(is_empty check) — Inverts logic: compliant txs rejected, violating txs allowedarbiter-server/src/evm/mod.rs:173— replace==with!=in RunKind check — Wrong grant set evaluated for each run kindarbiter-server/src/evm/mod.rs:94(×7 operator mutations) —</>/<=onvalid_from;>/</>=onexpires_at— Policy time window boundaries untested; transactions outside valid window could passarbiter-server/src/evm/mod.rs:94:51— replace||with&&in time window check — Both bounds must fail to reject; weakens time-window policyarbiter-server/src/evm/mod.rs:101(×3 operator mutations) —>/</>=onmax_fee— Fee cap boundary untested; transactions exceeding the cap could passarbiter-server/src/evm/mod.rs:104(×3 operator mutations) —>/</>=onmax_priority_fee— Priority fee boundary untestedarbiter-server/src/evm/mod.rs:105— replace||with&&in fee violation check — Fee policy weakened; only trips when both fees exceeded instead of eitherarbiter-server/src/evm/mod.rs:114— replace-with+in rate-limit window calculation — Rate limit window starts in the future; rate limit bypassedarbiter-server/src/evm/mod.rs:122— replace>=with<in rate limit count check — Rate limit inverted; blocks compliant transactions, allows unlimited onesarbiter-server/src/evm/policies.rs:168— delete match arm(Some(count), Some(window_secs))— Rate-limit settings silently ignored when both fields are presentarbiter-server/src/crypto/encryption/v1.rs:47— replacegenerate_saltwithDefault::default()— All-zero salt; identical passwords produce identical keysarbiter-server/src/crypto/integrity/v1.rs:72— replacepush_len_prefixedwith()— Length prefix omitted from MAC input; authentication codes become collision-pronearbiter-server/src/crypto/integrity/v1.rs:96(×3) — replaceinto_idwithvec![]/vec![0]/vec![1]— Wrong entity ID bytes in MAC input; all integrity checks fail or collidearbiter-server/src/crypto/integrity/v1.rs:123— delete match armSendError::HandlerError— Keyholder signing errors masked; transaction failures silently swallowedarbiter-server/src/evm/safe_signer.rs:46(×2) — replacegeneratewith trivial key bytes — Deterministic/trivial private key; all generated wallets share a known weak keyarbiter-server/src/evm/safe_signer.rs:95— delete!insign_tx_inner— Chain ID mismatch no longer detected; cross-chain replay attacks possiblearbiter-server/src/evm/safe_signer.rs:139(×2) — replacechain_id_syncwithNone/Some(Default)— Wrong chain ID returned; EIP-155 replay protection disabled or applied to wrong chainarbiter-server/src/evm/safe_signer.rs:146— replaceTxSigner::addresswithDefault::default()— Zero address returned; signer identity mismatcharbiter-server/src/evm/safe_signer.rs:159— replaceTxSignerSync::addresswithDefault::default()— Same as above, sync variantarbiter-server/src/actors/bootstrap.rs:13(×2) — replacegenerate_tokenwith empty / hardcoded string — Predictable/empty bootstrap token; bootstrap authentication bypassarbiter-proto/src/lib.rs:89(×3) — replaceformat_challengewith wrong bytes — Wrong challenge bytes; signature verification accepts garbage auth responsesarbiter-server/src/actors/user_agent/auth/state.rs:51— replaceget_current_nonce_and_idwithOk((1, 1))— Hardcoded nonce/ID instead of current DB value; replay attacks possiblearbiter-server/src/actors/user_agent/auth/state.rs:114(×3) — replacecreate_noncewith constant — Constant nonce enables replay attacksarbiter-server/src/actors/user_agent/auth/state.rs:124(×2) — replace+with-/*increate_nonce— Nonce arithmetic broken; monotonicity destroyedarbiter-server/src/actors/client/auth.rs:100(×2) — replace+with-/*in nonce calculation — Client nonce counter broken; replay protection disabledarbiter-server/src/actors/client/auth.rs:145(×3) — replaceinsert_clientwith hardcoded ID — Client ID not from DB; collisions and wrong identityarbiter-server/src/actors/client/auth.rs:213— replace&&with||insync_client_metadata— Metadata update condition invertedarbiter-server/src/actors/keyholder/mod.rs:324— replaceget_statewithDefault::default()— Always returnsUnbootstrapped; all state-dependent authorization decisions are wrongarbiter-server/src/actors/client/session.rs:33— replacehandle_query_vault_statewithOk(Default)— Wrong vault state returned to clientarbiter-server/src/actors/user_agent/auth.rs:92— delete match armErr(AuthError::GuardFailed)— Auth guard failures silenced; access control bypassarbiter-server/src/actors/user_agent/auth.rs:96— delete match armErr(AuthError::InvalidEvent)— Invalid protocol events ignored; state machine corruption silentarbiter-server/src/actors/user_agent/auth.rs:100— delete match armErr(AuthError::TransitionsFailed)— State transition failures ignored; auth state becomes inconsistentarbiter-server/src/actors/flow_coordinator/mod.rs:78— replaceregister_user_agentwith()— User agent not stored; approval flow lookups failarbiter-server/src/actors/flow_coordinator/mod.rs:89— replaceregister_clientwith()— Client not stored; connection cannot be resolved during approvalarbiter-server/src/actors/flow_coordinator/client_connect_approval.rs:31— replacesend_replywith()— Approval reply never sent; waiting client blocks indefinitelyarbiter-server/src/actors/flow_coordinator/client_connect_approval.rs:76— replace==with!=inon_link_died— Wrong approval cancelled on disconnect; legitimate clients rejectedarbiter-server/src/actors/flow_coordinator/client_connect_approval.rs:89— delete!inclient_approval_answer— Denial treated as approval; unauthorized clients gain accessarbiter-server/src/actors/flow_coordinator/client_connect_approval.rs:96(×2) — replace+=with-=/*=— Approval counter corrupted; "all approved" threshold never reachedarbiter-server/src/actors/flow_coordinator/client_connect_approval.rs:99— replace==with!=in completion check — Completion condition inverted; approval flow never closesarbiter-server/src/actors/user_agent/session.rs:106— replacebegin_new_client_approvalwith()— Approval flow never triggered; connected clients silently ignoredarbiter-server/src/actors/user_agent/session.rs:161— replace==with!=inon_link_died— Wrong pending approval cancelled on disconnectarbiter-client/src/auth.rs:59— replacesend_auth_challenge_requestwithOk(())— Challenge never sentarbiter-client/src/auth.rs:80— replacereceive_auth_challengewithOk(Default)— Zero-value challenge returned; response signed over wrong dataarbiter-client/src/auth.rs:87— delete match armClientResponsePayload::Authinreceive_auth_challenge— Challenge never unpackedarbiter-client/src/auth.rs:101— replacesend_auth_challenge_solutionwithOk(())— Solution never transmittedarbiter-client/src/auth.rs:120— replacereceive_auth_confirmationwithOk(())— Confirmation never awaited; auth silently "succeeds"arbiter-client/src/auth.rs:127— delete match armClientResponsePayload::Authinreceive_auth_confirmation— Confirmation branch deletedarbiter-client/src/auth.rs:129(×3) — replace success guard withtrue/false/!=— Auth failure silently treated as success; authentication bypassarbiter-client/src/auth.rs:133— delete match armSome(AuthResponsePayload::Result(result))— Auth failure silently ignoredarbiter-client/src/auth.rs:145— replaceauthenticatewithOk(())— Entire auth handshake skippedarbiter-client/src/wallets/evm.rs:80— replacevalidate_chain_idwithOk(())— Chain ID validation skipped; transactions signed for wrong chainarbiter-client/src/wallets/evm.rs:81(×2) — replace&&with||/ delete!invalidate_chain_id— Mismatch accepted / match rejectedarbiter-client/src/wallets/evm.rs:96— replacesign_hashwithOk(Default)— Default zero-signature returnedarbiter-client/src/wallets/evm.rs:124— replacesign_transactionwithOk(Default)— Default zero-signature returned for full transaction signingarbiter-client/src/wallets/evm.rs:150— replace!=with==insign_transaction— Mismatched request/response IDs acceptedarbiter-server/src/actors/user_agent/session/connection.rs:283— replacehandle_query_vault_statewithOk(Default)— Always returnsUnbootstrappedto user agentarbiter-server/src/grpc/user_agent/vault.rs:85— replacehandle_unseal_startwithOk(None)— Server ephemeral key never sent; unseal handshake cannot proceedarbiter-server/src/grpc/user_agent/vault.rs:108— replacehandle_unseal_encrypted_keywithOk(None)— Unseal result never communicatedarbiter-server/src/grpc/user_agent/vault.rs:142— replacehandle_bootstrap_encrypted_keywithOk(None)— Bootstrap result dropped; client never learns if bootstrap succeededarbiter-server/src/grpc/user_agent/vault.rs:168— replacehandle_query_vault_statewithOk(None)— Vault state response never returned to user agent🟠 High
arbiter-server/src/grpc/request_tracker.rs:10(×3) — replacerequestwithOk(0/1/-1)— Request ID tracking broken; all requests get same IDarbiter-server/src/grpc/request_tracker.rs:10(×3) — replace<with==/>/<=— Out-of-order and duplicate IDs not detectedarbiter-server/src/grpc/request_tracker.rs:24(×3) — replacecurrent_request_idwith constant — Response ID offset broken; wrong responses matchedarbiter-server/src/grpc/request_tracker.rs:24(×2) — replace-with+//— Off-by-one in response IDarbiter-server/src/actors/evm/mod.rs:91(×3) — replaceEvmActor::generatewith hardcoded tuple — Wallet ID not from DB; causes collisionsarbiter-server/src/actors/evm/mod.rs:117(×4) — replaceEvmActor::list_walletswith empty/stub — Wallet listing bypassedarbiter-server/src/actors/evm/mod.rs:139(×3) — replaceuseragent_create_grantwith hardcoded ID — Grant ID not from DB; all grants collidearbiter-server/src/actors/evm/mod.rs:186— replaceuseragent_list_grantswithOk(vec![])— All active grants invisiblearbiter-server/src/evm/mod.rs:213(×3) — replaceEngine::create_grantwith hardcoded ID — Grant ID not from DBarbiter-server/src/evm/mod.rs:277— replaceEngine::list_one_kindwithOk(empty)— Grants not fetched; policy engine sees no grantsarbiter-server/src/evm/mod.rs:294— replaceEngine::list_all_grantswithOk(vec![])— All grants hiddenarbiter-server/src/actors/user_agent/session/connection.rs:301(×3) — replacehandle_evm_wallet_createwith hardcoded tuple — Wallet ID not from DBarbiter-server/src/actors/user_agent/session/connection.rs:315(×4) — replacehandle_evm_wallet_listwith stubs — Wallet list bypassedarbiter-server/src/actors/user_agent/session/connection.rs:329— replacehandle_grant_listwithOk(vec![])— Grant list bypassedarbiter-server/src/actors/user_agent/session/connection.rs:344(×3) — replacehandle_grant_createwith hardcoded ID — Grant ID not from DBarbiter-server/src/actors/user_agent/session/connection.rs:364— replacehandle_grant_deletewithOk(())— Grant never deleted; access remains activearbiter-server/src/actors/user_agent/session/connection.rs:413— replacehandle_grant_evm_wallet_accesswithOk(())— Access grant DB insert skippedarbiter-server/src/actors/user_agent/session/connection.rs:438— replacehandle_revoke_evm_wallet_accesswithOk(())— Access revocation DB delete skippedarbiter-server/src/actors/user_agent/session/connection.rs:460— replacehandle_list_wallet_accesswithOk(vec![])— All access entries hiddenarbiter-server/src/actors/user_agent/session/connection.rs:479— replacehandle_new_client_approvewithOk(())— Approval controller never notifiedarbiter-server/src/actors/user_agent/session/connection.rs:508— replacehandle_sdk_client_listwithOk(vec![])— All connected clients invisiblearbiter-server/src/grpc/user_agent/evm.rs:67— replacehandle_wallet_createwithOk(None)— Response never sent; client receives no wallet IDarbiter-server/src/grpc/user_agent/evm.rs:87— replacehandle_wallet_listwithOk(None)— Wallet list response droppedarbiter-server/src/grpc/user_agent/evm.rs:112— replacehandle_grant_listwithOk(None)— Grant list response droppedarbiter-server/src/grpc/user_agent/evm.rs:140— replacehandle_grant_createwithOk(None)— Grant ID response droppedarbiter-server/src/grpc/user_agent/evm.rs:170— replacehandle_grant_deletewithOk(None)— Delete result droppedarbiter-server/src/grpc/user_agent/evm.rs:196— replacehandle_sign_transactionwithOk(None)— Signature bytes never returned to user agentarbiter-server/src/grpc/user_agent/sdk_client.rs:114— replacehandle_listwithOk(None)— Client list response droppedarbiter-server/src/grpc/user_agent/sdk_client.rs:183— replacehandle_list_wallet_accesswithOk(None)— Access list response droppedarbiter-server/src/grpc/user_agent/inbound.rs:26— replaceaddress_from_byteswithOk(Default)— Wrong-length input accepted as zero addressarbiter-server/src/grpc/user_agent/inbound.rs:26— replace!=with==in length check — Length check inverted; valid addresses rejected, invalid acceptedarbiter-server/src/grpc/user_agent/inbound.rs:33— replaceu256_from_proto_byteswithOk(Default)— All amounts silently zeroedarbiter-server/src/grpc/user_agent/inbound.rs:33(×3) — replace>with==/</>=in length check — U256 byte-length boundary uncheckedarbiter-server/src/grpc/common/inbound.rs:12— replaceRawEvmAddress::try_convertwithOk(Default)— Malformed addresses acceptedarbiter-server/src/grpc/common/inbound.rs:31— replaceRawEvmTransaction::try_convertwithOk(Default)— Malformed transactions accepted; empty tx bypasses all field validation🟡 Medium
arbiter-server/src/grpc/user_agent/outbound.rs:23— replaceDateTime::convertwithDefault— Timestamp serialization untested; zero epoch breaks time-based grant displayarbiter-server/src/grpc/user_agent/outbound.rs:34— replaceTransactionRateLimit::convertwithDefault— Rate limit silently zeroed in responsearbiter-server/src/grpc/user_agent/outbound.rs:45— replaceVolumeRateLimit::convertwithDefault— Volume limit zeroed in responsearbiter-server/src/grpc/user_agent/outbound.rs:56— replaceSharedGrantSettings::convertwithDefault— All grant settings lost in serializationarbiter-server/src/grpc/user_agent/outbound.rs:76— replaceSpecificGrant::convertwithDefault— Grant type (ether vs token) lostarbiter-server/src/grpc/user_agent/outbound.rs:103— replaceEvmWalletAccess::convertwithDefault— Wallet access data lost in serializationarbiter-server/src/grpc/common/outbound.rs:26(×3) — replaceu256_to_proto_byteswith wrong bytes — U256 amounts lost in all proto responsesarbiter-server/src/grpc/common/outbound.rs:33— replaceSpecificMeaning::convertwithDefault— Transaction meaning (ether/token) lostarbiter-server/src/grpc/common/outbound.rs:63— replaceEvalViolation::convertwithDefault— Policy violation details lost; client cannot show which constraint was violatedarbiter-client/src/wallets/evm.rs:45— replaceTryFrom<&Error>withOk(Default)— Wrong error types accepted asArbiterEvmSignTransactionErrorarbiter-client/src/wallets/evm.rs:46— replace&&with||in error downcast — Both conditions needed;||accepts wrong typesarbiter-client/src/wallets/evm.rs:71— replaceaddresswithDefault::default()— Wallet returns zero addressarbiter-client/src/wallets/evm.rs:75— replacewith_chain_idwithDefault::default()— Chain ID assignment lostarbiter-client/src/wallets/evm.rs:102— replaceSigner::addresswithDefault— Signer trait impl returns zero addressarbiter-client/src/wallets/evm.rs:106(×2) — replacechain_idwithNone/Some(Default)— Wrong chain ID from Signer traitarbiter-client/src/wallets/evm.rs:110— replaceset_chain_idwith()— Chain ID not stored; reads return stale valuearbiter-client/src/wallets/evm.rs:117— replaceTxSigner::addresswithDefault— TxSigner trait impl returns zero addressarbiter-client/src/transport.rs:9(×3) — replacenext_request_idwith constant — Request IDs not incremented; all requests collidearbiter-client/src/transport.rs:31— replaceClientTransport::sendwithOk(())— Messages silently droppedarbiter-client/src/transport.rs:38— replaceClientTransport::recvwithOk(Default)— Default response returned instead of real server dataarbiter-client/src/storage.rs:70(×3) — replaceAlreadyExistsguard withtrue/false/!=— Concurrent file-creation race not handled correctlyarbiter-server/src/db/models.rs:41— replaceSqliteTimestamp::fromwithDefault— All timestamps become Unix epoch zeroarbiter-proto/src/transport/grpc.rs:18— replaceGrpcSender::sendwithOk(())— Send errors silenced; channel-closed failures hiddenarbiter-proto/src/transport/grpc.rs:34— replaceGrpcReceiver::recvwithNone— Stream immediately closedarbiter-proto/src/transport/grpc.rs:66— replaceGrpcBi::sendwithOk(())— Same asGrpcSenderarbiter-proto/src/transport/grpc.rs:77— replaceGrpcBi::recvwithNone— Same asGrpcReceiver🟢 Low
arbiter-server/src/utils.rs:7— replaceDeferClosure::dropwith()— Deferred closure never runs; resource cleanup silently skippedFalse Positives
These mutations were not caught by tests but represent no real test gap.
arbiter-client/src/bin/test_connect.rs:8— replacemainwith()— Test binary entry point; infrastructurearbiter-client/src/client.rs:92(×2) — replaceevm_walletswith stubs — Function body istodo!(); unimplemented feature stubarbiter-proto/src/lib.rs:76— replacehome_pathwithOk(Default)— Path construction; OS infrastructure, no business logicarbiter-proto/src/transport.rs:151— replaceDummyTransport::recvwithNone—DummyTransportis test infrastructure itself; documented no-oparbiter-server/src/db/mod.rs:52— replacedatabase_pathwithOk(Default)— OS path construction; infrastructurearbiter-server/src/db/mod.rs:62— replacedb_configwithOk(())— SQLite PRAGMA setup; configuration side-effect, no unit-testable logicarbiter-server/src/safe_cell.rs:58— replaceMemSafeCell::fmt(Debug) withOk(Default)—Debugimpl; display output onlyarbiter-server/src/context/tls.rs:56— replaceencode_cert_to_pemwithDefault— Thin wrapper aroundpemlibrary; no independent logicarbiter-server/src/context/tls.rs:245— replaceTlsManager::cert_pemwithDefault— Simple getter; display/accessorarbiter-server/src/context/tls.rs:248(×2) — replaceTlsManager::key_pemwith""/"xyzzy"— Simple getter; accessor/displayarbiter-server/src/actors/user_agent/mod.rs:25— replaceEcdsaVisitor::expectingwithOk(Default)— Serdeexpecting(); human-readable error hint onlyarbiter-server/src/evm/safe_signer.rs:30— replaceSafeSigner::fmt(Debug) withOk(Default)—Debugimpl; display onlyarbiter-server/src/evm/policies/ether_transfer/mod.rs:45— replaceDisplay::fmtwithOk(Default)—Displayimpl; output onlyarbiter-server/src/evm/policies/token_transfers/mod.rs:53— replaceDisplay::fmtwithOk(Default)—Displayimpl; output onlyarbiter-server/src/actors/evm/mod.rs:181— replaceuseragent_delete_grantwithOk(())— Function body istodo!(); unimplementedarbiter-server/src/actors/flow_coordinator/mod.rs:32— replaceFlowCoordinator::on_startwithOk(Default)—Default(empty HashMaps) is the correct initial statearbiter-server/src/actors/user_agent/session/connection.rs:222— replacehandle_bootstrap_encrypted_keywithOk(())— Substantial handler but exercised through higher-level integration pathsarbiter-server/src/grpc/client.rs:28— replacedispatch_loopwith()— Long-running async stream loop; not unit-testable in isolationarbiter-server/src/grpc/client.rs:94— replacestartwith()— Orchestration entry point; tested via integrationarbiter-server/src/grpc/user_agent.rs:33— replaceOutOfBandAdapter::sendwithOk(())— Pure delegation to inner transport; no added logicarbiter-server/src/grpc/user_agent.rs:46— replacedispatch_loopwith()— Long-running async looparbiter-server/src/grpc/user_agent.rs:110— replacedispatch_innerwithOk(None)— Dispatcher delegates to sub-handlers tested independentlyarbiter-server/src/grpc/user_agent.rs:125— replacestartwith()— Orchestration entry pointarbiter-server/src/grpc/client/auth.rs:81— replacesend_client_responsewithOk(())— Delegation wrapper; no validation logicarbiter-server/src/grpc/client/auth.rs:92— replacesend_auth_resultwithOk(())— Thin wrapper callingsend_client_responsearbiter-server/src/grpc/client/auth.rs:103— replaceSender::sendwithOk(())— Transport delegationarbiter-server/src/grpc/client/auth.rs:115— replaceReceiver::recvwithNone— Transport delegationarbiter-server/src/grpc/client/auth.rs:203(×3) — replacestartwithOk(0/1/-1)— Orchestration; protocol correctness tested at lower layerarbiter-server/src/grpc/user_agent/auth.rs:46— replacesend_user_agent_responsewithOk(())— Delegation wrapperarbiter-server/src/grpc/user_agent/auth.rs:63— replaceSender::sendwithOk(())— Transport delegationarbiter-server/src/grpc/user_agent/auth.rs:98— replaceReceiver::recvwithNone— Transport delegationarbiter-server/src/grpc/user_agent/evm.rs:50— replacedispatchwithOk(None)— Dispatcher; delegates to sub-handlersarbiter-server/src/grpc/user_agent/sdk_client.rs:64— replacedispatchwithOk(None)— Dispatcherarbiter-server/src/grpc/user_agent/sdk_client.rs:92— replacehandle_connection_responsewithOk(None)—Noneis the correct return; no outbound response neededarbiter-server/src/grpc/user_agent/sdk_client.rs:146— replacehandle_grant_wallet_accesswithOk(None)—Noneis correct; no response neededarbiter-server/src/grpc/user_agent/sdk_client.rs:163— replacehandle_revoke_wallet_accesswithOk(None)—Noneis correct; no response neededarbiter-server/src/grpc/user_agent/vault.rs:56— replacedispatchwithOk(None)— Dispatcherarbiter-server/src/grpc/user_agent/vault.rs:71— replacedispatch_unseal_requestwithOk(None)— Dispatcherarbiter-server/src/grpc/user_agent/vault.rs:132— replacehandle_bootstrap_requestwithOk(None)— Thin wrapper delegating tohandle_bootstrap_encrypted_key