185 lines
5.1 KiB
Go
185 lines
5.1 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"log/slog"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"galaxy/mail/internal/config"
|
|
|
|
"github.com/alicebob/miniredis/v2"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNewRuntimeStartsWithStubMode(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
redisServer := miniredis.RunT(t)
|
|
templateDir := writeStage6Templates(t)
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Redis.Addr = redisServer.Addr()
|
|
cfg.Templates.Dir = templateDir
|
|
cfg.InternalHTTP.Addr = mustFreeAddr(t)
|
|
|
|
runtime, err := NewRuntime(context.Background(), cfg, testLogger())
|
|
require.NoError(t, err)
|
|
require.NoError(t, runtime.Close())
|
|
}
|
|
|
|
func TestNewRuntimeRejectsInvalidRedisConfig(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
templateDir := writeStage6Templates(t)
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Redis.Addr = "127.0.0.1"
|
|
cfg.Templates.Dir = templateDir
|
|
cfg.InternalHTTP.Addr = mustFreeAddr(t)
|
|
|
|
_, err := NewRuntime(context.Background(), cfg, testLogger())
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "redis addr")
|
|
}
|
|
|
|
func TestNewRuntimeRejectsUnavailableRedis(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
templateDir := writeStage6Templates(t)
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Redis.Addr = "127.0.0.1:6399"
|
|
cfg.Redis.OperationTimeout = 100 * time.Millisecond
|
|
cfg.Templates.Dir = templateDir
|
|
cfg.InternalHTTP.Addr = mustFreeAddr(t)
|
|
|
|
_, err := NewRuntime(context.Background(), cfg, testLogger())
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "ping redis")
|
|
}
|
|
|
|
func TestNewRuntimeRejectsMissingTemplateDirectory(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
redisServer := miniredis.RunT(t)
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Redis.Addr = redisServer.Addr()
|
|
cfg.Templates.Dir = filepath.Join(t.TempDir(), "missing")
|
|
cfg.InternalHTTP.Addr = mustFreeAddr(t)
|
|
|
|
_, err := NewRuntime(context.Background(), cfg, testLogger())
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "template catalog")
|
|
}
|
|
|
|
func TestNewRuntimeRejectsMissingRequiredTemplateFile(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
redisServer := miniredis.RunT(t)
|
|
rootDir := t.TempDir()
|
|
require.NoError(t, os.MkdirAll(filepath.Join(rootDir, "auth.login_code", "en"), 0o755))
|
|
require.NoError(t, os.WriteFile(filepath.Join(rootDir, "auth.login_code", "en", "subject.tmpl"), []byte("Subject"), 0o644))
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Redis.Addr = redisServer.Addr()
|
|
cfg.Templates.Dir = rootDir
|
|
cfg.InternalHTTP.Addr = mustFreeAddr(t)
|
|
|
|
_, err := NewRuntime(context.Background(), cfg, testLogger())
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "text.tmpl")
|
|
}
|
|
|
|
func TestNewRuntimeRejectsBrokenTemplateCatalog(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
redisServer := miniredis.RunT(t)
|
|
rootDir := t.TempDir()
|
|
require.NoError(t, os.MkdirAll(filepath.Join(rootDir, "auth.login_code", "en"), 0o755))
|
|
require.NoError(t, os.WriteFile(filepath.Join(rootDir, "auth.login_code", "en", "subject.tmpl"), []byte("Your login code"), 0o644))
|
|
require.NoError(t, os.WriteFile(filepath.Join(rootDir, "auth.login_code", "en", "text.tmpl"), []byte("Code: {{.code}}"), 0o644))
|
|
require.NoError(t, os.MkdirAll(filepath.Join(rootDir, "game.turn_ready", "en"), 0o755))
|
|
require.NoError(t, os.WriteFile(filepath.Join(rootDir, "game.turn_ready", "en", "subject.tmpl"), []byte("{{if .turn_number}"), 0o644))
|
|
require.NoError(t, os.WriteFile(filepath.Join(rootDir, "game.turn_ready", "en", "text.tmpl"), []byte("Turn ready"), 0o644))
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Redis.Addr = redisServer.Addr()
|
|
cfg.Templates.Dir = rootDir
|
|
cfg.InternalHTTP.Addr = mustFreeAddr(t)
|
|
|
|
_, err := NewRuntime(context.Background(), cfg, testLogger())
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "template parse failed")
|
|
}
|
|
|
|
func TestRuntimeRunStopsOnContextCancellation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
redisServer := miniredis.RunT(t)
|
|
templateDir := writeStage6Templates(t)
|
|
|
|
cfg := config.DefaultConfig()
|
|
cfg.Redis.Addr = redisServer.Addr()
|
|
cfg.Templates.Dir = templateDir
|
|
cfg.InternalHTTP.Addr = mustFreeAddr(t)
|
|
cfg.ShutdownTimeout = time.Second
|
|
|
|
runtime, err := NewRuntime(context.Background(), cfg, testLogger())
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
require.NoError(t, runtime.Close())
|
|
}()
|
|
|
|
runCtx, cancel := context.WithCancel(context.Background())
|
|
done := make(chan error, 1)
|
|
go func() {
|
|
done <- runtime.Run(runCtx)
|
|
}()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
cancel()
|
|
|
|
require.Eventually(t, func() bool {
|
|
select {
|
|
case err := <-done:
|
|
return err == nil
|
|
default:
|
|
return false
|
|
}
|
|
}, 5*time.Second, 10*time.Millisecond)
|
|
}
|
|
|
|
func writeStage6Templates(t *testing.T) string {
|
|
t.Helper()
|
|
|
|
rootDir := t.TempDir()
|
|
templateDir := filepath.Join(rootDir, "auth.login_code", "en")
|
|
require.NoError(t, os.MkdirAll(templateDir, 0o755))
|
|
require.NoError(t, os.WriteFile(filepath.Join(templateDir, "subject.tmpl"), []byte("Your login code"), 0o644))
|
|
require.NoError(t, os.WriteFile(filepath.Join(templateDir, "text.tmpl"), []byte("Code: {{.code}}"), 0o644))
|
|
|
|
return rootDir
|
|
}
|
|
|
|
func testLogger() *slog.Logger {
|
|
return slog.New(slog.NewJSONHandler(io.Discard, nil))
|
|
}
|
|
|
|
func mustFreeAddr(t *testing.T) string {
|
|
t.Helper()
|
|
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
require.NoError(t, listener.Close())
|
|
}()
|
|
|
|
return listener.Addr().String()
|
|
}
|