/**
 * @deprecated [DC-1] 无运行时消费方。MCP 工具可能通过动态导入调用，保留但标记废弃。
 */
import path from "node:path";
import { debug } from "../core/logger.js";
/** 文件类型 → 测试策略映射表: 根据文件名模式匹配测试类型、夹具方式和 Mock 策略 */
const FILE_TEST_MAP = [
    {
        pattern: /Controller\.java$/,
        test_type: "integration",
        fixture: "@SpringBootTest + MockMvc",
        mock: "@MockBean Service 层依赖",
    },
    {
        pattern: /Service\.java$/,
        test_type: "unit",
        fixture: "@ExtendWith(MockitoExtension)",
        mock: "@Mock Mapper/Repository 层依赖",
    },
    {
        pattern: /Mapper\.java$|Repository\.java$/,
        test_type: "integration",
        fixture: "@MybatisTest / @DataJpaTest",
        mock: "测试数据库或 H2 内存库",
    },
    {
        pattern: /api\/|route/,
        test_type: "e2e",
        fixture: "supertest(app)",
        mock: "数据库和外部服务模拟",
    },
    {
        pattern: /\.tsx?$/,
        test_type: "unit",
        fixture: "render() + fireEvent()",
        mock: "jest.mock() 模拟外部依赖",
    },
];
// ────────────────────────────────────────────
// 各测试类型的默认场景
// ────────────────────────────────────────────
/** 各测试类型的默认场景集 — 单元/集成/E2E 测试的 Given-When-Then 标准场景 */
const DEFAULT_SCENARIOS = {
    unit: [
        {
            name: "正常输入返回预期结果",
            given: "输入合法的参数",
            when: "调用被测函数/方法",
            then: "返回值符合预期",
            priority: "high",
        },
        {
            name: "空值/null 输入正确处理",
            given: "输入为 null / undefined / 空字符串",
            when: "调用被测函数/方法",
            then: "抛出异常或返回安全的默认值",
            priority: "high",
        },
        {
            name: "边界条件处理",
            given: "输入处于边界值 (如 0, MAX_VALUE, 空数组)",
            when: "调用被测函数/方法",
            then: "返回值符合边界约定",
            priority: "medium",
        },
    ],
    integration: [
        {
            name: "完整请求链路返回正确状态码",
            given: "发起一个完整的 HTTP 请求",
            when: "请求经过 Controller → Service → Mapper/Repository",
            then: "返回正确的状态码和响应体",
            priority: "high",
        },
        {
            name: "数据持久化后可正确查询",
            given: "通过接口写入一条数据",
            when: "查询相同条件",
            then: "能正确检索到写入的数据",
            priority: "high",
        },
        {
            name: "异常输入返回适当错误码",
            given: "发送格式错误或非法参数的请求",
            when: "请求经过各层处理",
            then: "返回 400/422 等适当的错误状态码和错误信息",
            priority: "medium",
        },
    ],
    e2e: [
        {
            name: "用户完整操作流程畅通",
            given: "用户处于初始状态",
            when: "执行完整的用户操作链路",
            then: "最终状态符合预期",
            priority: "high",
        },
        {
            name: "错误状态正确展示",
            given: "用户触发一个错误场景",
            when: "页面渲染错误响应",
            then: "错误信息正确展示且不影响其他功能",
            priority: "medium",
        },
    ],
};
/** 知识关键词 → 额外测试场景映射表: 当文件路径或知识模式包含关键词时自动追加场景 */
const KNOWLEDGE_SCENARIOS = [
    {
        keywords: ["并发", "concurrent", "thread", "lock", "mutex", "race"],
        scenario: {
            name: "并发场景下数据一致性",
            given: "多个请求同时操作同一资源",
            when: "并发执行",
            then: "数据保持一致性，无脏写",
            priority: "high",
        },
    },
    {
        keywords: ["权限", "permission", "auth", "rbac", "role"],
        scenario: {
            name: "权限校验正确拦截非法访问",
            given: "用户无对应操作权限",
            when: "尝试访问受限资源",
            then: "返回 403 或抛出权限异常",
            priority: "high",
        },
    },
    {
        keywords: ["缓存", "cache", "redis", "expire", "ttl"],
        scenario: {
            name: "缓存命中与未命中行为一致",
            given: "同一请求首次和后续调用",
            when: "缓存从空到有再到过期",
            then: "业务结果一致",
            priority: "medium",
        },
    },
    {
        keywords: ["分页", "pagination", "page", "limit", "offset"],
        scenario: {
            name: "分页参数边界验证",
            given: "传入 page=0, page=-1, limit=0 等非法分页参数",
            when: "执行分页查询",
            then: "返回合理结果或参数校验错误",
            priority: "medium",
        },
    },
    {
        keywords: ["文件上传", "upload", "multipart", "attachment"],
        scenario: {
            name: "文件大小和格式校验",
            given: "上传超大文件或非法格式文件",
            when: "执行上传操作",
            then: "返回明确的校验错误信息",
            priority: "high",
        },
    },
    {
        keywords: ["事务", "transaction", "rollback", "commit"],
        scenario: {
            name: "事务回滚后数据恢复原状",
            given: "操作执行过程中发生异常",
            when: "事务触发回滚",
            then: "所有变更被正确撤销",
            priority: "high",
        },
    },
];
/**
 * 为变更文件生成测试引导 — 根据文件类型匹配测试策略，叠加知识驱动场景。
 * @param changedFiles - 变更文件路径列表
 * @param knowledgePatterns - 从知识库提取的关键词模式列表
 * @param _config - 项目配置（预留，当前未使用）
 * @returns 测试引导数组，每项包含测试类型、测试文件建议路径、场景列表和 Mock 要点
 */
