#[cfg(feature = "yaml")]
use har::to_yaml;
use har::{Error, HarVersion, Spec, from_path, from_slice, from_str};
use std::fs;
use std::path::{Path, PathBuf};
fn fixtures_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures")
}
fn fixture_paths() -> Vec<PathBuf> {
let mut fixtures = fs::read_dir(fixtures_dir())
.expect("failed to read fixture directory")
.map(|entry| entry.expect("failed to read fixture entry").path())
.filter(|path| path.extension().is_some_and(|extension| extension == "har"))
.collect::<Vec<_>>();
fixtures.sort();
fixtures
}
fn read_file(path: &Path) -> String {
fs::read_to_string(path).expect("failed to read fixture file")
}
fn write_to_file(path: &Path, filename: &str, data: &str) {
fs::create_dir_all(path).expect("failed to create test output directory");
fs::write(path.join(filename), data).expect("failed to write test output");
}
fn normalize_json_str(json_str: &str) -> String {
let json: serde_json::Value = serde_json::from_str(json_str).expect("fixture should be JSON");
serde_json::to_string_pretty(&json).expect("failed to normalize fixture JSON")
}
fn compare_har_through_json(input_file: &Path, save_path_base: &Path) -> (String, String, String) {
let source_json_str = normalize_json_str(&read_file(input_file));
let parsed_har = from_path(input_file).expect("fixture should deserialize");
let parsed_har_json: serde_json::Value =
serde_json::to_value(parsed_har).expect("failed to convert HAR to JSON value");
let parsed_har_json_str =
serde_json::to_string_pretty(&parsed_har_json).expect("failed to pretty-print HAR JSON");
let fixture_filename = input_file
.file_name()
.expect("fixture should have a file name")
.to_string_lossy()
.replace(".har", ".json");
write_to_file(
&save_path_base.join("json_to_json"),
&fixture_filename,
&source_json_str,
);
write_to_file(
&save_path_base.join("json_to_har_to_json"),
&fixture_filename,
&parsed_har_json_str,
);
(fixture_filename, parsed_har_json_str, source_json_str)
}
fn expected_version(path: &Path) -> HarVersion {
match path
.file_name()
.expect("fixture should have a file name")
.to_string_lossy()
.as_ref()
{
"someapi123.har" => HarVersion::V1_2,
"someapi13.har" => HarVersion::V1_3,
other => panic!("unexpected fixture {other}"),
}
}
#[test]
fn can_find_test_fixtures() {
let fixtures = fixture_paths();
assert!(
!fixtures.is_empty(),
"expected at least one .har fixture in {}",
fixtures_dir().display()
);
}
#[test]
fn fixtures_deserialize_to_expected_versions() {
for fixture in fixture_paths() {
let har = from_path(&fixture).expect("fixture should deserialize");
let expected_version = expected_version(&fixture);
assert_eq!(
har.version(),
expected_version,
"fixture {}",
fixture.display()
);
match (expected_version, &har.log) {
(HarVersion::V1_2, Spec::V1_2(_)) | (HarVersion::V1_3, Spec::V1_3(_)) => {}
_ => panic!(
"fixture {} deserialized to the wrong spec",
fixture.display()
),
}
}
}
#[test]
fn from_slice_matches_from_path() {
for fixture in fixture_paths() {
let bytes = fs::read(&fixture).expect("failed to read fixture bytes");
let from_bytes = from_slice(&bytes).expect("fixture bytes should deserialize");
let from_disk = from_path(&fixture).expect("fixture path should deserialize");
assert_eq!(from_bytes, from_disk, "fixture {}", fixture.display());
}
}
#[test]
fn from_str_matches_from_path() {
for fixture in fixture_paths() {
let content = read_file(&fixture);
let from_text = from_str(&content).expect("fixture string should deserialize");
let from_disk = from_path(&fixture).expect("fixture path should deserialize");
assert_eq!(from_text, from_disk, "fixture {}", fixture.display());
}
}
#[cfg(feature = "yaml")]
#[test]
fn can_serialize_to_yaml_when_feature_is_enabled() {
let input = r#"{
"log": {
"version": "1.2",
"creator": { "name": "example", "version": "1.0" },
"entries": []
}
}"#;
let har = from_str(input).expect("input should deserialize");
let yaml = to_yaml(&har).expect("serializing parsed HAR as YAML should succeed");
assert!(yaml.contains("log:"), "unexpected YAML output: {yaml}");
assert!(yaml.contains("creator:"), "unexpected YAML output: {yaml}");
assert!(
yaml.contains("name: example"),
"unexpected YAML output: {yaml}"
);
assert!(
yaml.contains("version: '1.2'")
|| yaml.contains("version: \"1.2\"")
|| yaml.contains("version: 1.2"),
"unexpected YAML output: {yaml}"
);
assert!(yaml.contains("entries:"), "unexpected YAML output: {yaml}");
}
#[test]
fn rejects_missing_log_version() {
let input = r#"{
"log": {
"creator": { "name": "example", "version": "1.0" },
"entries": []
}
}"#;
let error = from_str(input).expect_err("missing version should fail");
assert!(matches!(error, Error::MissingVersion));
}
#[test]
fn rejects_unsupported_log_version() {
let input = r#"{
"log": {
"version": "1.4",
"creator": { "name": "example", "version": "1.0" },
"entries": []
}
}"#;
let error = from_str(input).expect_err("unsupported version should fail");
assert!(matches!(error, Error::UnsupportedVersion(version) if version == "1.4"));
}
#[test]
fn can_deserialize_and_reserialize() {
let save_path_base =
Path::new(env!("CARGO_MANIFEST_DIR")).join("target/tests/can_deserialize_and_reserialize");
let mut invalid_diffs = Vec::new();
for fixture in fixture_paths() {
let (fixture_filename, parsed_har_json_str, source_json_str) =
compare_har_through_json(&fixture, &save_path_base);
if parsed_har_json_str != source_json_str {
invalid_diffs.push((fixture_filename, parsed_har_json_str, source_json_str));
}
}
for invalid_diff in &invalid_diffs {
println!("File {} failed JSON comparison!", invalid_diff.0);
}
assert_eq!(invalid_diffs.len(), 0);
}