quinjet 0.0.58

A fast, live, keyboard-first Git source-control interface for the terminal
use super::*;

fn stack_response(entries: &str, selected_position: usize, size: usize) -> Vec<u8> {
    format!(
        r#"{{"data":{{"repository":{{"pullRequest":{{"stackEntry":{{"position":{selected_position}}},"stack":{{"id":"STACK_node","number":12,"size":{size},"baseRefName":"main","entries":{{"totalCount":{size},"nodes":[{entries}]}}}}}}}}}}}}"#
    )
    .into_bytes()
}

fn entry(position: usize, number: u64, base: &str, head: &str) -> String {
    format!(
        r#"{{"id":"ENTRY_{position}","position":{position},"pullRequest":{{"id":"PR_{number}","number":{number},"title":"Layer {position}","author":{{"login":"octocat"}},"state":"OPEN","isDraft":false,"updatedAt":"2026-08-26T00:00:00Z","url":"https://github.com/acme/widget/pull/{number}","baseRefName":"base-{position}","baseRefOid":"{base}","headRefName":"head-{position}","headRefOid":"{head}","headRepository":{{"nameWithOwner":"acme/widget"}},"isCrossRepository":false,"additions":10,"deletions":2,"changedFiles":3,"mergeStateStatus":"CLEAN","mergeable":"MERGEABLE","reviewDecision":"APPROVED","mergeQueueEntry":null,"commits":{{"nodes":[{{"commit":{{"statusCheckRollup":{{"state":"SUCCESS"}}}}}}]}}}}}}"#
    )
}

#[test]
fn stack_response_sorts_members_and_builds_exact_comparison() {
    let base = "1".repeat(40);
    let middle = "2".repeat(40);
    let head = "3".repeat(40);
    let entries = format!(
        "{},{}",
        entry(2, 42, &middle, &head),
        entry(1, 41, &base, &middle)
    );
    let repository = repository("acme/widget", "https://github.com/acme/widget", &["origin"]);
    let stack = parse_pull_request_stack(&stack_response(&entries, 2, 2), &repository, 42)
        .unwrap()
        .unwrap();

    assert_eq!(stack.number, 12);
    assert_eq!(stack.selected_position, 2);
    assert_eq!(
        stack
            .members
            .iter()
            .map(|member| member.number)
            .collect::<Vec<_>>(),
        vec![41, 42]
    );
    assert!(!stack.truncated);
    let comparison = stack.comparison(1, 2).unwrap();
    assert_eq!(comparison.base_oid, base);
    assert_eq!(comparison.head_oid, head);
    assert_eq!(comparison.base_ref, "base-1");
    assert_eq!(comparison.head_ref, "head-2");
    assert_eq!(comparison.number, 42);
}

#[test]
fn standalone_pull_request_is_a_successful_empty_stack() {
    let response = br#"{"data":{"repository":{"pullRequest":{"stackEntry":null,"stack":null}}}}"#;
    let repository = repository("acme/widget", "https://github.com/acme/widget", &[]);

    assert_eq!(
        parse_pull_request_stack(response, &repository, 42).unwrap(),
        None
    );
}

#[test]
fn incomplete_and_duplicate_entries_are_not_silently_trusted() {
    let base = "1".repeat(40);
    let head = "2".repeat(40);
    let one = entry(1, 41, &base, &head);
    let repository = repository("acme/widget", "https://github.com/acme/widget", &[]);
    let incomplete = parse_pull_request_stack(
        &stack_response(&format!("{one},null"), 1, 2),
        &repository,
        41,
    )
    .unwrap()
    .unwrap();
    assert!(incomplete.truncated);

    let duplicate = stack_response(&format!("{one},{one}"), 1, 2);
    let error = parse_pull_request_stack(&duplicate, &repository, 41)
        .unwrap_err()
        .to_string();
    assert!(error.contains("duplicate or invalid"));
}

#[test]
fn graphql_errors_are_reported_as_stack_failures() {
    let repository = repository("acme/widget", "https://github.com/acme/widget", &[]);
    let response = br#"{"data":null,"errors":[{"message":"stack preview unavailable"}]}"#;
    let error = parse_pull_request_stack(response, &repository, 42)
        .unwrap_err()
        .to_string();

    assert!(error.contains("stack preview unavailable"));
}