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

Database dependency injection in planner #1427

Merged
merged 4 commits into from
Jul 8, 2024
Merged

Database dependency injection in planner #1427

merged 4 commits into from
Jul 8, 2024

Conversation

grod220
Copy link
Contributor

@grod220 grod220 commented Jul 5, 2024

While working on #1426, it became evident that the state of testing in the wasm code is quite rough given the dependency on the indexeddb global.

This PR adds a database interface that storage.rs gets passed upon initialization. This allows us to use a mock database that can be used in tests, making our goals around test coverage much more attainable.

Copy link

changeset-bot bot commented Jul 5, 2024

🦋 Changeset detected

Latest commit: 86a2dca

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@penumbra-zone/wasm Minor
@penumbra-zone/perspective Major
@penumbra-zone/query Major
@penumbra-zone/services Major
@penumbra-zone/storage Major
minifront Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@grod220 grod220 force-pushed the db-abstraction branch 4 times, most recently from 5502149 to 737e918 Compare July 8, 2024 13:10
Comment on lines +29 to +67
async fn mock_test_database(mut db_req: OpenDbRequest) -> OpenDbRequest {
db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
// Check if the object store exists; create it if it doesn't
if evt.db().name() == "penumbra-db-wasm-test" {
let note_key: JsValue = serde_wasm_bindgen::to_value("noteCommitment.inner")?;
let note_object_store_params = IdbObjectStoreParameters::new()
.key_path(Some(&IdbKeyPath::new(note_key)))
.to_owned();
let note_object_store = evt
.db()
.create_object_store_with_params("SPENDABLE_NOTES", &note_object_store_params)?;

let nullifier_key: JsValue = serde_wasm_bindgen::to_value("nullifier.inner")?;
note_object_store.create_index_with_params(
"nullifier",
&IdbKeyPath::new(nullifier_key),
web_sys::IdbIndexParameters::new().unique(false),
)?;
evt.db().create_object_store("TREE_LAST_POSITION")?;
evt.db().create_object_store("TREE_LAST_FORGOTTEN")?;

let commitment_key: JsValue = serde_wasm_bindgen::to_value("commitment.inner")?;
let commitment_object_store_params = IdbObjectStoreParameters::new()
.key_path(Some(&IdbKeyPath::new(commitment_key)))
.to_owned();
evt.db().create_object_store_with_params(
"TREE_COMMITMENTS",
&commitment_object_store_params,
)?;
evt.db().create_object_store("TREE_HASHES")?;
evt.db().create_object_store("FMD_PARAMETERS")?;
evt.db().create_object_store("APP_PARAMETERS")?;
evt.db().create_object_store("GAS_PRICES")?;
}
Ok(())
}));

db_req
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copied over 1:1. We should seek in later PRs to migrate this to MockDb.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The core idea of this PR is this interface which allows us to write a mock and write tests that feel much more manageable to write

Comment on lines -193 to -202
let gas_prices: GasPrices = {
let gas_prices: penumbra_proto::core::component::fee::v1::GasPrices =
serde_wasm_bindgen::from_value(
storage
.get_gas_prices_by_asset_id(fee_asset_id)
.await?
.ok_or_else(|| anyhow!("GasPrices not available"))?,
)?;
gas_prices.try_into()?
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using the serialization built into the method now

@@ -463,7 +462,7 @@ pub async fn plan_transaction(

save_auction_nft_metadata_if_needed(auction_id, &storage, seq).await?;
let outstanding_reserves: OutstandingReserves =
storage.get_auction_oustanding_reserves(auction_id).await?;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

typo

Copy link
Contributor Author

Choose a reason for hiding this comment

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

An example test suite, but the next PR is to implement a test suite for get_notes_for_voting()

@grod220 grod220 self-assigned this Jul 8, 2024
Copy link
Contributor

@Valentine1898 Valentine1898 left a comment

Choose a reason for hiding this comment

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

LGTM! 👍

@TalDerei
Copy link
Contributor

TalDerei commented Jul 8, 2024

reviewing the PR now 👍

Copy link
Contributor

@TalDerei TalDerei left a comment

Choose a reason for hiding this comment

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

This is a huge improvement to our storage crate, and it's more readable! 🎉

T: DeserializeOwned,
K: Into<JsValue>,
{
unimplemented!()
Copy link
Contributor

@TalDerei TalDerei Jul 8, 2024

Choose a reason for hiding this comment

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

ideally we should avoid leaving methods unimplemented, and explicitly state why they are unimplemented.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added comments. In short, the person writing the tests that use these methods will have to write them. Figured it's a task for later.

// Previous method of testing that requires features in prod code to seed indexed db for tests
// TODO: Swap out with new MockDb utility for testing
async fn mock_test_database(mut db_req: OpenDbRequest) -> OpenDbRequest {
db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
Copy link
Contributor

Choose a reason for hiding this comment

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

tagging penumbra-zone/penumbra#3289 (comment) as relevant discussion.

how did you resolve the onupgradeneeded conflict I was facing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In short, I didn't. By passing in an alternative database, the test suite has full flexibility in instantiating it as it needs.

@@ -114,7 +115,7 @@ async fn save_unbonding_token_metadata_if_needed(
/// action renders correctly in the transaction approval dialog.
async fn save_auction_nft_metadata_if_needed(
id: AuctionId,
storage: &IndexedDBStorage,
storage: &Storage<IdbDatabase>,
Copy link
Contributor

Choose a reason for hiding this comment

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

I really like the generic storage interface!

let key = "test_key";
let value = AddressIndex::new(1);

block_on(db.put_with_key(table_name, key, &value)).unwrap();
Copy link
Contributor

@TalDerei TalDerei Jul 8, 2024

Choose a reason for hiding this comment

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

how does block_on work in the test suite?

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 didn't occur to me that I could just define the test fn as async and use await syntax. This was an unnecessary workaround. Will update.

@grod220 grod220 merged commit 5641af2 into main Jul 8, 2024
6 checks passed
@grod220 grod220 deleted the db-abstraction branch July 8, 2024 22:57
@grod220 grod220 mentioned this pull request Jul 15, 2024
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