a73x

tests/archive_test.rs

Ref:   Size: 7.5 KiB

mod common;

use tempfile::TempDir;

use git_collab::dag;
use git_collab::event::{Action, Event};
use git_collab::state::{self, IssueState, IssueStatus, PatchState, PatchStatus};

use common::{alice, close_issue, create_patch, init_repo, now, open_issue, test_signing_key};

// ---------------------------------------------------------------------------
// Archive on close: issues
// ---------------------------------------------------------------------------

#[test]
fn test_close_issue_moves_ref_to_archive() {
    let tmp = TempDir::new().unwrap();
    let repo = init_repo(tmp.path(), &alice());

    let (ref_name, id) = open_issue(&repo, &alice(), "To be archived");

    // Close + archive
    close_issue(&repo, &ref_name, &alice());
    state::archive_issue_ref(&repo, &id).unwrap();

    // Old ref should be gone
    assert!(repo.refname_to_id(&ref_name).is_err());

    // New archive ref should exist
    let archive_ref = format!("refs/collab/archive/issues/{}", id);
    assert!(repo.refname_to_id(&archive_ref).is_ok());

    // Should still be able to materialize state from archive ref
    let issue = IssueState::from_ref(&repo, &archive_ref, &id).unwrap();
    assert_eq!(issue.status, IssueStatus::Closed);
    assert_eq!(issue.title, "To be archived");
}

#[test]
fn test_list_issues_excludes_archived() {
    let tmp = TempDir::new().unwrap();
    let repo = init_repo(tmp.path(), &alice());

    let (_ref1, _id1) = open_issue(&repo, &alice(), "Active issue");
    let (ref2, id2) = open_issue(&repo, &alice(), "Archived issue");
    close_issue(&repo, &ref2, &alice());
    state::archive_issue_ref(&repo, &id2).unwrap();

    // Default list should only show active
    let issues = state::list_issues(&repo).unwrap();
    assert_eq!(issues.len(), 1);
    assert_eq!(issues[0].title, "Active issue");
}

#[test]
fn test_list_issues_with_archived_shows_both() {
    let tmp = TempDir::new().unwrap();
    let repo = init_repo(tmp.path(), &alice());

    let (_ref1, _id1) = open_issue(&repo, &alice(), "Active issue");
    let (ref2, id2) = open_issue(&repo, &alice(), "Archived issue");
    close_issue(&repo, &ref2, &alice());
    state::archive_issue_ref(&repo, &id2).unwrap();

    // With archived flag should show both
    let issues = state::list_issues_with_archived(&repo).unwrap();
    assert_eq!(issues.len(), 2);
}

#[test]
fn test_resolve_issue_ref_finds_archived() {
    let tmp = TempDir::new().unwrap();
    let repo = init_repo(tmp.path(), &alice());

    let (ref_name, id) = open_issue(&repo, &alice(), "Will be archived");
    close_issue(&repo, &ref_name, &alice());
    state::archive_issue_ref(&repo, &id).unwrap();

    // resolve_issue_ref should find it in archive
    let (resolved_ref, resolved_id) = state::resolve_issue_ref(&repo, &id[..8]).unwrap();
    assert_eq!(resolved_id, id);
    assert!(resolved_ref.contains("archive"));
}

// ---------------------------------------------------------------------------
// Archive on close: patches
// ---------------------------------------------------------------------------

#[test]
fn test_close_patch_moves_ref_to_archive() {
    let tmp = TempDir::new().unwrap();
    let repo = init_repo(tmp.path(), &alice());

    let (ref_name, id) = create_patch(&repo, &alice(), "To be archived");

    // Close + archive
    let sk = test_signing_key();
    let event = Event {
        timestamp: now(),
        author: alice(),
        action: Action::PatchClose { reason: None },
        clock: 0,
    };
    dag::append_event(&repo, &ref_name, &event, &sk).unwrap();
    state::archive_patch_ref(&repo, &id).unwrap();

    // Old ref should be gone
    assert!(repo.refname_to_id(&ref_name).is_err());

    // New archive ref should exist
    let archive_ref = format!("refs/collab/archive/patches/{}", id);
    assert!(repo.refname_to_id(&archive_ref).is_ok());

    // Should still be able to materialize state from archive ref
    let patch = PatchState::from_ref(&repo, &archive_ref, &id).unwrap();
    assert_eq!(patch.status, PatchStatus::Closed);
}

