Files
acc-server-manager/tests/unit/service/state_history_service_test.go
Fran Jurmanović 5e7c96697a code cleanup
2025-09-18 13:33:51 +02:00

532 lines
16 KiB
Go

package service
import (
"acc-server-manager/local/model"
"acc-server-manager/local/repository"
"acc-server-manager/local/service"
"acc-server-manager/local/utl/tracking"
"acc-server-manager/tests"
"acc-server-manager/tests/testdata"
"testing"
"time"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
)
func TestStateHistoryService_GetAll_Success(t *testing.T) {
helper := tests.NewTestHelper(t)
defer helper.Cleanup()
if !helper.DB.Migrator().HasTable(&model.StateHistory{}) {
err := helper.DB.Migrator().CreateTable(&model.StateHistory{})
if err != nil {
t.Fatalf("Failed to create state_histories table: %v", err)
}
}
repo := repository.NewStateHistoryRepository(helper.DB)
stateHistoryService := service.NewStateHistoryService(repo)
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
err := repo.Insert(helper.CreateContext(), &history)
tests.AssertNoError(t, err)
app := fiber.New()
ctx := helper.CreateFiberCtx()
defer helper.ReleaseFiberCtx(app, ctx)
filter := testdata.CreateBasicFilter(helper.TestData.ServerID.String())
result, err := stateHistoryService.GetAll(ctx, filter)
tests.AssertNoError(t, err)
tests.AssertNotNil(t, result)
tests.AssertEqual(t, 1, len(*result))
tests.AssertEqual(t, model.SessionPractice, (*result)[0].Session)
tests.AssertEqual(t, 5, (*result)[0].PlayerCount)
}
func TestStateHistoryService_GetAll_WithFilter(t *testing.T) {
helper := tests.NewTestHelper(t)
defer helper.Cleanup()
if !helper.DB.Migrator().HasTable(&model.StateHistory{}) {
err := helper.DB.Migrator().CreateTable(&model.StateHistory{})
if err != nil {
t.Fatalf("Failed to create state_histories table: %v", err)
}
}
repo := repository.NewStateHistoryRepository(helper.DB)
stateHistoryService := service.NewStateHistoryService(repo)
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
practiceHistory := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
raceHistory := testData.CreateStateHistory(model.SessionRace, "spa", 10, uuid.New())
err := repo.Insert(helper.CreateContext(), &practiceHistory)
tests.AssertNoError(t, err)
err = repo.Insert(helper.CreateContext(), &raceHistory)
tests.AssertNoError(t, err)
app := fiber.New()
ctx := helper.CreateFiberCtx()
defer helper.ReleaseFiberCtx(app, ctx)
filter := testdata.CreateFilterWithSession(helper.TestData.ServerID.String(), model.SessionRace)
result, err := stateHistoryService.GetAll(ctx, filter)
tests.AssertNoError(t, err)
tests.AssertNotNil(t, result)
tests.AssertEqual(t, 1, len(*result))
tests.AssertEqual(t, model.SessionRace, (*result)[0].Session)
tests.AssertEqual(t, 10, (*result)[0].PlayerCount)
}
func TestStateHistoryService_GetAll_NoData(t *testing.T) {
helper := tests.NewTestHelper(t)
defer helper.Cleanup()
if !helper.DB.Migrator().HasTable(&model.StateHistory{}) {
err := helper.DB.Migrator().CreateTable(&model.StateHistory{})
if err != nil {
t.Fatalf("Failed to create state_histories table: %v", err)
}
}
repo := repository.NewStateHistoryRepository(helper.DB)
stateHistoryService := service.NewStateHistoryService(repo)
app := fiber.New()
ctx := helper.CreateFiberCtx()
defer helper.ReleaseFiberCtx(app, ctx)
filter := testdata.CreateBasicFilter(helper.TestData.ServerID.String())
result, err := stateHistoryService.GetAll(ctx, filter)
tests.AssertNoError(t, err)
tests.AssertNotNil(t, result)
tests.AssertEqual(t, 0, len(*result))
}
func TestStateHistoryService_Insert_Success(t *testing.T) {
helper := tests.NewTestHelper(t)
defer helper.Cleanup()
if !helper.DB.Migrator().HasTable(&model.StateHistory{}) {
err := helper.DB.Migrator().CreateTable(&model.StateHistory{})
if err != nil {
t.Fatalf("Failed to create state_histories table: %v", err)
}
}
repo := repository.NewStateHistoryRepository(helper.DB)
stateHistoryService := service.NewStateHistoryService(repo)
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
app := fiber.New()
ctx := helper.CreateFiberCtx()
defer helper.ReleaseFiberCtx(app, ctx)
err := stateHistoryService.Insert(ctx, &history)
tests.AssertNoError(t, err)
filter := testdata.CreateBasicFilter(helper.TestData.ServerID.String())
result, err := stateHistoryService.GetAll(ctx, filter)
tests.AssertNoError(t, err)
tests.AssertEqual(t, 1, len(*result))
}
func TestStateHistoryService_GetLastSessionID_Success(t *testing.T) {
helper := tests.NewTestHelper(t)
defer helper.Cleanup()
if !helper.DB.Migrator().HasTable(&model.StateHistory{}) {
err := helper.DB.Migrator().CreateTable(&model.StateHistory{})
if err != nil {
t.Fatalf("Failed to create state_histories table: %v", err)
}
}
repo := repository.NewStateHistoryRepository(helper.DB)
stateHistoryService := service.NewStateHistoryService(repo)
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
sessionID := uuid.New()
history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, sessionID)
err := repo.Insert(helper.CreateContext(), &history)
tests.AssertNoError(t, err)
app := fiber.New()
ctx := helper.CreateFiberCtx()
defer helper.ReleaseFiberCtx(app, ctx)
lastSessionID, err := stateHistoryService.GetLastSessionID(ctx, helper.TestData.ServerID)
tests.AssertNoError(t, err)
tests.AssertEqual(t, sessionID, lastSessionID)
}
func TestStateHistoryService_GetLastSessionID_NoData(t *testing.T) {
helper := tests.NewTestHelper(t)
defer helper.Cleanup()
if !helper.DB.Migrator().HasTable(&model.StateHistory{}) {
err := helper.DB.Migrator().CreateTable(&model.StateHistory{})
if err != nil {
t.Fatalf("Failed to create state_histories table: %v", err)
}
}
repo := repository.NewStateHistoryRepository(helper.DB)
stateHistoryService := service.NewStateHistoryService(repo)
app := fiber.New()
ctx := helper.CreateFiberCtx()
defer helper.ReleaseFiberCtx(app, ctx)
lastSessionID, err := stateHistoryService.GetLastSessionID(ctx, helper.TestData.ServerID)
tests.AssertNoError(t, err)
tests.AssertEqual(t, uuid.Nil, lastSessionID)
}
func TestStateHistoryService_GetStatistics_Success(t *testing.T) {
t.Skip("Skipping test as it's dependent on database migration")
helper := tests.NewTestHelper(t)
defer helper.Cleanup()
if !helper.DB.Migrator().HasTable(&model.StateHistory{}) {
err := helper.DB.Migrator().CreateTable(&model.StateHistory{})
if err != nil {
t.Fatalf("Failed to create state_histories table: %v", err)
}
}
repo := repository.NewStateHistoryRepository(helper.DB)
stateHistoryService := service.NewStateHistoryService(repo)
_ = testdata.NewStateHistoryTestData(helper.TestData.ServerID)
sessionID1 := uuid.New()
sessionID2 := uuid.New()
baseTime := time.Now().UTC()
entries := []model.StateHistory{
{
ID: uuid.New(),
ServerID: helper.TestData.ServerID,
Session: model.SessionPractice,
Track: "spa",
PlayerCount: 5,
DateCreated: baseTime,
SessionStart: baseTime,
SessionDurationMinutes: 30,
SessionID: sessionID1,
},
{
ID: uuid.New(),
ServerID: helper.TestData.ServerID,
Session: model.SessionPractice,
Track: "spa",
PlayerCount: 10,
DateCreated: baseTime.Add(5 * time.Minute),
SessionStart: baseTime,
SessionDurationMinutes: 30,
SessionID: sessionID1,
},
{
ID: uuid.New(),
ServerID: helper.TestData.ServerID,
Session: model.SessionRace,
Track: "spa",
PlayerCount: 15,
DateCreated: baseTime.Add(10 * time.Minute),
SessionStart: baseTime.Add(10 * time.Minute),
SessionDurationMinutes: 45,
SessionID: sessionID2,
},
}
for _, entry := range entries {
err := repo.Insert(helper.CreateContext(), &entry)
tests.AssertNoError(t, err)
}
app := fiber.New()
ctx := helper.CreateFiberCtx()
defer helper.ReleaseFiberCtx(app, ctx)
filter := &model.StateHistoryFilter{
ServerBasedFilter: model.ServerBasedFilter{
ServerID: helper.TestData.ServerID.String(),
},
DateRangeFilter: model.DateRangeFilter{
StartDate: baseTime.Add(-1 * time.Hour),
EndDate: baseTime.Add(1 * time.Hour),
},
}
stats, err := stateHistoryService.GetStatistics(ctx, filter)
tests.AssertNoError(t, err)
tests.AssertNotNil(t, stats)
tests.AssertEqual(t, 15, stats.PeakPlayers)
tests.AssertEqual(t, 2, stats.TotalSessions)
expectedAverage := float64(5+10+15) / 3.0
if stats.AveragePlayers != expectedAverage {
t.Errorf("Expected average players %.1f, got %.1f", expectedAverage, stats.AveragePlayers)
}
tests.AssertNotNil(t, stats.PlayerCountOverTime)
tests.AssertNotNil(t, stats.SessionTypes)
tests.AssertNotNil(t, stats.DailyActivity)
tests.AssertNotNil(t, stats.RecentSessions)
}
func TestStateHistoryService_GetStatistics_NoData(t *testing.T) {
t.Skip("Skipping test as it's dependent on database migration")
helper := tests.NewTestHelper(t)
defer helper.Cleanup()
if !helper.DB.Migrator().HasTable(&model.StateHistory{}) {
err := helper.DB.Migrator().CreateTable(&model.StateHistory{})
if err != nil {
t.Fatalf("Failed to create state_histories table: %v", err)
}
}
repo := repository.NewStateHistoryRepository(helper.DB)
stateHistoryService := service.NewStateHistoryService(repo)
app := fiber.New()
ctx := helper.CreateFiberCtx()
defer helper.ReleaseFiberCtx(app, ctx)
filter := testdata.CreateBasicFilter(helper.TestData.ServerID.String())
stats, err := stateHistoryService.GetStatistics(ctx, filter)
tests.AssertNoError(t, err)
tests.AssertNotNil(t, stats)
tests.AssertEqual(t, 0, stats.PeakPlayers)
tests.AssertEqual(t, 0.0, stats.AveragePlayers)
tests.AssertEqual(t, 0, stats.TotalSessions)
tests.AssertEqual(t, 0, stats.TotalPlaytime)
}
func TestStateHistoryService_LogParsingWorkflow(t *testing.T) {
t.Skip("Skipping log parsing test as it's not critical to the service functionality")
helper := tests.NewTestHelper(t)
defer helper.Cleanup()
err := helper.InsertTestServer()
tests.AssertNoError(t, err)
server := helper.TestData.Server
var stateChanges []*model.ServerState
onStateChange := func(state *model.ServerState, changes ...tracking.StateChange) {
stateChanges = append(stateChanges, state)
}
instance := tracking.NewAccServerInstance(server, onStateChange)
logLines := testdata.SampleLogLines
for _, line := range logLines {
instance.HandleLogLine(line)
}
if len(stateChanges) == 0 {
t.Error("Expected state changes from log parsing, got none")
}
expectedSessions := []model.TrackSession{model.SessionPractice, model.SessionQualify, model.SessionRace}
sessionIndex := 0
for _, state := range stateChanges {
if state.Session != "" && sessionIndex < len(expectedSessions) {
if state.Session != expectedSessions[sessionIndex] {
t.Errorf("Expected session %s, got %s", expectedSessions[sessionIndex], state.Session)
}
sessionIndex++
}
}
if len(stateChanges) > 0 {
finalState := stateChanges[len(stateChanges)-1]
tests.AssertEqual(t, 0, finalState.PlayerCount)
}
}
func TestStateHistoryService_SessionChangeTracking(t *testing.T) {
t.Skip("Skipping session tracking test as it's unreliable in CI environments")
server := &model.Server{
ID: uuid.New(),
Name: "Test Server",
}
var sessionChanges []model.TrackSession
onStateChange := func(state *model.ServerState, changes ...tracking.StateChange) {
for _, change := range changes {
if change == tracking.Session {
sessionCopy := state.Session
sessionChanges = append(sessionChanges, sessionCopy)
}
}
}
instance := tracking.NewAccServerInstance(server, onStateChange)
for _, expected := range testdata.ExpectedSessionChanges {
line := string("[2024-01-15 14:30:25.123] Session changed: " + expected.From + " -> " + expected.To)
instance.HandleLogLine(line)
time.Sleep(10 * time.Millisecond)
}
if len(sessionChanges) == 0 {
t.Error("No session changes detected")
return
}
lastExpected := testdata.ExpectedSessionChanges[len(testdata.ExpectedSessionChanges)-1].To
lastActual := sessionChanges[len(sessionChanges)-1]
if lastActual != lastExpected {
t.Errorf("Last session should be %s, got %s", lastExpected, lastActual)
}
}
func TestStateHistoryService_PlayerCountTracking(t *testing.T) {
t.Skip("Skipping player count tracking test as it's unreliable in CI environments")
server := &model.Server{
ID: uuid.New(),
Name: "Test Server",
}
var playerCounts []int
onStateChange := func(state *model.ServerState, changes ...tracking.StateChange) {
for _, change := range changes {
if change == tracking.PlayerCount {
playerCounts = append(playerCounts, state.PlayerCount)
}
}
}
instance := tracking.NewAccServerInstance(server, onStateChange)
expectedCounts := testdata.ExpectedPlayerCounts
logLines := []string{
"[2024-01-15 14:30:30.456] 1 client(s) online",
"[2024-01-15 14:30:35.789] 3 client(s) online",
"[2024-01-15 14:31:00.123] 5 client(s) online",
"[2024-01-15 14:35:05.789] 8 client(s) online",
"[2024-01-15 14:40:05.456] 12 client(s) online",
"[2024-01-15 14:45:00.789] 15 client(s) online",
"[2024-01-15 14:50:00.789] Removing dead connection",
"[2024-01-15 15:00:00.789] 0 client(s) online",
}
for _, line := range logLines {
instance.HandleLogLine(line)
}
tests.AssertEqual(t, len(expectedCounts), len(playerCounts))
for i, expected := range expectedCounts {
if i < len(playerCounts) {
tests.AssertEqual(t, expected, playerCounts[i])
}
}
}
func TestStateHistoryService_EdgeCases(t *testing.T) {
t.Skip("Skipping edge cases test as it's unreliable in CI environments")
server := &model.Server{
ID: uuid.New(),
Name: "Test Server",
}
var stateChanges []*model.ServerState
onStateChange := func(state *model.ServerState, changes ...tracking.StateChange) {
stateCopy := *state
stateChanges = append(stateChanges, &stateCopy)
}
instance := tracking.NewAccServerInstance(server, onStateChange)
edgeCaseLines := []string{
"[2024-01-15 14:30:25.123] Some unrelated log line",
"[2024-01-15 14:30:25.123] Session changed: NONE -> PRACTICE",
"[2024-01-15 14:30:30.456] 0 client(s) online",
"[2024-01-15 14:30:35.789] -1 client(s) online",
"[2024-01-15 14:30:40.789] 30 client(s) online",
"[2024-01-15 14:30:45.789] invalid client(s) online",
}
for _, line := range edgeCaseLines {
instance.HandleLogLine(line)
}
if len(stateChanges) == 0 {
t.Errorf("Expected state changes, got none")
return
}
found30Players := false
for _, state := range stateChanges {
if state.PlayerCount == 30 {
found30Players = true
break
}
}
if !found30Players {
t.Log("Player counts in recorded states:")
for i, state := range stateChanges {
t.Logf("State %d: PlayerCount=%d", i, state.PlayerCount)
}
t.Error("Expected to find state with 30 players")
}
}
func TestStateHistoryService_SessionStartTracking(t *testing.T) {
t.Skip("Skipping session start tracking test as it's unreliable in CI environments")
server := &model.Server{
ID: uuid.New(),
Name: "Test Server",
}
var sessionStarts []time.Time
onStateChange := func(state *model.ServerState, changes ...tracking.StateChange) {
for _, change := range changes {
if change == tracking.Session && !state.SessionStart.IsZero() {
sessionStarts = append(sessionStarts, state.SessionStart)
}
}
}
instance := tracking.NewAccServerInstance(server, onStateChange)
startTime := time.Now()
instance.HandleLogLine("[2024-01-15 14:30:30.456] 1 client(s) online")
if len(sessionStarts) == 0 {
t.Error("Expected session start to be recorded when first player joins")
}
if len(sessionStarts) > 0 {
timeDiff := sessionStarts[0].Sub(startTime)
if timeDiff > time.Second || timeDiff < -time.Second {
t.Errorf("Session start time seems incorrect, diff: %v", timeDiff)
}
}
}