Files
pangolin/server/internal/db/dialect_test.go
T
wangjia 439c0ea5da test(server/db): 多库(4/4)— SQLite 实库测试 + dialect SQL 断言 + 双引擎脚本
- internal/store SQLite 实库行为测试(免 docker、真连引擎):用量累加 upsert、
  节点累加、directory_version 自增、幂等 no-op、凭据 upsert、兑换码全流程
  (锁 + 标记已用 + 建订阅 + Go 端续期)
- internal/db dialect SQL 逐字断言(MySQL/SQLite 两侧生成串),无需起 MySQL
  即可锁定 MySQL 侧 SQL 与重构前一致
- run_sqlite_test.sh:双引擎测试矩阵的 SQLite 半边(零 docker)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 00:01:43 +08:00

72 lines
2.5 KiB
Go

package db
import "testing"
// These pin the exact SQL each dialect generates. The MySQL side is the part we
// can't exercise against a live server without docker, so asserting the strings
// here proves MySQL output is byte-for-byte what the store layer used before the
// refactor (the SQLite half is additionally exercised against a real engine).
func TestMySQLDialect_SQL(t *testing.T) {
d := MySQLDialect{}
if got := d.LockForUpdate(); got != "FOR UPDATE" {
t.Errorf("LockForUpdate = %q, want %q", got, "FOR UPDATE")
}
// No-op upsert (insert-or-ignore) — matches the old provision_idempotency SQL.
if got := d.Upsert([]string{"idempotency_key"}); got != "ON DUPLICATE KEY UPDATE idempotency_key = idempotency_key" {
t.Errorf("noop upsert = %q", got)
}
// Accumulate upsert — EXCLUDED.col must rewrite to VALUES(col).
got := d.Upsert([]string{"user_id", "date"},
"bytes_up = bytes_up + EXCLUDED.bytes_up",
"minutes_used = minutes_used + EXCLUDED.minutes_used")
want := "ON DUPLICATE KEY UPDATE bytes_up = bytes_up + VALUES(bytes_up), minutes_used = minutes_used + VALUES(minutes_used)"
if got != want {
t.Errorf("accumulate upsert:\n got=%q\nwant=%q", got, want)
}
// version bump
if got := d.Upsert([]string{"id"}, "version = version + 1"); got != "ON DUPLICATE KEY UPDATE version = version + 1" {
t.Errorf("bump upsert = %q", got)
}
}
func TestSQLiteDialect_SQL(t *testing.T) {
d := SQLiteDialect{}
if got := d.LockForUpdate(); got != "" {
t.Errorf("LockForUpdate = %q, want empty (BEGIN IMMEDIATE handles locking)", got)
}
if got := d.Upsert([]string{"idempotency_key"}); got != "ON CONFLICT(idempotency_key) DO NOTHING" {
t.Errorf("noop upsert = %q", got)
}
got := d.Upsert([]string{"user_id", "date"},
"bytes_up = bytes_up + EXCLUDED.bytes_up",
"minutes_used = minutes_used + EXCLUDED.minutes_used")
want := "ON CONFLICT(user_id, date) DO UPDATE SET bytes_up = bytes_up + EXCLUDED.bytes_up, minutes_used = minutes_used + EXCLUDED.minutes_used"
if got != want {
t.Errorf("accumulate upsert:\n got=%q\nwant=%q", got, want)
}
if got := d.Upsert([]string{"id"}, "version = version + 1"); got != "ON CONFLICT(id) DO UPDATE SET version = version + 1" {
t.Errorf("bump upsert = %q", got)
}
}
func TestDialectFor(t *testing.T) {
if DialectFor("mysql").Name() != "mysql" {
t.Error("DialectFor(mysql)")
}
if DialectFor("sqlite").Name() != "sqlite" {
t.Error("DialectFor(sqlite)")
}
if DialectFor("").Name() != "mysql" {
t.Error("DialectFor(empty) should default to mysql")
}
}