Skip to content

Commit

Permalink
fix content on Extension spec
Browse files Browse the repository at this point in the history
- remove the hardcoded ExtensionContentType, replace with arbitrary json
- this needed adding a new method in step to output Extension artifacts,
  in order to satisfy coverage

Signed-off-by: mimir-d <[email protected]>
  • Loading branch information
mimir-d committed Oct 8, 2024
1 parent 70f0f80 commit cadbe21
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 15 deletions.
4 changes: 3 additions & 1 deletion src/output/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,7 @@ pub use serde_json::Value;
pub enum OcptvError {
#[error("failed to write to output stream")]
IoError(#[from] std::io::Error),
// other?

#[error("failed to format input object")]
Format(Box<dyn std::error::Error + Send + Sync + 'static>), // opaque type so we don't leak impl
}
37 changes: 37 additions & 0 deletions src/output/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use crate::spec::{self, TestStepArtifactImpl};
use tv::measure::MeasurementSeries;
use tv::{emitter, error, log, measure};

use super::OcptvError;

/// A single test step in the scope of a [`TestRun`].
///
/// ref: https://github.com/opencomputeproject/ocp-diag-core/tree/main/json_spec#test-step-artifacts
Expand Down Expand Up @@ -357,6 +359,41 @@ impl StartedTestStep {
Ok(())
}

/// Emits an extension message;
///
/// ref: https://github.com/opencomputeproject/ocp-diag-core/tree/main/json_spec#extension
///
/// # Examples
///
/// ```rust
/// # tokio_test::block_on(async {
/// # use ocptv::output::*;
///
/// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?;
/// let step = run.add_step("step_name").start().await?;
///
/// #[derive(serde::Serialize)]
/// struct Ext { i: u32 }
///
/// step.extension("ext_name", Ext { i: 42 }).await?;
///
/// # Ok::<(), OcptvError>(())
/// # });
/// ```
pub async fn extension<S: serde::Serialize>(
&self,
name: &str,
any: S,
) -> Result<(), tv::OcptvError> {
let ext = TestStepArtifactImpl::Extension(spec::Extension {
name: name.to_owned(),
content: serde_json::to_value(&any).map_err(|e| OcptvError::Format(Box::new(e)))?,
});

self.step.emitter.emit(&ext).await?;
Ok(())
}

/// Emits a Measurement message.
///
/// ref: https://github.com/opencomputeproject/ocp-diag-core/tree/main/json_spec#measurement
Expand Down
17 changes: 3 additions & 14 deletions src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,6 @@ pub enum SubcomponentType {
Connector,
}

// TODO: this should be better typed
#[derive(Debug, Serialize, PartialEq, Clone)]
pub enum ExtensionContentType {
#[serde(rename = "float")]
Float(f64),
#[serde(rename = "int")]
Int(i64),
#[serde(rename = "bool")]
Bool(bool),
#[serde(rename = "str")]
Str(String),
}

/// Outcome of a diagnosis operation.
/// ref: https://github.com/opencomputeproject/ocp-diag-core/tree/main/json_spec#diagnosistype
/// schema url: https://github.com/opencomputeproject/ocp-diag-core/blob/main/json_spec/output/diagnosis.json
Expand Down Expand Up @@ -793,8 +780,10 @@ pub struct Extension {
#[serde(rename = "name")]
pub name: String,

// note: have to use a json specific here; alternative is to propagate up an E: Serialize,
// which polutes all of the types. Trait Serialize is also not object safe.
#[serde(rename = "content")]
pub content: ExtensionContentType,
pub content: serde_json::Value,
}

#[cfg(test)]
Expand Down
104 changes: 104 additions & 0 deletions tests/output/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use assert_json_diff::{assert_json_eq, assert_json_include};
use futures::future::BoxFuture;
use futures::future::Future;
use futures::FutureExt;
use ocptv::output::OcptvError;
use predicates::prelude::*;
use serde_json::json;
use tokio::sync::Mutex;
Expand Down Expand Up @@ -1356,6 +1357,109 @@ async fn test_config_builder_with_file() -> Result<()> {
Ok(())
}

#[tokio::test]
async fn test_step_with_extension() -> Result<()> {
let expected = [
json_schema_version(),
json_run_default_start(),
json_step_default_start(),
json!({
"testStepArtifact": {
"testStepId": "step_0",
"extension": {
"name": "extension",
"content": {
"@type": "TestExtension",
"stringField": "string",
"numberField": 42
}
}
},
"sequenceNumber": 3,
"timestamp": DATETIME_FORMATTED
}),
json_step_complete(4),
json_run_pass(5),
];

#[derive(serde::Serialize)]
struct Ext {
#[serde(rename = "@type")]
r#type: String,
#[serde(rename = "stringField")]
string_field: String,
#[serde(rename = "numberField")]
number_field: u32,
}

check_output_step(&expected, |step| {
async {
step.extension(
"extension",
Ext {
r#type: "TestExtension".to_owned(),
string_field: "string".to_owned(),
number_field: 42,
},
)
.await?;

Ok(())
}
.boxed()
})
.await
}

#[tokio::test]
async fn test_step_with_extension_which_fails() -> Result<()> {
#[derive(thiserror::Error, Debug, PartialEq)]
enum TestError {
#[error("test_error_fail")]
Fail,
}

fn fail_serialize<S>(_: &u32, _serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
Err(serde::ser::Error::custom(TestError::Fail))
}

#[derive(serde::Serialize)]
struct Ext {
#[serde(serialize_with = "fail_serialize")]
i: u32,
}

let buffer: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
let dut = DutInfo::builder("dut_id").build();
let run = TestRun::builder("run_name", &dut, "1.0")
.config(
Config::builder()
.with_buffer_output(Arc::clone(&buffer))
.with_timestamp_provider(Box::new(FixedTsProvider {}))
.build(),
)
.build()
.start()
.await?;
let step = run.add_step("first step").start().await?;

let result = step.extension("extension", Ext { i: 0 }).await;

match result {
Err(OcptvError::Format(e)) => {
// `to_string` is the only way to check this error. `serde_json::Error` only
// implements source/cause for io errors, and this is a string
assert_eq!(e.to_string(), "test_error_fail");
}
_ => panic!("unexpected ocptv error type"),
}

Ok(())
}

#[tokio::test]
async fn test_testrun_instantiation_with_new() -> Result<()> {
let expected = [
Expand Down

0 comments on commit cadbe21

Please sign in to comment.