| 1 |
// Against a real hub when the binary exists: create a repository, an |
| 2 |
// issue and a proposal through the client. |
| 3 |
import { test } from "node:test"; |
| 4 |
import assert from "node:assert/strict"; |
| 5 |
import { spawn, execFileSync } from "node:child_process"; |
| 6 |
import { mkdtempSync, writeFileSync, existsSync, mkdirSync } from "node:fs"; |
| 7 |
import { tmpdir } from "node:os"; |
| 8 |
import { join } from "node:path"; |
| 9 |
import { HubClient, HubError } from "../out/hub.mjs"; |
| 10 |
|
| 11 |
const HUB = join(import.meta.dirname, "../../oxygenhub/target/release/oxygenhub"); |
| 12 |
const OXY = join(import.meta.dirname, "../../oxygen/target/release/oxy"); |
| 13 |
|
| 14 |
test("hub client end to end", { skip: !existsSync(HUB) || !existsSync(OXY) }, async () => { |
| 15 |
const data = mkdtempSync(join(tmpdir(), "hubvs-")); |
| 16 |
const port = 17300 + Math.floor(Math.random() * 1000); |
| 17 |
const base = `http://127.0.0.1:${port}`; |
| 18 |
writeFileSync(join(data, "hub.toml"), `[server]\nbind = "127.0.0.1:${port}"\nbase_url = "${base}"\n[auth]\npush_tokens = ["site"]\n[registration]\nmode = "open"\n`); |
| 19 |
execFileSync(HUB, ["--data", data, "init"], { stdio: "ignore" }); |
| 20 |
execFileSync(HUB, ["--data", data, "admin", "user", "create", "andy", "--admin"], { stdio: "ignore" }); |
| 21 |
const proc = spawn(HUB, ["--data", data, "serve"], { stdio: "ignore" }); |
| 22 |
try { |
| 23 |
for (let i = 0; i < 50; i++) { |
| 24 |
try { |
| 25 |
await fetch(`${base}/health`); |
| 26 |
break; |
| 27 |
} catch { |
| 28 |
await new Promise((r) => setTimeout(r, 100)); |
| 29 |
} |
| 30 |
} |
| 31 |
const client = new HubClient(base, "site"); |
| 32 |
// The client has no createRepository method by design; use fetch directly. |
| 33 |
const res = await fetch(`${base}/api/v1/repos`, { method: "POST", headers: { authorization: "Bearer site", "content-type": "application/json" }, body: JSON.stringify({ owner: "andy", name: "demo" }) }); |
| 34 |
assert.equal(res.status, 201); |
| 35 |
// Push a bookmark with oxy so a proposal has something to add. |
| 36 |
const work = mkdtempSync(join(tmpdir(), "hubvs-work-")); |
| 37 |
const env = { ...process.env, OXY_HOME: mkdtempSync(join(tmpdir(), "hubvs-home-")) }; |
| 38 |
const oxy = (...args) => execFileSync(OXY, ["--plain", ...args], { cwd: work, env, stdio: ["ignore", "pipe", "pipe"] }).toString(); |
| 39 |
oxy("init"); |
| 40 |
oxy("remote", "add", "origin", `${base}/andy/demo`); |
| 41 |
oxy("config", "remote.origin.token", "site"); |
| 42 |
mkdirSync(join(work, "src"), { recursive: true }); |
| 43 |
writeFileSync(join(work, "src/a.txt"), "a\n"); |
| 44 |
oxy("commit", "-m", "base"); |
| 45 |
oxy("push", "origin"); |
| 46 |
oxy("bookmark", "create", "feat"); |
| 47 |
oxy("bookmark", "switch", "feat"); |
| 48 |
writeFileSync(join(work, "src/b.txt"), "b\n"); |
| 49 |
oxy("commit", "-m", "feat"); |
| 50 |
oxy("push", "origin", "--bookmark", "feat"); |
| 51 |
const p = await client.createProposal("andy", "demo", { title: "Feat", target: "main", source: "feat", body: "", draft: false }); |
| 52 |
assert.equal(p.number, 1); |
| 53 |
assert.equal(p.changes.length, 1); |
| 54 |
const list = await client.proposals("andy", "demo"); |
| 55 |
assert.equal(list[0].title, "Feat"); |
| 56 |
const issue = await client.createIssue("andy", "demo", "Bug", "text"); |
| 57 |
assert.equal(issue.number, 2); |
| 58 |
await client.commentIssue("andy", "demo", 2, "more"); |
| 59 |
const closed = await client.setIssueState("andy", "demo", 2, "closed"); |
| 60 |
assert.equal(closed.state, "closed"); |
| 61 |
const me = await client.me(); |
| 62 |
assert.equal(me.username, "andy"); |
| 63 |
await assert.rejects(() => new HubClient(base, "nope").me(), (e) => e instanceof HubError && e.status === 401); |
| 64 |
} finally { |
| 65 |
proc.kill(); |
| 66 |
} |
| 67 |
}); |