package model import ( "bytes" "fmt" "testing" "github.com/ClickHouse/clickhouse-go/v2/lib/proto" "github.com/QuantumNous/new-api/common" "github.com/glebarez/sqlite" "github.com/go-sql-driver/mysql" "github.com/jackc/pgx/v5/pgconn" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/gorm" ) // 保护契约:数据库驱动错误消息可能内联数据值,非 DEBUG 下日志只保留错误码。 func TestSanitizeDBErrorStripsDriverMessage(t *testing.T) { tests := []struct { name string err error want string leaked string }{ { name: "mysql duplicate entry", err: &mysql.MySQLError{Number: 1062, Message: "Duplicate entry 'secret-value' for key 'users.idx'"}, want: "mysql error 1062", leaked: "secret-value", }, { name: "postgres unique violation", err: &pgconn.PgError{Code: "23505", Message: "duplicate key value", Detail: "Key (k)=(secret-value) already exists."}, want: "postgres error SQLSTATE 23505", leaked: "secret-value", }, { name: "clickhouse exception", err: &proto.Exception{Code: 241, Message: "Memory limit exceeded while processing 'secret-value'"}, want: "clickhouse error 241", leaked: "secret-value", }, { name: "wrapped driver error", err: fmt.Errorf("exec failed: %w", &mysql.MySQLError{Number: 1064, Message: "syntax error near 'secret-value'"}), want: "mysql error 1064", leaked: "secret-value", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := sanitizeDBError(tc.err) require.Error(t, got) assert.Equal(t, tc.want, got.Error()) assert.NotContains(t, got.Error(), tc.leaked) }) } } func TestSanitizeDBErrorSQLiteDriver(t *testing.T) { db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) execErr := db.Exec("INSERT INTO missing_table (k) VALUES (?)", "secret-value").Error require.Error(t, execErr) got := sanitizeDBError(execErr) assert.Regexp(t, `^sqlite error \d+$`, got.Error()) assert.NotContains(t, got.Error(), "secret-value") } func TestSanitizeDBErrorKeepsNonDriverErrors(t *testing.T) { err := fmt.Errorf("dial tcp 127.0.0.1:3306: connect: connection refused") assert.Equal(t, err, sanitizeDBError(err)) } // 保护契约:经 gorm 真实链路,错误日志同时满足 SQL 参数化、驱动错误脱敏、 // 调用点归因到业务代码;DEBUG=true 恢复参数值与错误原文。 func TestGormLoggerEndToEndSanitizedOutput(t *testing.T) { previousDebug := common.DebugEnabled t.Cleanup(func() { common.DebugEnabled = previousDebug }) execQuery := func() string { var buf bytes.Buffer db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{Logger: newGormLogger(&buf)}) require.NoError(t, err) db.Exec("SELECT * FROM missing_table WHERE k = ?", "secret-value") return buf.String() } common.DebugEnabled = false out := execQuery() assert.Contains(t, out, "k = ?") assert.NotContains(t, out, "secret-value") assert.Contains(t, out, "sqlite error") assert.Contains(t, out, "gorm_logger_test.go") common.DebugEnabled = true debugOut := execQuery() assert.Contains(t, debugOut, "secret-value") assert.Contains(t, debugOut, "no such table") }