Initial Daily Campaign Agent release
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
// @vitest-environment jsdom
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { SkillsPanel } from "../src/components/SkillsPanel.js";
|
||||
import { EventTimeline } from "../src/components/EventTimeline.js";
|
||||
describe("training panels",()=>{
|
||||
it("renders Skills diagnostics and active state",()=>{ render(<SkillsPanel skills={[{name:"ads-analysis",description:"广告分析",source:"project",filePath:".agents/skills/ads-analysis/SKILL.md",diagnostics:[]}]} events={[]}/>); expect(screen.getByText("ads-analysis")).toBeVisible(); expect(screen.getByText(/Valid/)).toBeVisible(); });
|
||||
it("renders empty and reconnecting timeline states",()=>{ render(<EventTimeline events={[]} reconnecting/>); expect(screen.getByText("重连中")).toBeVisible(); expect(screen.getByText(/等待 Harness Event/)).toBeVisible(); });
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
// @vitest-environment jsdom
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { HarnessClientStore } from "../src/assistant/HarnessClientStore.js";
|
||||
import { toolkit } from "../src/assistant/useHarnessRuntime.js";
|
||||
describe("web harness adapter",()=>{
|
||||
it("registers the analyzer and Skill-owned Word renderer",()=>{ expect(Object.keys(toolkit)).toEqual(expect.arrayContaining(["mock_ads_metrics","analyze_uploaded_report","build_daily_brief"])); expect(Object.keys(toolkit)).not.toContain("save_report_draft"); });
|
||||
it("never writes an API key to Web Storage",async()=>{ const store=new HarnessClientStore(); Object.defineProperty(store,"state",{value:{...store.getSnapshot(),sessionId:"s"},writable:true}); const fetchMock=vi.spyOn(globalThis,"fetch").mockResolvedValue(new Response(JSON.stringify({model:{source:"session",configured:true,providerId:"openai",modelId:"gpt-5",supportsImages:false,keyHint:"••••cret"}}),{status:200,headers:{"Content-Type":"application/json"}})); const key="sk-training-super-secret"; await store.configureModel({providerId:"openai",api:"openai-responses",apiKey:key,modelId:"gpt-5",supportsImages:false}); expect(localStorage.length).toBe(0); expect(sessionStorage.length).toBe(0); expect(store.getSnapshot().model).not.toHaveProperty("apiKey"); fetchMock.mockRestore(); });
|
||||
it("turns browser fetch failures into an actionable server message",async()=>{ const store=new HarnessClientStore(); const fetchMock=vi.spyOn(globalThis,"fetch").mockRejectedValue(new TypeError("Failed to fetch")); await expect(store.request("/health")).rejects.toThrow("默认端口 3001"); fetchMock.mockRestore(); });
|
||||
it("recreates an expired in-memory session and retries the model request",async()=>{
|
||||
const store=new HarnessClientStore(); Object.defineProperty(store,"state",{value:{...store.getSnapshot(),sessionId:"old"},writable:true}); Object.defineProperty(store,"connect",{value:vi.fn()});
|
||||
const json=(value:unknown,status=200)=>new Response(JSON.stringify(value),{status,headers:{"Content-Type":"application/json"}});
|
||||
const fetchMock=vi.spyOn(globalThis,"fetch")
|
||||
.mockResolvedValueOnce(json({error:{code:"SESSION_NOT_FOUND",message:"Session not found"}},404))
|
||||
.mockResolvedValueOnce(json({sessionId:"fresh"},201))
|
||||
.mockResolvedValueOnce(json({model:{source:"missing",configured:false}}))
|
||||
.mockResolvedValueOnce(json({model:{source:"session",configured:true,providerId:"openai",modelId:"gpt-5",supportsImages:false}}));
|
||||
await store.configureModel({providerId:"openai",api:"openai-responses",apiKey:"secret",modelId:"gpt-5",supportsImages:false});
|
||||
expect(store.getSnapshot().sessionId).toBe("fresh"); expect(store.getSnapshot().model.configured).toBe(true); expect(fetchMock.mock.calls[3]?.[0]).toBe("/api/sessions/fresh/model-config"); fetchMock.mockRestore();
|
||||
});
|
||||
it("creates a session before model actions instead of requesting sessions/undefined",async()=>{
|
||||
const store=new HarnessClientStore(); Object.defineProperty(store,"connect",{value:vi.fn()});
|
||||
const json=(value:unknown,status=200)=>new Response(JSON.stringify(value),{status,headers:{"Content-Type":"application/json"}});
|
||||
const fetchMock=vi.spyOn(globalThis,"fetch")
|
||||
.mockResolvedValueOnce(json({sessionId:"created"},201))
|
||||
.mockResolvedValueOnce(json({skills:[]}))
|
||||
.mockResolvedValueOnce(json({model:{source:"missing",configured:false}}))
|
||||
.mockResolvedValueOnce(json({profile:null}))
|
||||
.mockResolvedValueOnce(json({model:{source:"session",configured:true,providerId:"openai",modelId:"gpt-5",supportsImages:false}}));
|
||||
await store.configureModel({providerId:"openai",api:"openai-responses",apiKey:"secret",modelId:"gpt-5",supportsImages:false});
|
||||
expect(fetchMock.mock.calls.map(([url])=>String(url))).not.toContain(expect.stringContaining("/undefined/"));
|
||||
expect(fetchMock.mock.calls[0]?.[1]?.headers).not.toHaveProperty("Content-Type");
|
||||
expect(fetchMock.mock.calls[4]?.[0]).toBe("/api/sessions/created/model-config"); fetchMock.mockRestore();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user