#[test]
fn test_list_patches_excludes_archived() {
    let tmp = TempDir::new().unwrap();
    let repo = init_repo(tmp.path(), &alice());

    let (_ref1, _id1) = create_patch(&repo, &alice(), "Active patch");
    let (ref2, id2) = create_patch(&repo, &alice(), "Archived patch");

    let sk = test_signing_key();
    let event = Event {
        timestamp: now(),
        author: alice(),
        action: Action::PatchClose { reason: None },
        clock: 0,
    };
    dag::append_event(&repo, &ref2, &event, &sk).unwrap();
    state::archive_patch_ref(&repo, &id2).unwrap();

    let patches = state::list_patches(&repo).unwrap();
    assert_eq!(patches.len(), 1);
    assert_eq!(patches[0].title, "Active patch");
}

#[test]
fn test_list_patches_with_archived_shows_both() {
    let tmp = TempDir::new().unwrap();
    let repo = init_repo(tmp.path(), &alice());

    let (_ref1, _id1) = create_patch(&repo, &alice(), "Active patch");
    let (ref2, id2) = create_patch(&repo, &alice(), "Archived patch");

    let sk = test_signing_key();
    let event = Event {
        timestamp: now(),
        author: alice(),
        action: Action::PatchClose { reason: None },
        clock: 0,
    };
    dag::append_event(&repo, &ref2, &event, &sk).unwrap();
    state::archive_patch_ref(&repo, &id2).unwrap();

    let patches = state::list_patches_with_archived(&repo).unwrap();
    assert_eq!(patches.len(), 2);
}

#[test]
fn test_resolve_patch_ref_finds_archived() {
    let tmp = TempDir::new().unwrap();
    let repo = init_repo(tmp.path(), &alice());

    let (ref_name, id) = create_patch(&repo, &alice(), "Will archive");

    let sk = test_signing_key();
    let event = Event {
        timestamp: now(),
        author: alice(),
        action: Action::PatchClose { reason: None },
        clock: 0,
    };
    dag::append_event(&repo, &ref_name, &event, &sk).unwrap();
    state::archive_patch_ref(&repo, &id).unwrap();

    let (resolved_ref, resolved_id) = state::resolve_patch_ref(&repo, &id[..8]).unwrap();
    assert_eq!(resolved_id, id);
    assert!(resolved_ref.contains("archive"));
}

// ---------------------------------------------------------------------------
// --relates-to on issue open
// ---------------------------------------------------------------------------

#[test]
fn test_issue_open_with_relates_to() {
    let tmp = TempDir::new().unwrap();
    let repo = init_repo(tmp.path(), &alice());

    // First create an issue to relate to
    let (_ref1, id1) = open_issue(&repo, &alice(), "Parent issue");

    // Open an issue with relates_to
    let sk = test_signing_key();
    let event = Event {
        timestamp: now(),
        author: alice(),
        action: Action::IssueOpen {
            title: "Child issue".to_string(),
            body: "".to_string(),
            relates_to: Some(id1.clone()),
        },
        clock: 0,
    };
    let oid = dag::create_root_event(&repo, &event, &sk).unwrap();
    let id = oid.to_string();
    let ref_name = format!("refs/collab/issues/{}", id);
    repo.reference(&ref_name, oid, false, "test open with relates_to")
        .unwrap();

    let issue = IssueState::from_ref(&repo, &ref_name, &id).unwrap();
    assert_eq!(issue.title, "Child issue");
    assert_eq!(issue.relates_to.as_deref(), Some(id1.as_str()));
}

#[test]
fn test_issue_open_without_relates_to() {
    let tmp = TempDir::new().unwrap();
    let repo = init_repo(tmp.path(), &alice());

    let (ref_name, id) = open_issue(&repo, &alice(), "Solo issue");

    let issue = IssueState::from_ref(&repo, &ref_name, &id).unwrap();
    assert_eq!(issue.relates_to, None);
}