|
|
package server
|
|
|
|
|
|
import (
|
|
|
"bytes"
|
|
|
"net/http"
|
|
|
"net/http/httptest"
|
|
|
"testing"
|
|
|
|
|
|
"github.com/noahlann/nnet/pkg/config"
|
|
|
ctxpkg "github.com/noahlann/nnet/pkg/context"
|
|
|
"github.com/noahlann/nnet/pkg/errors"
|
|
|
"github.com/noahlann/nnet/pkg/health"
|
|
|
routerpkg "github.com/noahlann/nnet/pkg/router"
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
"github.com/stretchr/testify/require"
|
|
|
)
|
|
|
|
|
|
func TestServerNew(t *testing.T) {
|
|
|
cfg := &config.Config{
|
|
|
Addr: "tcp://:8080", // 使用标准格式
|
|
|
}
|
|
|
|
|
|
server, err := NewServer(cfg)
|
|
|
require.NoError(t, err, "Expected no error when creating server")
|
|
|
assert.NotNil(t, server, "Expected server to be non-nil")
|
|
|
}
|
|
|
|
|
|
func TestServerNewInvalidAddr(t *testing.T) {
|
|
|
// Config.Validate() 只检查地址是否为空,不检查格式
|
|
|
// 所以即使地址格式无效,NewServer也可能成功
|
|
|
// 实际的地址验证会在Start()时进行
|
|
|
cfg := &config.Config{
|
|
|
Addr: "invalid://address",
|
|
|
}
|
|
|
|
|
|
server, err := NewServer(cfg)
|
|
|
// NewServer可能成功(因为Validate只检查非空)
|
|
|
// 实际的错误会在Start()时发现
|
|
|
if err != nil {
|
|
|
assert.Nil(t, server, "Expected server to be nil on error")
|
|
|
} else {
|
|
|
// 如果NewServer成功,服务器应该不为nil
|
|
|
assert.NotNil(t, server, "Expected server to be non-nil if no error")
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func TestServerRouter(t *testing.T) {
|
|
|
cfg := &config.Config{
|
|
|
Addr: "tcp://:8080",
|
|
|
}
|
|
|
|
|
|
server, err := NewServer(cfg)
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
router := server.Router()
|
|
|
assert.NotNil(t, router, "Expected router to be non-nil")
|
|
|
}
|
|
|
|
|
|
func TestServerStartStop(t *testing.T) {
|
|
|
t.Skip("Skipping start/stop test - requires actual network binding, may fail in some environments")
|
|
|
|
|
|
// 这个测试需要实际的网络绑定,可能会在某些环境中失败
|
|
|
// 可以放在集成测试中
|
|
|
}
|
|
|
|
|
|
func TestServerRegisterRoute(t *testing.T) {
|
|
|
cfg := &config.Config{
|
|
|
Addr: "tcp://:8080",
|
|
|
}
|
|
|
|
|
|
server, err := NewServer(cfg)
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
// 注册路由(使用router包的Handler类型,Handler实际上是func(ctx Context) error)
|
|
|
handler := routerpkg.Handler(func(ctx ctxpkg.Context) error {
|
|
|
// 简单的handler,只验证可以注册
|
|
|
return nil
|
|
|
})
|
|
|
route := server.Router().RegisterString("/test", handler)
|
|
|
assert.NotNil(t, route, "Expected route to be returned")
|
|
|
|
|
|
// 验证路由已注册(通过Router接口)
|
|
|
router := server.Router()
|
|
|
assert.NotNil(t, router, "Expected router to be non-nil")
|
|
|
}
|
|
|
|
|
|
func TestServerConfig(t *testing.T) {
|
|
|
cfg := &config.Config{
|
|
|
Addr: "tcp://:8080",
|
|
|
}
|
|
|
|
|
|
server, err := NewServer(cfg)
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
// 测试获取配置
|
|
|
serverConfig := server.Config()
|
|
|
assert.NotNil(t, serverConfig, "Expected config to be non-nil")
|
|
|
assert.Equal(t, cfg.Addr, serverConfig.Addr, "Expected address to match")
|
|
|
}
|
|
|
|
|
|
func TestServerStarted(t *testing.T) {
|
|
|
cfg := &config.Config{
|
|
|
Addr: "tcp://:8080",
|
|
|
}
|
|
|
|
|
|
server, err := NewServer(cfg)
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
// 测试初始状态(不启动服务器,避免网络连接)
|
|
|
assert.False(t, server.Started(), "Expected server to not be started initially")
|
|
|
}
|
|
|
|
|
|
func TestServerMultipleStart(t *testing.T) {
|
|
|
t.Skip("Skipping multiple start test - requires actual server start")
|
|
|
|
|
|
// 这个测试需要实际的服务器启动,可能会失败
|
|
|
// 可以放在集成测试中
|
|
|
}
|
|
|
|
|
|
func TestServerStopBeforeStart(t *testing.T) {
|
|
|
cfg := &config.Config{
|
|
|
Addr: "tcp://:8080",
|
|
|
}
|
|
|
|
|
|
server, err := NewServer(cfg)
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
// 在未启动时停止应该返回错误
|
|
|
err = server.Stop()
|
|
|
assert.Error(t, err, "Expected error when stopping server that hasn't started")
|
|
|
assert.Equal(t, errors.ErrServerNotStarted, err, "Expected ErrServerNotStarted")
|
|
|
}
|
|
|
|
|
|
func TestServerExportMetrics(t *testing.T) {
|
|
|
cfg := &config.Config{Addr: "tcp://:8080"}
|
|
|
srv, err := NewServer(cfg)
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
metrics := srv.Metrics()
|
|
|
metrics.IncConnections()
|
|
|
metrics.IncRequests()
|
|
|
metrics.IncErrors()
|
|
|
metrics.AddBytesReceived(42)
|
|
|
metrics.AddBytesSent(24)
|
|
|
|
|
|
var buf bytes.Buffer
|
|
|
require.NoError(t, srv.ExportMetrics(&buf))
|
|
|
|
|
|
output := buf.String()
|
|
|
assert.Contains(t, output, "nnet_connections 1")
|
|
|
assert.Contains(t, output, "nnet_requests 1")
|
|
|
assert.Contains(t, output, "nnet_errors 1")
|
|
|
assert.Contains(t, output, "nnet_bytes_received 42")
|
|
|
assert.Contains(t, output, "nnet_bytes_sent 24")
|
|
|
}
|
|
|
|
|
|
func TestServerMetricsHandler(t *testing.T) {
|
|
|
cfg := &config.Config{Addr: "tcp://:8080"}
|
|
|
srv, err := NewServer(cfg)
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/metrics", nil)
|
|
|
rr := httptest.NewRecorder()
|
|
|
|
|
|
srv.Metrics().IncConnections()
|
|
|
|
|
|
srv.MetricsHandler().ServeHTTP(rr, req)
|
|
|
|
|
|
assert.Equal(t, http.StatusOK, rr.Code)
|
|
|
assert.Equal(t, "text/plain; version=0.0.4", rr.Header().Get("Content-Type"))
|
|
|
assert.Contains(t, rr.Body.String(), "nnet_connections 1")
|
|
|
}
|
|
|
|
|
|
type stubHealthCheck struct {
|
|
|
status health.Status
|
|
|
message string
|
|
|
}
|
|
|
|
|
|
func (s stubHealthCheck) Check() (health.Status, string) {
|
|
|
return s.status, s.message
|
|
|
}
|
|
|
|
|
|
func TestServerHealthHandler(t *testing.T) {
|
|
|
cfg := &config.Config{Addr: "tcp://:8080"}
|
|
|
srv, err := NewServer(cfg)
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
checker := srv.HealthChecker()
|
|
|
require.NoError(t, checker.Register("stub", stubHealthCheck{status: health.StatusHealthy, message: "ok"}))
|
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/health", nil)
|
|
|
rr := httptest.NewRecorder()
|
|
|
|
|
|
srv.HealthHandler().ServeHTTP(rr, req)
|
|
|
|
|
|
assert.Equal(t, http.StatusOK, rr.Code)
|
|
|
assert.Equal(t, "application/json", rr.Header().Get("Content-Type"))
|
|
|
assert.Contains(t, rr.Body.String(), `"status":"healthy"`)
|
|
|
assert.Contains(t, rr.Body.String(), `"stub"`)
|
|
|
}
|