v0.3.6-pre.002
This commit is contained in:
81
crates/ksp-job-api/tests/dependency_boundary.rs
Normal file
81
crates/ksp-job-api/tests/dependency_boundary.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
// file: crates/ksp-job-api/tests/dependency_boundary.rs
|
||||
// version: 1
|
||||
|
||||
//! Dependency and runtime-neutrality canaries for the Job API foundation.
|
||||
|
||||
#[test]
|
||||
fn pre_002_manifest_has_exact_core_only_dependency_graph() {
|
||||
let manifest = include_str!("../Cargo.toml");
|
||||
assert!(!manifest.contains("[features]"));
|
||||
assert!(!manifest.contains("[dev-dependencies]"));
|
||||
assert!(!manifest.contains("[build-dependencies]"));
|
||||
assert_eq!(manifest.matches("[dependencies]").count(), 1);
|
||||
let dependencies_tail = manifest.split("[dependencies]").nth(1);
|
||||
assert!(dependencies_tail.is_some());
|
||||
let dependencies_tail = match dependencies_tail {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let dependencies = match dependencies_tail.split("[lints]").next() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
assert_eq!(manifest_dependency_names(dependencies), std::vec!["ksp-core-lib"]);
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_production_sources_forbid_runtime_domain_and_wire_dependencies() {
|
||||
let sources = [
|
||||
include_str!("../src/cancellation.rs"),
|
||||
include_str!("../src/error.rs"),
|
||||
include_str!("../src/identity.rs"),
|
||||
include_str!("../src/lib.rs"),
|
||||
include_str!("../src/lifecycle.rs"),
|
||||
];
|
||||
for source in sources {
|
||||
for forbidden in [
|
||||
"ksp_config_lib::",
|
||||
"ksp_interface_lib::",
|
||||
"ksp_logging_lib::",
|
||||
"ksp_offchain_transport_lib::",
|
||||
"ksp_onchain_transport_lib::",
|
||||
"ksp_store_api::",
|
||||
"ksp_store_lib::",
|
||||
"ksp_worker",
|
||||
"reqwest::",
|
||||
"serde::",
|
||||
"serde_json::",
|
||||
"solana_",
|
||||
"tauri::",
|
||||
"tokio::",
|
||||
"tonic::",
|
||||
concat!("tracing", "::"),
|
||||
] {
|
||||
assert!(!source.contains(forbidden), "forbidden Job API dependency path detected: {forbidden}");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fn manifest_dependency_names(section: &str) -> std::vec::Vec<&str> {
|
||||
let mut names = std::vec::Vec::new();
|
||||
for line in section.lines() {
|
||||
let content = match line.split('#').next() {
|
||||
std::option::Option::Some(value) => value.trim(),
|
||||
std::option::Option::None => continue,
|
||||
};
|
||||
if content.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let name = match content.split('=').next() {
|
||||
std::option::Option::Some(value) => value.trim().trim_end_matches(".workspace"),
|
||||
std::option::Option::None => continue,
|
||||
};
|
||||
if !name.is_empty() {
|
||||
names.push(name);
|
||||
}
|
||||
}
|
||||
names.sort_unstable();
|
||||
return names;
|
||||
}
|
||||
53
crates/ksp-job-api/tests/public_api.rs
Normal file
53
crates/ksp-job-api/tests/public_api.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
// file: crates/ksp-job-api/tests/public_api.rs
|
||||
// version: 1
|
||||
|
||||
//! External-consumer canaries for the public Job API foundation.
|
||||
|
||||
#[test]
|
||||
fn pre_002_identity_lifecycle_and_cancellation_are_consumable_from_crate_root() {
|
||||
let id = ksp_job_api::JobId::new("campaign-2026.09.01:001");
|
||||
let kind = ksp_job_api::JobKindCode::new("backfill_raw");
|
||||
assert!(id.is_ok());
|
||||
assert!(kind.is_ok());
|
||||
let id = match id {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let kind = match kind {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let mut lifecycle = ksp_job_api::JobLifecycle::new(id.clone(), kind.clone());
|
||||
assert_eq!(lifecycle.id(), &id);
|
||||
assert_eq!(lifecycle.kind(), &kind);
|
||||
assert_eq!(lifecycle.state(), ksp_job_api::JobState::Created);
|
||||
assert!(lifecycle.start().is_ok());
|
||||
assert!(lifecycle.complete(ksp_job_api::JobCompletion::Partial).is_ok());
|
||||
assert_eq!(lifecycle.state().completion(), std::option::Option::Some(ksp_job_api::JobCompletion::Partial));
|
||||
let token = ksp_job_api::JobCancellationToken::new();
|
||||
let clone = token.clone();
|
||||
assert!(token.cancel());
|
||||
assert!(clone.is_cancellation_requested());
|
||||
assert!(!clone.cancel());
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_error_codes_are_stable_and_core_owned() {
|
||||
let codes: [ksp_core_lib::ErrorCode; 3] =
|
||||
[ksp_job_api::ERROR_CODE_JOB_ID_INVALID, ksp_job_api::ERROR_CODE_JOB_KIND_INVALID, ksp_job_api::ERROR_CODE_JOB_TRANSITION_INVALID];
|
||||
assert_eq!(codes[0].domain(), "job_api");
|
||||
assert_eq!(codes[0].code(), "job_id_invalid");
|
||||
assert_eq!(codes[1].domain(), "job_api");
|
||||
assert_eq!(codes[1].code(), "job_kind_invalid");
|
||||
assert_eq!(codes[2].domain(), "job_api");
|
||||
assert_eq!(codes[2].code(), "job_transition_invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_public_bounds_are_exact() {
|
||||
assert_eq!(ksp_job_api::MAX_JOB_ID_BYTES, 128);
|
||||
assert_eq!(ksp_job_api::MAX_JOB_KIND_CODE_BYTES, 128);
|
||||
return;
|
||||
}
|
||||
102
crates/ksp-job-api/tests/release_completeness.rs
Normal file
102
crates/ksp-job-api/tests/release_completeness.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
// file: crates/ksp-job-api/tests/release_completeness.rs
|
||||
// version: 1
|
||||
|
||||
//! Completeness canaries for the initial Job API foundation.
|
||||
|
||||
#[test]
|
||||
fn pre_002_crate_root_export_inventory_is_exact() {
|
||||
let crate_root = include_str!("../src/lib.rs");
|
||||
let mut actual = std::vec::Vec::new();
|
||||
for line in crate_root.lines() {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.starts_with("pub use ") {
|
||||
actual.push(trimmed);
|
||||
}
|
||||
}
|
||||
actual.sort_unstable();
|
||||
let mut expected = std::vec![
|
||||
"pub use self::cancellation::JobCancellationToken;",
|
||||
"pub use self::error::ERROR_CODE_JOB_ID_INVALID;",
|
||||
"pub use self::error::ERROR_CODE_JOB_KIND_INVALID;",
|
||||
"pub use self::error::ERROR_CODE_JOB_TRANSITION_INVALID;",
|
||||
"pub use self::identity::JobId;",
|
||||
"pub use self::identity::JobKindCode;",
|
||||
"pub use self::identity::MAX_JOB_ID_BYTES;",
|
||||
"pub use self::identity::MAX_JOB_KIND_CODE_BYTES;",
|
||||
"pub use self::lifecycle::JobCompletion;",
|
||||
"pub use self::lifecycle::JobLifecycle;",
|
||||
"pub use self::lifecycle::JobState;",
|
||||
"pub use ksp_core_lib::Error;",
|
||||
"pub use ksp_core_lib::ErrorCode;",
|
||||
"pub use ksp_core_lib::ErrorContext;",
|
||||
"pub use ksp_core_lib::Result;",
|
||||
];
|
||||
expected.sort_unstable();
|
||||
assert_eq!(actual, expected);
|
||||
assert!(!crate_root.contains("pub mod "));
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_production_module_inventory_is_exact() -> std::io::Result<()> {
|
||||
let source_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
|
||||
let entries = match std::fs::read_dir(source_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut names = std::vec::Vec::new();
|
||||
for entry in entries {
|
||||
let entry = match entry {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let file_type = match entry.file_type() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if !file_type.is_file() {
|
||||
continue;
|
||||
}
|
||||
let name = match entry.file_name().into_string() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => continue,
|
||||
};
|
||||
if name.ends_with(".rs") {
|
||||
names.push(name);
|
||||
}
|
||||
}
|
||||
names.sort_unstable();
|
||||
assert_eq!(names, std::vec!["cancellation.rs", "error.rs", "identity.rs", "lib.rs", "lifecycle.rs"]);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_surface_does_not_open_notifications_backfill_or_worker_contracts() {
|
||||
let sources = [
|
||||
include_str!("../src/cancellation.rs"),
|
||||
include_str!("../src/identity.rs"),
|
||||
include_str!("../src/lib.rs"),
|
||||
include_str!("../src/lifecycle.rs"),
|
||||
];
|
||||
for source in sources {
|
||||
for forbidden in [
|
||||
"JobNotification",
|
||||
"JobNotificationSequence",
|
||||
"JobSnapshot",
|
||||
"JobSnapshotSource",
|
||||
"BackfillRequest",
|
||||
"WorkerControl",
|
||||
"WorkerState",
|
||||
"spawn(",
|
||||
"RawTransaction",
|
||||
"provider",
|
||||
"endpoint",
|
||||
] {
|
||||
assert!(!source.contains(forbidden), "future or domain-specific Job contract leaked early: {forbidden}");
|
||||
}
|
||||
}
|
||||
let lifecycle_source = include_str!("../src/lifecycle.rs");
|
||||
assert!(lifecycle_source.contains("#[derive(Eq, PartialEq)]\npub struct JobLifecycle"));
|
||||
assert!(!lifecycle_source.contains("#[derive(Clone, Eq, PartialEq)]\npub struct JobLifecycle"));
|
||||
return;
|
||||
}
|
||||
72
crates/ksp-job-api/tests/security_hardening.rs
Normal file
72
crates/ksp-job-api/tests/security_hardening.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
// file: crates/ksp-job-api/tests/security_hardening.rs
|
||||
// version: 1
|
||||
|
||||
//! Adversarial lifecycle, cancellation and redaction canaries.
|
||||
|
||||
const HOSTILE_MARKER: &str = "JOB-IDENTITY-SECRET-CANARY";
|
||||
|
||||
#[test]
|
||||
fn pre_002_job_id_and_lifecycle_debug_redact_caller_identity() {
|
||||
let id = ksp_job_api::JobId::new(HOSTILE_MARKER);
|
||||
let kind = ksp_job_api::JobKindCode::new("backfill_raw");
|
||||
assert!(id.is_ok());
|
||||
assert!(kind.is_ok());
|
||||
let id = match id {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let kind = match kind {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert_eq!(std::format!("{id:?}"), "JobId(..)");
|
||||
let lifecycle = ksp_job_api::JobLifecycle::new(id, kind);
|
||||
let debug = std::format!("{lifecycle:?}");
|
||||
assert!(debug.contains("JobLifecycle"));
|
||||
assert!(debug.contains("backfill_raw"));
|
||||
assert!(debug.contains("Created"));
|
||||
assert!(!debug.contains(HOSTILE_MARKER));
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_terminal_states_are_immutable_under_all_public_mutators() {
|
||||
let id = ksp_job_api::JobId::new("terminal-job");
|
||||
let kind = ksp_job_api::JobKindCode::new("test_job");
|
||||
assert!(id.is_ok());
|
||||
assert!(kind.is_ok());
|
||||
let id = match id {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let kind = match kind {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let mut lifecycle = ksp_job_api::JobLifecycle::new(id, kind);
|
||||
assert!(lifecycle.start().is_ok());
|
||||
assert!(lifecycle.complete(ksp_job_api::JobCompletion::Complete).is_ok());
|
||||
assert!(lifecycle.start().is_err());
|
||||
assert!(lifecycle.mark_cancelling().is_err());
|
||||
assert!(lifecycle.complete(ksp_job_api::JobCompletion::Partial).is_err());
|
||||
assert!(lifecycle.mark_cancelled().is_err());
|
||||
assert!(lifecycle.fail().is_err());
|
||||
assert_eq!(lifecycle.state(), ksp_job_api::JobState::Completed(ksp_job_api::JobCompletion::Complete));
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_cancellation_token_is_send_sync_and_cross_thread_visible() {
|
||||
fn require_send_sync<T: std::marker::Send + std::marker::Sync>() {}
|
||||
require_send_sync::<ksp_job_api::JobCancellationToken>();
|
||||
let token = ksp_job_api::JobCancellationToken::new();
|
||||
let worker_token = token.clone();
|
||||
let thread = std::thread::spawn(move || return worker_token.cancel());
|
||||
let request = match thread.join() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert!(request);
|
||||
assert!(token.is_cancellation_requested());
|
||||
return;
|
||||
}
|
||||
Reference in New Issue
Block a user