quinjet 0.0.77

A fast, live, keyboard-first Git source-control interface for the terminal
use super::*;
use crate::git::github::{PullRequestCommit, PullRequestCommits};

fn commit_app() -> App {
    let mut app = App::new("/tmp/repo", "repo");
    app.view = View::PullRequests;
    app.pull_request = Some(pull_request(42, "Ship rocket", "acme/widget"));
    app.pr_commits.loaded = true;
    app.pr_commits.snapshot.commits = vec![
        PullRequestCommit {
            oid: "a".repeat(40),
            abbreviated_oid: "aaaaaaa".to_owned(),
            subject: "Add launch".to_owned(),
            ..PullRequestCommit::default()
        },
        PullRequestCommit {
            oid: "b".repeat(40),
            abbreviated_oid: "bbbbbbb".to_owned(),
            subject: "Add engines".to_owned(),
            ..PullRequestCommit::default()
        },
    ];
    app
}

#[test]
fn choosing_a_commit_scopes_files_and_checks_and_rejects_old_responses() {
    let mut app = commit_app();
    app.pull_request_checks = vec![check("head checks", PullRequestCheckStatus::Passed)];
    let stale_checks = app.pull_request_checks_generation;
    let stale_diff = app.diff_generation;
    let mut effects = Vec::new();
    app.inspect_pr_commit(Some(1), &mut effects);
    assert_eq!(app.pull_request_section, PullRequestSection::Files);
    assert_eq!(app.selected_pr_commit().unwrap().oid, "b".repeat(40));
    assert_eq!(app.pull_request_checks, Vec::new());
    assert!(effects.iter().any(|effect| matches!(effect, AppEffect::Git(command) if matches!(command.as_ref(), WorkerCommand::PreparePullRequest { commit: Some(oid), .. } if oid == &"b".repeat(40)))));
    assert!(effects.iter().any(|effect| matches!(effect, AppEffect::Git(command) if matches!(command.as_ref(), WorkerCommand::LoadPullRequestChecks { commit: Some(oid), .. } if oid == &"b".repeat(40)))));
    drop(app.handle_worker_event(
        WorkerEvent::PullRequestChecks {
            generation: stale_checks,
            result: Ok(crate::git::github::PullRequestChecks {
                checks: vec![check("wrong head", PullRequestCheckStatus::Passed)],
                from_cache: false,
            }),
        },
        Instant::now(),
    ));
    drop(app.handle_worker_event(
        WorkerEvent::PullRequestIndex {
            generation: stale_diff,
            result: Ok(PullRequestDiffIndex {
                files: Vec::new(),
                total_files: 0,
                truncated: false,
            }),
        },
        Instant::now(),
    ));
    assert_eq!(app.pull_request_checks, Vec::new());
    assert_eq!(app.pull_request_files, Vec::new());
}

#[test]
fn switching_commits_invalidates_logs_and_returning_restores_pr_scope() {
    let mut app = commit_app();
    let mut effects = Vec::new();
    app.inspect_pr_commit(Some(0), &mut effects);
    let old_log = app.pull_request_check_log_generation;
    app.inspect_pr_commit(Some(1), &mut effects);
    assert!(app.pull_request_check_log_generation > old_log);
    app.inspect_pr_commit(None, &mut effects);
    assert!(app.selected_pr_commit().is_none());
    assert!(
        matches!(effects.last(), Some(AppEffect::Git(command)) if matches!(command.as_ref(), WorkerCommand::LoadPullRequestChecks { commit: None, .. }))
    );
    assert_eq!(
        app.pull_request_diff_source,
        Some(PullRequestDiffSource::PullRequest)
    );
}

