recordservice_test.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package main
  2. import (
  3. "database/sql"
  4. "path/filepath"
  5. "testing"
  6. )
  7. // TestRecordServiceSQLite 验证记录 SQLite 存储:建表、插入、查询、去重、全量替换。
  8. // 直接测试 openRecordDB 返回的 *sql.DB(绕过 SettingsService 的 loadSettings 依赖)。
  9. func TestRecordServiceSQLite(t *testing.T) {
  10. dir := t.TempDir()
  11. dbPath := filepath.Join(dir, "records.db")
  12. // 未配置路径时应报错(openRecordDB 空路径)
  13. if _, err := openRecordDB(""); err == nil {
  14. t.Fatal("空数据库路径应返回错误")
  15. }
  16. // 打开(自动建表 + WAL)
  17. db, err := openRecordDB(dbPath)
  18. if err != nil {
  19. t.Fatalf("openRecordDB 失败: %v", err)
  20. }
  21. defer db.Close()
  22. // 插入 3 条
  23. for _, r := range []struct{ n, i string; v float64 }{
  24. {"张三", "一月", 12.5},
  25. {"张三", "二月", 30},
  26. {"李四", "一月", 8.2},
  27. } {
  28. if _, err := db.Exec(`INSERT INTO records (name, item, value) VALUES (?,?,?)`, r.n, r.i, r.v); err != nil {
  29. t.Fatalf("插入失败: %v", err)
  30. }
  31. }
  32. // 按 id 升序读取
  33. rows, err := db.Query(`SELECT name, item, value FROM records ORDER BY id ASC`)
  34. if err != nil {
  35. t.Fatalf("查询失败: %v", err)
  36. }
  37. var got []RecordItem
  38. for rows.Next() {
  39. var it RecordItem
  40. if err := rows.Scan(&it.Name, &it.Item, &it.Value); err != nil {
  41. t.Fatalf("扫描失败: %v", err)
  42. }
  43. got = append(got, it)
  44. }
  45. rows.Close()
  46. if len(got) != 3 {
  47. t.Fatalf("期望 3 条记录,实际 %d", len(got))
  48. }
  49. if got[0].Name != "张三" || got[0].Item != "一月" || got[0].Value != 12.5 {
  50. t.Fatalf("首条记录不符: %+v", got[0])
  51. }
  52. // 去重姓名(UTF-8 字节序:张三 < 李四)
  53. names := distinctTest(t, db, "name")
  54. if len(names) != 2 || names[0] != "张三" || names[1] != "李四" {
  55. t.Fatalf("去重姓名不符: %v", names)
  56. }
  57. // 去重名称(排序:一月、二月)
  58. items := distinctTest(t, db, "item")
  59. if len(items) != 2 || items[0] != "一月" || items[1] != "二月" {
  60. t.Fatalf("去重名称不符: %v", items)
  61. }
  62. // 非法列名应报错
  63. if _, err := recordDistinctValues("evil"); err == nil {
  64. t.Fatal("非法列名应返回错误")
  65. }
  66. // 全量替换:清空后写 1 条
  67. tx, err := db.Begin()
  68. if err != nil {
  69. t.Fatalf("开启事务失败: %v", err)
  70. }
  71. if _, err := tx.Exec(`DELETE FROM records`); err != nil {
  72. t.Fatalf("清空失败: %v", err)
  73. }
  74. stmt, err := tx.Prepare(`INSERT INTO records (name, item, value) VALUES (?,?,?)`)
  75. if err != nil {
  76. t.Fatalf("准备语句失败: %v", err)
  77. }
  78. if _, err := stmt.Exec("王五", "三月", 99); err != nil {
  79. t.Fatalf("替换插入失败: %v", err)
  80. }
  81. stmt.Close()
  82. if err := tx.Commit(); err != nil {
  83. t.Fatalf("提交失败: %v", err)
  84. }
  85. var cnt int
  86. if err := db.QueryRow(`SELECT COUNT(*) FROM records`).Scan(&cnt); err != nil || cnt != 1 {
  87. t.Fatalf("全量替换后应有 1 条,实际 %d (err=%v)", cnt, err)
  88. }
  89. }
  90. // distinctTest 对已打开 db 执行与 recordDistinctValues 相同的去重 SQL。
  91. func distinctTest(t *testing.T, db *sql.DB, col string) []string {
  92. t.Helper()
  93. rows, err := db.Query(`SELECT DISTINCT ` + col + ` FROM records WHERE TRIM(` + col + `) <> '' ORDER BY ` + col + ` COLLATE NOCASE ASC`)
  94. if err != nil {
  95. t.Fatalf("去重查询失败: %v", err)
  96. }
  97. defer rows.Close()
  98. var vals []string
  99. for rows.Next() {
  100. var v string
  101. if err := rows.Scan(&v); err != nil {
  102. t.Fatalf("扫描去重值失败: %v", err)
  103. }
  104. vals = append(vals, v)
  105. }
  106. return vals
  107. }