~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
182 lines · 7.8 KB Raw History
//! Owners delegate their writes and share repositories from the pages,
//! and make, share and land private ones there, through the same code as
//! the command line.

use super::*;

/// Runs the browser that approves as `who` on the page at `url`.
fn approve(who: &Account, url: &str) {
    let command = format!("{} {url}", browser(who, ""));
    let mut words = command.split_whitespace();
    let out = std::process::Command::new(words.next().unwrap())
        .args(words)
        .output()
        .unwrap();
    assert!(
        out.status.success(),
        "{}",
        String::from_utf8_lossy(&out.stderr)
    );
}

/// An owner delegates their writes to the server from the pages, as
/// `knotless account delegate` does, and can then add a collaborator there; and a
/// pull into a repository with a runner but no workflows waits for no
/// checks.
#[test]
fn delegating_writes_from_the_pages() {
    // Started first, so its index sees the repositories made after.
    let server = gateway_with(None, true);
    let owner = person(account());
    let collab = account();
    let url = owner.url("shared");
    let work = owner.work("w", 1);
    owner.git(&work, &["push", "-q", &url, "main"]);
    owner.git(&work, &["checkout", "-q", "-b", "x"]);
    owner.commit_file(&work, "x", "x\n");
    owner.git(&work, &["push", "-q", &url, "x"]);
    let (code, pull) = owner.cli(&["pull", "create", &url, "main", &url, "x", "X"], b"");
    assert_eq!(code, 0, "{pull}");
    let mine = session_cookie(&server, &owner.account);
    let me = Some(mine.as_str());
    let xrpc = |nsid: &str| format!("{}/xrpc/dev.knotless.{nsid}", server.base);
    let write = |nsid: &str, cookie: Option<&str>, body: Value| {
        browse("POST", &xrpc(nsid), cookie, None, Some(body))
    };
    let repo = format!("at://{}/dev.knotless.repo/shared", owner.account.did);
    let ask = |nsid: &str, query: &str| {
        let url = format!("{}?repo={repo}{query}", xrpc(nsid));
        browse("GET", &url, me, None, None).2
    };

    // Not yet delegated, sharing for writing says to delegate first.
    assert_eq!(ask("getRepoSettings", "").get("delegated"), None);
    let share = json!({"repo": repo, "with": collab.handle});
    let (code, _, said) = write("shareRepo", me, share.clone());
    assert_eq!(code, 400, "{said}");
    assert!(
        said["message"]
            .as_str()
            .unwrap()
            .contains("Delegate writes to this server"),
        "{said}"
    );

    // Only someone logged in delegates; nothing waits under a state never
    // given.
    assert_eq!(write("delegateWrites", None, json!({})).0, 401);
    let nope = json!({"state": "nope"});
    assert_eq!(write("awaitDelegation", None, nope.clone()).0, 401);
    assert_eq!(write("awaitDelegation", me, nope).0, 400);

    // Started, approved at the PDS, and waited for.
    let (code, _, started) = write("delegateWrites", me, json!({}));
    assert_eq!(code, 200, "{started}");
    approve(&owner.account, started["url"].as_str().unwrap());
    let state = json!({"state": started["state"]});
    let mut said = json!({"done": false});
    while said["done"] == false {
        let (code, _, now) = write("awaitDelegation", me, state.clone());
        assert_eq!(code, 200, "{now}");
        said = now;
    }
    assert_eq!(said["gateway"], server.did);
    assert_eq!(ask("getRepoSettings", "")["delegated"], server.did);

    // Now a collaborator is added, through this server.
    let (code, _, said) = write("shareRepo", me, share);
    assert_eq!(code, 200, "{said}");
    assert_eq!(said["collaborators"][0]["did"], collab.did);
    assert_eq!(ask("getRepoSettings", "")["gateway"], server.did);

    // With a runner but no workflow a pull runs, nothing waits for checks.
    let runner = json!({"repo": repo, "runner": collab.handle});
    assert_eq!(write("addRunner", me, runner).0, 200);
    let said = ask("getMergeStatus", &format!("&pull={pull}"));
    assert_eq!(
        (&said["state"], &said["checks"]),
        (&json!("clean"), &json!("none")),
        "{said}"
    );
}