#[test]
fn commit_list_ignores_stale_generations_and_orders_newest_first() {
    let mut app = commit_app();
    app.pr_commits.loaded = false;
    let mut effects = Vec::new();
    app.request_pr_commits(&mut effects);
    let generation = app.pr_commits.generation;
    let request = app.pull_request.as_ref().unwrap();
    let snapshot = PullRequestCommits {
        commits: app.pr_commits.snapshot.commits.clone(),
        total_commits: 2,
        base_oid: request.base_oid.clone(),
        head_oid: request.head_oid.clone(),
        ..PullRequestCommits::default()
    };
    app.apply_pr_commits(
        generation.wrapping_sub(1),
        Ok(snapshot.clone()),
        &mut effects,
    );
    assert!(!app.pr_commits.loaded);
    app.apply_pr_commits(generation, Ok(snapshot), &mut effects);
    assert!(app.pr_commits.loaded);
    assert_eq!(app.pr_commits.snapshot.commits[0].oid, "b".repeat(40));
}

#[test]
fn keyboard_commit_inspection_has_files_ci_and_a_return_route() {
    let mut app = commit_app();
    let now = Instant::now();
    drop(app.handle_key(KeyEvent::new(KeyCode::Char('C'), KeyModifiers::SHIFT), now));
    assert_eq!(app.pull_request_section, PullRequestSection::Commits);
    drop(app.handle_key(KeyEvent::new(KeyCode::Down, KeyModifiers::NONE), now));
    drop(app.handle_key(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE), now));
    assert_eq!(app.selected_pr_commit().unwrap().oid, "b".repeat(40));
    drop(app.handle_key(KeyEvent::new(KeyCode::Char('P'), KeyModifiers::SHIFT), now));
    assert_eq!(app.pull_request_section, PullRequestSection::Overview);
    drop(app.handle_key(KeyEvent::new(KeyCode::Char('A'), KeyModifiers::SHIFT), now));
    assert!(app.selected_pr_commit().is_none());
}

#[test]
fn stack_commit_selection_uses_its_member_instead_of_the_root_pr() {
    let mut app = commit_app();
    app.pull_request_stack = Some(pull_request_stack(2));
    app.pull_request_stack_cursor = Some(1);
    app.pull_request_section = PullRequestSection::Stack;
    app.reconcile_stack_inspector();
    app.stack_inspector.commits = app.pr_commits.snapshot.clone();
    let mut effects = Vec::new();
    app.inspect_stack_commit(1, &mut effects);
    assert_eq!(app.selected_pull_request().unwrap().number, 41);
    assert_eq!(app.selected_pr_commit().unwrap().oid, "b".repeat(40));
    assert!(effects.iter().any(|effect| matches!(effect, AppEffect::Git(command) if matches!(command.as_ref(), WorkerCommand::PreparePullRequest { pull_request, commit: Some(_), .. } if pull_request.number == 41))));
    app.select_pull_request_section(PullRequestSection::Stack, &mut effects);
    assert!(app.selected_pr_commit().is_none());
    assert!(app.pr_commits.owner.is_none());
}

#[test]
fn changed_pr_boundaries_reload_the_visible_commit_list() {
    for change_base in [false, true] {
        let mut app = commit_app();
        app.pull_request_section = PullRequestSection::Commits;
        let mut current = app.pull_request.clone().unwrap();
        if change_base {
            current.base_oid = "new-base".to_owned();
        } else {
            current.head_oid = "new-head".to_owned();
        }
        let old_generation = app.pr_commits.generation;
        let effects = app.handle_worker_event(
            WorkerEvent::PullRequestLookup {
                generation: app.pull_request_generation,
                result: Ok(crate::git::github::PullRequestSnapshot {
                    repositories: Vec::new(),
                    selected_repository: None,
                    pull_request: current.clone(),
                    warnings: Vec::new(),
                    exact_number: Some(current.number),
                    from_cache: false,
                }),
            },
            Instant::now(),
        );
        assert!(!app.pr_commits.loaded);
        assert!(app.pr_commits.generation > old_generation);
        assert!(effects.iter().any(|effect| matches!(effect, AppEffect::Git(command) if matches!(command.as_ref(), WorkerCommand::LoadPullRequestCommits { pull_request, .. } if pull_request.base_oid == current.base_oid && pull_request.head_oid == current.head_oid))));
    }
}

