todomd 0.3.1

A simple markdown-based todo list CLI and TUI - Added Kanban
Documentation
use todomd::core::model::Category;
use todomd::core::ops;
use todomd::core::store::Store;
use todomd::persist::atomic::FileStore;
use todomd::persist::trueno_store::TruenoStore;
use todomd::persist::dual_store::DualStore;
use tempfile::tempdir;

#[test]
fn test_dual_store_integration() {
    let dir = tempdir().unwrap();
    let md_path = dir.path().join("todo.md");
    let db_path = dir.path().join("todo.parquet");
    
    let file_store = FileStore::new(md_path.clone());
    let trueno_store = TruenoStore::new(db_path.clone());
    let dual_store = DualStore::new(file_store, trueno_store);
    
    // 1. Add a todo
    let id = ops::add_todo(&dual_store, Category::Short, "Integration Task".to_string(), Some("Integration notes".to_string())).unwrap();
    
    // 2. Verify files exist
    assert!(md_path.exists());
    assert!(db_path.exists());
    
    // 3. Load from dual_store (which loads from primary/markdown)
    let state = dual_store.load().unwrap();
    assert_eq!(state.short.len(), 1);
    assert_eq!(state.short[0].title, "Integration Task");
    assert_eq!(state.short[0].id, id);
    
    // 4. Update the task
    ops::edit_todo(&dual_store, id, Some("Updated Title".to_string()), None).unwrap();
    
    // 5. Verify the update in primary
    let state_updated = dual_store.load().unwrap();
    assert_eq!(state_updated.short[0].title, "Updated Title");
    
    // 6. Manual check of Markdown content (basic check)
    let md_content = std::fs::read_to_string(&md_path).unwrap();
    assert!(md_content.contains("Updated Title"));
}

#[test]
fn test_recovery_from_parquet() {
    let dir = tempdir().unwrap();
    let md_path = dir.path().join("todo.md");
    let db_path = dir.path().join("todo.parquet");
    
    let file_store = FileStore::new(md_path.clone());
    let trueno_store = TruenoStore::new(db_path.clone());
    let dual_store = DualStore::new(file_store, trueno_store);
    
    // 1. Add data
    ops::add_todo(&dual_store, Category::Short, "Backup Task".to_string(), None).unwrap();
    
    // 2. Delete markdown
    std::fs::remove_file(&md_path).unwrap();
    assert!(!md_path.exists());
    
    // 3. Load - should fallback to parquet
    let recovered = dual_store.load().unwrap();
    assert_eq!(recovered.short.len(), 1);
    assert_eq!(recovered.short[0].title, "Backup Task");
}