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

tests: serialization-deserialization tests #1232

Open
wants to merge 4 commits into
base: branch-hackathon
Choose a base branch
from

Conversation

k0machi
Copy link

@k0machi k0machi commented Feb 12, 2025

This commit refactors and adds additional tests related to serialization
inside integration tests. It adds coverage for most scalar types,
additional cases for more complex types such as Uuid and Text, Null
checking inside queries and also splits certain test cases into multiple
tests to separate expected failures from expected successes.

Pre-review checklist

  • I have split my patch into logically separate commits.
  • All commit messages clearly explain what they change and why.
  • I added relevant tests for new features and bug fixes.
  • All commits compile, pass static checks and pass test.
  • PR description sums up the changes and reasons why they should be introduced.
  • I have provided docstrings for the public items that I want to introduce.
  • I have adjusted the documentation in ./docs/source/.
  • I added appropriate Fixes: annotations to PR description.

@k0machi k0machi force-pushed the serialization-tests branch 2 times, most recently from 2c3e6d6 to 22071b4 Compare February 12, 2025 12:37
Copy link

github-actions bot commented Feb 12, 2025

cargo semver-checks found no API-breaking changes in this PR! 🎉🥳
Checked commit: 8b93622

@Lorak-mmk
Copy link
Collaborator

I see there is a conflict, could you rebase on curent branch-hackathon to fix it?

@k0machi k0machi force-pushed the serialization-tests branch 2 times, most recently from 35ebefe to ab61b5d Compare February 13, 2025 10:47
@k0machi
Copy link
Author

k0machi commented Feb 13, 2025

I see there is a conflict, could you rebase on curent branch-hackathon to fix it?

Done, there should be 3 tests that are currently failing, I'm going to push a fix to handle them properly - they fail on bad values, so I will either split them into separate test or just add an error handler callback to the test function.

@k0machi k0machi force-pushed the serialization-tests branch 2 times, most recently from 6dacbfd to 1d3719d Compare February 13, 2025 11:39
Copy link
Collaborator

@wprzytula wprzytula left a comment

Choose a reason for hiding this comment

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

🔧 It's difficult to follow all the changes, as they are all introduced in one commit. Could you please split them into at least the following commits:

  1. Rename one of {cql_types.rs,cql_value.rs} to serialization.rs.
  2. Move contents of the remaining one of {cql_types.rs,cql_value.rs} to serialization.rs.
  3. Change the existing tests, if needed.
  4. Add your own new tests, if any.

@k0machi k0machi force-pushed the serialization-tests branch 3 times, most recently from abce619 to 09db999 Compare February 13, 2025 12:10
@k0machi
Copy link
Author

k0machi commented Feb 13, 2025

🔧 It's difficult to follow all the changes, as they are all introduced in one commit. Could you please split them into at least the following commits:

  1. Rename one of {cql_types.rs,cql_value.rs} to serialization.rs.
  2. Move contents of the remaining one of {cql_types.rs,cql_value.rs} to serialization.rs.
  3. Change the existing tests, if needed.
  4. Add your own new tests, if any.

Done.

@k0machi k0machi requested a review from wprzytula February 13, 2025 12:13
Copy link
Collaborator

@wprzytula wprzytula left a comment

Choose a reason for hiding this comment

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

👍 Thanks for the split, commits 1, 2, 4 are now very readable.

🔧 The third commit (refactor and split old serialization tests) is still very large and the displayed diff is complicated. Could you split it further, preferably into separate logical steps of changes, so that it's easier to follow?