/// A private repository made from the pages, shared there for reading,
/// and its pull landed there, each read and written as the account
/// logged in, never as the server.
#[test]
fn private_repositories_made_and_shared_from_the_pages() {
    let server = gateway_with(None, true);
    let [owner, member] = people();
    let mine = session_cookie(&server, &owner.account);
    let theirs = session_cookie(&server, &member.account);
    let (me, them) = (Some(mine.as_str()), Some(theirs.as_str()));
    let xrpc = |nsid: &str| format!("{}/xrpc/dev.knotless.{nsid}", server.base);
    let write = |nsid: &str, cookie: Option<&str>, body: Value| {
        browse("POST", &xrpc(nsid), cookie, None, Some(body))
    };
    let made = write("createRepo", me, json!({"name": "secret", "private": true}));
    if !net().spaces {
        // Without spaces, there is no private repository to make.
        assert_eq!(made.0, 400, "{}", made.2);
        return;
    }
    assert_eq!(made.0, 200, "{}", made.2);
    let (_, listed) = owner.cli(&["repo", "list", &owner.account.handle], b"");
    let private = listed.lines().any(|l| l == "secret\t\tprivate");
    assert!(private, "{listed}");
    let url = owner.url("secret");
    let work = owner.work("w", 1);
    owner.git(&work, &["push", "-q", &url, "main"]);
    owner.git(&work, &["checkout", "-q", "-b", "x"]);
    let head = owner.commit_file(&work, "x", "x\n");
    owner.git(&work, &["push", "-q", &url, "x"]);
    let (code, pull) = owner.cli(&["pull", "create", &url, "main", &url, "x", "X"], b"");
    assert_eq!(code, 0, "{pull}");
    let repo = format!("at://{}/dev.knotless.repo/secret", owner.account.did);
    let ask = |nsid: &str, query: &str, cookie: Option<&str>| {
        let url = format!("{}?repo={repo}{query}", xrpc(nsid));
        browse("GET", &url, cookie, None, None)
    };
    let readers = || ask("getRepoSettings", "", me).2["readers"].clone();

    // Shared for reading, the member is listed to its owner, and reads it.
    assert_eq!(readers(), json!([]));
    assert_eq!(ask("getRepo", "", them).0, 404);
    let share = json!({"repo": repo, "with": member.account.handle, "write": false});
    let (code, _, said) = write("shareRepo", me, share);
    assert_eq!((code, &said), (200, &json!({"collaborators": []})));
    assert_eq!(readers()[0]["did"], member.account.did);
    let (code, _, shown) = ask("getRepo", "", them);
    assert_eq!((code, &shown["private"]), (200, &json!(true)), "{shown}");
    // Only its owner sees who reads it.
    assert_eq!(ask("getRepoSettings", "", them).2["readers"], json!([]));
    // Its owner changes it, which no public index holds.
    let described = json!({"repo": repo, "description": "Hush"});
    assert_eq!(write("updateRepo", me, described).0, 200);

    // Its pull's merge box, workflows and runners read as each asker.
    let status = |cookie| ask("getMergeStatus", &format!("&pull={pull}"), cookie);
    let (code, _, said) = status(them);
    assert_eq!(code, 200, "{said}");
    assert_eq!(
        (&said["state"], &said["may"]["merge"]),
        (&json!("clean"), &json!(false))
    );
    assert_eq!(status(me).2["may"]["merge"], true);
    let listed = ask("listWorkflows", "", them);
    assert_eq!(listed.0, 200, "{}", listed.2);
    assert_eq!(ask("listRunners", "", them).2, json!({"runners": []}));
    let one = json!({"repo": repo, "pull": pull});
    assert_eq!(write("mergePull", them, one.clone()).0, 400);
    let (code, _, merged) = write("mergePull", me, one);
    assert_eq!(code, 200, "{merged}");
    assert!(
        merged["commit"].is_string() && merged["commit"] != json!(head),
        "{merged}"
    );

    // Unshared, they read it no more.
    let unshare = json!({"repo": repo, "with": member.account.did});
    assert_eq!(write("unshareRepo", me, unshare).0, 200);
    assert_eq!(readers(), json!([]));
}