wsb-daemon 0.275.7622

wsb's daemon: the workbench, the GitHub mirror's schedule and peer continuity, for every workspace on the machine
use super::*;
use crate::code::text::{Position, Range};

fn insert(line: u32, character: u32, text: &str) -> Change {
    let p = Position { line, character };
    Change { range: Some(Range { start: p, end: p }), text: text.to_owned() }
}

/// TEST20076: the server's copy follows the page that edits or asks, with one full text when
/// it moves.
///
/// Two pages with one file open, each editing it: whichever asks must be answered about its
/// own text. Answering about the other page's — what the server held — gives positions that
/// point at the wrong characters.
#[test]
fn test20076_the_server_copy_follows_the_page_that_asks() {
    let mut r = Registry::default();
    assert_eq!(
        r.open("a", "f.py", "python", 1, "x = 1\n"),
        vec![Outgoing::Open { path: "f.py".into(), language_id: "python".into(), version: 1, text: "x = 1\n".into() }]
    );
    // The same text from a second page changes nothing on the server; it is that page's now.
    assert_eq!(r.open("b", "f.py", "python", 1, "x = 1\n"), vec![]);
    assert_eq!(
        r.change("b", "f.py", 1, 2, &[insert(0, 0, "#")]).unwrap(),
        vec![Outgoing::Edit { path: "f.py".into(), version: 2, changes: vec![insert(0, 0, "#")] }],
        "the owner's edit goes as it was made"
    );
    // Page a edits its own copy: its text replaces the server's, and it owns the file.
    assert_eq!(
        r.change("a", "f.py", 1, 2, &[insert(0, 0, "!")]).unwrap(),
        vec![Outgoing::Replace { path: "f.py".into(), version: 3, text: "!x = 1\n".into() }]
    );
    assert_eq!(
        r.change("b", "f.py", 2, 3, &[insert(0, 1, "?")]).unwrap(),
        vec![Outgoing::Replace { path: "f.py".into(), version: 4, text: "#?x = 1\n".into() }]
    );
    assert_eq!(r.server_copy("f.py"), Some((4, "#?x = 1\n")));
    // Page a asks about its text: it becomes the server's again, once.
    assert_eq!(
        r.ensure("a", "f.py", 2).unwrap(),
        vec![Outgoing::Replace { path: "f.py".into(), version: 5, text: "!x = 1\n".into() }]
    );
    assert_eq!(r.ensure("a", "f.py", 2).unwrap(), vec![], "asking again moves nothing");
}

/// TEST20077: a question about text the page no longer has, and an edit from a version the
/// daemon never had, are refused and change nothing.
#[test]
fn test20077_stale_questions_and_gapped_edits_are_refused() {
    let mut r = Registry::default();
    r.open("a", "f.go", "go", 5, "package f\n");
    assert_eq!(r.ensure("a", "f.go", 4), Err(SyncError::Stale { path: "f.go".into(), have: 5, asked: 4 }));
    assert_eq!(
        r.change("a", "f.go", 3, 4, &[insert(0, 0, "x")]),
        Err(SyncError::Gap { path: "f.go".into(), have: 5, from: 3 })
    );
    let invalid = r.change("a", "f.go", 5, 6, &[insert(9, 0, "x")]).unwrap_err();
    assert!(matches!(invalid, SyncError::Invalid { .. }), "{invalid:?}");
    assert_eq!(r.server_copy("f.go"), Some((1, "package f\n")), "a refused edit changed the server's copy");
    assert_eq!(r.ensure("a", "f.go", 5).unwrap(), vec![], "a refused edit moved the page's version");
    assert_eq!(r.ensure("z", "f.go", 1), Err(SyncError::NotOpen { path: "f.go".into() }));
}

/// TEST20078: the last page to close a file closes it on the server; one that stays keeps
/// the server on its text; a page that goes away closes everything it had.
#[test]
fn test20078_closing_hands_the_file_to_a_page_that_has_it() {
    let mut r = Registry::default();
    r.open("a", "f.rs", "rust", 1, "fn a() {}\n");
    r.open("b", "f.rs", "rust", 1, "fn a() {}\n");
    r.open("a", "g.rs", "rust", 1, "fn g() {}\n");
    r.change("b", "f.rs", 1, 2, &[insert(0, 0, "//\n")]).unwrap();
    r.change("a", "f.rs", 1, 2, &[insert(0, 0, "#[test]\n")]).unwrap();
    // a owns f.rs with its text; b's differs. a closes: b's text becomes the server's.
    assert_eq!(
        r.close("a", "f.rs"),
        vec![Outgoing::Replace { path: "f.rs".into(), version: 4, text: "//\nfn a() {}\n".into() }]
    );
    assert_eq!(r.close_client("a"), vec![Outgoing::Close { path: "g.rs".into() }]);
    assert_eq!(r.replay(), vec![Outgoing::Open {
        path: "f.rs".into(),
        language_id: "rust".into(),
        version: 4,
        text: "//\nfn a() {}\n".into()
    }]);
    assert_eq!(r.close("b", "f.rs"), vec![Outgoing::Close { path: "f.rs".into() }]);
    assert!(r.is_empty());
}