Files
hotime/db/row_test.go
T
hoteas fab7931d3c refactor(app): 优化 URL 处理和 JSON 解析逻辑
- 更新应用程序处理程序中的 URL 赋值逻辑,确保静态文件使用原始路径
- 修改缓存数据库的 JSON 解析方法,使用 JsonToObj 函数替代 json.Unmarshal,提升代码可读性和性能
- 在 Map 和 Slice 类型中新增获取四舍五入浮点数的方法,增强数据处理能力
- 在 Obj 类型中添加四舍五入功能,支持精度控制
- 改进数据库查询结果的处理逻辑,确保数据类型的准确性和一致性
- 优化日志格式设置,增强日志信息的可读性
2026-03-10 23:44:41 +08:00

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)
}
})
}