package utils import ( "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestParseCardCSV(t *testing.T) { tests := []struct { name string csvContent string wantCards []CardInfo wantTotalCount int wantErrorCount int wantError error }{ { name: "标准双列无表头", csvContent: "89860012345678901234,13800000001\n89860012345678901235,13800000002", wantCards: []CardInfo{ {ICCID: "89860012345678901234", MSISDN: "13800000001"}, {ICCID: "89860012345678901235", MSISDN: "13800000002"}, }, wantTotalCount: 2, wantErrorCount: 0, }, { name: "标准双列有表头-英文", csvContent: "iccid,msisdn\n89860012345678901234,13800000001\n89860012345678901235,13800000002", wantCards: []CardInfo{ {ICCID: "89860012345678901234", MSISDN: "13800000001"}, {ICCID: "89860012345678901235", MSISDN: "13800000002"}, }, wantTotalCount: 2, wantErrorCount: 0, }, { name: "标准双列有表头-中文", csvContent: "卡号,接入号\n89860012345678901234,13800000001", wantCards: []CardInfo{ {ICCID: "89860012345678901234", MSISDN: "13800000001"}, }, wantTotalCount: 1, wantErrorCount: 0, }, { name: "标准双列有表头-手机号", csvContent: "ICCID,手机号\n89860012345678901234,13800000001", wantCards: []CardInfo{ {ICCID: "89860012345678901234", MSISDN: "13800000001"}, }, wantTotalCount: 1, wantErrorCount: 0, }, { name: "单列CSV格式拒绝-有表头", csvContent: "iccid\n89860012345678901234", wantCards: nil, wantTotalCount: 0, wantErrorCount: 0, wantError: ErrInvalidCSVFormat, }, { name: "单列CSV格式-无表头记录错误", csvContent: "89860012345678901234\n89860012345678901235", wantCards: []CardInfo{}, wantTotalCount: 2, wantErrorCount: 2, }, { name: "MSISDN为空记录失败", csvContent: "iccid,msisdn\n89860012345678901234,13800000001\n89860012345678901235,", wantCards: []CardInfo{{ICCID: "89860012345678901234", MSISDN: "13800000001"}}, wantTotalCount: 2, wantErrorCount: 1, }, { name: "ICCID为空记录失败", csvContent: "iccid,msisdn\n89860012345678901234,13800000001\n,13800000002", wantCards: []CardInfo{{ICCID: "89860012345678901234", MSISDN: "13800000001"}}, wantTotalCount: 2, wantErrorCount: 1, }, { name: "空文件", csvContent: "", wantCards: []CardInfo{}, wantTotalCount: 0, wantErrorCount: 0, }, { name: "只有表头", csvContent: "iccid,msisdn", wantCards: []CardInfo{}, wantTotalCount: 0, wantErrorCount: 0, }, { name: "包含空行", csvContent: "89860012345678901234,13800000001\n\n89860012345678901235,13800000002", wantCards: []CardInfo{ {ICCID: "89860012345678901234", MSISDN: "13800000001"}, {ICCID: "89860012345678901235", MSISDN: "13800000002"}, }, wantTotalCount: 2, wantErrorCount: 0, }, { name: "ICCID和MSISDN前后有空格", csvContent: " 89860012345678901234 , 13800000001 ", wantCards: []CardInfo{ {ICCID: "89860012345678901234", MSISDN: "13800000001"}, }, wantTotalCount: 1, wantErrorCount: 0, }, { name: "多于两列只取前两列", csvContent: "89860012345678901234,13800000001,额外数据\n89860012345678901235,13800000002,忽略", wantCards: []CardInfo{ {ICCID: "89860012345678901234", MSISDN: "13800000001"}, {ICCID: "89860012345678901235", MSISDN: "13800000002"}, }, wantTotalCount: 2, wantErrorCount: 0, }, { name: "Windows换行符CRLF", csvContent: "89860012345678901234,13800000001\r\n89860012345678901235,13800000002", wantCards: []CardInfo{ {ICCID: "89860012345678901234", MSISDN: "13800000001"}, {ICCID: "89860012345678901235", MSISDN: "13800000002"}, }, wantTotalCount: 2, wantErrorCount: 0, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { reader := strings.NewReader(tt.csvContent) result, err := ParseCardCSV(reader) if tt.wantError != nil { require.ErrorIs(t, err, tt.wantError) return } require.NoError(t, err) assert.Equal(t, tt.wantCards, result.Cards, "Cards 不匹配") assert.Equal(t, tt.wantTotalCount, result.TotalCount, "TotalCount 不匹配") assert.Equal(t, tt.wantErrorCount, len(result.ParseErrors), "ParseErrors 数量不匹配") }) } } func TestParseCardCSV_ErrorDetails(t *testing.T) { t.Run("MSISDN为空时记录详细错误", func(t *testing.T) { csvContent := "iccid,msisdn\n89860012345678901234," reader := strings.NewReader(csvContent) result, err := ParseCardCSV(reader) require.NoError(t, err) require.Len(t, result.ParseErrors, 1) assert.Equal(t, 2, result.ParseErrors[0].Line) assert.Equal(t, "89860012345678901234", result.ParseErrors[0].ICCID) assert.Equal(t, "MSISDN 不能为空", result.ParseErrors[0].Reason) }) t.Run("ICCID为空时记录详细错误", func(t *testing.T) { csvContent := "iccid,msisdn\n,13800000001" reader := strings.NewReader(csvContent) result, err := ParseCardCSV(reader) require.NoError(t, err) require.Len(t, result.ParseErrors, 1) assert.Equal(t, 2, result.ParseErrors[0].Line) assert.Equal(t, "13800000001", result.ParseErrors[0].MSISDN) assert.Equal(t, "ICCID 不能为空", result.ParseErrors[0].Reason) }) t.Run("列数不足时记录详细错误", func(t *testing.T) { csvContent := "89860012345678901234" reader := strings.NewReader(csvContent) result, err := ParseCardCSV(reader) require.NoError(t, err) require.Len(t, result.ParseErrors, 1) assert.Equal(t, 1, result.ParseErrors[0].Line) assert.Equal(t, "89860012345678901234", result.ParseErrors[0].ICCID) assert.Contains(t, result.ParseErrors[0].Reason, "列数不足") }) } func TestIsHeader(t *testing.T) { tests := []struct { col1 string col2 string expected bool }{ {"iccid", "msisdn", true}, {"ICCID", "MSISDN", true}, {"卡号", "接入号", true}, {"号码", "手机号", true}, {"iccid", "电话", true}, {"89860012345678901234", "13800000001", false}, {"iccid", "", false}, {"", "msisdn", false}, } for _, tt := range tests { t.Run(tt.col1+"_"+tt.col2, func(t *testing.T) { result := isHeader(tt.col1, tt.col2) assert.Equal(t, tt.expected, result) }) } }