test: cover code lease portal paths

This commit is contained in:
Vincent Koc 2026-05-04 23:50:22 -07:00
parent b386b9a737
commit 28418c16d0
No known key found for this signature in database
5 changed files with 139 additions and 1 deletions

View File

@ -85,6 +85,25 @@ func TestCloudInitBrowserProfile(t *testing.T) {
}
}
func TestCloudInitCodeProfile(t *testing.T) {
cfg := baseConfig()
cfg.Code = true
got := cloudInit(cfg, "ssh-ed25519 test")
for _, want := range []string{
"https://code-server.dev/install.sh",
"--method=standalone --prefix=/usr/local",
"/usr/local/bin/code-server --version >/dev/null",
"test -x /usr/local/bin/code-server",
} {
if !strings.Contains(got, want) {
t.Fatalf("cloudInit(code) missing %q", want)
}
}
if strings.Contains(cloudInit(baseConfig(), "ssh-ed25519 test"), "code-server") {
t.Fatal("cloudInit should not install code-server by default")
}
}
func TestCloudInitTailscaleProfile(t *testing.T) {
cfg := baseConfig()
cfg.SSHUser = "runner"

46
internal/cli/code_test.go Normal file
View File

@ -0,0 +1,46 @@
package cli
import (
"strings"
"testing"
)
func TestWebCodeURLs(t *testing.T) {
if got := webCodeAgentURL("https://crabbox.openclaw.ai", "cbx_abcdef123456", "code_abc"); got != "wss://crabbox.openclaw.ai/v1/leases/cbx_abcdef123456/code/agent?ticket=code_abc" {
t.Fatalf("agent URL=%q", got)
}
if got := webCodePortalURL("https://crabbox.openclaw.ai/", "cbx_abcdef123456"); got != "https://crabbox.openclaw.ai/portal/leases/cbx_abcdef123456/code/" {
t.Fatalf("portal URL=%q", got)
}
}
func TestCodeUpstreamPathStripsPortalLeasePrefix(t *testing.T) {
tests := map[string]string{
"/portal/leases/cbx_abcdef123456/code/": "/",
"/portal/leases/cbx_abcdef123456/code/static/main.js": "/static/main.js",
"/portal/leases/cbx_abcdef123456/code/?folder=/work/repo": "/?folder=/work/repo",
"/portal/leases/blue-lobster/code/vscode-remote-resource": "/vscode-remote-resource",
"/portal/leases/blue-lobster/vnc/viewer": "/portal/leases/blue-lobster/vnc/viewer",
"/portal/leases/blue-lobster/code/proxy/3000/?q=hello+you": "/proxy/3000/?q=hello+you",
}
for input, want := range tests {
if got := codeUpstreamPath(input); got != want {
t.Fatalf("codeUpstreamPath(%q)=%q want %q", input, got, want)
}
}
}
func TestStartCodeServerCommand(t *testing.T) {
got := startCodeServerCommand("/work/crabbox/cbx_abcdef123456/repo")
for _, want := range []string{
"/usr/local/bin/code-server",
"--auth none",
"--bind-addr 127.0.0.1:8080",
"VSCODE_PROXY_URI='./proxy/{{port}}'",
"/tmp/crabbox-code-server.log",
} {
if !strings.Contains(got, want) {
t.Fatalf("startCodeServerCommand missing %q:\n%s", want, got)
}
}
}

View File

@ -1668,7 +1668,7 @@ function codeLeaseError(lease: LeaseRecord): string {
if (!lease.code) {
return "lease was not created with code=true";
}
if (lease.target !== "linux") {
if (lease.target && lease.target !== "linux") {
return "code is currently available for Linux leases only";
}
if (!lease.host) {

View File

@ -104,6 +104,16 @@ describe("cloud-init bootstrap", () => {
expect(got).toContain('"$BROWSER" --version >/dev/null');
});
it("adds code-server setup only when requested", () => {
const plain = cloudInit(config);
expect(plain).not.toContain("code-server");
const got = cloudInit({ ...config, code: true });
expect(got).toContain("https://code-server.dev/install.sh");
expect(got).toContain("--method=standalone --prefix=/usr/local");
expect(got).toContain("/usr/local/bin/code-server --version >/dev/null");
expect(got).toContain("test -x /usr/local/bin/code-server");
});
it("adds Tailscale setup only when requested", () => {
const plain = cloudInit(config);
expect(plain).not.toContain("tailscale up");

View File

@ -473,6 +473,7 @@ describe("fleet lease identity and idle", () => {
owner: "peter@example.com",
org: "openclaw",
desktop: true,
code: true,
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
}),
);
@ -499,9 +500,71 @@ describe("fleet lease identity and idle", () => {
const body = await response.text();
expect(body).toContain("blue-lobster");
expect(body).toContain("/portal/leases/cbx_000000000001/vnc");
expect(body).toContain("/portal/leases/cbx_000000000001/code/");
expect(body).not.toContain("amber-krill");
});
it("serves code pages only for code leases and requires a bridge ticket", async () => {
const storage = new MemoryStorage();
const fleet = testFleet(storage);
const headers = {
"x-crabbox-owner": "peter@example.com",
"x-crabbox-org": "openclaw",
};
storage.seed(
"lease:cbx_000000000001",
testLease({
id: "cbx_000000000001",
slug: "blue-lobster",
owner: "peter@example.com",
org: "openclaw",
code: true,
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
}),
);
storage.seed(
"lease:cbx_000000000002",
testLease({
id: "cbx_000000000002",
slug: "plain-lobster",
owner: "peter@example.com",
org: "openclaw",
code: false,
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
}),
);
const page = await fleet.fetch(request("GET", "/portal/leases/blue-lobster/code/", { headers }));
expect(page.status).toBe(200);
const pageBody = await page.text();
expect(pageBody).toContain("crabbox code --id blue-lobster --open");
const plain = await fleet.fetch(
request("GET", "/portal/leases/plain-lobster/code/", { headers }),
);
expect(plain.status).toBe(409);
const ticket = await fleet.fetch(
request("POST", "/v1/leases/blue-lobster/code/ticket", { headers, body: {} }),
);
expect(ticket.status).toBe(200);
const ticketBody = (await ticket.json()) as { ticket: string; leaseID: string };
expect(ticketBody.ticket).toMatch(/^code_[a-f0-9]{32}$/);
expect(ticketBody.leaseID).toBe("cbx_000000000001");
const agent = await fleet.fetch(
request("GET", "/v1/leases/blue-lobster/code/agent", { headers }),
);
expect(agent.status).toBe(426);
const missingTicket = await fleet.fetch(
request("GET", "/v1/leases/blue-lobster/code/agent", {
headers: { upgrade: "websocket" },
}),
);
expect(missingTicket.status).toBe(401);
});
it("serves WebVNC pages only for desktop leases and requires an agent upgrade", async () => {
const storage = new MemoryStorage();
const fleet = testFleet(storage);