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

GH-606: Initialize start_block to none to use latest block #374

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

masqrauder
Copy link
Contributor

No description provided.

Copy link
Contributor

@bertllll bertllll left a comment

Choose a reason for hiding this comment

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

There is an important comment at the beginning of the file "migration_9_to_10.rs".

} else {
match start_block.parse::<u64>() {
Ok(_) => Ok(()),
_ => Err(start_block),
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd like to see a longer err message that would contain also either the parse err or an assistive instruction that it needs to be a valid integer.

@@ -126,6 +130,7 @@ mod tests {
fn validate_start_block_works() {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is actually a bad test because if the function returns an err we want to know if it is an appropriate one, or its message meaningful.

I'd like to see two-sides assertion with Ok(()) or Err("The error message").

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this now does what you mean... 2 separate test methods one for Err cases and this one for Ok cases.

Err (e) => panic! ("Cannot retrieve start block from database; payments to you may not be processed: {:?}", e)
Ok(Some(sb)) => sb,
Ok(None) => u64::MAX,
Err(e) => panic!("Cannot retrieve start block from database; payments to you may not be processed: {:?}", e)
};
let max_block_count = match self.persistent_config.max_block_count() {
Ok(Some(mbc)) => mbc,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm having a hard time to accept that we ignore the error. I know it isn't dangerous if we don't have it, on the other hand, at all places I remember we treat such an error from our communication with the database manager, specifically, we always go and panic.

An argument is, though, that there is a card that should make this reaction implicit, ripping the Result structure from all the methods of PersistentConfiguration. Now, advice is worth gold. I don't feel like needing to insist on this.

if max_block_count == u64::MAX {
info!(
self.logger,
"Using 'latest' block number instead of a literal number. {:?}", e
Copy link
Contributor

Choose a reason for hiding this comment

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

You first need to think about what to do with the error. Please don't just throw it away. I think logging would be fair, we do it all the time.

The way from here, I think, goes by dividing this message into two logs (or even three, according to the differences I mentioned about the if statement). One for the error, maybe perceived as WARN and another less sever with this little informative message (or actually two like that).

If you want to keep an info log, which means one that "we want the user to see", it maybe should have more information to give so that they understand the context. Like what bigger operation is going on of which this is a smaller part.

Remember there is a rule that all info logs will eventually display to the user directly in the UI, not just write to the file.

I do think this kind of information is useful for us developers rather than anybody else so I'd choose just a debug log instead (or actually two debug logs). The fine think about this is you wouldn't have to make assertions on them.

@@ -314,25 +315,31 @@ impl BlockchainBridge {
.get_block_number()
{
Ok(eb) => {
if u64::MAX == max_block_count {
if u64::MAX == max_block_count || u64::MAX == start_block_nbr {
Copy link
Contributor

Choose a reason for hiding this comment

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

You use this boolean condition a couple of times again and again. Maybe you could create a variable before here, with a nice name, that would store the resolution of this expression. Then use the variable where you need to.

I think I'd find it easier to understand since I wouldn't have to check if those are different conditions or the same one all the time.

fn assert_none(key: &str, map: &Map<String, Value>) {
assert!(!map.get(key).is_none());
}

Copy link
Contributor

Choose a reason for hiding this comment

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

I have two notes on this.

First, I somewhat cannot believe this assertion stands right. I think it tests something completely different than what the name of this helper function tries to convey.

It only tests that "a certain parameter (and its row) IS present in the db". That is far from what you intended to get.

Let me suggest a solution:
You should simply reach the value from the set by using 'get(key)' but don't stop there, now you got an enum with variants representing possible values that can take part in a JSON. Look at the body of "assert_value" above your function. Do you see 'Value::String()'? That's one of the variants. Another one is 'Value::Null'. Your assertion should only make sure that you get exactly this Value when you ask for your parameter.

assert_eq!(*check_start_block_params, vec![Some(166666)]);
TestLogHandler::new().exists_log_containing(&format!(
"DEBUG: {}: A request from UI received: {:?} from context id: {}",
test_name, msg, context_id
Copy link
Contributor

Choose a reason for hiding this comment

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

We try not to assert on Debug logs 🤷‍♂️

@@ -549,7 +549,8 @@ impl Configurator {
persistent_config.earning_wallet_address(),
"earningWalletAddressOpt",
)?;
let start_block = Self::value_required(persistent_config.start_block(), "startBlock")?;
let start_block_opt =
Self::value_not_required(persistent_config.start_block(), "startBlock")?;
Copy link
Contributor

Choose a reason for hiding this comment

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

You haven't provided any assertion for this where the value was None.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

let block_number = match string_number.parse::<u64>() {
Ok(num) => num,
Err(e) => return Err((NON_PARSABLE_VALUE, format!("start block: {:?}", e))),
let block_number_opt = if "none".eq_ignore_ascii_case(&string_number) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm sorry to be adding in more work for you but I need to be thorough. I'll bet the use of this function (from a library) isn't tested.

Once you use something that can behave variously or produce different outcomes you will need to test it separately.

I recommend to test this small function only, on values like "noNe, none, NONE", always passing the same. Using a PersistentConfigurationMock and assertions on the params of set_start_block(), making sure it is supplied by the 'None' value properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

eq_ignore_ascii_case() looks to me like it's part of the Rust standard library, isn't it? Normally I try not to write tests that only test external library code already having its own tests. Do you agree?

@@ -0,0 +1,71 @@
use crate::database::db_migrations::db_migrator::DatabaseMigration;
use crate::database::db_migrations::migrator_utils::DBMigDeclarator;

Copy link
Contributor

Choose a reason for hiding this comment

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

Ohhhhhhh. This took me a good amount of time before I realised. That what I want to tell you is probably not going to be good news to you.

It will affect a lot of comments that I'd made before this moment.

Thinking of schema of the database, you must admit that with your card there has been no change. Not even a fingernail.

The data types in the columns for the config table and the values of the parameters have ever been declared as an optional TEXT.

Which is exactly what you are using.

Now, let's thing about what older databases could have had in their row for the start_block. Well, it was always a number. (And even if there was a way how it could yield 'Null', it would still be completely in accordance with your design.

That means you don't need any db migration at all. For that reason, you should make sure all the related code will go away. I'm sorry.

Please don't forget delete this whole file.

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.

None yet

2 participants