108 lines
2.9 KiB
Go
108 lines
2.9 KiB
Go
|
|
package spaserver
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"go/ast"
|
||
|
|
"go/parser"
|
||
|
|
"go/token"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"code.nochebuena.dev/einherjar/contracts/lifecycle"
|
||
|
|
"code.nochebuena.dev/einherjar/contracts/logging"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Compile-time interface check (CT-5 / I-8).
|
||
|
|
var _ lifecycle.Component = (*Server)(nil)
|
||
|
|
|
||
|
|
// TestAtMostOneExportedTypePerFile enforces CT-6: at most one exported TypeSpec
|
||
|
|
// per non-test, non-doc .go file in the root package.
|
||
|
|
func TestAtMostOneExportedTypePerFile(t *testing.T) {
|
||
|
|
fset := token.NewFileSet()
|
||
|
|
pkgs, err := parser.ParseDir(fset, ".", func(fi os.FileInfo) bool {
|
||
|
|
name := fi.Name()
|
||
|
|
return !strings.HasSuffix(name, "_test.go") && name != "doc.go"
|
||
|
|
}, 0)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("parse: %v", err)
|
||
|
|
}
|
||
|
|
for _, pkg := range pkgs {
|
||
|
|
for path, file := range pkg.Files {
|
||
|
|
base := filepath.Base(path)
|
||
|
|
count := 0
|
||
|
|
for _, decl := range file.Decls {
|
||
|
|
gd, ok := decl.(*ast.GenDecl)
|
||
|
|
if !ok {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
for _, spec := range gd.Specs {
|
||
|
|
ts, ok := spec.(*ast.TypeSpec)
|
||
|
|
if ok && ts.Name.IsExported() {
|
||
|
|
count++
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if count > 1 {
|
||
|
|
t.Errorf("%s: %d exported TypeSpecs (max 1)", base, count)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// TestDefaultConfig_Defaults verifies that DefaultConfig returns non-zero values (S-4).
|
||
|
|
func TestDefaultConfig_Defaults(t *testing.T) {
|
||
|
|
cfg := DefaultConfig()
|
||
|
|
if cfg.Port == 0 {
|
||
|
|
t.Error("Port must have a non-zero default")
|
||
|
|
}
|
||
|
|
if cfg.StaticDir == "" {
|
||
|
|
t.Error("StaticDir must have a non-empty default")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// TestDefaultConfig_EnvOverride verifies that environment variables are respected.
|
||
|
|
func TestDefaultConfig_EnvOverride(t *testing.T) {
|
||
|
|
t.Setenv("EINHERJAR_SPA_PORT", "9090")
|
||
|
|
t.Setenv("EINHERJAR_SPA_STATIC_DIR", "/tmp/www")
|
||
|
|
|
||
|
|
cfg := DefaultConfig()
|
||
|
|
if cfg.Port != 9090 {
|
||
|
|
t.Errorf("Port = %d, want 9090", cfg.Port)
|
||
|
|
}
|
||
|
|
if cfg.StaticDir != "/tmp/www" {
|
||
|
|
t.Errorf("StaticDir = %q, want /tmp/www", cfg.StaticDir)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// TestServer_Lifecycle verifies that OnInit/OnStart/OnStop complete without error
|
||
|
|
// on a free port.
|
||
|
|
func TestServer_Lifecycle(t *testing.T) {
|
||
|
|
cfg := Config{Port: 0, StaticDir: t.TempDir()}
|
||
|
|
srv := NewServer(newStubLogger(), cfg)
|
||
|
|
|
||
|
|
if err := srv.OnInit(); err != nil {
|
||
|
|
t.Fatalf("OnInit: %v", err)
|
||
|
|
}
|
||
|
|
if err := srv.OnStart(); err != nil {
|
||
|
|
t.Fatalf("OnStart: %v", err)
|
||
|
|
}
|
||
|
|
if err := srv.OnStop(); err != nil {
|
||
|
|
t.Fatalf("OnStop: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- helpers ---
|
||
|
|
|
||
|
|
type stubLogger struct{}
|
||
|
|
|
||
|
|
func newStubLogger() *stubLogger { return &stubLogger{} }
|
||
|
|
|
||
|
|
func (s *stubLogger) Debug(msg string, args ...any) {}
|
||
|
|
func (s *stubLogger) Info(msg string, args ...any) {}
|
||
|
|
func (s *stubLogger) Warn(msg string, args ...any) {}
|
||
|
|
func (s *stubLogger) Error(msg string, err error, args ...any) {}
|
||
|
|
func (s *stubLogger) With(args ...any) logging.Logger { return s }
|
||
|
|
func (s *stubLogger) WithContext(ctx context.Context) logging.Logger { return s }
|