// // Instances that answer SSH on a different hostname than their web UI. // // The remote host is what discovery matches against, so `git@ssh.gitea.test:…` // on an instance at `https://gitea.test` resolves to nothing until an alias // exists — either configured by hand, or detected once from the ssh_url the // repository advertises and then reused from preferences ever after. // const { EXTENSION_ROOT } = require("./nova-stub.js"); require("./fake-gitea.js"); const { Checker } = require("./assert.js"); const path = require("node:path"); const fs = require("node:fs"); const os = require("node:os"); const { execFileSync } = require("node:child_process"); const script = (...parts) => path.join(EXTENSION_ROOT, "Scripts", ...parts); const t = new Checker(); const INSTANCE = "https://gitea.test"; const SSH_HOST = "ssh.gitea.test"; const { InstanceRouter } = require(script("gitea", "router.js")); const credentials = require(script("gitea", "credentials.js")); const discovery = require(script("gitea", "discovery.js")); const config = require(script("util", "config.js")); // -- fixtures -------------------------------------------------------------- const temporary = []; /** A throwaway workspace holding one git repository with the given remote. */ function workspaceWith(name, remoteUrl) { const workspace = fs.mkdtempSync(path.join(os.tmpdir(), "gitea-nova-alias-")); temporary.push(workspace); const repo = path.join(workspace, name); fs.mkdirSync(repo, { recursive: true }); fs.writeFileSync(path.join(repo, "README.md"), `# ${name}\n`); const git = (...args) => execFileSync("git", args, { cwd: repo, encoding: "utf-8" }); git("init", "-q", "-b", "main"); git("config", "user.email", "test@example.com"); git("config", "user.name", "Test"); git("remote", "add", "origin", remoteUrl); git("add", "-A"); git("commit", "-qm", "initial"); return workspace; } function cleanup() { for (const dir of temporary) fs.rmSync(dir, { recursive: true, force: true }); } /** A fresh router with the alias preference set to `entries`. */ function routerWith(entries) { nova.config.set("gitea.hostAliases", entries); return new InstanceRouter(); } nova.workspace.path = workspaceWith("widget", `git@${SSH_HOST}:acme/widget.git`); nova.config.set("gitea.baseUrl", INSTANCE); (async () => { t.section("alias parsing"); for (const [label, entry] of [ ["equals separator", `${SSH_HOST} = ${INSTANCE}`], ["arrow separator", `${SSH_HOST} -> ${INSTANCE}`], ["fat arrow separator", `${SSH_HOST}=>${INSTANCE}`], ["bare host as the target", `${SSH_HOST} = gitea.test`], ["untidy whitespace", ` ${SSH_HOST} = ${INSTANCE} `], ["ports ignored on both sides", `${SSH_HOST}:2222 = ${INSTANCE}`], ["case-insensitive", `${SSH_HOST.toUpperCase()} = ${INSTANCE}`], ]) { t.equals(label, routerWith([entry]).baseUrlForHost(SSH_HOST), INSTANCE); } t.equals( "a direct host match still wins", routerWith([`${SSH_HOST} = ${INSTANCE}`]).baseUrlForHost("gitea.test"), INSTANCE, ); t.section("alias entries that should be ignored"); for (const [label, entry] of [ ["blank", " "], ["comment", `# ${SSH_HOST} = ${INSTANCE}`], ["no separator", `${SSH_HOST} ${INSTANCE}`], ["too many separators", `${SSH_HOST} = ${INSTANCE} = extra`], ["self-referential", `${SSH_HOST} = ${SSH_HOST}`], ]) { t.equals(label, routerWith([entry]).baseUrlForHost(SSH_HOST), null); } t.equals( "an unrelated host stays unmatched", routerWith([`${SSH_HOST} = ${INSTANCE}`]).baseUrlForHost("github.com"), null, ); t.section("unmatched: no alias and nothing to ask"); // Without a token there is no instance to interrogate, so this is the pure // host-matching path and it must fail visibly rather than silently. nova.config.set("gitea.hostAliases", []); credentials.removeToken(INSTANCE); const bare = new InstanceRouter(); const unmatched = await discovery.discover(bare); t.equals("no repository resolves", unmatched.repos.length, 0); t.equals("the unmatched host is reported", unmatched.unmatched.map((u) => u.host), [SSH_HOST]); t.equals( "the report carries the remote for context", unmatched.unmatched[0]?.remoteUrl, `git@${SSH_HOST}:acme/widget.git`, ); t.equals("and nothing was persisted", nova.config.get("gitea.hostAliases", "array"), []); t.section("matched: alias configured by hand"); const configured = routerWith([`${SSH_HOST} = ${INSTANCE}`]); const viaConfig = await discovery.discover(configured); t.equals("the repository resolves", viaConfig.repos.length, 1); t.equals("routed to the web instance", viaConfig.repos[0]?.baseUrl, INSTANCE); t.equals("owner and name come from the remote", viaConfig.repos[0]?.fullName, "acme/widget"); t.equals("nothing left unmatched", viaConfig.unmatched.length, 0); t.section("matched: alias detected from the API"); // No alias configured, but a token is available. The instance is asked for // the repository and identifies itself through the ssh_url it advertises. nova.config.set("gitea.hostAliases", []); credentials.setToken(INSTANCE, "tok_test"); const detecting = new InstanceRouter(); const detected = await discovery.discover(detecting); t.equals("the repository resolves with no configuration", detected.repos.length, 1); t.equals("routed to the web instance", detected.repos[0]?.baseUrl, INSTANCE); t.equals("nothing left unmatched", detected.unmatched.length, 0); t.check("the host was probed", detecting._probedHosts.has(SSH_HOST)); t.check( "the alias is live on the router", detecting.aliases.get(SSH_HOST) === "gitea.test", detecting.aliases.get(SSH_HOST), ); t.check( "and was written to preferences", (nova.config.get("gitea.hostAliases", "array") || []).includes(`${SSH_HOST} = ${INSTANCE}`), JSON.stringify(nova.config.get("gitea.hostAliases", "array")), ); t.section("a later repository on the same host"); // What the stored alias buys: a different repository, in a different // workspace, resolves straight away without another round trip. nova.workspace.path = workspaceWith("gadget", `git@${SSH_HOST}:acme/gadget.git`); const returning = new InstanceRouter(); const later = await discovery.discover(returning); t.equals("the new repository resolves", later.repos.length, 1); t.equals("identified correctly", later.repos[0]?.fullName, "acme/gadget"); t.equals("routed to the web instance", later.repos[0]?.baseUrl, INSTANCE); t.equals("nothing left unmatched", later.unmatched.length, 0); t.equals("and no probe was needed", returning._probedHosts.size, 0); t.section("detection is not repeated"); // A host belonging to no instance must not be probed on every refresh. nova.config.set("gitea.hostAliases", []); const probing = new InstanceRouter(); t.equals("first probe finds nothing", await probing.detectHostAlias("elsewhere.test", "a", "b"), null); t.check("the host is remembered as probed", probing._probedHosts.has("elsewhere.test")); t.equals("a second probe short-circuits", await probing.detectHostAlias("elsewhere.test", "a", "b"), null); probing.invalidate(); t.check("an explicit refresh clears the record", !probing._probedHosts.has("elsewhere.test")); t.section("persisting an alias"); nova.config.set("gitea.hostAliases", []); t.equals("a new alias is written", config.addHostAlias(SSH_HOST, INSTANCE), true); t.equals("writing the same alias again is a no-op", config.addHostAlias(SSH_HOST, INSTANCE), false); t.equals( "re-pointing a host replaces rather than duplicates", config.addHostAlias(SSH_HOST, "https://other.test"), true, ); t.equals("one entry per host", (nova.config.get("gitea.hostAliases", "array") || []).length, 1); t.section("workspace aliases add to global ones"); nova.config.set("gitea.hostAliases", [`${SSH_HOST} = ${INSTANCE}`]); nova.workspace.config.set("gitea.hostAliases", [`other.test = ${INSTANCE}`]); const merged = new InstanceRouter(); t.equals("global entry applies", merged.baseUrlForHost(SSH_HOST), INSTANCE); t.equals("workspace entry applies", merged.baseUrlForHost("other.test"), INSTANCE); nova.workspace.config.remove("gitea.hostAliases"); cleanup(); t.finish("HOST ALIAS CHECKS"); })().catch((error) => { console.error("HARNESS ERROR", error); cleanup(); process.exit(2); });