Skip to content

Commit

Permalink
feat: Add asserts with custom messages (#408)
Browse files Browse the repository at this point in the history
* feat: Add asserts with custom messages

Signed-off-by: Maksim Dimitrov <[email protected]>

* chore: Run fmt

Signed-off-by: Maksim Dimitrov <[email protected]>

* refactor: Use logging::critical instead of panic. Return error and fail test instead of panicing

Signed-off-by: Maksim Dimitrov <[email protected]>

* chore: Run fmt

Signed-off-by: Maksim Dimitrov <[email protected]>

---------

Signed-off-by: Maksim Dimitrov <[email protected]>
  • Loading branch information
dimitrovmaksim committed Sep 13, 2023
1 parent 1cf1c82 commit 02759ea
Show file tree
Hide file tree
Showing 2 changed files with 253 additions and 24 deletions.
239 changes: 215 additions & 24 deletions src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,12 @@ impl<C: Blockchain> MatchstickInstanceContext<C> {
template_ptr: AscPtr<AscString>,
) -> Result<(), HostExportError> {
let template: String = asc_get(&self.wasm_ctx, template_ptr, &GasCounter::new(), 0)?;
let data_sources = self
.templates
.get(&template)
.unwrap_or_else(|| panic!("No template with name '{}' found.", template));
let data_sources = self.templates.get(&template).unwrap_or_else(|| {
logging::critical!(
"(logDataSources) No template with name '{}' found.",
template
)
});

let string_pretty = to_string_pretty(&data_sources).unwrap_or_else(|err| {
logging::critical!(
Expand Down Expand Up @@ -271,8 +273,8 @@ impl<C: Blockchain> MatchstickInstanceContext<C> {

// validates whether the provided entity exists in the schema file
if !self.schema.contains_key(&entity_type) {
panic!(
"Entity \"{}\" does not match any of the schema definitions",
logging::critical!(
"(logEntity) Entity \"{}\" does not match any of the schema definitions",
&entity_type
);
}
Expand Down Expand Up @@ -505,18 +507,23 @@ impl<C: Blockchain> MatchstickInstanceContext<C> {
let template_name: String =
asc_get(&self.wasm_ctx, template_name_ptr, &GasCounter::new(), 0)?;

let actual_count = self
.templates
.get(&template_name)
.unwrap_or_else(|| panic!("No template with name '{}' found.", template_name))
.len() as u32;
if let Some(template) = self.templates.get(&template_name) {
let actual_count = template.len() as u32;

if actual_count != expected_count {
logging::error!(
"(assert.dataSourceCount) Expected dataSource count for template `{}` to be '{}' but was '{}'",
template_name,
expected_count,
actual_count
);

if actual_count != expected_count {
return Ok(false);
}
} else {
logging::error!(
"(assert.dataSourceCount) Expected dataSource count for template `{}` to be '{}' but was '{}'",
template_name,
expected_count,
actual_count
"(assert.dataSourceCoutn) No template with name '{}' found.",
template_name
);
return Ok(false);
}
Expand All @@ -534,15 +541,199 @@ impl<C: Blockchain> MatchstickInstanceContext<C> {
asc_get(&self.wasm_ctx, template_name_ptr, &GasCounter::new(), 0)?;
let address: String = asc_get(&self.wasm_ctx, address_ptr, &GasCounter::new(), 0)?;

let template = self
.templates
.get(&template_name)
.unwrap_or_else(|| panic!("No template with name '{}' found.", template_name));
if let Some(template) = self.templates.get(&template_name) {
if !template.contains_key(&address) {
logging::error!(
"(assert.dataSourceExists) No dataSource with address '{}' found for template '{}'",
address,
template_name
);
return Ok(false);
}
} else {
logging::error!(
"(assert.dataSourceExists) No template with name '{}' found.",
template_name
);
return Ok(false);
}

Ok(true)
}

/// Overloading the assert function with custom error message for backwards compatibility with matchstick-as
/// function _assert.fieldEquals(
/// entityType: string, id: string,
/// fieldName: string, expectedVal: string,
/// message: string,
/// ): bool
pub fn assert_field_equals_with_message(
&mut self,
_gas: &GasCounter,
entity_type_ptr: AscPtr<AscString>,
id_ptr: AscPtr<AscString>,
field_name_ptr: AscPtr<AscString>,
expected_val_ptr: AscPtr<AscString>,
message_ptr: AscPtr<AscString>,
) -> Result<bool, HostExportError> {
let entity_type: String = asc_get(&self.wasm_ctx, entity_type_ptr, &GasCounter::new(), 0)?;
let id: String = asc_get(&self.wasm_ctx, id_ptr, &GasCounter::new(), 0)?;
let field_name: String = asc_get(&self.wasm_ctx, field_name_ptr, &GasCounter::new(), 0)?;
let expected_val: String =
asc_get(&self.wasm_ctx, expected_val_ptr, &GasCounter::new(), 0)?;
let message: String = asc_get(&self.wasm_ctx, message_ptr, &GasCounter::new(), 0)?;

if !self.store.contains_key(&entity_type) {
logging::error!(
"(assert.fieldEquals) No entities with type '{}' found.",
&entity_type
);

return Ok(false);
}

let entities = self.store.get(&entity_type).unwrap();
if !entities.contains_key(&id) {
logging::error!(
"(assert.fieldEquals) No entity with type '{}' and id '{}' found.",
&entity_type,
&id
);

return Ok(false);
}

let entity = entities.get(&id).unwrap();
if !entity.contains_key(&field_name) {
logging::error!(
"(assert.fieldEquals) No field named '{}' on entity with type '{}' and id '{}' found.",
&field_name,
&entity_type,
&id
);

return Ok(false);
}

let val = entity.get(&field_name).unwrap();
if val.to_string() != expected_val {
logging::error!("(assert.fieldEquals) {}", message);

return Ok(false);
};

Ok(true)
}

/// function _assert.equals(expected: ethereum.Value, actual: ethereum.Value, message: string): bool
pub fn assert_equals_with_message(
&mut self,
_gas: &GasCounter,
expected_ptr: u32,
actual_ptr: u32,
message_ptr: AscPtr<AscString>,
) -> Result<bool, HostExportError> {
let expected: Token = asc_get::<_, AscEnum<EthereumValueKind>, _>(
&self.wasm_ctx,
expected_ptr.into(),
&GasCounter::new(),
0,
)?;
let actual: Token = asc_get::<_, AscEnum<EthereumValueKind>, _>(
&self.wasm_ctx,
actual_ptr.into(),
&GasCounter::new(),
0,
)?;
let message: String = asc_get(&self.wasm_ctx, message_ptr, &GasCounter::new(), 0)?;

let exp_val = get_token_value(expected);
let act_val = get_token_value(actual);

if exp_val != act_val {
logging::error!("(assert.equals) {}", message);

return Ok(false);
}

Ok(true)
}

/// function _assert.notInStore(entityType: string, id: string, message: string): bool
pub fn assert_not_in_store_with_message(
&mut self,
_gas: &GasCounter,
entity_type_ptr: AscPtr<AscString>,
id_ptr: AscPtr<AscString>,
message_ptr: AscPtr<AscString>,
) -> Result<bool, HostExportError> {
let entity_type: String = asc_get(&self.wasm_ctx, entity_type_ptr, &GasCounter::new(), 0)?;
let id: String = asc_get(&self.wasm_ctx, id_ptr, &GasCounter::new(), 0)?;
let message: String = asc_get(&self.wasm_ctx, message_ptr, &GasCounter::new(), 0)?;

if self.store.contains_key(&entity_type)
&& self.store.get(&entity_type).unwrap().contains_key(&id)
{
logging::error!("(assert.notInStore) {}", message);

if !template.contains_key(&address) {
return Ok(false);
}

Ok(true)
}

pub fn assert_data_source_count_with_message(
&mut self,
_gas: &GasCounter,
template_name_ptr: AscPtr<AscString>,
expected_count: u32,
message_ptr: AscPtr<AscString>,
) -> Result<bool, HostExportError> {
let template_name: String =
asc_get(&self.wasm_ctx, template_name_ptr, &GasCounter::new(), 0)?;

let message: String = asc_get(&self.wasm_ctx, message_ptr, &GasCounter::new(), 0)?;

if let Some(template) = self.templates.get(&template_name) {
let actual_count = template.len() as u32;

if actual_count != expected_count {
logging::error!("(assert.dataSourceCount) {}", message);

return Ok(false);
}
} else {
logging::error!(
"(assert.dataSourceCoutn) No template with name '{}' found.",
template_name
);
return Ok(false);
}

Ok(true)
}

pub fn assert_data_source_exists_with_message(
&mut self,
_gas: &GasCounter,
template_name_ptr: AscPtr<AscString>,
address_ptr: AscPtr<AscString>,
message_ptr: AscPtr<AscString>,
) -> Result<bool, HostExportError> {
let template_name: String =
asc_get(&self.wasm_ctx, template_name_ptr, &GasCounter::new(), 0)?;
let address: String = asc_get(&self.wasm_ctx, address_ptr, &GasCounter::new(), 0)?;
let message: String = asc_get(&self.wasm_ctx, message_ptr, &GasCounter::new(), 0)?;

if let Some(template) = self.templates.get(&template_name) {
if !template.contains_key(&address) {
logging::error!("(assert.dataSourceExists) {}", message);
return Ok(false);
}
} else {
logging::error!(
"(assert.dataSourceExists) No dataSource with address '{}' found for template '{}'",
address,
"(assert.dataSourceExists) No template with name '{}' found.",
template_name
);
return Ok(false);
Expand Down Expand Up @@ -1080,7 +1271,7 @@ impl<C: Blockchain> MatchstickInstanceContext<C> {

match self.store.get(&entity_type) {
Some(inner_map) => Ok(inner_map.len().try_into().unwrap_or_else(|err| {
panic!(
logging::critical!(
"Couldn't cast usize value: {} into i32.\n{}",
inner_map.len(),
err
Expand Down
38 changes: 38 additions & 0 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,44 @@ impl<C: Blockchain> MatchstickInstance<C> {
address_ptr
);

link!(
"_assert.fieldEqualsWithMessage",
assert_field_equals_with_message,
entity_type_ptr,
id_ptr,
field_name_ptr,
expected_val_ptr,
message_ptr
);
link!(
"_assert.equalsWithMessage",
assert_equals_with_message,
expected_ptr,
actual_ptr,
message_ptr
);
link!(
"_assert.notInStoreWithMessage",
assert_not_in_store_with_message,
entity_type_ptr,
id_ptr,
message_ptr
);
link!(
"_assert.dataSourceCountWithMessage",
assert_data_source_count_with_message,
template_ptr,
expected_count_ptr,
message_ptr
);
link!(
"_assert.dataSourceExistsWithMessage",
assert_data_source_exists_with_message,
template_ptr,
address_ptr,
message_ptr
);

link!("countEntities", count_entities, entity_type);

// Linking gas function
Expand Down

0 comments on commit 02759ea

Please sign in to comment.