Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/clipboard.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, describe, expect, it, vi } from "vitest";
import { copyToClipboard } from "./clipboard";

describe("copyToClipboard", () => {
Expand Down
6 changes: 6 additions & 0 deletions src/test/setup.ts
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
import "@testing-library/jest-dom/vitest";
import { cleanup } from "@testing-library/react";
import { afterEach } from "vitest";

afterEach(() => {
cleanup();
});
150 changes: 149 additions & 1 deletion src/testkit.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { describe, expect, it } from "vitest";
import { buildCommand } from "./testkit";
import {
buildCommand,
cliCompatibility,
formatCommandLine,
localFileGuides,
} from "./testkit";

describe("buildCommand", () => {
it("builds a coverage command", () => {
Expand Down Expand Up @@ -47,3 +52,146 @@ describe("buildCommand", () => {
.toBe('soroban-testkit audit "./a b c"');
});
});

describe("formatCommandLine", () => {
it("returns single-line identical to buildCommand when multiline is false", () => {
const options = {
command: "coverage" as const,
path: ".",
format: "html" as const,
open: true,
fn: "",
ramp: "",
strict: false,
};
expect(formatCommandLine(options, false)).toBe(buildCommand(options));
});

it("formats multi-line coverage command with POSIX line continuations", () => {
const formatted = formatCommandLine(
{
command: "coverage",
path: "./contracts/escrow",
format: "lcov",
open: true,
fn: "",
ramp: "",
strict: false,
},
true,
);

expect(formatted).toBe(
[
"soroban-testkit coverage \\",
" ./contracts/escrow \\",
" --format lcov \\",
" --open",
].join("\n"),
);
});

it("formats multi-line limits command with quoted paths if needed", () => {
const formatted = formatCommandLine(
{
command: "limits",
path: "target/release/my contract.wasm",
format: "html",
open: false,
fn: "execute_equal",
ramp: "recipients",
strict: false,
},
true,
);

expect(formatted).toBe(
[
"soroban-testkit limits \\",
' --contract "target/release/my contract.wasm" \\',
" --fn execute_equal \\",
" --ramp recipients",
].join("\n"),
);
});

it("formats multi-line audit command with strict flag", () => {
const formatted = formatCommandLine(
{
command: "audit",
path: "./my-contract/src",
format: "text",
open: false,
fn: "",
ramp: "",
strict: true,
},
true,
);

expect(formatted).toBe(
[
"soroban-testkit audit \\",
" ./my-contract/src \\",
" --strict",
].join("\n"),
);
});

it("formats multi-line audit command without strict flag", () => {
const formatted = formatCommandLine(
{
command: "audit",
path: "./src",
format: "text",
open: false,
fn: "",
ramp: "",
strict: false,
},
true,
);

expect(formatted).toBe(
[
"soroban-testkit audit \\",
" ./src",
].join("\n"),
);
});
});

describe("cliCompatibility", () => {
it("provides version entries with required compatibility targets", () => {
expect(cliCompatibility.length).toBeGreaterThanOrEqual(3);

const current = cliCompatibility.find((c) => c.status === "Current");
expect(current).toBeDefined();
expect(current?.sorobanSdk).toBe("27.0.x");
expect(current?.rustTarget).toContain("wasm32v1-none");

for (const row of cliCompatibility) {
expect(row.version).toBeTruthy();
expect(row.sorobanSdk).toBeTruthy();
expect(row.stellarCli).toBeTruthy();
expect(row.rustTarget).toBeTruthy();
expect(row.status).toBeTruthy();
expect(row.notes).toBeTruthy();
}
});
});

describe("localFileGuides", () => {
it("covers all commands with input/output documentation", () => {
for (const cmd of ["coverage", "limits", "audit"] as const) {
const guide = localFileGuides[cmd];
expect(guide).toBeDefined();
expect(guide.filesRead.length).toBeGreaterThan(0);
expect(guide.filesWritten.length).toBeGreaterThan(0);
expect(guide.offlineGuarantee).toContain("offline");
expect(guide.resolvedPathExample("test-path")).toBeTruthy();
}
});
});


161 changes: 161 additions & 0 deletions src/testkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,164 @@ export function buildCommand(options: CommandOptions): string {
.filter(Boolean)
.join(" ");
}

export function formatCommandLine(
options: CommandOptions,
multiline = false,
): string {
if (!multiline) {
return buildCommand(options);
}

if (options.command === "coverage") {
const args: string[] = [];
if (options.path) args.push(quote(options.path));
args.push(`--format ${options.format}`);
if (options.open) args.push("--open");

if (args.length === 0) {
return "soroban-testkit coverage";
}

return [
"soroban-testkit coverage \\",
...args.map((arg, index) => ` ${arg}${index < args.length - 1 ? " \\" : ""}`),
].join("\n");
}

if (options.command === "limits") {
const contractPath = quote(
options.path || "target/wasm32v1-none/release/contract.wasm",
);
const fn = options.fn || "batch_payout";
const ramp = options.ramp || "recipients";

return [
"soroban-testkit limits \\",
` --contract ${contractPath} \\`,
` --fn ${fn} \\`,
` --ramp ${ramp}`,
].join("\n");
}

// audit
const auditPath = quote(options.path || "./src");
if (options.strict) {
return [
"soroban-testkit audit \\",
` ${auditPath} \\`,
" --strict",
].join("\n");
}

return ["soroban-testkit audit \\", ` ${auditPath}`].join("\n");
}

export interface CliCompatibility {
version: string;
sorobanSdk: string;
stellarCli: string;
rustTarget: string;
status: "Current" | "Supported" | "Upcoming";
notes: string;
}

export const cliCompatibility: readonly CliCompatibility[] = [
{
version: "v0.2.0",
sorobanSdk: "27.0.x",
stellarCli: ">= 22.0.0",
rustTarget: "wasm32v1-none (Rust >= 1.84)",
status: "Current",
notes:
"AuthMatrix multi-actor enforcement, auth-mocked limits ramp, SAC heuristic mocking, cargo-llvm-cov integration.",
},
{
version: "v0.1.0",
sorobanSdk: "22.x - 26.x",
stellarCli: ">= 21.0.0",
rustTarget: "wasm32-unknown-unknown / wasm32v1-none",
status: "Supported",
notes:
"Deterministic TestEnv, ledger timestamp/sequence warping, conservation balance checks, AST source code audit scanner.",
},
{
version: "v0.3.0",
sorobanSdk: "28.0.x+",
stellarCli: ">= 23.0.0",
rustTarget: "wasm32v1-none (Rust 2024)",
status: "Upcoming",
notes:
"Extended persistent TTL storage simulation, multi-contract cross-call limits ramp, and gas telemetry export.",
},
];

export interface LocalFileGuide {
command: CommandName;
title: string;
summary: string;
defaultPath: string;
resolvedPathExample: (path: string) => string;
filesRead: string[];
filesWritten: string[];
offlineGuarantee: string;
}

export const localFileGuides: Record<CommandName, LocalFileGuide> = {
coverage: {
command: "coverage",
title: "Contract Coverage (cargo-llvm-cov)",
summary:
"Instruments contract crates and runs local test suites to compute exact source and branch coverage.",
defaultPath: ".",
resolvedPathExample: (p) => (p || ".").replace(/\/$/, "") + "/Cargo.toml",
filesRead: [
"Cargo.toml / Cargo.lock (crate dependencies and test harness)",
"src/**/*.rs (contract source files)",
"tests/**/*.rs (integration test suites)",
],
filesWritten: [
"target/llvm-cov/html/** (interactive report when --format html)",
"target/llvm-cov/lcov.info (coverage trace when --format lcov)",
"None (stdout summary only when --format text)",
],
offlineGuarantee:
"Execution is 100% offline. No contract bytecode or source files are ever transmitted remotely.",
},
limits: {
command: "limits",
title: "Resource Limit Ramp (soroban-testkit limits)",
summary:
"Probes compiled WASM bytecode across stepped parameter values to find exact instruction and memory boundaries.",
defaultPath: "target/wasm32v1-none/release/contract.wasm",
resolvedPathExample: (p) =>
p || "target/wasm32v1-none/release/contract.wasm",
filesRead: [
"Pre-compiled contract WebAssembly bytecode (.wasm artifact)",
"Local function export table and symbol signatures",
],
filesWritten: [
"None (metrics and resource ceilings stream directly to terminal stdout)",
],
offlineGuarantee:
"100% offline. Probes execute in isolated local child processes using in-memory mock ledger state with zero network RPC calls.",
},
audit: {
command: "audit",
title: "Static Source Audit (soroban-testkit audit)",
summary:
"Performs AST-level static security checks for authentication gating, arithmetic safety, and TTL state.",
defaultPath: "./src",
resolvedPathExample: (p) => (p || "./src").replace(/\/$/, "") + "/*.rs",
filesRead: [
"Rust source files (*.rs) in the specified project directory",
"Syntax trees for require_auth, arithmetic operations, and storage keys",
],
filesWritten: [
"None (findings and warning locations stream directly to terminal stdout)",
],
offlineGuarantee:
"100% offline, read-only and non-destructive. Source code is parsed locally without external telemetry.",
},
};

Binary file added tests/visual/__screenshots__/commands-audit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/visual/__screenshots__/commands-limits.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/visual/__screenshots__/full-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/visual/__screenshots__/hero.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/visual/__screenshots__/modules.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export default defineConfig({
test: {
environment: "jsdom",
setupFiles: "./src/test/setup.ts",
exclude: ["tests/visual/**", "node_modules/**"],
},
});