#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
fn declared_modules(source: &str) -> Vec<String> {
source
.lines()
.filter_map(|line| {
let name = line.trim().strip_prefix("pub mod ")?.strip_suffix(';')?;
Some(name.trim().to_owned())
})
.collect()
}
fn runtime_modules() -> Vec<String> {
declared_modules(include_str!("../../turnframe-runtime/src/lib.rs"))
}
fn core_modules() -> Vec<String> {
declared_modules(include_str!("../../turnframe-core/src/lib.rs"))
}
fn facade_modules() -> Vec<String> {
const OPENING: &str = "pub use turnframe_runtime::{";
let source = include_str!("../src/lib.rs");
let start = source
.find(OPENING)
.expect("the facade re-exports a block of runtime modules");
let rest = &source[start + OPENING.len()..];
let end = rest.find("};").expect("the re-export block is closed");
rest[..end]
.split(',')
.map(|name| name.trim().to_owned())
.filter(|name| !name.is_empty())
.collect()
}
#[test]
fn every_runtime_module_is_reachable_through_the_facade() {
let declared = runtime_modules();
assert!(
declared.len() > 10,
"the runtime's module list did not parse, so this test proves nothing: {declared:?}"
);
let exported = facade_modules();
let missing: Vec<&String> = declared
.iter()
.filter(|name| !exported.contains(name))
.collect();
assert!(
missing.is_empty(),
"these runtime modules are not re-exported by the facade, so an adopter depending on \
`turnframe` cannot name any type in them: {missing:?}. Add them to the \
`pub use turnframe_runtime::{{…}}` block in crates/turnframe/src/lib.rs."
);
let unknown: Vec<&String> = exported
.iter()
.filter(|name| !declared.contains(name))
.collect();
assert!(
unknown.is_empty(),
"the facade re-exports names the runtime does not declare: {unknown:?}"
);
}
#[test]
fn every_core_module_is_reachable_through_the_facade() {
let facade = include_str!("../src/lib.rs");
let missing: Vec<String> = core_modules()
.into_iter()
.filter(|name| !facade.contains(&format!("pub use turnframe_core::{name}::*;")))
.collect();
assert!(
missing.is_empty(),
"these core modules have no module of their own in the facade, so anything in them that \
is not individually re-exported at the root cannot be named at all: {missing:?}. Add a \
`pub mod` wrapper for each in crates/turnframe/src/lib.rs."
);
}
#[test]
fn a_public_type_always_lives_in_a_public_module() {
for (crate_name, source) in [
(
"turnframe-core",
include_str!("../../turnframe-core/src/lib.rs"),
),
(
"turnframe-runtime",
include_str!("../../turnframe-runtime/src/lib.rs"),
),
] {
let modules = declared_modules(source);
let smuggled: Vec<&str> = source
.lines()
.map(str::trim)
.filter_map(|line| line.strip_prefix("pub use crate::"))
.filter(|rest| {
let first = rest.split(&[':', '{', ';'][..]).next().unwrap_or_default();
!first.is_empty() && !modules.iter().any(|module| module == first)
})
.collect();
assert!(
smuggled.is_empty(),
"{crate_name} makes types public from somewhere that is not one of its public \
modules: {smuggled:?}. The facade wraps this crate one module at a time, so a type \
that reaches the outside any other way reaches no facade module at all. Give it a \
public module, or add it to the facade's root and prelude deliberately."
);
}
}
mod the_shadow_path_can_be_written_down {
use turnframe::runtime::divergence::TurnSummary;
use turnframe::runtime::planning::{PlannedTurn, SeededCase, SeededTurnPlanner, TurnPlanner};
#[allow(dead_code)]
struct ShadowRunner {
planner: TurnPlanner,
}
#[allow(dead_code)]
struct CorpusReplayer {
planner: SeededTurnPlanner,
cases: Vec<SeededCase>,
}
#[allow(dead_code)]
fn summarize(planned: &PlannedTurn) -> TurnSummary {
planned.summary()
}
#[allow(dead_code)]
fn would_have_done(planned: &PlannedTurn) -> (usize, usize, usize) {
(
planned.would_persist.len(),
planned.would_execute.len(),
planned.would_claim.len(),
)
}
}
mod the_divergence_vocabulary_can_be_written_down {
use turnframe::runtime::divergence::{
Attribution, Divergence, DivergenceReport, Finding, RefusalReason, Side, TurnSummary,
compare,
};
#[allow(dead_code)]
struct ComparisonRow {
report: DivergenceReport,
blamed: Attribution,
looked_at: Side,
}
#[allow(dead_code)]
fn run(shadow: &TurnSummary, authoritative: &TurnSummary) -> DivergenceReport {
compare(shadow, authoritative)
}
#[allow(dead_code)]
fn label(finding: &Finding) -> (&'static str, &'static str) {
let kind: &'static str = Divergence::kind(&finding.divergence);
(kind, finding.attribution.as_str())
}
#[allow(dead_code)]
fn blames_the_target(reason: &RefusalReason) -> bool {
matches!(reason, RefusalReason::AmbiguousTarget) || reason.is_unresolved_target()
}
#[allow(dead_code)]
fn is_the_asymmetry(divergence: &Divergence) -> bool {
matches!(
divergence,
Divergence::RefusedUnresolvedTargetThatRan { .. }
)
}
}
mod the_outbox_traits_can_be_implemented {
use async_trait::async_trait;
use turnframe::event::OutboxEntry;
use turnframe::runtime::dispatch::{
DispatchConfig, DispatchReport, Dispatched, OutboxDispatcher, OutboxSender,
};
struct AlwaysAccepts;
#[async_trait]
impl OutboxSender for AlwaysAccepts {
async fn send(&self, _entry: &OutboxEntry) -> Dispatched {
Dispatched::Completed { remote_ref: None }
}
}
#[allow(dead_code)]
fn configure(config: DispatchConfig) -> DispatchConfig {
config
}
#[allow(dead_code)]
fn unresolved(report: &DispatchReport) -> usize {
report.unknown.len() + report.unsettled.len()
}
#[allow(dead_code)]
fn hand_over(dispatcher: OutboxDispatcher) -> OutboxDispatcher {
dispatcher
}
#[allow(dead_code)]
fn as_sender(sender: &AlwaysAccepts) -> &dyn OutboxSender {
sender
}
}
mod budgets_and_resumption_can_be_written_down {
use turnframe::runtime::budget::{BudgetLimit, BudgetSpend, TurnBudget};
use turnframe::runtime::resume::{DeferredAct, Resumption};
#[allow(dead_code)]
struct Limits {
budget: TurnBudget,
}
#[allow(dead_code)]
fn over(
budget: &TurnBudget,
spent: BudgetSpend,
now: chrono::DateTime<chrono::Utc>,
) -> Option<BudgetLimit> {
budget.exhausted(spent, now)
}
#[allow(dead_code)]
fn continues(resumption: &Resumption) -> bool {
resumption.act().is_some()
}
#[allow(dead_code)]
fn deferred_key(act: &DeferredAct) -> &DeferredAct {
act
}
}
mod prelude_level_types_can_be_built_from_the_prelude {
use turnframe::prelude::*;
#[allow(dead_code)]
fn every_command_origin() -> Vec<CommandOrigin> {
vec![
CommandOrigin::DirectSafeUserAct {
evidence_digest: Digest::of_bytes(b"evidence"),
},
CommandOrigin::ConfirmedInteraction {
interaction_id: InteractionId::new(),
payload_hash: Digest::of_bytes(b"payload"),
interaction_kind: InteractionKind::ConfirmCommand,
action_class: ActionClass::ConfirmsCommands,
channel: ResolutionChannel::Click,
},
CommandOrigin::InternalPolicy {
policy_key: String::from("nightly-reconcile"),
},
CommandOrigin::ExternalCallback {
callback_id: String::from("cb-1"),
signature_verified: true,
},
]
}
#[allow(dead_code)]
fn a_rejection() -> DomainRejection {
DomainRejection::new(RejectionCode::from("trip.locked"), "trip.locked.message")
}
#[allow(dead_code)]
fn its_copy() -> LocalizedText {
LocalizedText::new("That trip is locked.")
.with(Locale::from("it-IT"), "Quel viaggio è bloccato.")
}
#[allow(dead_code)]
fn an_actor() -> ActorContext {
ActorContext::new(AccountId::from("acct-1"), UserId::from("user-1"))
}
}
mod a_response_block_can_be_built_from_outside {
use turnframe::plan::AnswerBasis;
use turnframe::prelude::*;
use turnframe::response::{AnswerStatus, GeneratedAnswer};
#[allow(dead_code)]
fn an_answer(block_id: turnframe::ids::BlockId) -> ResponseBlock {
ResponseBlock::Answer(GeneratedAnswer {
block_id,
question_id: None,
text: String::from("The trip is called Lisbon."),
basis: AnswerBasis::CurrentCommittedState,
status: AnswerStatus::Answered,
facts_used: Vec::new(),
citations: Vec::new(),
enumerations: Vec::new(),
})
}
#[allow(dead_code)]
fn every_basis() -> Vec<AnswerBasis> {
vec![
AnswerBasis::CurrentCommittedState,
AnswerBasis::ProposedState,
AnswerBasis::CommittedStateAfterTurn,
AnswerBasis::GeneralDomainKnowledge,
]
}
}