fab7931d3c
- 更新应用程序处理程序中的 URL 赋值逻辑,确保静态文件使用原始路径 - 修改缓存数据库的 JSON 解析方法,使用 JsonToObj 函数替代 json.Unmarshal,提升代码可读性和性能 - 在 Map 和 Slice 类型中新增获取四舍五入浮点数的方法,增强数据处理能力 - 在 Obj 类型中添加四舍五入功能,支持精度控制 - 改进数据库查询结果的处理逻辑,确保数据类型的准确性和一致性 - 优化日志格式设置,增强日志信息的可读性
246 lines
7.1 KiB
Go
246 lines
7.1 KiB
Go
package db
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
)
|
|
|
|
// ─────────────────────────────────────────────
|
|
// classifyDBType
|
|
// ─────────────────────────────────────────────
|
|
func TestClassifyDBType(t *testing.T) {
|
|
cases := []struct {
|
|
typeName string
|
|
want int
|
|
}{
|
|
// MySQL 整数类型
|
|
{"INT", dbTypeInteger},
|
|
{"TINYINT", dbTypeInteger},
|
|
{"SMALLINT", dbTypeInteger},
|
|
{"MEDIUMINT", dbTypeInteger},
|
|
{"BIGINT", dbTypeInteger},
|
|
{"INT UNSIGNED", dbTypeInteger},
|
|
{"BIGINT UNSIGNED", dbTypeInteger},
|
|
{"TINYINT(1)", dbTypeInteger},
|
|
{"YEAR", dbTypeInteger},
|
|
{"BOOL", dbTypeInteger},
|
|
{"BOOLEAN", dbTypeInteger},
|
|
|
|
// PostgreSQL 整数类型
|
|
{"INT2", dbTypeInteger},
|
|
{"INT4", dbTypeInteger},
|
|
{"INT8", dbTypeInteger},
|
|
{"SERIAL", dbTypeInteger},
|
|
{"BIGSERIAL", dbTypeInteger},
|
|
{"SMALLSERIAL", dbTypeInteger},
|
|
{"OID", dbTypeInteger},
|
|
|
|
// DECIMAL / NUMERIC
|
|
{"DECIMAL", dbTypeDecimal},
|
|
{"DECIMAL(10,2)", dbTypeDecimal},
|
|
{"NUMERIC", dbTypeDecimal},
|
|
{"NUMERIC(8,4)", dbTypeDecimal},
|
|
{"NEWDECIMAL", dbTypeDecimal},
|
|
|
|
// FLOAT / DOUBLE
|
|
{"FLOAT", dbTypeFloat},
|
|
{"DOUBLE", dbTypeFloat},
|
|
{"REAL", dbTypeFloat},
|
|
{"FLOAT4", dbTypeFloat},
|
|
{"FLOAT8", dbTypeFloat},
|
|
{"DOUBLE PRECISION", dbTypeFloat},
|
|
|
|
// 未知类型
|
|
{"VARCHAR", dbTypeUnknown},
|
|
{"TEXT", dbTypeUnknown},
|
|
{"DATETIME", dbTypeUnknown},
|
|
{"TIMESTAMP", dbTypeUnknown},
|
|
{"BLOB", dbTypeUnknown},
|
|
{"", dbTypeUnknown},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.typeName, func(t *testing.T) {
|
|
got := classifyDBType(c.typeName)
|
|
if got != c.want {
|
|
t.Errorf("classifyDBType(%q) = %d, want %d", c.typeName, got, c.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// fixFloatValue
|
|
// ─────────────────────────────────────────────
|
|
func TestFixFloatValue(t *testing.T) {
|
|
t.Run("integer category: round to int64", func(t *testing.T) {
|
|
// 这是核心场景:float64 精度噪声导致整数变为 1.9999...
|
|
got := fixFloatValue(1.9999999999999998, dbTypeInteger, 0)
|
|
if got != int64(2) {
|
|
t.Errorf("got %v (%T), want int64(2)", got, got)
|
|
}
|
|
})
|
|
|
|
t.Run("integer category: 2.0000000000001 rounds to 2", func(t *testing.T) {
|
|
got := fixFloatValue(2.0000000000001, dbTypeInteger, 0)
|
|
if got != int64(2) {
|
|
t.Errorf("got %v (%T), want int64(2)", got, got)
|
|
}
|
|
})
|
|
|
|
t.Run("decimal category: scale=2 roundtrip precision", func(t *testing.T) {
|
|
got := fixFloatValue(1.1999999999999999, dbTypeDecimal, 2)
|
|
f, ok := got.(float64)
|
|
if !ok {
|
|
t.Fatalf("expected float64, got %T", got)
|
|
}
|
|
if f != 1.2 {
|
|
t.Errorf("got %v, want 1.2", f)
|
|
}
|
|
})
|
|
|
|
t.Run("decimal category: scale=2, 1.20000000001 -> 1.2", func(t *testing.T) {
|
|
got := fixFloatValue(1.20000000001, dbTypeDecimal, 2)
|
|
f := got.(float64)
|
|
if f != 1.2 {
|
|
t.Errorf("got %v, want 1.2", f)
|
|
}
|
|
})
|
|
|
|
t.Run("decimal category: scale=-1 (unknown), no modification", func(t *testing.T) {
|
|
got := fixFloatValue(1.23456789, dbTypeDecimal, -1)
|
|
if got != 1.23456789 {
|
|
t.Errorf("got %v, want 1.23456789", got)
|
|
}
|
|
})
|
|
|
|
t.Run("decimal category: scale=0, round to whole number", func(t *testing.T) {
|
|
got := fixFloatValue(2.7, dbTypeDecimal, 0)
|
|
if got != float64(3) {
|
|
t.Errorf("got %v (%T), want float64(3)", got, got)
|
|
}
|
|
})
|
|
|
|
t.Run("float category: scale=-1 no modification", func(t *testing.T) {
|
|
got := fixFloatValue(3.14159265, dbTypeFloat, -1)
|
|
if got != 3.14159265 {
|
|
t.Errorf("got %v, want 3.14159265", got)
|
|
}
|
|
})
|
|
|
|
t.Run("unknown category: passthrough", func(t *testing.T) {
|
|
got := fixFloatValue(99.9, dbTypeUnknown, -1)
|
|
if got != 99.9 {
|
|
t.Errorf("got %v, want 99.9", got)
|
|
}
|
|
})
|
|
|
|
t.Run("NaN -> 0", func(t *testing.T) {
|
|
got := fixFloatValue(math.NaN(), dbTypeInteger, 0)
|
|
if got != float64(0) {
|
|
t.Errorf("got %v, want 0", got)
|
|
}
|
|
})
|
|
|
|
t.Run("+Inf -> 0", func(t *testing.T) {
|
|
got := fixFloatValue(math.Inf(1), dbTypeDecimal, 2)
|
|
if got != float64(0) {
|
|
t.Errorf("got %v, want 0", got)
|
|
}
|
|
})
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// roundFloat
|
|
// ─────────────────────────────────────────────
|
|
func TestRoundFloat(t *testing.T) {
|
|
t.Run("scale=2: 1.19999... -> 1.2", func(t *testing.T) {
|
|
got := roundFloat(1.1999999999999999, 2)
|
|
if got != float64(1.2) {
|
|
t.Errorf("got %v, want 1.2", got)
|
|
}
|
|
})
|
|
|
|
t.Run("scale=2: 1.20000...1 -> 1.2", func(t *testing.T) {
|
|
got := roundFloat(1.20000000001, 2)
|
|
if got != float64(1.2) {
|
|
t.Errorf("got %v, want 1.2", got)
|
|
}
|
|
})
|
|
|
|
t.Run("scale=0: round to integer float", func(t *testing.T) {
|
|
got := roundFloat(2.5, 0)
|
|
if got != float64(3) {
|
|
t.Errorf("got %v, want 3.0", got)
|
|
}
|
|
})
|
|
|
|
t.Run("scale=-1: passthrough unchanged", func(t *testing.T) {
|
|
got := roundFloat(1.23456789, -1)
|
|
if got != 1.23456789 {
|
|
t.Errorf("got %v, want 1.23456789", got)
|
|
}
|
|
})
|
|
|
|
t.Run("NaN -> 0", func(t *testing.T) {
|
|
got := roundFloat(math.NaN(), 2)
|
|
if got != float64(0) {
|
|
t.Errorf("got %v, want 0", got)
|
|
}
|
|
})
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// convertBytes
|
|
// ─────────────────────────────────────────────
|
|
func TestConvertBytes(t *testing.T) {
|
|
t.Run("integer category: '2' -> int64(2)", func(t *testing.T) {
|
|
got := convertBytes([]byte("2"), dbTypeInteger, 0)
|
|
if got != int64(2) {
|
|
t.Errorf("got %v (%T), want int64(2)", got, got)
|
|
}
|
|
})
|
|
|
|
t.Run("integer category: '2.00' -> int64(2)", func(t *testing.T) {
|
|
got := convertBytes([]byte("2.00"), dbTypeInteger, 0)
|
|
if got != int64(2) {
|
|
t.Errorf("got %v (%T), want int64(2)", got, got)
|
|
}
|
|
})
|
|
|
|
t.Run("decimal category: '1.20' scale=2 -> float64(1.2)", func(t *testing.T) {
|
|
got := convertBytes([]byte("1.20"), dbTypeDecimal, 2)
|
|
if got != float64(1.2) {
|
|
t.Errorf("got %v (%T), want float64(1.2)", got, got)
|
|
}
|
|
})
|
|
|
|
t.Run("decimal category: '0.00' scale=2 -> float64(0)", func(t *testing.T) {
|
|
got := convertBytes([]byte("0.00"), dbTypeDecimal, 2)
|
|
if got != float64(0) {
|
|
t.Errorf("got %v (%T), want float64(0)", got, got)
|
|
}
|
|
})
|
|
|
|
t.Run("float category: '3.14' scale=-1 -> float64(3.14)", func(t *testing.T) {
|
|
got := convertBytes([]byte("3.14"), dbTypeFloat, -1)
|
|
if got != float64(3.14) {
|
|
t.Errorf("got %v (%T), want float64(3.14)", got, got)
|
|
}
|
|
})
|
|
|
|
t.Run("unknown category: passthrough as string", func(t *testing.T) {
|
|
got := convertBytes([]byte("hello"), dbTypeUnknown, 0)
|
|
if got != "hello" {
|
|
t.Errorf("got %v (%T), want 'hello'", got, got)
|
|
}
|
|
})
|
|
|
|
t.Run("integer category: invalid bytes -> string fallback", func(t *testing.T) {
|
|
got := convertBytes([]byte("abc"), dbTypeInteger, 0)
|
|
if got != "abc" {
|
|
t.Errorf("got %v (%T), want 'abc'", got, got)
|
|
}
|
|
})
|
|
}
|