-
Notifications
You must be signed in to change notification settings - Fork 17
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
Conversation
🦋 Changeset detectedLatest commit: 86a2dca The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
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 |
5502149
to
737e918
Compare
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", ¬e_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 | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
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()? | ||
}; |
There was a problem hiding this comment.
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?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
There was a problem hiding this comment.
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()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! 👍
reviewing the PR now 👍 |
There was a problem hiding this 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!() |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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> { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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>, |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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.