All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m33s
主要改动: - 新增交互式环境配置脚本 (scripts/setup-env.sh) - 新增本地启动快捷脚本 (scripts/run-local.sh) - 新增环境变量模板文件 (.env.example) - 部署模式改版:使用嵌入式配置 + 环境变量覆盖 - 添加对象存储功能支持 - 改进 IoT 卡片导入任务 - 优化 OpenAPI 文档生成 - 删除旧的配置文件,改用嵌入式默认配置
221 lines
6.4 KiB
Go
221 lines
6.4 KiB
Go
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)
|
|
})
|
|
}
|
|
}
|