fix(test): resolve lint errors in workspace and init route tests

Remove unused useEnvWorkspace; fix base-to-string in identity content assertion.
This commit is contained in:
kumarabhirup 2026-03-03 13:48:05 -08:00
parent f6eee0b398
commit 365e1650bc
No known key found for this signature in database
GPG Key ID: DB7CA2289CAB0167
8 changed files with 178 additions and 10 deletions

View File

@ -111,8 +111,8 @@ describe("POST /api/workspace/init", () => {
const workspaceDir = join(STATE_DIR, "workspace-work");
vi.mocked(existsSync).mockImplementation((p) => {
const s = String(p);
if (s.endsWith("docs/reference/templates/AGENTS.md")) return true;
if (s.endsWith("skills/dench/SKILL.md")) return true;
if (s.endsWith("docs/reference/templates/AGENTS.md")) {return true;}
if (s.endsWith("skills/dench/SKILL.md")) {return true;}
return false;
});
@ -142,7 +142,7 @@ describe("POST /api/workspace/init", () => {
vi.mocked(existsSync).mockImplementation((p) => {
const s = String(p);
if (s.endsWith("docs/reference/templates/AGENTS.md")) return true;
if (s.endsWith("docs/reference/templates/AGENTS.md")) {return true;}
return false;
});
@ -155,7 +155,8 @@ describe("POST /api/workspace/init", () => {
(call) => String(call[0]).endsWith("IDENTITY.md"),
);
expect(identityWrites.length).toBeGreaterThan(0);
const identityContent = String(identityWrites[identityWrites.length - 1][1]);
const raw = identityWrites[identityWrites.length - 1][1];
const identityContent = typeof raw === "string" ? raw : JSON.stringify(raw);
expect(identityContent).toContain(expectedSkillPath);
expect(identityContent).toContain("Ironclaw");
expect(identityContent).not.toContain("~skills");

View File

@ -94,12 +94,6 @@ describe("workspace utilities", () => {
};
}
/** Set up mocks so resolveWorkspaceRoot() returns WS_DIR via OPENCLAW_WORKSPACE env. */
function useEnvWorkspace(mockExists: ReturnType<typeof vi.mocked<typeof existsSync>>) {
process.env.OPENCLAW_WORKSPACE = WS_DIR;
mockExists.mockImplementation((p) => String(p) === WS_DIR);
}
// ─── resolveWorkspaceRoot ────────────────────────────────────────
describe("resolveWorkspaceRoot", () => {

View File

@ -0,0 +1,131 @@
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import path from "node:path";
import os from "node:os";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { seedWorkspaceFromAssets } from "./workspace-seed.js";
function createTempDir(): string {
const dir = path.join(
os.tmpdir(),
`ironclaw-seed-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
);
mkdirSync(dir, { recursive: true });
return dir;
}
function createPackageRoot(tempDir: string): string {
const pkgRoot = path.join(tempDir, "pkg");
const seedDir = path.join(pkgRoot, "assets", "seed");
const skillsDir = path.join(pkgRoot, "skills", "dench");
mkdirSync(seedDir, { recursive: true });
mkdirSync(skillsDir, { recursive: true });
writeFileSync(path.join(seedDir, "workspace.duckdb"), "SEED_DB_CONTENT", "utf-8");
writeFileSync(
path.join(skillsDir, "SKILL.md"),
"---\nname: database-crm-system\n---\n# Dench CRM\n",
"utf-8",
);
return pkgRoot;
}
describe("seedWorkspaceFromAssets", () => {
let tempDir: string;
beforeEach(() => {
tempDir = createTempDir();
});
afterEach(() => {
rmSync(tempDir, { recursive: true, force: true });
});
it("seeds Dench skill inside the workspace (not in state dir)", () => {
const packageRoot = createPackageRoot(tempDir);
const workspaceDir = path.join(tempDir, "workspace-main");
seedWorkspaceFromAssets({ workspaceDir, packageRoot });
const skillPath = path.join(workspaceDir, "skills", "dench", "SKILL.md");
expect(existsSync(skillPath)).toBe(true);
expect(readFileSync(skillPath, "utf-8")).toContain("database-crm-system");
const stateSkillPath = path.join(tempDir, "skills", "dench", "SKILL.md");
expect(existsSync(stateSkillPath)).toBe(false);
});
it("generates IDENTITY.md referencing workspace CRM skill path (not virtual ~skills)", () => {
const packageRoot = createPackageRoot(tempDir);
const workspaceDir = path.join(tempDir, "workspace-test");
seedWorkspaceFromAssets({ workspaceDir, packageRoot });
const identityPath = path.join(workspaceDir, "IDENTITY.md");
expect(existsSync(identityPath)).toBe(true);
const identityContent = readFileSync(identityPath, "utf-8");
expect(identityContent).toContain("Ironclaw");
expect(identityContent).toContain(path.join(workspaceDir, "skills", "dench", "SKILL.md"));
expect(identityContent).not.toContain("~skills/dench/SKILL.md");
});
it("IDENTITY.md references Ironclaw system prompt contract", () => {
const packageRoot = createPackageRoot(tempDir);
const workspaceDir = path.join(tempDir, "workspace-contract");
seedWorkspaceFromAssets({ workspaceDir, packageRoot });
const identityContent = readFileSync(path.join(workspaceDir, "IDENTITY.md"), "utf-8");
expect(identityContent).toContain("Ironclaw system prompt contract");
});
it("creates CRM object projection files on first seed", () => {
const packageRoot = createPackageRoot(tempDir);
const workspaceDir = path.join(tempDir, "workspace-proj");
const result = seedWorkspaceFromAssets({ workspaceDir, packageRoot });
expect(result.seeded).toBe(true);
expect(result.reason).toBe("seeded");
expect(existsSync(path.join(workspaceDir, "people", ".object.yaml"))).toBe(true);
expect(existsSync(path.join(workspaceDir, "company", ".object.yaml"))).toBe(true);
expect(existsSync(path.join(workspaceDir, "task", ".object.yaml"))).toBe(true);
expect(existsSync(path.join(workspaceDir, "WORKSPACE.md"))).toBe(true);
});
it("skips DuckDB seeding when workspace.duckdb already exists", () => {
const packageRoot = createPackageRoot(tempDir);
const workspaceDir = path.join(tempDir, "workspace-existing");
mkdirSync(workspaceDir, { recursive: true });
writeFileSync(path.join(workspaceDir, "workspace.duckdb"), "EXISTING_DB", "utf-8");
const result = seedWorkspaceFromAssets({ workspaceDir, packageRoot });
expect(result.seeded).toBe(false);
expect(result.reason).toBe("already-exists");
expect(readFileSync(path.join(workspaceDir, "workspace.duckdb"), "utf-8")).toBe("EXISTING_DB");
});
it("always force-syncs IDENTITY.md even when workspace already exists (keeps updates current)", () => {
const packageRoot = createPackageRoot(tempDir);
const workspaceDir = path.join(tempDir, "workspace-resync");
mkdirSync(workspaceDir, { recursive: true });
writeFileSync(path.join(workspaceDir, "workspace.duckdb"), "DB", "utf-8");
writeFileSync(path.join(workspaceDir, "IDENTITY.md"), "# stale identity\n", "utf-8");
seedWorkspaceFromAssets({ workspaceDir, packageRoot });
const identityContent = readFileSync(path.join(workspaceDir, "IDENTITY.md"), "utf-8");
expect(identityContent).toContain("Ironclaw");
expect(identityContent).not.toContain("# stale identity");
});
it("includes skills/dench/SKILL.md in projection files list", () => {
const packageRoot = createPackageRoot(tempDir);
const workspaceDir = path.join(tempDir, "workspace-list");
const result = seedWorkspaceFromAssets({ workspaceDir, packageRoot });
expect(result.projectionFiles).toContain("skills/dench/SKILL.md");
expect(result.projectionFiles).toContain("IDENTITY.md");
});
});

View File

@ -0,0 +1,6 @@
/** Placeholder for Google Chat channel config. Extension provides full schema. */
export type GoogleChatConfig = {
enabled?: boolean;
accounts?: unknown[];
[key: string]: unknown;
};

7
src/discord/pluralkit.ts Normal file
View File

@ -0,0 +1,7 @@
/** PluralKit identity resolution for Discord proxied messages. */
export type DiscordPluralKitConfig = {
/** Enable PluralKit resolution (default: false). */
enabled?: boolean;
/** Optional PluralKit API token for private systems. */
token?: string;
};

View File

@ -0,0 +1,9 @@
/** Stub type for test setup; full implementation may live in upstream. */
export type OutboundSendDeps = {
sendDiscord?: (to: string, text: string, opts?: { verbose?: boolean; mediaUrl?: string }) => Promise<{ channel?: string; messageId?: string }>;
sendSlack?: (to: string, text: string, opts?: { verbose?: boolean; mediaUrl?: string }) => Promise<{ channel?: string; messageId?: string }>;
sendTelegram?: (to: string, text: string, opts?: { verbose?: boolean; mediaUrl?: string }) => Promise<{ channel?: string; messageId?: string }>;
sendWhatsApp?: (to: string, text: string, opts?: { verbose?: boolean; mediaUrl?: string }) => Promise<{ channel?: string; messageId?: string }>;
sendSignal?: (to: string, text: string, opts?: { verbose?: boolean; mediaUrl?: string }) => Promise<{ channel?: string; messageId?: string }>;
sendIMessage?: (to: string, text: string, opts?: { verbose?: boolean; mediaUrl?: string }) => Promise<{ channel?: string; messageId?: string }>;
};

10
src/plugins/runtime.ts Normal file
View File

@ -0,0 +1,10 @@
/** Stub for test setup; maintains active plugin registry for tests. */
let activeRegistry: unknown = null;
export function setActivePluginRegistry(registry: unknown): void {
activeRegistry = registry;
}
export function getActivePluginRegistry(): unknown {
return activeRegistry;
}

View File

@ -0,0 +1,10 @@
/** Stub for test setup; creates an immutable plugin registry from entries. */
export type RegistryEntry = {
pluginId: string;
plugin: unknown;
source: string;
};
export function createTestRegistry(entries: RegistryEntry[]): unknown {
return Object.freeze({ entries, plugins: new Map(entries.map((e) => [e.pluginId, e.plugin])) });
}