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:
parent
f6eee0b398
commit
365e1650bc
@ -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");
|
||||
|
||||
@ -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", () => {
|
||||
|
||||
131
src/cli/workspace-seed.test.ts
Normal file
131
src/cli/workspace-seed.test.ts
Normal 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");
|
||||
});
|
||||
});
|
||||
6
src/config/types.googlechat.ts
Normal file
6
src/config/types.googlechat.ts
Normal 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
7
src/discord/pluralkit.ts
Normal 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;
|
||||
};
|
||||
9
src/infra/outbound/deliver.ts
Normal file
9
src/infra/outbound/deliver.ts
Normal 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
10
src/plugins/runtime.ts
Normal 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;
|
||||
}
|
||||
10
src/test-utils/channel-plugins.ts
Normal file
10
src/test-utils/channel-plugins.ts
Normal 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])) });
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user