80 lines
2.3 KiB
Go
80 lines
2.3 KiB
Go
package database
|
||
|
||
import (
|
||
"context"
|
||
"database/sql/driver"
|
||
"errors"
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
type sqliteDeadlineFaultConn struct {
|
||
sqliteDriverConn
|
||
commands []string
|
||
failAt int
|
||
err error
|
||
}
|
||
|
||
func (c *sqliteDeadlineFaultConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
|
||
c.commands = append(c.commands, query)
|
||
if len(c.commands) == c.failAt {
|
||
return nil, c.err
|
||
}
|
||
return driver.RowsAffected(0), nil
|
||
}
|
||
|
||
func (c *sqliteDeadlineFaultConn) IsValid() bool { return true }
|
||
|
||
func TestSQLiteDeadlineFailureNeverReplaysOperation(t *testing.T) {
|
||
for _, name := range []string{"setup_failure", "restore_failure", "operation_failure", "already_canceled"} {
|
||
t.Run(name, func(t *testing.T) {
|
||
failure := errors.New("注入的隔离测试错误")
|
||
raw := &sqliteDeadlineFaultConn{err: failure}
|
||
conn := &sqliteDeadlineConn{sqliteDriverConn: raw}
|
||
ctx, cancel := context.WithTimeout(t.Context(), time.Second)
|
||
defer cancel()
|
||
wantRuns := 1
|
||
switch name {
|
||
case "setup_failure":
|
||
raw.failAt = 1
|
||
wantRuns = 0
|
||
case "restore_failure":
|
||
raw.failAt = 2
|
||
case "already_canceled":
|
||
cancel()
|
||
wantRuns = 0
|
||
}
|
||
runs := 0
|
||
_, err := sqliteWithDeadline(conn, ctx, func() (int, error) {
|
||
runs++
|
||
if name == "operation_failure" {
|
||
return 0, failure
|
||
}
|
||
return 7, nil
|
||
})
|
||
if err == nil || runs != wantRuns || errors.Is(err, driver.ErrBadConn) {
|
||
t.Fatalf("错误处理发生重放或丢失:执行次数=%d,错误=%v", runs, err)
|
||
}
|
||
if name == "setup_failure" || name == "restore_failure" {
|
||
if conn.IsValid() || !errors.Is(conn.ResetSession(t.Context()), driver.ErrBadConn) {
|
||
t.Fatal("配置状态不确定的连接仍可被复用")
|
||
}
|
||
}
|
||
if name == "operation_failure" {
|
||
if !errors.Is(err, failure) || len(raw.commands) != 2 || raw.commands[1] != "PRAGMA busy_timeout = 5000" || !conn.IsValid() {
|
||
t.Fatalf("业务错误后没有保留错误或恢复连接配置:%v", raw.commands)
|
||
}
|
||
}
|
||
if name == "already_canceled" && (len(raw.commands) != 0 || !errors.Is(err, context.Canceled)) {
|
||
t.Fatal("已经取消的调用仍然修改了连接")
|
||
}
|
||
for _, command := range raw.commands {
|
||
if !strings.HasPrefix(command, "PRAGMA busy_timeout = ") {
|
||
t.Fatalf("执行了额外 SQL:%s", command)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|