Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

chore: add sanity tests #512

Merged
merged 44 commits into from
May 17, 2023
Merged

chore: add sanity tests #512

merged 44 commits into from
May 17, 2023

Conversation

Ad96el
Copy link
Member

@Ad96el Ad96el commented Apr 26, 2023

fixes KILTProtocol/ticket#312

The sanity tests are executed after each unit test, to validate the pallets state.

One bug could be detected in the staking pallet.

Finding good variable names is difficult: If a sanity test fails, I return DispatchError::Other("Test"). I appreciate suggestions 😆 .

@Ad96el Ad96el marked this pull request as ready for review May 8, 2023 13:45
@Ad96el Ad96el requested review from ntn-x2 and weichweich May 8, 2023 13:56
@@ -87,7 +87,7 @@ use frame_support::{
use kilt_support::traits::StorageDepositCollector;
use parity_scale_codec::Encode;
use sp_runtime::{traits::Hash, DispatchError};
use sp_std::{marker::PhantomData, vec::Vec};
use sp_std::{marker::PhantomData, vec, vec::Vec};
Copy link
Member

Choose a reason for hiding this comment

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

This dependendency is only used in try-runtime, right? In this case it should not be pulled in regardless, and also it could either be used directly with sp_std::vec, or imported inside the function that needs it.

Copy link
Member

@ntn-x2 ntn-x2 left a comment

Choose a reason for hiding this comment

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

A second round of comments! I love the solution that runs integrity tests after every unit test case. Nevertheless, I would still like to see the following:

  • Clearer error messages when tests fail, especially (but not only) when inside loops, since otherwise it becomes much harder to find out when the test failed
  • A separate try_state.rs module for each pallet, with a single pub(crate) try_state() function, which then internally calls several different functions, if needed.

Other than that, this looks like a very very very nice improvement, which will be completed once we have cross-pallet checks in place, especially regarding deposits and locked balances.

ConnectedAccounts::<T>::iter().try_for_each(
|(did_identifier, linked_account_id, _)| -> Result<(), &'static str> {
let connected_did = ConnectedDids::<T>::get(linked_account_id);
ensure!(connected_did.is_some(), "Unknown did");
Copy link
Member

Choose a reason for hiding this comment

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

This can still be simplified by checking against Some(...).

Copy link
Member Author

Choose a reason for hiding this comment

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

Are you sure? I want to check a property of connected_did.

Doing something like:

connected_did == Some(ConnectionRecord {
					did: did_identifier,
					deposit: deposit
				})

looks to me over-engineered.

Copy link
Member Author

Choose a reason for hiding this comment

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

I also do not know the deposit – I needed to copy it from the connected_did variable -> I have to unwrap it...

Copy link
Member

Choose a reason for hiding this comment

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

I see, then probably calling .expect(...) on ConnectedDids::::get()` and then matching the DID is a better option, in my opinion. All cases panic anyway.

// check if for each owner there is a name stored.
Owner::<T>::iter().try_for_each(
|(w3n, ownership): (Web3NameOf<T>, Web3OwnershipOf<T>)| -> Result<(), &'static str> {
ensure!(Names::<T>::contains_key(ownership.owner.clone()), "W3n unknown");
Copy link
Member

Choose a reason for hiding this comment

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

This check seems redundant, given the line below, no?

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, Names and Owner should have a 1to1 relation. It can happen that a name record is pointing to an owner, which does not exist. The check_claiming_preconditions ensures, that the relation is correct, but I think testing it does not hurt.

Copy link
Member Author

Choose a reason for hiding this comment

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

Owner and names should have a 1 to 1 relation. It might happen, that an owner is pointing to a name which does not exist. Iterating over only one of these storage, would not reveal these issues.

Copy link
Member

Choose a reason for hiding this comment

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

No I meant that having contains_key and get is redundant, since you are checking for the same thing. But I see that is gone now, so I'll mark this as resolved 👍

if let Some(authorization_id) = attestation_details.authorization_id {
ensure!(
ExternalAttestations::<T>::get(authorization_id, claim_hash),
"Unknown external attestation"
Copy link
Member

Choose a reason for hiding this comment

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

What about including some more information in the error strings? Something like format!("External attestation entry does not exist for attestation with authorization ID {:?}", authorization_id). This will save us a lot of time in case something does actually break in the future. Probably the same could be applied to most of the error messages.

@Ad96el
Copy link
Member Author

Ad96el commented May 12, 2023

All requested changes should be resolved now.

Copy link
Member

@ntn-x2 ntn-x2 left a comment

Choose a reason for hiding this comment

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

Apart from the offline discussion about better errors, I just have a couple more suggestions, then it looks good for me!

@Ad96el
Copy link
Member Author

Ad96el commented May 15, 2023

@ntn-x2 I am always happy to see your suggestions!

Copy link
Contributor

@weichweich weichweich left a comment

Choose a reason for hiding this comment

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

still some minor requests that need changing. I'm not a fan of convert_error_message. Is this the result of a discussion with @ntn-x2?


// total stake should be the sum of delegators stake + colator stake.
ensure!(
sum_delegations.saturating_add(candidate.stake) == candidate.total,
Copy link
Contributor

Choose a reason for hiding this comment

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

In theory we want to notice a wrap here. While the normal + should panic when it overflows in debug builds, we could use checked_add with an expected(...) to ensure no overflows happen.

Copy link
Member

@ntn-x2 ntn-x2 left a comment

Choose a reason for hiding this comment

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

Few more suggestions 😄 The bigger ones are about 1. the new utility function to return static strings for testing, 2. better error messages, that also include the failed condition, where it makes sense, so that it's easier to see what failed and for which reason. Other minor points include the way of importing the required types for this new feature.


// If you feel like getting in touch with us, you can do so at [email protected]

use scale_info::prelude::{boxed::Box, string::String};
Copy link
Member

Choose a reason for hiding this comment

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

I think taking them from sp_std would be cleaner, as that's supposed to be the replacement of the standard library in Substrate.

Copy link
Member Author

Choose a reason for hiding this comment

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

I import the Box struct from sp_std now. I think sp_std has no String struct…

@@ -20,6 +20,9 @@
pub mod deposit;
pub use deposit::{free_deposit, reserve_deposit};

#[cfg(any(feature = "try-runtime", test))]
pub mod test;
Copy link
Member

Choose a reason for hiding this comment

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

Not sure this is the best nemaing. This is a testing utility, right? Maybe test_utils or test_utilities would be better? We usually use test for actual tests.

Copy link
Member

Choose a reason for hiding this comment

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

Also the function name itself is not very indicative, and a comment would not hurt about its expected usage.

let stake_total = sum_delegations.saturating_add(candidate.stake);
ensure!(
stake_total == candidate.total,
convert_error_message(format!("Total stake of collator {:?} does not match", candidate.id))
Copy link
Member

Choose a reason for hiding this comment

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

Should we add what the expected and returned values are? In general if we are testing a condition, we might want to also want to output, in the case of a failure, what the expected condition was and what was actually returned.

Copy link
Member Author

Choose a reason for hiding this comment

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

I updated all possible error messages

Copy link
Member

@ntn-x2 ntn-x2 left a comment

Choose a reason for hiding this comment

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

Sehr gut!

@Ad96el Ad96el merged commit 18daba5 into develop May 17, 2023
@Ad96el Ad96el deleted the chore/sanity-tests branch May 17, 2023 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants