#![allow(clippy::expect_used, clippy::panic)]
use std::fs;
use std::path::{Path, PathBuf};
use super::COMMANDS;
const DECLARED_DOCUMENTS: &[&str] = &[
"docs/landing/index.html",
"docs/src/guide/backup-restore.md",
"docs/src/guide/checkout/checkout.md",
"docs/src/guide/checkout/snapshot-checkout.md",
"docs/src/guide/checkout/snapshot-materialization.md",
"docs/src/guide/faq.md",
"docs/src/guide/history.md",
"docs/src/guide/ignore.md",
"docs/src/guide/merge-evidence.md",
"docs/src/guide/merge.md",
"docs/src/guide/merge-plan.md",
"docs/src/guide/patches/patch-deletions.md",
"docs/src/guide/patches/patch-inverse.md",
"docs/src/guide/patches/patch-materialization.md",
"docs/src/guide/patches/patch-replay.md",
"docs/src/guide/patches/text-edits.md",
"docs/src/guide/patches/worktree-patch.md",
"docs/src/guide/rollback/rollback-draft.md",
"docs/src/guide/rollback/rollback-draft-verify.md",
"docs/src/guide/rollback/rollback-preview.md",
"docs/src/guide/rollback/sealed-rollback-history.md",
"docs/src/guide/security-setup.md",
"docs/src/guide/status.md",
"docs/src/guide/sync.md",
"docs/src/guide/troubleshooting.md",
"docs/src/guide/tutorial.md",
"docs/src/guide/worktree-status.md",
"docs/src/reference/architecture.md",
"docs/src/reference/commands.md",
"docs/src/reference/concurrency-locking.md",
"docs/src/reference/current-state.md",
"docs/src/reference/data-model-lifecycle.md",
"docs/src/reference/data-model.md",
"docs/src/reference/durability-recovery.md",
"docs/src/reference/git-mapping.md",
"docs/src/reference/integrity-recovery.md",
"docs/src/reference/patch-algebra.md",
"docs/src/reference/path-safety.md",
"docs/src/reference/platform-support.md",
"docs/src/reference/release-compatibility.md",
"docs/src/reference/repository-layout.md",
"docs/src/reference/trust-threat-model.md",
"README.md",
];
const DECLARED_UNDOCUMENTED: &[(&str, &str)] = &[];
fn repo_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(Path::parent)
.expect("prikk-cli's manifest dir has a workspace root two levels up")
.to_path_buf()
}
#[test]
fn every_declared_document_exists() {
let root = repo_root();
for path in DECLARED_DOCUMENTS {
assert!(
root.join(path).is_file(),
"declared document is missing: {path}"
);
}
}
fn html_tag_regions<'a>(text: &'a str, tag: &str) -> Vec<(&'a str, (usize, usize))> {
let lower = text.to_ascii_lowercase();
let open_needle = format!("<{tag}");
let close_needle = format!("</{tag}>");
let mut out = Vec::new();
let mut offset = 0usize;
while let Some(rel) = lower[offset..].find(open_needle.as_str()) {
let start = offset + rel;
let after_name = start + open_needle.len();
let is_real_tag = lower[after_name..]
.chars()
.next()
.is_some_and(|c| c == '>' || c.is_whitespace());
if !is_real_tag {
offset = after_name;
continue;
}
let Some(close_rel) = lower[after_name..].find('>') else {
break;
};
let after_open = after_name + close_rel + 1;
match lower[after_open..].find(close_needle.as_str()) {
Some(end_rel) => {
let end = after_open + end_rel + close_needle.len();
out.push((&text[start..end], (start, end)));
offset = end;
}
None => {
out.push((&text[start..], (start, text.len())));
break;
}
}
}
out
}
fn code_regions(text: &str) -> Vec<&str> {
let mut fenced_ranges = Vec::new();
let mut regions = Vec::new();
let mut offset = 0usize;
while let Some(rel_start) = text[offset..].find("```") {
let start = offset + rel_start;
let after_open = start + 3;
let Some(rel_end) = text[after_open..].find("```") else {
regions.push(&text[start..]);
fenced_ranges.push((start, text.len()));
break;
};
let end = after_open + rel_end + 3;
regions.push(&text[start..end]);
fenced_ranges.push((start, end));
offset = end;
}
for (region, range) in html_tag_regions(text, "pre") {
if fenced_ranges
.iter()
.any(|&(range_start, range_end)| range.0 >= range_start && range.0 < range_end)
{
continue;
}
regions.push(region);
fenced_ranges.push(range);
}
let mut offset = 0usize;
while let Some(rel_start) = text[offset..].find('`') {
let start = offset + rel_start;
if fenced_ranges
.iter()
.any(|&(range_start, range_end)| start >= range_start && start < range_end)
{
offset = start + 1;
continue;
}
let Some(rel_end) = text[start + 1..].find('`') else {
break;
};
let end = start + 1 + rel_end + 1;
regions.push(&text[start..end]);
offset = end;
}
for (region, (start, _end)) in html_tag_regions(text, "code") {
if fenced_ranges
.iter()
.any(|&(range_start, range_end)| start >= range_start && start < range_end)
{
continue;
}
regions.push(region);
}
regions
}
fn command_tokens(region: &str) -> Vec<&str> {
let mut tokens = Vec::new();
let mut rest = region;
while let Some(rel) = rest.find("prikk ") {
let after = &rest[rel + "prikk ".len()..];
let token_len = after
.find(|c: char| !(c.is_ascii_alphanumeric() || c == '-'))
.unwrap_or(after.len());
let token = &after[..token_len];
if token
.chars()
.next()
.is_some_and(|first| first.is_ascii_alphabetic())
{
tokens.push(token);
}
let advance = token_len.max(1).min(after.len());
rest = &after[advance..];
}
tokens
}
fn strip_readme_command_listing(text: &str) -> String {
let Some(start) = text.find("## Useful Commands") else {
return text.to_string();
};
let after_heading = &text[start + "## Useful Commands".len()..];
let end = after_heading
.find("\n## ")
.map_or(text.len(), |rel| start + "## Useful Commands".len() + rel);
let mut result = String::with_capacity(text.len());
result.push_str(&text[..start]);
result.push_str(&text[end..]);
result
}
const LANDING_PAGE_PATH_PREFIX: &str = "docs/landing/";
fn document_text(root: &Path, path: &str) -> String {
if path.starts_with(LANDING_PAGE_PATH_PREFIX) {
return String::new();
}
let text = fs::read_to_string(root.join(path))
.unwrap_or_else(|err| panic!("declared document {path} must read: {err}"));
if path == "README.md" {
strip_readme_command_listing(&text)
} else {
text
}
}
#[test]
fn rule_a_every_documented_command_names_a_real_registry_entry() {
let root = repo_root();
for path in DECLARED_DOCUMENTS {
let text = fs::read_to_string(root.join(path))
.unwrap_or_else(|err| panic!("declared document {path} must read: {err}"));
for region in code_regions(&text) {
for token in command_tokens(region) {
assert!(
COMMANDS.iter().any(|command| command.name == token),
"{path} documents `prikk {token}`, which is not a real registry command name"
);
}
}
}
}
fn is_explained(root: &Path, name: &str) -> bool {
DECLARED_DOCUMENTS.iter().any(|path| {
let text = document_text(root, path);
code_regions(&text)
.iter()
.any(|region| command_tokens(region).contains(&name))
})
}
#[test]
fn rule_b_every_registry_entry_is_explained_or_declared_undocumented() {
let root = repo_root();
for command in COMMANDS {
if DECLARED_UNDOCUMENTED
.iter()
.any(|(name, _)| *name == command.name)
{
continue;
}
assert!(
is_explained(&root, command.name),
"`{}` is neither explained in a declared document outside README's bare command \
listing, nor declared undocumented with a reason in DECLARED_UNDOCUMENTED",
command.name
);
}
}
#[test]
fn declared_undocumented_names_are_real_registry_entries() {
for (name, reason) in DECLARED_UNDOCUMENTED {
assert!(
COMMANDS.iter().any(|command| &command.name == name),
"DECLARED_UNDOCUMENTED names `{name}`, which is not a real registry command"
);
assert!(
!reason.trim().is_empty(),
"DECLARED_UNDOCUMENTED entry for `{name}` has no reason"
);
}
}
#[test]
fn code_regions_finds_bare_html_code_and_pre() {
let code_only = "See <code>prikk seal</code> for details.";
let regions = code_regions(code_only);
assert_eq!(
regions.len(),
1,
"expected exactly one region, got {regions:?}"
);
assert_eq!(
command_tokens(regions.first().expect("checked len == 1 above")),
vec!["seal"]
);
let pre_only = "<pre>prikk verify</pre>";
let regions = code_regions(pre_only);
assert_eq!(
regions.len(),
1,
"expected exactly one region, got {regions:?}"
);
assert_eq!(
command_tokens(regions.first().expect("checked len == 1 above")),
vec!["verify"]
);
}
#[test]
fn code_regions_recognizes_attributed_html_tags() {
for (text, expected) in [
(r#"<code id="c1">prikk commit</code>"#, "commit"),
(r#"<code class="a b">prikk log</code>"#, "log"),
(r#"<pre class="term">prikk doctor</pre>"#, "doctor"),
] {
let regions = code_regions(text);
assert_eq!(
regions.len(),
1,
"{text:?}: expected one region, got {regions:?}"
);
assert_eq!(
command_tokens(regions.first().expect("checked len == 1 above")),
vec![expected],
"input: {text:?}"
);
}
}
#[test]
fn code_regions_does_not_match_html_tag_name_prefix() {
assert_eq!(
code_regions("<codex>prikk notacommand</codex>"),
Vec::<&str>::new(),
"`<codex>` must not be recognised as a `<code>` opening tag"
);
assert_eq!(
code_regions("<precise>prikk notacommand</precise>"),
Vec::<&str>::new(),
"`<precise>` must not be recognised as a `<pre>` opening tag"
);
}
#[test]
fn code_regions_does_not_double_count_pre_code_nesting() {
let text = "<pre><code>prikk seal</code></pre>";
let regions = code_regions(text);
assert_eq!(
regions.len(),
1,
"expected the nested form to yield exactly one region, got {regions:?}"
);
let only_region = regions.first().expect("checked len == 1 above");
assert_eq!(*only_region, text);
assert_eq!(command_tokens(only_region), vec!["seal"]);
}
#[test]
fn code_regions_does_not_double_count_pre_inside_a_fence() {
let text = "```\n<pre>prikk seal</pre>\n```";
let regions = code_regions(text);
assert_eq!(
regions.len(),
1,
"expected the fenced form to yield exactly one region, got {regions:?}"
);
let only_region = regions.first().expect("checked len == 1 above");
assert_eq!(*only_region, text);
}
#[test]
fn code_regions_matches_html_tags_case_insensitively() {
assert_eq!(
code_regions("<CODE>prikk seal</CODE>"),
vec!["<CODE>prikk seal</CODE>"]
);
assert_eq!(
code_regions("<Pre>prikk verify</Pre>"),
vec!["<Pre>prikk verify</Pre>"]
);
}
#[test]
fn code_regions_html_unterminated_tag_runs_to_end_of_text() {
let text = "before <code>prikk seal";
let regions = code_regions(text);
assert_eq!(regions, vec!["<code>prikk seal"]);
}
#[test]
fn command_tokens_cannot_absorb_an_html_entity() {
assert_eq!(
command_tokens("prikk se&al"),
vec!["se"],
"an entity must terminate the token, never be absorbed into it"
);
}
#[test]
fn document_text_returns_nothing_for_the_landing_page() {
let root = repo_root();
let text = document_text(&root, "docs/landing/index.html");
assert_eq!(
text, "",
"the landing page must contribute nothing to rule (B)"
);
}