v0.3.6-pre.002

This commit is contained in:
2026-09-01 10:45:39 +02:00
parent ecfc30a94b
commit a25d8dd55a
17 changed files with 1087 additions and 18 deletions

View 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;
}

View 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;
}

View 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;
}

View 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;
}