The eviction logic is one of the most important parts in our code base: a bug in the eviction logic could allow for attacker triggered network partitioning.

Unfortunately the eviction logic is currently not tested properly.

The unit tests and fuzz tests don't cover the eviction logic at all.

The functional tests cover some parts of the eviction logic but the coverage is spotty/incomplete.

Unfortunately `CConnman::AttemptToEvictConnection` is currently somewhat tricky to test.

To solve this I suggest simplifying `CConnman::AttemptToEvictConnection()` by introducing a new function ...

```
[[nodiscard]] Optional<NodeEvictionCandidate> SelectNodeToEvict(std::vector<NodeEvictionCandidate> vEvictionCandidates)
```

... which would be called from the now much simplified `CConnman::AttemptToEvictConnection`.

`SelectNodeToEvict` would contain all eviction selection logic and could thus be tested trivially.



    std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory);
    /** UNIX epoch time of the last block received from this peer that we had
     * not yet seen (e.g. not already received from another peer), that passed
     * preliminary validity checks and was saved to disk, even if we don't
     * connect the block or it eventually fails connection. Used as an inbound
     * peer eviction criterium in CConnman::AttemptToEvictConnection. */
    std::atomic<int64_t> nLastBlockTime{0};
    /** UNIX epoch time of the last transaction received from this peer that we
     * had not yet seen (e.g. not already received from another peer) and that
     * was accepted into our mempool. Used as an inbound peer eviction criterium
     * in CConnman::AttemptToEvictConnection. */
    std::atomic<int64_t> nLastTXTime{0};
    // 
    // 
    std::atomic<uint64_t> nPingNonceSent{0};
    /*
    std::atomic<std::chrono::microseconds> m_ping_start{0us};
    // 
    std::atomic<int64_t> nPingUsecTime{0};
    // 
    std::atomic<int64_t> nMinPingUsecTime{std::numeric_limits<int64_t>::max()};
    // 
    std::atomic<bool> fPingQueued{false};
    CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn, ConnectionType conn_type_in, bool inbound_onion = false);
    ~CNode();
    CNode(const CNode&) = delete;
    CNode& operator=(const CNode&) = delete;
private:
    const NodeId id;
    const uint64_t nLocalHostNonce;
    const ConnectionType m_conn_type;
    std::atomic<int> m_greatest_common_version{INIT_PROTO_VERSION};
    //
    //
    //
    //
    //
    //
    //
    //
    //
    //
    //
    //
    //
    //
    //
    const ServiceFlags nLocalServices;
    const int nMyStartingHeight;
    NetPermissionFlags m_permissionFlags{ PF_NONE };
    std::list<CNetMessage> vRecvMsg;  // 
    mutable Recursiv