async fn run_literal_and_bound_value_test_with_callback<LitVal, BoundVal, ErrHandler>(
table_name: &str,
type_name: &str,
values: Vec<(LitVal, BoundVal)>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

🔧 A slice would suffice here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
values: Vec<(LitVal, BoundVal)>,
values: &[(LitVal, BoundVal)],

Copy link
Collaborator

Choose a reason for hiding this comment

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

Then, you could pass stack-allocated slices to this function and elide allocations. Not so important in tests, but useful in general.

Copy link
Author

Choose a reason for hiding this comment

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

Done

async fn run_literal_input_maybe_empty_output_test<X>(
table_name: &str,
type_name: &str,
values: Vec<(Option<&str>, Option<MaybeEmpty<X>>)>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

🔧 ditto: non-owned slice is enough.

Copy link
Author

Choose a reason for hiding this comment

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

Done

Comment on lines 266 to 270
.filter(|row| match row {
Ok((_,)) => true,
Err(e) => handle_deserialize_error(e, || false),
})
.map(Result::unwrap)
Copy link
Collaborator

Choose a reason for hiding this comment

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

filter_map could help avoid unwrap here.

Copy link
Author

Choose a reason for hiding this comment

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

Done

Comment on lines 337 to 348
#[cfg(any(
feature = "num-bigint-03",
feature = "num-bigint-04",
feature = "bigdecimal-04"
))]
fn bigint_from_str<T>(val: &str) -> T
where
T: FromStr,
<T as FromStr>::Err: Debug,
{
T::from_str(val).expect("Failed to parse BigInt")
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This function has nothing to do with bigint - it justs calls the <T as FromStr>::from_str() implementation. It's thus not a good generic use IMO.

Copy link
Author

Choose a reason for hiding this comment

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

Removed.

@k0machi k0machi force-pushed the serialization-tests branch 3 times, most recently from feb9649 to 99817b8 Compare February 13, 2025 15:47
@k0machi
Copy link
Author

k0machi commented Feb 13, 2025

🔧 The third commit (refactor and split old serialization tests) is still very large and the displayed diff is complicated. Could you split it further, preferably into separate logical steps of changes, so that it's easier to follow?

I don't think further splitting is possible - I started by replacing old test functions with new ones and then went and cleaned bodies of most of the tests, alongside extracting some bits into separate tests - I guess I could separate the extraction part - I commented them out first, but other than that I think I will end up with non-compiling commits.

@k0machi k0machi requested a review from wprzytula February 13, 2025 15:51
Copy link
Collaborator

@Lorak-mmk Lorak-mmk left a comment

Choose a reason for hiding this comment

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

Just posting a few comments because I can't review the whole thing now, I have to go to sleep

Comment on lines 1 to 7
use core::str;
use std::{
fmt::{Debug, Display},
net::{IpAddr, Ipv4Addr, Ipv6Addr},
str::FromStr,
vec,
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please group imports by module. There is some options in rust-analyzer to do that, or you can do that manually.
By module I mean, in this case:

use std::fmt::{Debug, Display};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
use std::vec;

Grouping more than by module makes it much more difficult to do certain refactors, and introduces a lot of unnecessary conflicts when rebasing.
Not grouping at all results in a lot of code. Grouping by module is a good compromise.

Copy link
Contributor

Choose a reason for hiding this comment

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

Here is my config for rust-analyzer:

    "rust-analyzer.imports.merge.glob": false,
    "rust-analyzer.imports.granularity.group": "module",
    "rust-analyzer.imports.granularity.enforce": true,

I'm not sure if it will fix the existing imports - you probably will need to fix those manually. But all new automatic imports (ctrl + .) will be grouped properly

Copy link
Author

Choose a reason for hiding this comment

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

@muzarski Thanks, added to my config.

Done

Comment on lines 15 to 20
use scylla::{
client::session::Session,
deserialize::{
row::BuiltinDeserializationError,
value::{Emptiable, MaybeEmpty},
},
errors::DeserializationError,
serialize::value::SerializeValue,
value::{
Counter, CqlDate, CqlDuration, CqlTime, CqlTimestamp, CqlTimeuuid, CqlValue, CqlVarint,
},
DeserializeValue, SerializeValue,
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ditto, don't group by more than module.

Copy link
Author

Choose a reason for hiding this comment

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

Done

Comment on lines 69 to 64
fn handle_deserialize_error<F, T>(error: &DeserializationError, callback: F) -> T
where
F: FnOnce() -> T,
{
match &error.downcast_ref::<BuiltinDeserializationError>().unwrap().kind{
scylla::deserialize::row::BuiltinDeserializationErrorKind::ColumnDeserializationFailed { err, .. } => match err.downcast_ref::<scylla::deserialize::value::BuiltinDeserializationError>().unwrap().kind {
scylla::deserialize::value::BuiltinDeserializationErrorKind::ExpectedNonNull => {
callback()
},
_ => panic!("Unexpected error: {:?}", error),
}
_ => panic!("Unexpected error: {:?}", error),
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

It looks like you are always calling this function with callback set to || None. In that case I don't see why the callback is useful, please remove it.
After removing it, a much better name for this function would be assert_expected_non_null_error.

Copy link
Author

Choose a reason for hiding this comment

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

Indeed, that callback was useful when I initially wrote .filter().map() chain, so in some cases I needed T as the return value of the callback and in others bool, but I think this can be removed now.

Copy link
Author

Choose a reason for hiding this comment

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

Done

Comment on lines 84 to 79
#[cfg(any(feature = "chrono-04", feature = "time-03",))]
fn handle_incorrect_dates<T>(error: &DeserializationError) -> Option<T> {
match &error.downcast_ref::<BuiltinDeserializationError>().unwrap().kind {
scylla::deserialize::row::BuiltinDeserializationErrorKind::ColumnDeserializationFailed { err, .. } => match err.downcast_ref::<scylla::deserialize::value::BuiltinDeserializationError>().unwrap().kind {
scylla::deserialize::value::BuiltinDeserializationErrorKind::ValueOverflow => {
None
},
scylla::deserialize::value::BuiltinDeserializationErrorKind::ExpectedNonNull => {
None
},
_ => panic!("Unexpected error: {:?}", error),
}
_ => panic!("Unexpected error: {:?}", error),
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This function either panics or returns None. In that case there is no reason to return anything.
Make it just panic or not and rename it to assert_overflow_or_expected_non_null_error - or another name if you can think of some.

Copy link
Author

Choose a reason for hiding this comment

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

I use this function inside a filter_map - wouldn't this break the functionality of that?

Copy link
Collaborator

Choose a reason for hiding this comment

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

This function always returns None.
So you can change it just as I suggested, and use None literal instead of its return value

Copy link
Author

Choose a reason for hiding this comment

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

Oh right, I see, okay, I'll do that.

Copy link
Author

Choose a reason for hiding this comment

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

Done

Comment on lines 63 to 67
// Creates a new keyspace
// Drops and creates table {table_name} (id int PRIMARY KEY, val {type_name})
async fn init_test(table_name: &str, type_name: &str) -> Session {
init_test_maybe_without_tablets(table_name, type_name, true).await
prepare_test_table(table_name, type_name, true).await
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason for this function to exist? Its name doesn't say anything about tablets, and the only difference between this one and prepare_test_table is that this one always creates the ks and table with tablet support.
Maybe just remove it and call prepare_test_table directly?

Copy link
Author

Choose a reason for hiding this comment

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

This function was there previously and I initially started without it but then ported it back over when wholesale copying some of the tests - I'll just remove it, it was there to set tablets to true for pretty much every test except counter one.

Copy link
Author

Choose a reason for hiding this comment

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

Done

@k0machi k0machi force-pushed the serialization-tests branch from 99817b8 to 24dda73 Compare February 14, 2025 16:27
@k0machi k0machi requested a review from Lorak-mmk February 14, 2025 16:29
Generalized most tests and split some of them into distinct tests to
see the results easier
This commit adds additional test to test various simple types such as
int and float, as well as one more uuid test and some simple string type
tests
@k0machi k0machi force-pushed the serialization-tests branch from 24dda73 to 8b93622 Compare February 14, 2025 16:31
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.

4 participants