Files
sub2api-cn-relay-manager/internal/app/app.go
phamnazage-jpg 71cbaf5fa6 test(project): achieve ≥70% package coverage across all internal packages
- store/sqlite: 75.4% (repos + db coverage)
- host/sub2api: 80.8% (httptest mock server, pure function tests)
- app: 74.2% (handler error paths, NewActionSet closures)
- pack: 72.4%
- provision: 75.2%
- access: 77.3%
- config: 94.7% (lookup mock tests)

All tests pass: build, vet, race, coverage gates.
2026-05-15 19:26:25 +08:00

69 lines
1.3 KiB
Go

package app
import (
"context"
"errors"
"net"
"net/http"
"time"
)
type ListenerFactory func(network, address string) (net.Listener, error)
type Server struct {
server *http.Server
listen ListenerFactory
}
func NewServer(listenAddr string, handler http.Handler, listenerFactory ListenerFactory) *Server {
if handler == nil {
handler = NewAPIHandler("", ActionSet{})
}
server := &Server{
server: &http.Server{
Addr: listenAddr,
Handler: handler,
},
listen: net.Listen,
}
if listenerFactory != nil {
server.listen = listenerFactory
}
return server
}
func (s *Server) Addr() string {
return s.server.Addr
}
func (s *Server) Run(ctx context.Context) error {
listener, err := s.listen("tcp", s.server.Addr)
if err != nil {
return err
}
return s.Serve(ctx, listener)
}
func (s *Server) Serve(ctx context.Context, listener net.Listener) error {
errCh := make(chan error, 1)
go func() {
err := s.server.Serve(listener)
if errors.Is(err, http.ErrServerClosed) {
err = nil
}
errCh <- err
}()
select {
case <-ctx.Done():
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := s.server.Shutdown(shutdownCtx); err != nil {
return err
}
return <-errCh
case err := <-errCh:
return err
}
}