test(H-271): remove race-suite lifecycle blockers (#45)

Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
Rogee
2026-08-18 10:20:11 +08:00
committed by GitHub
co-authored by rogee
parent dae6301a13
commit fad2631b23
6 changed files with 86 additions and 84 deletions
@@ -5,6 +5,7 @@ import (
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
"github.com/gin-gonic/gin"
@@ -26,12 +27,32 @@ func init() {
// ============ DB-Backed Test Helpers (Cov16) ============
// ponytail: coverage tests are sequential; use per-test DBs if they adopt t.Parallel.
var (
coverageTestDBMu sync.Mutex
coverageTestDBs = make(map[string]*gorm.DB)
)
func sharedCoverageTestDB(t *testing.T, name string, models ...interface{}) *gorm.DB {
t.Helper()
coverageTestDBMu.Lock()
defer coverageTestDBMu.Unlock()
db := coverageTestDBs[name]
if db == nil {
var err error
db, err = gorm.Open(sqlite.Open("file:"+name+"?mode=memory&cache=shared"), &gorm.Config{})
require.NoError(t, err, "failed to open test database")
require.NoError(t, db.AutoMigrate(models...), "failed to auto-migrate models")
coverageTestDBs[name] = db
}
t.Cleanup(func() { testutil.TruncateAll(t, db) })
return db
}
// newTestDB_Cov16 creates an in-memory SQLite DB with a broad set of models auto-migrated.
func newTestDB_Cov16(t *testing.T) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err, "failed to open test database")
models := []interface{}{
&model.Account{},
&model.User{},
@@ -100,10 +121,7 @@ func newTestDB_Cov16(t *testing.T) *gorm.DB {
&model.Audit{},
}
err = db.AutoMigrate(models...)
require.NoError(t, err, "failed to auto-migrate models")
return db
return sharedCoverageTestDB(t, "coverage16", models...)
}
// seedAccount_Cov16 creates and persists a test account.
@@ -8,7 +8,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"github.com/gochat/gochat/internal/model"
@@ -18,8 +17,6 @@ import (
func newTestDB_Cov18(t *testing.T) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
models := []interface{}{
&model.Account{}, &model.User{}, &model.AccountUser{}, &model.Inbox{},
&model.Contact{}, &model.ContactInbox{}, &model.Conversation{}, &model.Message{},
@@ -36,10 +33,7 @@ func newTestDB_Cov18(t *testing.T) *gorm.DB {
&model.WorkingHour{}, &model.CaptainAssistant{}, &model.CaptainDocument{},
&model.CaptainPreference{},
}
for _, m := range models {
require.NoError(t, db.AutoMigrate(m))
}
return db
return sharedCoverageTestDB(t, "coverage18", models...)
}
func seedAccount_Cov18(t *testing.T, db *gorm.DB) uint {
@@ -10,7 +10,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"github.com/gochat/gochat/internal/model"
@@ -22,9 +21,6 @@ import (
func newTestDB_Cov19(t *testing.T) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err, "failed to open test database")
models := []interface{}{
&model.Account{}, &model.User{}, &model.AccountUser{}, &model.Inbox{},
&model.Contact{}, &model.ContactInbox{}, &model.Conversation{}, &model.Message{},
@@ -42,8 +38,7 @@ func newTestDB_Cov19(t *testing.T) *gorm.DB {
&model.ContactNote{}, &model.CaptainAssistant{}, &model.CaptainDocument{},
&model.CaptainPreference{},
}
require.NoError(t, db.AutoMigrate(models...), "failed to auto-migrate models")
return db
return sharedCoverageTestDB(t, "coverage19", models...)
}
func seedAccount_Cov19(t *testing.T, db *gorm.DB) *model.Account {
@@ -12,7 +12,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"github.com/gochat/gochat/internal/model"
@@ -24,9 +23,6 @@ import (
func newTestDB_Cov20(t *testing.T) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err, "failed to open test database")
models := []interface{}{
&model.Account{}, &model.User{}, &model.AccountUser{}, &model.Inbox{},
&model.Contact{}, &model.ContactInbox{}, &model.Conversation{}, &model.Message{},
@@ -44,8 +40,7 @@ func newTestDB_Cov20(t *testing.T) *gorm.DB {
&model.WorkingHour{}, &model.ContactNote{}, &model.CaptainAssistant{},
&model.CaptainDocument{}, &model.CaptainPreference{}, &model.WidgetTest{},
}
require.NoError(t, db.AutoMigrate(models...), "failed to auto-migrate models")
return db
return sharedCoverageTestDB(t, "coverage20", models...)
}
func seedAccount_Cov20(t *testing.T, db *gorm.DB) *model.Account {
+9 -33
View File
@@ -1162,18 +1162,16 @@ func TestWatermillZapAdapter_With(t *testing.T) {
// Returns the server side and the client side.
func newTestWSConn(t *testing.T) (*websocket.Conn, *websocket.Conn) {
t.Helper()
// Create an in-process WebSocket server
upgrader := websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
}
serverConn := make(chan *websocket.Conn, 1)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
// Block forever — the client side will close
<-r.Context().Done()
conn.Close()
serverConn <- conn
}))
t.Cleanup(srv.Close)
@@ -1182,36 +1180,14 @@ func newTestWSConn(t *testing.T) (*websocket.Conn, *websocket.Conn) {
clientConn, _, err := dialer.Dial(wsURL, nil)
require.NoError(t, err)
// Accept the server-side connection by making a second request
// Actually, httptest.NewServer with the upgrader already accepted it.
// We need to get the server conn. Let's use a different approach:
// Use net.Pipe-based websocket pair.
clientConn.Close()
srv.Close()
// Use websocket.NewPreparedMessages approach — or just use a pipe-based approach
// Actually, simplest: use a real WS server that returns the server conn
var serverConn *websocket.Conn
srv2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
serverConn = conn
}))
t.Cleanup(srv2.Close)
wsURL2 := "ws" + strings.TrimPrefix(srv2.URL, "http")
clientConn2, _, err := dialer.Dial(wsURL2, nil)
require.NoError(t, err)
// Wait briefly for serverConn to be set
for i := 0; i < 100 && serverConn == nil; i++ {
time.Sleep(time.Millisecond)
select {
case conn := <-serverConn:
return conn, clientConn
case <-time.After(2 * time.Second):
clientConn.Close()
t.Fatal("server connection was not established")
return nil, nil
}
require.NotNil(t, serverConn, "server connection was not established")
return serverConn, clientConn2
}
func TestHandlerHandleUnsubscribe_InvalidIdentifier(t *testing.T) {
+49 -25
View File
@@ -2,11 +2,15 @@ package service
import (
"context"
"fmt"
"reflect"
"sync"
"testing"
"time"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/repository"
"github.com/gochat/gochat/pkg/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
@@ -14,33 +18,53 @@ import (
"gorm.io/gorm/logger"
)
// ponytail: coverage tests are sequential; use per-test DBs if they adopt t.Parallel.
var (
simpleServiceTestDBMu sync.Mutex
simpleServiceDBs = make(map[string]*gorm.DB)
)
func newSimpleServiceTestDB(t *testing.T, models ...interface{}) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open("file::memory:"), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)})
require.NoError(t, err)
allModels := append([]interface{}{
&model.Account{}, &model.User{}, &model.AccountUser{},
&model.Inbox{}, &model.Contact{}, &model.ContactInbox{},
&model.Conversation{}, &model.Message{}, &model.Attachment{},
&model.Notification{}, &model.NotificationPreference{},
&model.Tag{}, &model.ConversationLabel{}, &model.ContactLabel{},
&model.Team{}, &model.TeamMember{},
&model.Banner{}, &model.Note{}, &model.ContactNote{},
&model.InstallationConfig{},
&model.InboxLimit{}, &model.InboxMember{},
&model.AgentBot{}, &model.AgentBotInbox{},
&model.DeliveryStatus{},
&model.ReportingEvent{},
&model.SlaEvent{}, &model.SlaPolicy{}, &model.AppliedSLA{},
&model.WidgetTest{},
&model.PushToken{},
&model.NotificationSubscription{},
&model.EmailChannelMigration{},
&model.Article{},
&model.Folder{},
&model.CustomAttributeDefinition{},
}, models...)
require.NoError(t, db.AutoMigrate(allModels...))
simpleServiceTestDBMu.Lock()
defer simpleServiceTestDBMu.Unlock()
key := ""
for _, model := range models {
key += "|" + reflect.TypeOf(model).String()
}
db := simpleServiceDBs[key]
if db == nil {
baseModels := []interface{}{
&model.Account{}, &model.User{}, &model.AccountUser{},
&model.Inbox{}, &model.Contact{}, &model.ContactInbox{},
&model.Conversation{}, &model.Message{}, &model.Attachment{},
&model.Notification{}, &model.NotificationPreference{},
&model.Tag{}, &model.ConversationLabel{}, &model.ContactLabel{},
&model.Team{}, &model.TeamMember{},
&model.Banner{}, &model.Note{}, &model.ContactNote{},
&model.InstallationConfig{},
&model.InboxLimit{}, &model.InboxMember{},
&model.AgentBot{}, &model.AgentBotInbox{},
&model.DeliveryStatus{},
&model.ReportingEvent{},
&model.SlaEvent{}, &model.SlaPolicy{}, &model.AppliedSLA{},
&model.WidgetTest{},
&model.PushToken{},
&model.NotificationSubscription{},
&model.EmailChannelMigration{},
&model.Article{},
&model.Folder{},
&model.CustomAttributeDefinition{},
}
allModels := append(baseModels, models...)
var err error
db, err = gorm.Open(sqlite.Open(fmt.Sprintf("file:service-coverage-%d?mode=memory&cache=shared", len(simpleServiceDBs))), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)})
require.NoError(t, err)
require.NoError(t, db.AutoMigrate(allModels...))
simpleServiceDBs[key] = db
}
t.Cleanup(func() { testutil.TruncateAll(t, db) })
return db
}