export function generateTestGuide(changedFiles, knowledgePatterns, _config) {
    debug("测试生成", "generateTestGuide() 开始, ${changedFiles.length} 个变更文件, ${knowledgePatterns.length} 个知识模式");
    const guides = [];
    for (const file of changedFiles) {
        // 匹配 FILE_TEST_MAP
        const mapping = matchFileTestMapping(file);
        if (!mapping) {
            debug("测试生成", "文件 ${file} 未匹配测试策略，跳过");
            continue;
        }
        // 确定测试文件建议路径
        const testFileSuggestion = suggestTestFilePath(file, mapping.test_type);
        // 该测试类型的基础场景
        const scenarios = structuredClone(DEFAULT_SCENARIOS[mapping.test_type]);
        // 如果关键词匹配则追加知识驱动场景
        const extraScenarios = matchKnowledgeScenarios(file, knowledgePatterns);
        if (extraScenarios.length > 0) {
            debug("测试生成", "文件 ${file} 追加 ${extraScenarios.length} 个知识驱动场景");
        }
        scenarios.push(...extraScenarios);
        guides.push({
            source_file: file,
            test_type: mapping.test_type,
            test_file_suggestion: testFileSuggestion,
            scenarios,
            fixtures_needed: [mapping.fixture],
            mock_points: [mapping.mock],
        });
    }
    debug("测试生成", "生成 ${guides.length} 个测试引导");
    return guides;
}
// ────────────────────────────────────────────
// 内部辅助函数
// ────────────────────────────────────────────
/** 根据文件路径匹配测试策略 — 遍历 FILE_TEST_MAP 返回首个命中的映射规则 */
function matchFileTestMapping(filePath) {
    for (const mapping of FILE_TEST_MAP) {
        if (mapping.pattern.test(filePath)) {
            return mapping;
        }
    }
    return null;
}
/**
 * 推荐测试文件路径 — Java 文件遵循 src/main → src/test 和 *Test.java 命名约定，TS/JS 文件追加 .test 后缀。
 * @param sourceFile - 源文件路径
 * @param testType - 测试类型（用于未来扩展不同约定）
 * @returns 推荐的测试文件路径
 */
function suggestTestFilePath(sourceFile, _testType) {
    const ext = path.extname(sourceFile);
    const base = sourceFile.slice(0, -ext.length);
    if (ext === ".java") {
        // Java 约定: ClassName → ClassNameTest，src/main/java → src/test/java
        const dir = path.dirname(sourceFile);
        const fileName = path.basename(sourceFile, ext);
        const testDir = dir
            .replace(/(^|\/)src\/main\/java(\/|$)/, "$1src/test/java$2")
            .replace(/(^|\/)src\/main\/kotlin(\/|$)/, "$1src/test/kotlin$2");
        return path.join(testDir, `${fileName}Test.java`);
    }
    // TypeScript / JavaScript: *.ts → *.test.ts，*.tsx → *.test.tsx（测试文件命名映射）
    const testExt = `.test${ext}`;
    return `${base}${testExt}`;
}
/**
 * 匹配知识驱动的额外测试场景 — 将文件路径和知识模式拼接后搜索关键词命中。
 * @param file - 源文件路径（用于关键词匹配）
 * @param knowledgePatterns - 知识库关键词列表
 * @returns 命中的额外测试场景数组（自动去重）
 */
function matchKnowledgeScenarios(file, knowledgePatterns) {
    const matched = [];
    const seenNames = new Set();
    // 将文件路径与所有知识关键词拼接为搜索文本，统一转小写进行匹配
    const searchText = (file + " " + knowledgePatterns.join(" ")).toLowerCase();
    for (const entry of KNOWLEDGE_SCENARIOS) {
        if (seenNames.has(entry.scenario.name)) {
            continue;
        }
        for (const keyword of entry.keywords) {
            if (searchText.includes(keyword.toLowerCase())) {
                matched.push(structuredClone(entry.scenario));
                seenNames.add(entry.scenario.name);
                break;
            }
        }
    }
    return matched;
}
//# sourceMappingURL=test_generator.js.map