use std::path::{Path, PathBuf};
use common::*;
use rocfl::ocfl::{
ErrorCode, ObjectValidationResult, OcflRepo, ProblemLocation, ValidationError,
ValidationResult, ValidationWarning, WarnCode,
};
mod common;
#[test]
fn extra_dir_in_root() {
let result = official_error_test("E001_extra_dir_in_root");
has_errors(
&result,
&[root_error(
ErrorCode::E001,
"Unexpected file in object root: extra_dir",
)],
);
has_warnings(
&result,
&[
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'message'",
),
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'user'",
),
],
);
}
#[test]
fn extra_file_in_root() {
let result = official_error_test("E001_extra_file_in_root");
has_errors(
&result,
&[root_error(
ErrorCode::E001,
"Unexpected file in object root: extra_file",
)],
);
has_warnings(
&result,
&[
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'message'",
),
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'user'",
),
],
);
}
#[test]
fn invalid_version_format() {
let result = official_error_test("E001_invalid_version_format");
has_errors(&result, &[
root_error(
ErrorCode::E011,
"Inventory 'head' must be a valid version number. Found: 1",
),
root_error(
ErrorCode::E046,
"Inventory 'versions' contains an invalid version number. Found: 1",
),
root_error(
ErrorCode::E008,
"Inventory does not contain any valid versions",
),
root_error(
ErrorCode::E099,
"Inventory manifest key 'ffc150e7944b5cf5ddb899b2f48efffbd490f97632fc258434aefc4afb92aef2e3441ddcceae11404e5805e1b6c804083c9398c28f061c9ba42dd4bac53d5a2e' contains a path containing an illegal path part. Found: 1/content/my_content/dracula.txt",
),
root_error(
ErrorCode::E099,
"Inventory manifest key '69f54f2e9f4568f7df4a4c3b07e4cbda4ba3bba7913c5218add6dea891817a80ce829b877d7a84ce47f93cbad8aa522bf7dd8eda2778e16bdf3c47cf49ee3bdf' contains a path containing an illegal path part. Found: 1/content/my_content/poe.txt",
)
]);
no_warnings(&result);
}
#[test]
fn v2_file_in_root() {
let result = official_error_test("E001_v2_file_in_root");
has_errors(
&result,
&[root_error(
ErrorCode::E001,
"Unexpected file in object root: v2",
)],
);
no_warnings(&result);
}
#[test]
fn empty_object() {
let result = official_error_test("E003_E063_empty");
has_errors(
&result,
&[
root_error(ErrorCode::E003, "Object version declaration does not exist"),
root_error(ErrorCode::E063, "Inventory does not exist"),
],
);
no_warnings(&result);
}
#[test]
fn no_decl() {
let result = official_error_test("E003_no_decl");
has_errors(
&result,
&[root_error(
ErrorCode::E003,
"Object version declaration does not exist",
)],
);
no_warnings(&result);
}
#[test]
fn bad_declaration_contents() {
let result = official_error_test("E007_bad_declaration_contents");
has_errors(&result, &[
root_error(
ErrorCode::E007,
"Object version declaration is invalid. Expected: ocfl_object_1.0; Found: This is not the right content!\n",
),
]);
has_warnings(
&result,
&[
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'message'",
),
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'user'",
),
],
);
}
#[test]
fn missing_versions() {
let result = official_error_test("E010_missing_versions");
has_errors(
&result,
&[root_error(
ErrorCode::E010,
"Object root does not contain version directory 'v3'",
)],
);
has_warnings(
&result,
&[version_warning(
"v3",
WarnCode::W010,
"Inventory file does not exist",
)],
);
}
#[test]
fn skipped_versions() {
let result = official_error_test("E010_skipped_versions");
has_errors(
&result,
&[
root_error(
ErrorCode::E010,
"Inventory 'versions' is missing version 'v2'",
),
root_error(
ErrorCode::E010,
"Inventory 'versions' is missing version 'v3'",
),
root_error(
ErrorCode::E010,
"Inventory 'versions' is missing version 'v6'",
),
],
);
no_warnings(&result);
}
#[test]
fn invalid_padded_head_version() {
let result = official_error_test("E011_E013_invalid_padded_head_version");
has_errors(
&result,
&[root_error(
ErrorCode::E013,
"Inventory 'versions' contains inconsistently padded version numbers",
)],
);
has_warnings(
&result,
&[root_warning(
WarnCode::W001,
"Contains zero-padded version numbers",
)],
)
}
#[test]
fn content_not_in_content_dir() {
let result = official_error_test("E015_content_not_in_content_dir");
has_errors(&result, &[
root_error(
ErrorCode::E092,
"Inventory manifest references a file that does not exist in a content directory: v3/a_file.txt",
),
root_error(
ErrorCode::E092,
"Inventory manifest references a file that does not exist in a content directory: v1/a_file.txt",
),
root_error(
ErrorCode::E092,
"Inventory manifest references a file that does not exist in a content directory: v2/a_file.txt",
),
version_error(
"v3",
ErrorCode::E015,
"Version directory contains unexpected file: a_file.txt",
),
version_error(
"v2",
ErrorCode::E092,
"Inventory manifest references a file that does not exist in a content directory: v1/a_file.txt",
),
version_error(
"v2",
ErrorCode::E092,
"Inventory manifest references a file that does not exist in a content directory: v2/a_file.txt",
),
version_error(
"v2",
ErrorCode::E015,
"Version directory contains unexpected file: a_file.txt",
),
version_error(
"v1",
ErrorCode::E092,
"Inventory manifest references a file that does not exist in a content directory: v1/a_file.txt",
),
version_error(
"v1",
ErrorCode::E015,
"Version directory contains unexpected file: a_file.txt",
),
]);
no_warnings(&result);
}
#[test]
fn invalid_content_dir() {
let result = official_error_test("E017_invalid_content_dir");
has_errors(
&result,
&[root_error(
ErrorCode::E017,
"Inventory 'contentDirectory' cannot contain '/'. Found: content/dir",
)],
);
no_warnings(&result);
}
#[test]
fn inconsistent_content_dir() {
let result = official_error_test("E019_inconsistent_content_dir");
has_errors(&result, &[
root_error(
ErrorCode::E092,
"Inventory manifest references a file that does not exist in a content directory: v1/content-dir/test.txt",
),
version_error(
"v1",
ErrorCode::E019,
"Inventory 'contentDirectory' is inconsistent. Expected: content; Found: content-dir",
),
version_error(
"v1",
ErrorCode::E092,
"Inventory manifest references a file that does not exist in a content directory: v1/content-dir/test.txt",
),
]);
has_warnings(
&result,
&[version_warning(
"v1",
WarnCode::W002,
"Version directory contains unexpected directory: content-dir",
)],
);
}
#[test]
fn extra_file() {
let result = official_error_test("E023_extra_file");
has_errors(
&result,
&[root_error(
ErrorCode::E023,
"A content file exists that is not referenced in the manifest: v1/content/file2.txt",
)],
);
has_warnings(
&result,
&[root_warning(
WarnCode::W009,
"Inventory version v1 user 'address' should be a URI. Found: somewhere",
)],
);
}
#[test]
fn missing_file() {
let result = official_error_test("E023_missing_file");
has_errors(&result, &[
root_error(
ErrorCode::E092,
"Inventory manifest references a file that does not exist in a content directory: v1/content/file2.txt",
),
]);
has_warnings(
&result,
&[root_warning(
WarnCode::W009,
"Inventory version v1 user 'address' should be a URI. Found: somewhere",
)],
);
}
#[test]
fn old_manifest_missing_entries() {
let result = official_error_test("E023_old_manifest_missing_entries");
has_errors(
&result,
&[version_error(
"v2",
ErrorCode::E023,
"A content file exists that is not referenced in the manifest: v1/content/file-3.txt",
)],
);
no_warnings(&result);
}
#[test]
fn wrong_digest_algorithm() {
let result = official_error_test("E025_wrong_digest_algorithm");
has_errors(
&result,
&[root_error(
ErrorCode::E025,
"Inventory 'digestAlgorithm' must be 'sha512' or 'sha256. Found: md5",
)],
);
no_warnings(&result);
}
#[test]
fn no_head() {
let result = official_error_test("E036_no_head");
has_errors(
&result,
&[root_error(
ErrorCode::E036,
"Inventory is missing required key 'head'",
)],
);
no_warnings(&result);
}
#[test]
fn no_id() {
let result = official_error_test("E036_no_id");
has_errors(
&result,
&[root_error(
ErrorCode::E036,
"Inventory is missing required key 'id'",
)],
);
no_warnings(&result);
}
#[test]
fn inconsistent_id() {
let result = official_error_test("E037_inconsistent_id");
has_errors(
&result,
&[version_error(
"v1",
ErrorCode::E037,
"Inventory 'id' is inconsistent. Expected: urn:example-2; Found: urn:example-two",
)],
);
no_warnings(&result);
}
#[test]
fn head_not_most_recent() {
let result = official_error_test("E040_head_not_most_recent");
has_errors(
&result,
&[root_error(
ErrorCode::E040,
"Inventory 'head' references 'v1' but 'v2' was expected",
)],
);
no_warnings(&result);
}
#[test]
fn wrong_head_doesnt_exist() {
let result = official_error_test("E040_wrong_head_doesnt_exist");
has_errors(
&result,
&[
root_error(
ErrorCode::E040,
"Inventory 'head' references 'v2' but 'v1' was expected",
),
root_error(
ErrorCode::E010,
"Inventory 'versions' is missing version 'v2'",
),
],
);
has_warnings(
&result,
&[
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'message'",
),
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'user'",
),
],
);
}
#[test]
fn wrong_head_format() {
let result = official_error_test("E040_wrong_head_format");
has_errors(
&result,
&[root_error(
ErrorCode::E040,
"Inventory 'head' must be a string",
)],
);
no_warnings(&result);
}
#[test]
fn wrong_version_in_version_dir() {
let result = official_error_test("E040_wrong_version_in_version_dir");
has_errors(
&result,
&[version_error(
"v2",
ErrorCode::E040,
"Inventory 'head' must equal 'v2'. Found: v3",
)],
);
no_warnings(&result);
}
#[test]
fn no_manifest() {
let result = official_error_test("E041_no_manifest");
has_errors(
&result,
&[root_error(
ErrorCode::E041,
"Inventory is missing required key 'manifest'",
)],
);
has_warnings(
&result,
&[
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'message'",
),
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'user'",
),
],
);
}
#[test]
fn root_no_most_recent() {
let result = official_error_test("E046_root_not_most_recent");
has_errors(
&result,
&[root_error(
ErrorCode::E001,
"Unexpected file in object root: v2",
)],
);
no_warnings(&result);
}
#[test]
fn created_no_timezone() {
let result = official_error_test("E049_created_no_timezone");
has_errors(&result, &[
root_error(
ErrorCode::E049,
"Inventory version v1 'created' must be an RFC3339 formatted date. Found: 2019-01-01T02:03:04",
),
]);
no_warnings(&result);
}
#[test]
fn created_not_to_seconds() {
let result = official_error_test("E049_created_not_to_seconds");
has_errors(&result, &[
root_error(
ErrorCode::E049,
"Inventory version v1 'created' must be an RFC3339 formatted date. Found: 2019-01-01T01:02Z",
),
]);
no_warnings(&result);
}
#[test]
fn bad_version_block_values() {
let result = official_error_test("E049_E050_E054_bad_version_block_values");
has_errors(
&result,
&[root_error(
ErrorCode::E049,
"Inventory version v1 'created' must be a string",
)],
);
no_warnings(&result);
}
#[allow(dead_code)]
fn file_in_manifest_not_used() {
let result = official_error_test("E050_file_in_manifest_not_used");
has_errors(&result, &[]);
no_warnings(&result);
}
#[test]
fn manifest_digest_wrong_case() {
let result = official_error_test("E050_manifest_digest_wrong_case");
has_errors(
&result,
&[root_error(
ErrorCode::E050,
"Inventory version v1 state contains a digest that is not present in the manifest. Found: 24F950AAC7B9EA9B3CB728228A0C82B67C39E96B4B344798870D5DAEE93E3AE5931BAAE8C7CACFEA4B629452C38026A81D138BC7AAD1AF3EF7BFD5EC646D6C28",
)],
);
no_warnings(&result);
}
#[test]
fn invalid_logical_paths() {
let result = official_error_test("E053_E052_invalid_logical_paths");
has_errors(&result, &[
root_error(
ErrorCode::E053,
"In inventory version v1, state key '07e41ccb166d21a5327d5a2ae1bb48192b8470e1357266c9d119c294cb1e95978569472c9de64fb6d93cbd4dd0aed0bf1e7c47fd1920de17b038a08a85eb4fa1' contains a path with a leading/trailing '/'. Found: /file-1.txt",
),
root_error(
ErrorCode::E052,
"In inventory version v1, state key '9fef2458ee1a9277925614272adfe60872f4c1bf02eecce7276166957d1ab30f65cf5c8065a294bf1b13e3c3589ba936a3b5db911572e30dfcb200ef71ad33d5' contains a path containing an illegal path part. Found: ../../file-2.txt",
),
root_error(
ErrorCode::E053,
"In inventory version v1, state key 'b3b26d26c9d8cfbb884b50e798f93ac6bef275a018547b1560af3e6d38f2723785731d3ca6338682fa7ac9acb506b3c594a125ce9d3d60cd14498304cc864cf2' contains a path with a leading/trailing '/'. Found: //file-3.txt",
),
]);
no_warnings(&result);
}
#[test]
fn no_sidecar() {
let result = official_error_test("E058_no_sidecar");
has_errors(
&result,
&[root_error(
ErrorCode::E058,
"Inventory sidecar inventory.json.sha512 does not exist",
)],
);
no_warnings(&result);
}
#[test]
fn root_inventory_digest_mismatch() {
let result = official_error_test("E060_E064_root_inventory_digest_mismatch");
has_errors(&result, &[
root_error(
ErrorCode::E060,
"Inventory does not match expected digest. Expected: cb7a451c595050e0e50d979b79bce86e28728b8557a3cf4ea430114278b5411c7bad6a7ecc1f4d0250e94f9d8add3b648194d75a74c0cb14c4439f427829569e; Found: 5bf08b6519f6692cc83f3d275de1f02414a41972d069ac167c5cf34468fad82ae621c67e1ff58a8ef15d5f58a193aa1f037f588372bdfc33ae6c38a2b349d846",
),
]);
no_warnings(&result);
}
#[test]
fn version_inventory_digest_mismatch() {
let result = official_error_test("E060_version_inventory_digest_mismatch");
has_errors(&result, &[
version_error(
"v1",
ErrorCode::E060,
"Inventory does not match expected digest. Expected: cb7a451c595050e0e50d979b79bce86e28728b8557a3cf4ea430114278b5411c7bad6a7ecc1f4d0250e94f9d8add3b648194d75a74c0cb14c4439f427829569e; Found: 5bf08b6519f6692cc83f3d275de1f02414a41972d069ac167c5cf34468fad82ae621c67e1ff58a8ef15d5f58a193aa1f037f588372bdfc33ae6c38a2b349d846",
),
]);
has_warnings(
&result,
&[version_warning(
"v1",
WarnCode::W011,
"Inventory version v1 'message' is inconsistent with the root inventory",
)],
);
}
#[test]
fn invalid_sidecar() {
let result = official_error_test("E061_invalid_sidecar");
has_errors(
&result,
&[root_error(ErrorCode::E061, "Inventory sidecar is invalid")],
);
no_warnings(&result);
}
#[test]
fn no_inv() {
let result = official_error_test("E063_no_inv");
has_errors(
&result,
&[root_error(ErrorCode::E063, "Inventory does not exist")],
);
no_warnings(&result);
}
#[test]
fn different_root_and_latest_inventories() {
let result = official_error_test("E064_different_root_and_latest_inventories");
has_errors(
&result,
&[version_error(
"v1",
ErrorCode::E064,
"Inventory file must be identical to the root inventory",
)],
);
no_warnings(&result);
}
#[test]
fn algorithm_change_state_mismatch() {
let result = official_error_test("E066_algorithm_change_state_mismatch");
has_errors(&result, &[
version_error(
"v1",
ErrorCode::E066,
"In inventory version v1, path 'file-3.txt' maps to different content paths than it does in later inventories. Expected: [v1/content/file-2.txt]; Found: [v1/content/file-3.txt]",
),
version_error(
"v1",
ErrorCode::E066,
"Inventory version v1 state is missing a path that exists in later inventories: changed",
),
version_error(
"v1",
ErrorCode::E066,
"In inventory version v1, path 'file-2.txt' maps to different content paths than it does in later inventories. Expected: [v1/content/file-3.txt]; Found: [v1/content/file-2.txt]",
),
version_error(
"v1",
ErrorCode::E066,
"Inventory version v1 state contains a path not in later inventories: file-1.txt",
),
]);
has_warnings(
&result,
&[root_warning(
WarnCode::W004,
"Inventory 'digestAlgorithm' should be 'sha512'. Found: sha256",
)],
)
}
#[test]
fn old_manifest_digest_incorrect() {
let result = official_error_test("E066_E092_old_manifest_digest_incorrect");
has_errors(&result, &[
version_error(
"v1",
ErrorCode::E066,
"In inventory version v1, path 'file-1.txt' does not match the digest in later inventories. Expected: 07e41ccb166d21a5327d5a2ae1bb48192b8470e1357266c9d119c294cb1e95978569472c9de64fb6d93cbd4dd0aed0bf1e7c47fd1920de17b038a08a85eb4fa1; Found: 17e41ccb166d21a5327d5a2ae1bb48192b8470e1357266c9d119c294cb1e95978569472c9de64fb6d93cbd4dd0aed0bf1e7c47fd1920de17b038a08a85eb4fa1",
),
version_error(
"v1",
ErrorCode::E092,
"Inventory manifest entry for content path 'v1/content/file-1.txt' differs from later versions. Expected: 07e41ccb166d21a5327d5a2ae1bb48192b8470e1357266c9d119c294cb1e95978569472c9de64fb6d93cbd4dd0aed0bf1e7c47fd1920de17b038a08a85eb4fa1; Found: 17e41ccb166d21a5327d5a2ae1bb48192b8470e1357266c9d119c294cb1e95978569472c9de64fb6d93cbd4dd0aed0bf1e7c47fd1920de17b038a08a85eb4fa1",
),
root_error(
ErrorCode::E092,
"Content file v1/content/file-1.txt failed sha512 fixity check. Expected: 17e41ccb166d21a5327d5a2ae1bb48192b8470e1357266c9d119c294cb1e95978569472c9de64fb6d93cbd4dd0aed0bf1e7c47fd1920de17b038a08a85eb4fa1; Found: 07e41ccb166d21a5327d5a2ae1bb48192b8470e1357266c9d119c294cb1e95978569472c9de64fb6d93cbd4dd0aed0bf1e7c47fd1920de17b038a08a85eb4fa1",
),
]);
no_warnings(&result);
}
#[test]
fn inconsistent_version_state() {
let result = official_error_test("E066_inconsistent_version_state");
has_errors(
&result,
&[
version_error(
"v1",
ErrorCode::E066,
"Inventory version v1 state contains a path not in later inventories: 2.txt",
),
version_error(
"v1",
ErrorCode::E066,
"Inventory version v1 state contains a path not in later inventories: 1.txt",
),
version_error(
"v1",
ErrorCode::E066,
"Inventory version v1 state contains a path not in later inventories: 3.txt",
),
],
);
no_warnings(&result);
}
#[test]
fn file_in_extensions_dir() {
let result = official_error_test("E067_file_in_extensions_dir");
has_errors(
&result,
&[root_error(
ErrorCode::E067,
"Extensions directory contains an illegal file: extra_file",
)],
);
has_warnings(
&result,
&[
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'message'",
),
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'user'",
),
root_warning(
WarnCode::W013,
"Extensions directory contains unknown extension: unregistered",
),
],
);
}
#[test]
fn algorithm_change_incorrect_digest() {
let result = official_error_test("E092_algorithm_change_incorrect_digest");
has_errors(&result, &[
root_error(
ErrorCode::E092,
"Content file v1/content/file-3.txt failed sha512 fixity check. Expected: 13b26d26c9d8cfbb884b50e798f93ac6bef275a018547b1560af3e6d38f2723785731d3ca6338682fa7ac9acb506b3c594a125ce9d3d60cd14498304cc864cf2; Found: b3b26d26c9d8cfbb884b50e798f93ac6bef275a018547b1560af3e6d38f2723785731d3ca6338682fa7ac9acb506b3c594a125ce9d3d60cd14498304cc864cf2",
),
root_error(
ErrorCode::E092,
"Content file v1/content/file-1.txt failed sha512 fixity check. Expected: 17e41ccb166d21a5327d5a2ae1bb48192b8470e1357266c9d119c294cb1e95978569472c9de64fb6d93cbd4dd0aed0bf1e7c47fd1920de17b038a08a85eb4fa1; Found: 07e41ccb166d21a5327d5a2ae1bb48192b8470e1357266c9d119c294cb1e95978569472c9de64fb6d93cbd4dd0aed0bf1e7c47fd1920de17b038a08a85eb4fa1",
),
root_error(
ErrorCode::E092,
"Content file v1/content/file-2.txt failed sha512 fixity check. Expected: 1fef2458ee1a9277925614272adfe60872f4c1bf02eecce7276166957d1ab30f65cf5c8065a294bf1b13e3c3589ba936a3b5db911572e30dfcb200ef71ad33d5; Found: 9fef2458ee1a9277925614272adfe60872f4c1bf02eecce7276166957d1ab30f65cf5c8065a294bf1b13e3c3589ba936a3b5db911572e30dfcb200ef71ad33d5",
),
]);
has_warnings(
&result,
&[root_warning(
WarnCode::W004,
"Inventory 'digestAlgorithm' should be 'sha512'. Found: sha256",
)],
);
}
#[test]
fn content_file_digest_mismatch() {
let result = official_error_test("E092_content_file_digest_mismatch");
has_errors(&result, &[
root_error(
ErrorCode::E092,
"Content file v1/content/test.txt failed sha512 fixity check. Expected: 24f950aac7b9ea9b3cb728228a0c82b67c39e96b4b344798870d5daee93e3ae5931baae8c7cacfea4b629452c38026a81d138bc7aad1af3ef7bfd5ec646d6c28; Found: 1277a792c8196a2504007a40f31ed93bf826e71f16273d8503f7d3e46503d00b8d8cda0a59d6a33b9c1aebc84ea6a79f7062ee080f4a9587055a7b6fb92f5fa8",
),
]);
no_warnings(&result);
}
#[test]
fn content_path_does_not_exist() {
let result = official_error_test("E092_E093_content_path_does_not_exist");
has_errors(&result, &[
root_error(
ErrorCode::E092,
"Inventory manifest references a file that does not exist in a content directory: v1/content/bonus.txt",
),
root_error(
ErrorCode::E093,
"Inventory fixity references a file that does not exist in a content directory: v1/content/bonus.txt",
),
]);
no_warnings(&result);
}
#[test]
fn fixity_digest_mismatch() {
let result = official_error_test("E093_fixity_digest_mismatch");
has_errors(&result, &[
root_error(
ErrorCode::E093,
"Content file v1/content/test.txt failed md5 fixity check. Expected: 9eacfb9289073dd9c9a8c4cdf820ac71; Found: eb1a3227cdc3fedbaec2fe38bf6c044a",
),
]);
no_warnings(&result);
}
#[test]
fn conflicting_logical_paths() {
let result = official_error_test("E095_conflicting_logical_paths");
has_errors(&result, &[
root_error(
ErrorCode::E095,
"In inventory version v1, state contains a path, 'sub-path/a_file.txt', that conflicts with another path, 'sub-path'",
),
]);
no_warnings(&result);
}
#[test]
fn non_unique_logical_paths() {
let result = official_error_test("E095_non_unique_logical_paths");
has_errors(
&result,
&[
root_error(
ErrorCode::E095,
"In inventory version v1, state contains duplicate path 'file-1.txt'",
),
root_error(
ErrorCode::E095,
"In inventory version v1, state contains duplicate path 'file-3.txt'",
),
],
);
no_warnings(&result);
}
#[test]
fn manifest_duplicate_digests() {
let result = official_error_test("E096_manifest_duplicate_digests");
has_errors(&result, &[
root_error(
ErrorCode::E101,
"Inventory manifest contains duplicate path 'v1/content/test.txt'",
),
root_error(
ErrorCode::E096,
"Inventory manifest contains a duplicate key '24F950AAC7B9EA9B3CB728228A0C82B67C39E96B4B344798870D5DAEE93E3AE5931BAAE8C7CACFEA4B629452C38026A81D138BC7AAD1AF3EF7BFD5EC646D6C28'",
),
]);
no_warnings(&result);
}
#[test]
fn fixity_duplicate_digests() {
let result = official_error_test("E097_fixity_duplicate_digests");
has_errors(&result, &[
root_error(
ErrorCode::E101,
"Inventory fixity block 'md5' contains duplicate path 'v1/content/test.txt'",
),
root_error(
ErrorCode::E097,
"Inventory fixity block 'md5' contains duplicate digest 'eb1a3227cdc3fedbaec2fe38bf6c044a'",
),
]);
no_warnings(&result);
}
#[test]
fn fixity_invalid_content_paths() {
let result = official_error_test("E100_E099_fixity_invalid_content_paths");
has_errors(&result, &[
root_error(
ErrorCode::E099,
"Inventory fixity block 'md5' contains a path containing an illegal path part. Found: v1/content/../content/file-1.txt",
),
root_error(
ErrorCode::E100,
"Inventory fixity block 'md5' contains a path with a leading/trailing '/'. Found: /v1/content/file-3.txt",
),
root_error(
ErrorCode::E099,
"Inventory fixity block 'md5' contains a path containing an illegal path part. Found: v1/content//file-2.txt",
),
]);
no_warnings(&result);
}
#[test]
fn manifest_invalid_content_paths() {
let result = official_error_test("E100_E099_manifest_invalid_content_paths");
has_errors(&result, &[
root_error(
ErrorCode::E100,
"Inventory manifest key 'b3b26d26c9d8cfbb884b50e798f93ac6bef275a018547b1560af3e6d38f2723785731d3ca6338682fa7ac9acb506b3c594a125ce9d3d60cd14498304cc864cf2' contains a path with a leading/trailing '/'. Found: /v1/content/file-3.txt",
),
root_error(
ErrorCode::E099,
"Inventory manifest key '07e41ccb166d21a5327d5a2ae1bb48192b8470e1357266c9d119c294cb1e95978569472c9de64fb6d93cbd4dd0aed0bf1e7c47fd1920de17b038a08a85eb4fa1' contains a path containing an illegal path part. Found: v1/content/../content/file-1.txt",
),
root_error(
ErrorCode::E099,
"Inventory manifest key '9fef2458ee1a9277925614272adfe60872f4c1bf02eecce7276166957d1ab30f65cf5c8065a294bf1b13e3c3589ba936a3b5db911572e30dfcb200ef71ad33d5' contains a path containing an illegal path part. Found: v1/content//file-2.txt",
),
]);
no_warnings(&result);
}
#[test]
fn non_unique_content_paths() {
let result = official_error_test("E101_non_unique_content_paths");
has_errors(
&result,
&[root_error(
ErrorCode::E101,
"Inventory manifest contains duplicate path 'v1/content/test.txt'",
)],
);
no_warnings(&result);
}
#[test]
fn zero_padded_versions() {
let result = official_warn_test("W001_W004_W005_zero_padded_versions");
no_errors(&result);
has_warnings(
&result,
&[
root_warning(
WarnCode::W005,
"Inventory 'id' should be a URI. Found: bb123cd4567",
),
root_warning(
WarnCode::W004,
"Inventory 'digestAlgorithm' should be 'sha512'. Found: sha256",
),
root_warning(WarnCode::W001, "Contains zero-padded version numbers"),
version_warning(
"v0003",
WarnCode::W005,
"Inventory 'id' should be a URI. Found: bb123cd4567",
),
version_warning(
"v0003",
WarnCode::W004,
"Inventory 'digestAlgorithm' should be 'sha512'. Found: sha256",
),
version_warning(
"v0003",
WarnCode::W001,
"Contains zero-padded version numbers",
),
version_warning(
"v0002",
WarnCode::W005,
"Inventory 'id' should be a URI. Found: bb123cd4567",
),
version_warning(
"v0002",
WarnCode::W004,
"Inventory 'digestAlgorithm' should be 'sha512'. Found: sha256",
),
version_warning(
"v0002",
WarnCode::W001,
"Contains zero-padded version numbers",
),
version_warning(
"v0001",
WarnCode::W005,
"Inventory 'id' should be a URI. Found: bb123cd4567",
),
version_warning(
"v0001",
WarnCode::W004,
"Inventory 'digestAlgorithm' should be 'sha512'. Found: sha256",
),
version_warning(
"v0001",
WarnCode::W001,
"Contains zero-padded version numbers",
),
],
);
}
#[test]
fn zero_padded_versions_2() {
let result = official_warn_test("W001_zero_padded_versions");
no_errors(&result);
has_warnings(
&result,
&[
root_warning(WarnCode::W001, "Contains zero-padded version numbers"),
version_warning(
"v002",
WarnCode::W001,
"Contains zero-padded version numbers",
),
version_warning(
"v001",
WarnCode::W001,
"Contains zero-padded version numbers",
),
],
);
}
#[test]
fn extra_dir_in_version_dir() {
let result = official_warn_test("W002_extra_dir_in_version_dir");
no_errors(&result);
has_warnings(
&result,
&[version_warning(
"v1",
WarnCode::W002,
"Version directory contains unexpected directory: extra_dir",
)],
);
}
#[test]
fn uses_sha256() {
let result = official_warn_test("W004_uses_sha256");
no_errors(&result);
has_warnings(
&result,
&[root_warning(
WarnCode::W004,
"Inventory 'digestAlgorithm' should be 'sha512'. Found: sha256",
)],
);
}
#[test]
fn versions_diff_digests() {
let result = official_warn_test("W004_versions_diff_digests");
no_errors(&result);
has_warnings(
&result,
&[version_warning(
"v1",
WarnCode::W004,
"Inventory 'digestAlgorithm' should be 'sha512'. Found: sha256",
)],
);
}
#[test]
fn id_not_uri() {
let result = official_warn_test("W005_id_not_uri");
no_errors(&result);
has_warnings(
&result,
&[root_warning(
WarnCode::W005,
"Inventory 'id' should be a URI. Found: not_a_uri",
)],
);
}
#[test]
fn no_message_or_user() {
let result = official_warn_test("W007_no_message_or_user");
no_errors(&result);
has_warnings(
&result,
&[
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'message'",
),
root_warning(
WarnCode::W007,
"Inventory version 'v1' is missing recommended key 'user'",
),
],
);
}
#[test]
fn user_no_address() {
let result = official_warn_test("W008_user_no_address");
no_errors(&result);
has_warnings(
&result,
&[root_warning(
WarnCode::W008,
"Inventory version 'v1' is missing recommended key 'address'",
)],
);
}
#[test]
fn user_address_not_uri() {
let result = official_warn_test("W009_user_address_not_uri");
no_errors(&result);
has_warnings(&result, &[
root_warning(WarnCode::W009, "Inventory version v1 user 'address' should be a URI. Found: 1 Wonky Way, Wibblesville, WW"),
]);
}
#[test]
fn no_version_inventory() {
let result = official_warn_test("W010_no_version_inventory");
no_errors(&result);
has_warnings(
&result,
&[version_warning(
"v1",
WarnCode::W010,
"Inventory file does not exist",
)],
);
}
#[test]
fn version_inv_diff_metadata() {
let result = official_warn_test("W011_version_inv_diff_metadata");
no_errors(&result);
has_warnings(
&result,
&[
version_warning(
"v1",
WarnCode::W011,
"Inventory version v1 'message' is inconsistent with the root inventory",
),
version_warning(
"v1",
WarnCode::W011,
"Inventory version v1 'created' is inconsistent with the root inventory",
),
version_warning(
"v1",
WarnCode::W011,
"Inventory version v1 'user' is inconsistent with the root inventory",
),
],
);
}
#[test]
fn unregistered_extension() {
let result = official_warn_test("W013_unregistered_extension");
no_errors(&result);
has_warnings(
&result,
&[root_warning(
WarnCode::W013,
"Extensions directory contains unknown extension: unregistered",
)],
);
}
#[test]
fn official_valid() {
let names = [
"minimal_content_dir_called_stuff",
"minimal_mixed_digests",
"minimal_no_content",
"minimal_one_version_one_file",
"minimal_uppercase_digests",
"ocfl_object_all_fixity_digests",
"spec-ex-full",
"updates_all_actions",
"updates_three_versions_one_file",
];
for name in names {
let result = official_valid_test(name);
assert!(
!result.has_errors(),
"{} should have no errors; found: {:?}",
name,
result.errors()
);
assert!(
!result.has_warnings(),
"{} should have no warnings; found: {:?}",
name,
result.warnings()
);
}
}
#[test]
#[should_panic(expected = "Not found: Object at path bogus")]
fn validate_object_does_not_exist() {
official_warn_test("bogus");
}
#[test]
fn validate_valid_repo() {
let repo = new_repo(&repo_test_path("valid"));
let mut validator = repo.validate_repo(true).unwrap();
no_errors_storage(validator.storage_root_result());
no_warnings_storage(validator.storage_root_result());
for result in &mut validator {
let result = result.unwrap();
no_errors(&result);
no_warnings(&result);
}
no_errors_storage(validator.storage_hierarchy_result());
no_warnings_storage(validator.storage_hierarchy_result());
}
#[test]
fn validate_invalid_repo() {
let repo = new_repo(&repo_test_path("invalid"));
let mut validator = repo.validate_repo(true).unwrap();
has_errors_storage(
&validator.storage_root_result(),
&[
ValidationError::new(
ProblemLocation::StorageRoot,
ErrorCode::E069,
"Root version declaration does not exist".to_string(),
),
ValidationError::new(
ProblemLocation::StorageRoot,
ErrorCode::E067,
"Extensions directory contains an illegal file: file.txt".to_string(),
),
],
);
has_warnings_storage(
&validator.storage_root_result(),
&[ValidationWarning::new(
ProblemLocation::StorageRoot,
WarnCode::W013,
"Extensions directory contains unknown extension: bogus-ext".to_string(),
)],
);
for result in &mut validator {
let result = result.unwrap();
match result.object_id.as_ref().unwrap().as_ref() {
"urn:example:rocfl:obj-2" => {
error_count(2, &result);
warning_count(0, &result);
}
"urn:example:rocfl:obj-1" => {
no_errors(&result);
no_warnings(&result);
}
id => {
panic!("Unexpected object: {}", id)
}
}
}
has_errors_storage(&validator.storage_hierarchy_result(), &[
ValidationError::new(ProblemLocation::StorageHierarchy, ErrorCode::E072, "Found a file in the storage hierarchy: b01/0ba/world.txt".to_string()),
ValidationError::new(ProblemLocation::StorageHierarchy, ErrorCode::E072, "Found a file in the storage hierarchy: b99/7a6/7ea/b997a67eacd839691ff9d6e490c5654e14a1783d460e4a4ef8d027547ddbf9e2/v1/content/dir/sub/file3.txt".to_string()),
ValidationError::new(ProblemLocation::StorageHierarchy, ErrorCode::E072, "Found a file in the storage hierarchy: b99/7a6/7ea/b997a67eacd839691ff9d6e490c5654e14a1783d460e4a4ef8d027547ddbf9e2/v1/content/dir/file2.txt".to_string()),
ValidationError::new(ProblemLocation::StorageHierarchy, ErrorCode::E072, "Found a file in the storage hierarchy: b99/7a6/7ea/b997a67eacd839691ff9d6e490c5654e14a1783d460e4a4ef8d027547ddbf9e2/v1/content/file1.txt".to_string()),
ValidationError::new(ProblemLocation::StorageHierarchy, ErrorCode::E072, "Found a file in the storage hierarchy: b99/7a6/7ea/b997a67eacd839691ff9d6e490c5654e14a1783d460e4a4ef8d027547ddbf9e2/v1/inventory.json".to_string()),
ValidationError::new(ProblemLocation::StorageHierarchy, ErrorCode::E072, "Found a file in the storage hierarchy: b99/7a6/7ea/b997a67eacd839691ff9d6e490c5654e14a1783d460e4a4ef8d027547ddbf9e2/v1/inventory.json.sha512".to_string()),
ValidationError::new(ProblemLocation::StorageHierarchy, ErrorCode::E072, "Found a file in the storage hierarchy: b99/7a6/7ea/b997a67eacd839691ff9d6e490c5654e14a1783d460e4a4ef8d027547ddbf9e2/inventory.json".to_string()),
ValidationError::new(ProblemLocation::StorageHierarchy, ErrorCode::E072, "Found a file in the storage hierarchy: b99/7a6/7ea/b997a67eacd839691ff9d6e490c5654e14a1783d460e4a4ef8d027547ddbf9e2/inventory.json.sha512".to_string()),
]);
no_warnings_storage(validator.storage_hierarchy_result());
}
fn official_valid_test(name: &str) -> ObjectValidationResult {
let repo = new_repo(official_valid_root());
repo.validate_object_at(name, true).unwrap()
}
fn official_error_test(name: &str) -> ObjectValidationResult {
let repo = new_repo(official_error_root());
repo.validate_object_at(name, true).unwrap()
}
fn official_warn_test(name: &str) -> ObjectValidationResult {
let repo = new_repo(official_warn_root());
repo.validate_object_at(name, true).unwrap()
}
fn repo_test_path(name: &str) -> PathBuf {
let mut path = validate_repo_root();
path.push("custom");
path.push("repos");
path.push(name);
path
}
fn new_repo(root: impl AsRef<Path>) -> OcflRepo {
OcflRepo::fs_repo(root, None).unwrap()
}
fn official_valid_root() -> PathBuf {
let mut path = validate_repo_root();
path.push("official-1.0");
path.push("valid");
path
}
fn official_error_root() -> PathBuf {
let mut path = validate_repo_root();
path.push("official-1.0");
path.push("error");
path
}
fn official_warn_root() -> PathBuf {
let mut path = validate_repo_root();
path.push("official-1.0");
path.push("warn");
path
}
fn validate_repo_root() -> PathBuf {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("resources");
path.push("test");
path.push("validate");
path
}