#[test]
fn refreshed_stack_boundaries_replace_the_commit_owner_and_reject_old_history() {
    let mut app = commit_app();
    app.pull_request_stack = Some(pull_request_stack(2));
    app.pull_request_stack_cursor = Some(1);
    app.pull_request_section = PullRequestSection::Stack;
    app.reconcile_stack_inspector();
    app.stack_inspector.commits = app.pr_commits.snapshot.clone();
    let mut effects = Vec::new();
    app.inspect_stack_commit(1, &mut effects);
    app.pull_request_section = PullRequestSection::Commits;
    let stale_generation = app.pr_commits.generation;
    let stale_snapshot = app.pr_commits.snapshot.clone();
    let mut stack = app.pull_request_stack.clone().unwrap();
    stack.members[0].head_oid = "new-member-head".to_owned();
    effects.clear();
    app.apply_pull_request_stack_snapshot(Some(stack), &mut effects);
    assert_eq!(app.selected_pull_request().unwrap().number, 41);
    assert_eq!(
        app.selected_pull_request().unwrap().head_oid,
        "new-member-head"
    );
    assert!(!app.pr_commits.loaded);
    assert!(app.selected_pr_commit().is_none());
    assert!(effects.iter().any(|effect| matches!(effect, AppEffect::Git(command) if matches!(command.as_ref(), WorkerCommand::LoadPullRequestCommits { pull_request, .. } if pull_request.number == 41 && pull_request.head_oid == "new-member-head"))));
    app.apply_pr_commits(stale_generation, Ok(stale_snapshot), &mut effects);
    assert!(!app.pr_commits.loaded);
}

#[test]
fn running_commit_checks_in_a_stack_keep_the_fast_poll_interval() {
    let mut app = commit_app();
    app.pull_request_stack = Some(pull_request_stack(2));
    app.pr_commits.owner = app.pull_request.clone();
    app.pull_request_checks = vec![check("commit build", PullRequestCheckStatus::Pending)];
    assert_eq!(app.pull_request_poll_interval(), PULL_REQUEST_ACTIVE_POLL);
    app.pull_request_checks[0].status = PullRequestCheckStatus::Passed;
    assert_eq!(app.pull_request_poll_interval(), PULL_REQUEST_IDLE_POLL);
    app.pr_commits.loading = true;
    assert!(app.pull_request_refreshing());
}

#[test]
fn switching_from_stack_commits_loads_full_history_without_changing_the_selected_hash() {
    let mut app = commit_app();
    app.pr_commits.introduced_only = true;
    app.pr_commits.selected = Some(1);
    let selected = app.selected_pr_commit().unwrap().oid.clone();
    let mut effects = Vec::new();
    app.request_pr_commits(&mut effects);
    assert!(app.pr_commits.loading);
    assert!(effects.iter().any(|effect| matches!(effect, AppEffect::Git(command) if matches!(command.as_ref(), WorkerCommand::LoadPullRequestCommits { .. }))));
    let request = app.pull_request.as_ref().unwrap();
    let mut commits = app.pr_commits.snapshot.commits.clone();
    commits.insert(
        0,
        PullRequestCommit {
            oid: "c".repeat(40),
            ..PullRequestCommit::default()
        },
    );
    let snapshot = PullRequestCommits {
        commits,
        total_commits: 3,
        base_oid: request.base_oid.clone(),
        head_oid: request.head_oid.clone(),
        ..PullRequestCommits::default()
    };
    app.apply_pr_commits(app.pr_commits.generation, Ok(snapshot), &mut effects);
    assert!(!app.pr_commits.introduced_only);
    assert_eq!(app.selected_pr_commit().unwrap().oid, selected);
}