439c0ea5da
- 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>
72 lines
2.5 KiB
Go
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")
|
|
}
|
|
}
|