clawdbot-workspace/goosefactory/tests/integration/modal-to-learning.test.ts

181 lines
5.6 KiB
TypeScript

/**
* Integration Test: Modal → Learning Pipeline
*
* Tests the full flow:
* 1. Mock modal postMessage feedback
* 2. Validate with shared Zod schemas
* 3. Transform to FeedbackEvent
* 4. Feed into learning pipeline
* 5. Verify output
*/
import { parseModalMessage, modalResponseToFeedbackEvent } from "../../packages/shared/src/integration/modal-to-learning.js";
import type { ModalResponse } from "../../packages/shared/src/types/feedback.js";
// ─── Test Helpers ───
let passed = 0;
let failed = 0;
function assert(condition: boolean, message: string) {
if (condition) {
console.log(`${message}`);
passed++;
} else {
console.error(`${message}`);
failed++;
}
}
// ─── Mock Data ───
const mockModalResponse: ModalResponse = {
type: "factory_modal_response",
modalType: "traffic-light",
modalVersion: "1.0.0",
pipelineId: "pipeline-123",
itemId: "item-456",
sessionId: "session-789",
timestamp: new Date().toISOString(),
responseTimeMs: 3500,
feedback: {
decision: {
decision: "approved",
reason: "Code looks clean, tests passing",
tags: ["clean-code", "tests-passing"],
},
confidence: {
confidencePercent: 85,
zone: "confident",
wouldDelegateToAI: false,
needsExpertReview: false,
},
},
meta: {
timeToFirstInteractionMs: 1200,
timeToDecisionMs: 3500,
fieldsModified: ["decision", "confidence"],
scrollDepth: 0.8,
revisits: 0,
totalInteractions: 5,
viewportSize: { width: 1920, height: 1080 },
deviceType: "desktop",
},
};
// ─── Tests ───
console.log("\n🧪 Modal → Learning Integration Tests\n");
// Test 1: Valid modal message parsing
console.log("Test 1: Parse valid modal response");
{
const result = parseModalMessage(mockModalResponse);
assert(result.valid === true, "Should parse valid modal response");
if (result.valid) {
assert(result.response.modalType === "traffic-light", "Modal type should be traffic-light");
assert(result.response.feedback.decision?.decision === "approved", "Decision should be approved");
}
}
// Test 2: Invalid message (wrong type)
console.log("\nTest 2: Reject non-response messages");
{
const result = parseModalMessage({ type: "factory_modal_ready", modalType: "test", version: "1.0.0" });
assert(result.valid === false, "Should reject non-response messages");
}
// Test 3: Invalid message (bad schema)
console.log("\nTest 3: Reject schema-invalid messages");
{
const result = parseModalMessage({
type: "factory_modal_response",
modalType: "test",
// Missing required fields
});
assert(result.valid === false, "Should reject schema-invalid messages");
}
// Test 4: Null/undefined input
console.log("\nTest 4: Handle null/undefined input");
{
assert(parseModalMessage(null).valid === false, "Should reject null");
assert(parseModalMessage(undefined).valid === false, "Should reject undefined");
assert(parseModalMessage("string").valid === false, "Should reject string");
}
// Test 5: Transform to FeedbackEvent
console.log("\nTest 5: Transform ModalResponse → FeedbackEvent");
{
const feedbackEvent = modalResponseToFeedbackEvent(mockModalResponse, {
workProductType: "mcp_server",
workProductId: "ghl-mcp",
workProductVersion: "abc123",
workProductSummary: "Go High Level MCP server",
pipelineStage: "review",
mcpServerType: "go-high-level",
});
assert(typeof feedbackEvent.id === "string", "Should have UUID id");
assert(feedbackEvent.modalType === "traffic-light", "Modal type preserved");
assert(feedbackEvent.pipelineId === "pipeline-123", "Pipeline ID preserved");
assert(feedbackEvent.workProduct.type === "mcp_server", "Work product type set");
assert(feedbackEvent.pipelineStage === "review", "Pipeline stage set");
assert(feedbackEvent.feedback.decision?.decision === "approved", "Feedback preserved");
assert(feedbackEvent.meta.timeOfDay !== undefined, "Time of day enriched");
assert(feedbackEvent.meta.dayOfWeek !== undefined, "Day of week enriched");
}
// Test 6: Scores validation in FeedbackEvent
console.log("\nTest 6: Scores feedback type");
{
const responseWithScores: ModalResponse = {
...mockModalResponse,
modalType: "report-card",
feedback: {
scores: [
{ dimension: "code_quality", score: 8, comment: "Clean" },
{ dimension: "test_coverage", score: 9 },
{ dimension: "documentation", score: 7 },
],
},
};
const result = parseModalMessage(responseWithScores);
assert(result.valid === true, "Should accept scores feedback");
if (result.valid) {
assert(result.response.feedback.scores?.length === 3, "Should have 3 scores");
}
}
// Test 7: Batch decisions feedback type
console.log("\nTest 7: Batch decisions feedback type");
{
const responseWithBatch: ModalResponse = {
...mockModalResponse,
modalType: "tinder-swipe",
feedback: {
batchDecisions: [
{ itemId: "item-1", decision: "approve", timeMs: 1200, flippedForDetails: false },
{ itemId: "item-2", decision: "reject", timeMs: 3500, flippedForDetails: true },
{ itemId: "item-3", decision: "love", timeMs: 800, flippedForDetails: false },
],
},
};
const result = parseModalMessage(responseWithBatch);
assert(result.valid === true, "Should accept batch decisions");
if (result.valid) {
assert(result.response.feedback.batchDecisions?.length === 3, "Should have 3 batch decisions");
}
}
// ─── Summary ───
console.log(`\n${"═".repeat(50)}`);
console.log(`Results: ${passed} passed, ${failed} failed out of ${passed + failed} assertions`);
console.log(`${"═".repeat(50)}\n`);
if (failed > 0) {
process.exit(1);
}