parse-docs.test.js 7.81 KB
import { describe, it, expect } from 'vitest'
import fs from 'fs'
import os from 'os'
import path from 'path'
import { generateFormSn, generateConfigCode, updateConfigContent, extractDocumentText, validateParsedConfig, detectFormSnConflicts, buildDryRunDiff, buildConfigUpdateResult, buildParseSummary } from './parse-docs'

describe('parse-docs 生成逻辑', () => {
    it('generateFormSn 使用稳定规则生成', () => {
        const form_sn = generateFormSn({
            product_name: 'WIOP3E 盈传创富保障计划 3 - 优选版',
            product_type: 'life-insurance'
        })
        const form_sn_repeat = generateFormSn({
            product_name: 'WIOP3E 盈传创富保障计划 3 - 优选版',
            product_type: 'life-insurance'
        })

        expect(form_sn).toBe(form_sn_repeat)
        expect(form_sn.startsWith('life-insurance-')).toBe(true)
        expect(form_sn).toMatch(/^life-insurance-[a-z0-9-]+-[a-f0-9]{8}$/)
    })

    it('generateConfigCode 储蓄配置包含顶层 category', () => {
        const result = generateConfigCode({
            product_name: '宏挚传承保障计划',
            product_type: 'savings',
            currency: 'USD',
            payment_periods: ['整付'],
            age_range: { min: 0, max: 75 },
            insurance_period: '终身',
            is_savings: true,
            withdrawal_modes: ['年龄指定金额'],
            withdrawal_periods: ['1年']
        })

        expect(result.code.includes("component: 'SavingsTemplate'")).toBe(true)
        expect(result.code.includes("category: 'savings'")).toBe(true)
        expect(result.code.includes("config: {\n      category")).toBe(false)
    })

    it('generateConfigCode 按类型输出默认 schema 与 mapping', () => {
        const savings_result = generateConfigCode({
            product_name: '宏挚传承保障计划',
            product_type: 'savings',
            currency: 'USD',
            payment_periods: ['整付'],
            age_range: { min: 0, max: 75 },
            insurance_period: '终身',
            is_savings: true,
            withdrawal_modes: ['年龄指定金额'],
            withdrawal_periods: ['1年'],
            form_schema: { base_fields: [], withdrawal_fields: [], reset_map: {} },
            submit_mapping: {}
        })
        const life_result = generateConfigCode({
            product_name: 'WIOP3E 盈传创富保障计划 3 - 优选版',
            product_type: 'life-insurance',
            currency: 'USD',
            payment_periods: ['整付'],
            age_range: { min: 0, max: 75 },
            insurance_period: '终身',
            form_schema: { base_fields: [] },
            submit_mapping: {}
        })

        expect(savings_result.code.includes('form_schema: savingsFormSchema')).toBe(true)
        expect(savings_result.code.includes('submit_mapping: savingsSubmitMapping')).toBe(true)
        expect(life_result.code.includes('form_schema: protectionFormSchema')).toBe(true)
        expect(life_result.code.includes('submit_mapping: baseSubmitMapping')).toBe(true)
    })

    it('updateConfigContent 插入到 PLAN_TEMPLATES 末尾', () => {
        const base_content = `export const PLAN_TEMPLATES = {
  'a': {
    name: 'A',
    component: 'LifeInsuranceTemplate',
    config: {
      currency: 'USD',
      payment_periods: [],
      age_range: { min: 0, max: 1 },
      insurance_period: '终身'
    }
  }
}

export const FEATURE_FLAGS = {}`

        const result = updateConfigContent(base_content, [
            {
                code: "  'b': {\n    name: 'B',\n    component: 'SavingsTemplate',\n    category: 'savings',\n    config: {\n      currency: 'USD',\n      payment_periods: [],\n      age_range: { min: 0, max: 1 },\n      insurance_period: '终身',\n      withdrawal_plan: {\n        enabled: true,\n        currencies: ['HKD', 'USD', 'CNY'],\n        default_currency: 'USD',\n        withdrawal_modes: [],\n        withdrawal_periods: []\n      }\n    }\n  }"
            }
        ])

        expect(result).toMatch(/'a'[\s\S]*},\n\s+'b'/)
        expect(result).toMatch(/'b'[\s\S]*}\n\nexport const FEATURE_FLAGS/)
    })

    it('updateConfigContent 无模板时返回 null', () => {
        const base_content = `export const OTHER = {}`
        const result = updateConfigContent(base_content, [
            { code: "  'b': {\n    name: 'B'\n  }" }
        ])

        expect(result).toBe(null)
    })

    it('extractDocumentText 统一抽取结构', async () => {
        const temp_dir = fs.mkdtempSync(path.join(os.tmpdir(), 'doc-parse-'))
        const temp_file = path.join(temp_dir, 'sample.txt')
        fs.writeFileSync(temp_file, 'hello parse')
        const result = await extractDocumentText(temp_file)

        expect(result.text).toBe('hello parse')
        expect(result.meta.ext).toBe('.txt')
        expect(Array.isArray(result.warnings)).toBe(true)
    })

    it('validateParsedConfig 能识别缺失字段', () => {
        const invalid = validateParsedConfig({
            product_type: 'savings',
            currency: 'USD'
        })
        const valid = validateParsedConfig({
            product_name: '宏挚传承保障计划',
            product_type: 'savings',
            currency: 'USD',
            form_schema: { base_fields: [], withdrawal_fields: [], reset_map: {} },
            submit_mapping: {}
        })

        expect(invalid.valid).toBe(false)
        expect(invalid.errors.length).toBeGreaterThan(0)
        expect(valid.valid).toBe(true)
    })

    it('detectFormSnConflicts 能识别重复 form_sn', () => {
        const base_content = `export const PLAN_TEMPLATES = {
  'a': {
    name: 'A',
    component: 'LifeInsuranceTemplate',
    config: {
      currency: 'USD',
      payment_periods: [],
      age_range: { min: 0, max: 1 },
      insurance_period: '终身'
    }
  }
}

export const FEATURE_FLAGS = {}`
        const conflicts = detectFormSnConflicts(base_content, [
            { formSn: 'a', code: '  ' },
            { formSn: 'b', code: '  ' }
        ])

        expect(conflicts).toEqual(['a'])
    })

    it('buildDryRunDiff 输出新增内容', () => {
        const diff = buildDryRunDiff([
            { formSn: 'b', code: "  'b': {\n    name: 'B'\n  }" }
        ])

        expect(diff.includes('--- plan-templates.js')).toBe(true)
        expect(diff.includes("+++ plan-templates.js")).toBe(true)
        expect(diff.includes("+   'b': {")).toBe(true)
    })

    it('buildConfigUpdateResult 覆盖冲突与 dry-run', () => {
        const base_content = `export const PLAN_TEMPLATES = {
  'a': {
    name: 'A',
    component: 'LifeInsuranceTemplate',
    config: {
      currency: 'USD',
      payment_periods: [],
      age_range: { min: 0, max: 1 },
      insurance_period: '终身'
    }
  }
}

export const FEATURE_FLAGS = {}`
        const conflict_result = buildConfigUpdateResult(base_content, [
            { formSn: 'a', code: "  'a': {\n    name: 'A'\n  }" }
        ])
        const dry_run_result = buildConfigUpdateResult(base_content, [
            { formSn: 'b', code: "  'b': {\n    name: 'B'\n  }" }
        ], { dry_run: true })

        expect(conflict_result.ok).toBe(false)
        expect(conflict_result.conflicts).toEqual(['a'])
        expect(dry_run_result.ok).toBe(true)
        expect(dry_run_result.diff).toContain('+   \'b\': {')
    })

    it('buildParseSummary 汇总成功失败与耗时', () => {
        const summary = buildParseSummary([
            { success: true, formSn: 'a', file: 'a.pdf', config: { product_name: 'A' } },
            { success: false, file: 'b.pdf', reason: 'parse_failed' }
        ], 1200)

        expect(summary.total).toBe(2)
        expect(summary.success).toBe(1)
        expect(summary.failed).toBe(1)
        expect(summary.duration_ms).toBe(1200)
        expect(summary.success_list[0].form_sn).toBe('a')
        expect(summary.failed_list[0].file).toBe('b.pdf')
    })
})