Files
MyGoNavi/internal/db/sqlserver_impl_test.go
Syngnat 88b427c41a 🔥 remove(go-test): 删除后端读取源码文本断言的测试函数
- 移除 99 个仅读取同包 .go 源码做字符串包含断言的测试函数,共 3746 行
- 判据为函数内出现 os.ReadFile 读取 .go 文件且剥离字符串字面量后不调用任何生产代码
- 保留 2 个同时调用生产接口的测试函数,清理 45 个文件中失去引用的 import
- go build、go vet 通过,测试失败集合由 40 项减至 39 项且无新增,减少项为已长期失败的墓碑用例
2026-07-26 21:37:22 +08:00

345 lines
9.4 KiB
Go

//go:build gonavi_full_drivers || gonavi_sqlserver_driver
package db
import (
"context"
"database/sql"
"database/sql/driver"
"errors"
"io"
"reflect"
"strings"
"sync"
"testing"
"GoNavi-Wails/shared/i18n"
"github.com/golang-sql/sqlexp"
_ "modernc.org/sqlite"
)
var rawSQLServerTableNameRequiredText = string([]rune{0x8868, 0x540d, 0x4e0d, 0x80fd, 0x4e3a, 0x7a7a})
const sqlServerPrintOnlyDriverName = "gonavi-sqlserver-print-only"
var registerSQLServerPrintOnlyDriver sync.Once
type sqlServerPrintOnlyDriver struct{}
type sqlServerPrintOnlyConn struct {
retmsg *sqlexp.ReturnMessage
}
type sqlServerPrintOnlyRows struct {
remainingBoundaries int
drained bool
}
type sqlServerPrintOnlyNotice string
func (sqlServerPrintOnlyDriver) Open(string) (driver.Conn, error) {
return &sqlServerPrintOnlyConn{}, nil
}
func (c *sqlServerPrintOnlyConn) Prepare(string) (driver.Stmt, error) {
return nil, errors.New("not implemented")
}
func (c *sqlServerPrintOnlyConn) Close() error {
return nil
}
func (c *sqlServerPrintOnlyConn) Begin() (driver.Tx, error) {
return nil, errors.New("not implemented")
}
func (c *sqlServerPrintOnlyConn) CheckNamedValue(value *driver.NamedValue) error {
retmsg, ok := value.Value.(*sqlexp.ReturnMessage)
if !ok {
return nil
}
sqlexp.ReturnMessageInit(retmsg)
c.retmsg = retmsg
return driver.ErrRemoveArgument
}
func (c *sqlServerPrintOnlyConn) QueryContext(ctx context.Context, _ string, _ []driver.NamedValue) (driver.Rows, error) {
for _, message := range []sqlexp.RawMessage{
sqlexp.MsgNextResultSet{},
sqlexp.MsgNotice{Message: sqlServerPrintOnlyNotice("INSERT c_user(userid) values('168')")},
sqlexp.MsgNextResultSet{},
sqlexp.MsgNotice{Message: sqlServerPrintOnlyNotice("INSERT c_user(userid) values('169')")},
sqlexp.MsgNextResultSet{},
sqlexp.MsgNextResultSet{},
} {
if err := sqlexp.ReturnMessageEnqueue(ctx, c.retmsg, message); err != nil {
return nil, err
}
}
return &sqlServerPrintOnlyRows{remainingBoundaries: 3}, nil
}
func (r *sqlServerPrintOnlyRows) Columns() []string {
// go-mssqldb Rowsq.Columns drains all empty DONE boundaries when no column
// metadata exists. Calling it before the message loop ends loses later PRINTs.
r.drained = true
r.remainingBoundaries = 0
return []string{}
}
func (*sqlServerPrintOnlyRows) Close() error {
return nil
}
func (*sqlServerPrintOnlyRows) Next([]driver.Value) error {
return io.EOF
}
func (*sqlServerPrintOnlyRows) HasNextResultSet() bool {
return true
}
func (r *sqlServerPrintOnlyRows) NextResultSet() error {
if r.drained || r.remainingBoundaries == 0 {
return io.EOF
}
r.remainingBoundaries--
return nil
}
func (m sqlServerPrintOnlyNotice) String() string {
return string(m)
}
type fakeSQLServerExecResult struct {
affected int64
rowErr error
}
func (r fakeSQLServerExecResult) LastInsertId() (int64, error) {
return 0, errors.New("not implemented")
}
func (r fakeSQLServerExecResult) RowsAffected() (int64, error) {
if r.rowErr != nil {
return 0, r.rowErr
}
return r.affected, nil
}
func TestSQLServerRowsAffectedIgnoresTransactionControlErrors(t *testing.T) {
rowErr := errors.New("不支持的方法")
for _, query := range []string{
"BEGIN TRANSACTION",
"COMMIT TRANSACTION",
"ROLLBACK TRANSACTION",
"SAVE TRANSACTION before_update",
"BEGIN TRY\nSELECT 1\nEND TRY",
} {
affected, err := sqlServerRowsAffected(query, fakeSQLServerExecResult{rowErr: rowErr})
if err != nil {
t.Fatalf("sqlServerRowsAffected(%q) returned unexpected error: %v", query, err)
}
if affected != 0 {
t.Fatalf("sqlServerRowsAffected(%q) = %d, want 0", query, affected)
}
}
}
func TestSQLServerRowsAffectedPreservesDMLCount(t *testing.T) {
affected, err := sqlServerRowsAffected(
"UPDATE dbo.users SET name = 'neo' WHERE id = 1",
fakeSQLServerExecResult{affected: 3},
)
if err != nil {
t.Fatalf("sqlServerRowsAffected returned unexpected error: %v", err)
}
if affected != 3 {
t.Fatalf("sqlServerRowsAffected = %d, want 3", affected)
}
}
func TestSQLServerRowsAffectedDoesNotHideDMLRowsAffectedErrors(t *testing.T) {
rowErr := errors.New("rows affected unsupported")
_, err := sqlServerRowsAffected(
"UPDATE dbo.users SET name = 'neo' WHERE id = 1",
fakeSQLServerExecResult{rowErr: rowErr},
)
if !errors.Is(err, rowErr) {
t.Fatalf("expected rows affected error to propagate for DML, got %v", err)
}
}
func TestSQLServerSessionExecerDiscardEvictsPhysicalConnection(t *testing.T) {
dbConn := openConfiguredPoolForTest(t, "sqlserver")
conn, err := dbConn.Conn(context.Background())
if err != nil {
t.Fatalf("acquire pinned SQL Server connection: %v", err)
}
session := &sqlServerSessionExecer{conn: conn}
var _ StatementExecerDiscarter = session
if err := session.Discard(); err != nil {
t.Fatalf("discard pinned SQL Server connection: %v", err)
}
if session.conn != nil {
t.Fatal("discard must clear the wrapper connection reference")
}
if got := poolRecordingCloseCount.Load(); got != 1 {
t.Fatalf("discard must close the contaminated physical connection, closed %d", got)
}
if err := session.Close(); err != nil {
t.Fatalf("deferred close after discard must be harmless: %v", err)
}
if err := dbConn.PingContext(context.Background()); err != nil {
t.Fatalf("ping after discard: %v", err)
}
if got := poolRecordingOpenCount.Load(); got != 2 {
t.Fatalf("pool must open a fresh physical connection after discard, opened %d", got)
}
}
func TestScanSQLServerRowsWithMessagesPreservesRowsFromMsgNext(t *testing.T) {
dbConn, err := sql.Open("sqlite", ":memory:")
if err != nil {
t.Fatalf("open sqlite: %v", err)
}
t.Cleanup(func() {
_ = dbConn.Close()
})
rows, err := dbConn.Query("SELECT 'config:roomType:add' AS menuName")
if err != nil {
t.Fatalf("query rows: %v", err)
}
defer rows.Close()
ctx := context.Background()
retmsg := &sqlexp.ReturnMessage{}
sqlexp.ReturnMessageInit(retmsg)
for _, message := range []sqlexp.RawMessage{
sqlexp.MsgNext{},
sqlexp.MsgRowsAffected{Count: 1},
sqlexp.MsgNextResultSet{},
} {
if err := sqlexp.ReturnMessageEnqueue(ctx, retmsg, message); err != nil {
t.Fatalf("enqueue SQL Server message: %v", err)
}
}
resultSets, messages, err := scanSQLServerRowsWithMessages(ctx, rows, retmsg)
if err != nil {
t.Fatalf("scanSQLServerRowsWithMessages returned error: %v", err)
}
if len(messages) != 0 {
t.Fatalf("expected no SQL Server notices, got %#v", messages)
}
if len(resultSets) != 2 {
t.Fatalf("expected SELECT rows plus affected-row status, got %#v", resultSets)
}
if !reflect.DeepEqual(resultSets[0].Columns, []string{"menuName"}) ||
len(resultSets[0].Rows) != 1 ||
resultSets[0].Rows[0]["menuName"] != "config:roomType:add" {
t.Fatalf("expected SELECT rows first, got %#v", resultSets)
}
if !reflect.DeepEqual(resultSets[1].Columns, []string{"affectedRows"}) ||
len(resultSets[1].Rows) != 1 ||
resultSets[1].Rows[0]["affectedRows"] != int64(1) {
t.Fatalf("expected affected-row status after SELECT rows, got %#v", resultSets)
}
}
func TestSQLServerQueryMultiWithMessagesPreservesPrintsAfterEmptyResultBoundaries(t *testing.T) {
registerSQLServerPrintOnlyDriver.Do(func() {
sql.Register(sqlServerPrintOnlyDriverName, sqlServerPrintOnlyDriver{})
})
dbConn, err := sql.Open(sqlServerPrintOnlyDriverName, "")
if err != nil {
t.Fatalf("open print-only SQL Server driver: %v", err)
}
t.Cleanup(func() {
_ = dbConn.Close()
})
dbInst := &SqlServerDB{conn: dbConn}
resultSets, messages, err := dbInst.QueryMultiWithMessages("p_get_select 'c_user','1=1',1")
if err != nil {
t.Fatalf("QueryMultiWithMessages returned error: %v", err)
}
wantMessages := []string{
"INSERT c_user(userid) values('168')",
"INSERT c_user(userid) values('169')",
}
if !reflect.DeepEqual(messages, wantMessages) {
t.Fatalf("expected all PRINT messages, got %#v", messages)
}
if len(resultSets) != 1 {
t.Fatalf("expected one message-only result set, got %#v", resultSets)
}
if len(resultSets[0].Rows) != 0 || len(resultSets[0].Columns) != 0 {
t.Fatalf("expected message-only result set without tabular data, got %#v", resultSets[0])
}
if !reflect.DeepEqual(resultSets[0].Messages, wantMessages) {
t.Fatalf("expected result set to preserve all PRINT messages, got %#v", resultSets[0].Messages)
}
}
func TestSQLServerMetadataErrorsUseCurrentLanguage(t *testing.T) {
SetBackendLanguage(i18n.LanguageEnUS)
t.Cleanup(func() {
SetBackendLanguage(i18n.LanguageZhCN)
})
sqlServer := &SqlServerDB{}
tests := []struct {
name string
call func() error
}{
{
name: "columns table name required",
call: func() error {
_, err := sqlServer.GetColumns("", " ")
return err
},
},
{
name: "indexes table name required",
call: func() error {
_, err := sqlServer.GetIndexes("", " ")
return err
},
},
{
name: "foreign keys table name required",
call: func() error {
_, err := sqlServer.GetForeignKeys("", " ")
return err
},
},
{
name: "triggers table name required",
call: func() error {
_, err := sqlServer.GetTriggers("", " ")
return err
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
err := tc.call()
if err == nil {
t.Fatal("expected SQL Server metadata call to fail")
}
if err.Error() != "Table name is required" {
t.Fatalf("expected English table-name-required error, got %q", err.Error())
}
if strings.Contains(err.Error(), rawSQLServerTableNameRequiredText) {
t.Fatalf("expected no raw Chinese SQL Server metadata text, got %q", err.Error())
}
})
}
}