lazyspec 0.8.0

A little TUI & CLI for project documentation.
Documentation
use chrono::NaiveDate;
use lazyspec::engine::document::{DocMeta, DocType, RelationType, Status};

#[test]
fn parse_frontmatter_from_markdown() {
    let content = r#"---
title: "Adopt Event Sourcing"
type: adr
status: draft
author: jkaloger
date: 2026-03-04
tags: [architecture, events]
related:
  - implements: rfcs/RFC-001-event-sourcing.md
---

## Context

Some body content here.
"#;

    let meta = DocMeta::parse(content).unwrap();

    assert_eq!(meta.title, "Adopt Event Sourcing");
    assert_eq!(meta.doc_type, DocType::new("adr"));
    assert_eq!(meta.status, Status::Draft);
    assert_eq!(meta.author, "jkaloger");
    assert_eq!(meta.date, NaiveDate::from_ymd_opt(2026, 3, 4).unwrap());
    assert_eq!(meta.tags, vec!["architecture", "events"]);
    assert_eq!(meta.related.len(), 1);
    assert_eq!(meta.related[0].rel_type, RelationType::Implements);
    assert_eq!(meta.related[0].target, "rfcs/RFC-001-event-sourcing.md");
}

#[test]
fn parse_frontmatter_minimal() {
    let content = r#"---
title: "Simple Doc"
type: rfc
status: review
author: someone
date: 2026-01-01
tags: []
---

Body.
"#;

    let meta = DocMeta::parse(content).unwrap();
    assert_eq!(meta.title, "Simple Doc");
    assert_eq!(meta.doc_type, DocType::new("rfc"));
    assert!(meta.related.is_empty());
}

#[test]
fn parse_frontmatter_invalid_yaml() {
    let content = "no frontmatter here";
    assert!(DocMeta::parse(content).is_err());
}

#[test]
fn extract_body_skips_frontmatter() {
    let content = r#"---
title: "Test"
type: story
status: draft
author: a
date: 2026-01-01
tags: []
---

## Body

Content here.
"#;

    let body = DocMeta::extract_body(content).unwrap();
    assert!(body.contains("## Body"));
    assert!(body.contains("Content here."));
    assert!(!body.contains("title:"));
}

#[test]
fn validate_ignore_defaults_to_false() {
    let content = r#"---
title: "No Ignore Flag"
type: rfc
status: draft
author: someone
date: 2026-01-01
tags: []
---

Body.
"#;

    let meta = DocMeta::parse(content).unwrap();
    assert!(!meta.validate_ignore);
}

#[test]
fn validate_ignore_parsed_when_true() {
    let content = r#"---
title: "Legacy Doc"
type: rfc
status: draft
author: someone
date: 2026-01-01
tags: []
validate-ignore: true
---

Body.
"#;

    let meta = DocMeta::parse(content).unwrap();
    assert!(meta.validate_ignore);
}

#[test]
fn relation_type_display() {
    use lazyspec::engine::document::RelationType;
    assert_eq!(format!("{}", RelationType::Implements), "implements");
    assert_eq!(format!("{}", RelationType::Supersedes), "supersedes");
    assert_eq!(format!("{}", RelationType::Blocks), "blocks");
    assert_eq!(format!("{}", RelationType::RelatedTo), "related-to");
}

#[test]
fn relation_type_fromstr_display_roundtrip() {
    use lazyspec::engine::document::RelationType;
    for canonical in RelationType::ALL_STRS {
        let parsed: RelationType = canonical.parse().unwrap();
        assert_eq!(parsed.to_string(), canonical);
    }
}

#[test]
fn relation_type_fromstr_rejects_unknown() {
    assert!("garbage".parse::<RelationType>().is_err());
}

#[test]
fn relation_type_fromstr_accepts_space_variant() {
    let rt: RelationType = "related to".parse().unwrap();
    assert_eq!(rt, RelationType::RelatedTo);
}

#[test]
fn doctype_new_lowercases_input() {
    assert_eq!(DocType::new("RFC"), DocType::new("rfc"));
    assert_eq!(DocType::new("Adr"), DocType::new("adr"));
    assert_eq!(DocType::new("STORY"), DocType::new("story"));
}

#[test]
fn doctype_display_returns_lowercase() {
    assert_eq!(format!("{}", DocType::new("RFC")), "rfc");
    assert_eq!(format!("{}", DocType::new("custom")), "custom");
}

#[test]
fn doctype_fromstr_accepts_any_string() {
    let dt: DocType = "something-custom".parse().unwrap();
    assert_eq!(dt, DocType::new("something-custom"));
}

#[test]
fn doctype_constants() {
    assert_eq!(DocType::RFC, "rfc");
    assert_eq!(DocType::STORY, "story");
    assert_eq!(DocType::ITERATION, "iteration");
    assert_eq!(DocType::ADR, "adr");
}

#[test]
fn doctype_deserializes_lowercase() {
    let content = r#"---
title: "Custom Type Doc"
type: SomeCustomType
status: draft
author: someone
date: 2026-01-01
tags: []
---

Body.
"#;

    let meta = DocMeta::parse(content).unwrap();
    assert_eq!(meta.doc_type, DocType::new("somecustomtype"));
}