Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/JAVA/Openclaw/src/infra/   (KI Agentensystem Version 22©)  Datei vom 26.3.2026 mit Größe 7 kB image not shown  

Quelle  tailscale.test.ts

  Sprache: JAVA
 

Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { captureEnv } from "../test-utils/env.js";
import * as tailscale from "./tailscale.js";

const {
  ensureGoInstalled,
  ensureTailscaledInstalled,
  getTailnetHostname,
  getTestTailscaleBinaryOverride,
  enableTailscaleServe,
  disableTailscaleServe,
  ensureFunnel,
} = tailscale;
const tailscaleBin = expect.stringMatching(/tailscale$/i);

function createRuntimeWithExitError() {
  return {
    error: vi.fn(),
    log: vi.fn(),
    exit: ((code: number) => {
      throw new Error(`exit ${code}`);
    }) as (code: number) => never,
  };
}

function expectServeFallbackCommand(params: { callArgs: string[]; sudoArgs: string[] }) {
  return [
    [tailscaleBin, expect.arrayContaining(params.callArgs)],
    ["sudo", expect.arrayContaining(["-n", tailscaleBin, ...params.sudoArgs])],
  ];
}

describe("tailscale helpers", () => {
  let envSnapshot: ReturnType<typeof captureEnv>;

  beforeEach(() => {
    envSnapshot = captureEnv(["OPENCLAW_TEST_TAILSCALE_BINARY", "NODE_ENV", "VITEST"]);
    process.env.OPENCLAW_TEST_TAILSCALE_BINARY = "tailscale";
    process.env.VITEST ??= "true";
  });

  afterEach(() => {
    envSnapshot.restore();
    vi.restoreAllMocks();
  });

  it("parses DNS name from tailscale status", async () => {
    const exec = vi.fn().mockResolvedValue({
      stdout: JSON.stringify({
        Self: { DNSName: "host.tailnet.ts.net.", TailscaleIPs: ["100.1.1.1"] },
      }),
    });
    const host = await getTailnetHostname(exec);
    expect(host).toBe("host.tailnet.ts.net");
  });

  it("falls back to IP when DNS missing", async () => {
    const exec = vi.fn().mockResolvedValue({
      stdout: JSON.stringify({ Self: { TailscaleIPs: ["100.2.2.2"] } }),
    });
    const host = await getTailnetHostname(exec);
    expect(host).toBe("100.2.2.2");
  });

  it("parses noisy JSON output from tailscale status", async () => {
    const exec = vi.fn().mockResolvedValue({
      stdout:
        'warning: stale state\n{"Self":{"DNSName":"noisy.tailnet.ts.net.","TailscaleIPs":["100.9.9.9"]}}\n',
    });
    const host = await getTailnetHostname(exec);
    expect(host).toBe("noisy.tailnet.ts.net");
  });

  it("allows the test binary override in explicit test environments", () => {
    process.env.OPENCLAW_TEST_TAILSCALE_BINARY = "/tmp/test-tailscale";
    process.env.NODE_ENV = "test";
    delete process.env.VITEST;

    expect(getTestTailscaleBinaryOverride()).toBe("/tmp/test-tailscale");
  });

  it("ignores the test binary override outside test environments", () => {
    process.env.OPENCLAW_TEST_TAILSCALE_BINARY = "/tmp/attacker-tailscale";
    process.env.NODE_ENV = "production";
    delete process.env.VITEST;

    expect(getTestTailscaleBinaryOverride()).toBeNull();
  });

  it.each([
    {
      name: "ensureGoInstalled installs when missing and user agrees",
      fn: ensureGoInstalled,
      missingError: new Error("no go"),
      installCommand: ["brew", ["install", "go"]] as const,
      promptResult: true,
    },
    {
      name: "ensureTailscaledInstalled installs when missing and user agrees",
      fn: ensureTailscaledInstalled,
      missingError: new Error("missing"),
      installCommand: ["brew", ["install", "tailscale"]] as const,
      promptResult: true,
    },
  ])("$name", async ({ fn, missingError, installCommand, promptResult }) => {
    const exec = vi.fn().mockRejectedValueOnce(missingError).mockResolvedValue({});
    const prompt = vi.fn().mockResolvedValue(promptResult);
    const runtime = createRuntimeWithExitError();
    await fn(exec as never, prompt, runtime);
    expect(exec).toHaveBeenCalledWith(installCommand[0], installCommand[1]);
  });

  it.each([
    {
      name: "ensureGoInstalled exits when missing and user declines install",
      fn: ensureGoInstalled,
      missingError: new Error("no go"),
      errorMessage: "Go is required to build tailscaled from source. Aborting.",
    },
    {
      name: "ensureTailscaledInstalled exits when missing and user declines install",
      fn: ensureTailscaledInstalled,
      missingError: new Error("missing"),
      errorMessage: "tailscaled is required for user-space funnel. Aborting.",
    },
  ])("$name", async ({ fn, missingError, errorMessage }) => {
    const exec = vi.fn().mockRejectedValueOnce(missingError);
    const prompt = vi.fn().mockResolvedValue(false);
    const runtime = createRuntimeWithExitError();

    await expect(fn(exec as never, prompt, runtime)).rejects.toThrow("exit 1");
    expect(runtime.error).toHaveBeenCalledWith(errorMessage);
    expect(exec).toHaveBeenCalledTimes(1);
  });

  it("enableTailscaleServe attempts normal first, then sudo", async () => {
    const exec = vi
      .fn()
      .mockRejectedValueOnce(new Error("permission denied"))
      .mockResolvedValueOnce({ stdout: "" });

    await enableTailscaleServe(3000, exec as never);

    const [firstCall, secondCall] = expectServeFallbackCommand({
      callArgs: ["serve", "--bg", "--yes", "3000"],
      sudoArgs: ["serve", "--bg", "--yes", "3000"],
    });
    expect(exec).toHaveBeenNthCalledWith(1, firstCall[0], firstCall[1], expect.any(Object));
    expect(exec).toHaveBeenNthCalledWith(2, secondCall[0], secondCall[1], expect.any(Object));
  });

  it("enableTailscaleServe does NOT use sudo if first attempt succeeds", async () => {
    const exec = vi.fn().mockResolvedValue({ stdout: "" });

    await enableTailscaleServe(3000, exec as never);

    expect(exec).toHaveBeenCalledTimes(1);
    expect(exec).toHaveBeenCalledWith(
      tailscaleBin,
      expect.arrayContaining(["serve", "--bg", "--yes", "3000"]),
      expect.any(Object),
    );
  });

  it("disableTailscaleServe uses fallback", async () => {
    const exec = vi
      .fn()
      .mockRejectedValueOnce(new Error("permission denied"))
      .mockResolvedValueOnce({ stdout: "" });

    await disableTailscaleServe(exec as never);

    expect(exec).toHaveBeenCalledTimes(2);
    expect(exec).toHaveBeenNthCalledWith(
      2,
      "sudo",
      expect.arrayContaining(["-n", tailscaleBin, "serve", "reset"]),
      expect.any(Object),
    );
  });

  it("ensureFunnel uses fallback for enabling", async () => {
    const exec = vi
      .fn()
      .mockResolvedValueOnce({ stdout: JSON.stringify({ BackendState: "Running" }) }) // status
      .mockRejectedValueOnce(new Error("permission denied")) // enable normal
      .mockResolvedValueOnce({ stdout: "" }); // enable sudo

    const runtime = {
      error: vi.fn(),
      log: vi.fn(),
      exit: vi.fn() as unknown as (code: number) => never,
    };
    const prompt = vi.fn();

    await ensureFunnel(8080, exec as never, runtime, prompt);

    expect(exec).toHaveBeenNthCalledWith(
      1,
      tailscaleBin,
      expect.arrayContaining(["funnel", "status", "--json"]),
    );
    expect(exec).toHaveBeenNthCalledWith(
      2,
      tailscaleBin,
      expect.arrayContaining(["funnel", "--yes", "--bg", "8080"]),
      expect.any(Object),
    );
    expect(exec).toHaveBeenNthCalledWith(
      3,
      "sudo",
      expect.arrayContaining(["-n", tailscaleBin, "funnel", "--yes", "--bg", "8080"]),
      expect.any(Object),
    );
  });

  it("enableTailscaleServe skips sudo on non-permission errors", async () => {
    const exec = vi.fn().mockRejectedValueOnce(new Error("boom"));

    await expect(enableTailscaleServe(3000, exec as never)).rejects.toThrow("boom");

    expect(exec).toHaveBeenCalledTimes(1);
  });

  it("enableTailscaleServe rethrows original error if sudo fails", async () => {
    const originalError = Object.assign(new Error("permission denied"), {
      stderr: "permission denied",
    });
    const exec = vi
      .fn()
      .mockRejectedValueOnce(originalError)
      .mockRejectedValueOnce(new Error("sudo: a password is required"));

    await expect(enableTailscaleServe(3000, exec as never)).rejects.toBe(originalError);

    expect(exec).toHaveBeenCalledTimes(2);
  });
});

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.