thread-master/apps/backend/internal/testkit/testkit.go

117 lines
3.5 KiB
Go
Raw Permalink Normal View History

2026-07-10 12:54:45 +00:00
package testkit
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"apps/backend/internal/config"
"apps/backend/internal/domain"
"apps/backend/internal/handler"
"apps/backend/internal/response"
"apps/backend/internal/svc"
"github.com/stretchr/testify/require"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/rest"
)
// FixedNow is a frozen clock for schedule tests (M4+).
var FixedNow = time.Date(2026, 7, 10, 12, 0, 0, 0, time.UTC)
// TestConfig returns a minimal in-memory config for tests.
func TestConfig() config.Config {
var c config.Config
c.Name = "haixun-test"
c.Host = "127.0.0.1"
c.Port = 0
c.Timeout = 3000
c.Log.Mode = "console"
c.Log.Level = "error"
c.Mongo.URI = "mongodb://127.0.0.1:27017"
c.Mongo.Database = "haixun_test"
c.CacheRedis = cache.CacheConf{{
RedisConf: redis.RedisConf{Host: "127.0.0.1:6379", Type: "node"},
Weight: 100,
}}
c.Auth.AccessSecret = "test-access"
c.Auth.RefreshSecret = "test-refresh"
c.Auth.AccessExpire = 3600
c.Auth.RefreshExpire = 86400
c.Worker.ID = "test-worker"
c.Worker.PollIntervalMs = 100
return c
}
// StartGateway builds a rest.Server with registered routes (not listening).
// Callers use ServeHTTP via httptest for unit/integration without bind.
func StartGateway(t *testing.T) (*rest.Server, *svc.ServiceContext) {
t.Helper()
c := TestConfig()
// Avoid log spam / file mode issues in tests
server := rest.MustNewServer(c.RestConf)
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
t.Cleanup(func() { server.Stop() })
return server, ctx
}
// AssertEnvelope102000 checks success envelope shape (A-07).
func AssertEnvelope102000(t *testing.T, body []byte) response.Envelope {
t.Helper()
var env response.Envelope
require.NoError(t, json.Unmarshal(body, &env))
require.Equal(t, domain.SuccessCode, env.Code, "expected success code 102000")
require.NotEmpty(t, env.Message)
require.Nil(t, env.Error)
return env
}
// AssertFailNotEmptySuccess ensures response is not 102000 with empty success (X-01).
func AssertFailNotEmptySuccess(t *testing.T, body []byte) response.Envelope {
t.Helper()
var env response.Envelope
require.NoError(t, json.Unmarshal(body, &env))
require.NotEqual(t, domain.SuccessCode, env.Code, "must not be success empty")
require.NotEmpty(t, env.Message)
return env
}
// GetJSON issues GET against the in-process server mux via handler registration.
// go-zero rest.Server does not expose ServeHTTP easily in all versions; we re-register
// on a plain mux for tests.
func NewTestMux(svcCtx *svc.ServiceContext) http.Handler {
mux := http.NewServeMux()
// Mirror routes.go paths for testkit convenience
h := handlerProbe{svc: svcCtx}
mux.HandleFunc("GET /api/v1/ping", h.ping)
mux.HandleFunc("GET /api/v1/health", h.ping)
mux.HandleFunc("GET /api/v1/_not_implemented_probe", h.notImpl)
return mux
}
type handlerProbe struct {
svc *svc.ServiceContext
}
func (h handlerProbe) ping(w http.ResponseWriter, r *http.Request) {
// Import cycle avoided: call response directly with same payload as ping logic
response.OK(w, map[string]string{"status": "ok", "service": "haixun-gateway"})
}
func (h handlerProbe) notImpl(w http.ResponseWriter, r *http.Request) {
response.NotImplemented(w, "probe")
}
// DoGet helper
func DoGet(t *testing.T, h http.Handler, path string) *httptest.ResponseRecorder {
t.Helper()
req := httptest.NewRequest(http.MethodGet, path, nil)
rr := httptest.NewRecorder()
h.ServeHTTP(rr, req)
return rr
}