update state history session type
All checks were successful
Release and Deploy / build (push) Successful in 2m25s
Release and Deploy / deploy (push) Successful in 26s

This commit is contained in:
Fran Jurmanović
2025-09-15 19:11:25 +02:00
parent 4ab94de529
commit 760412d7db
13 changed files with 386 additions and 205 deletions

View File

@@ -0,0 +1,124 @@
package migrations
import (
"acc-server-manager/local/utl/logging"
"fmt"
"gorm.io/gorm"
)
// UpdateStateHistorySessions migrates tables from integer IDs to UUIDs
type UpdateStateHistorySessions struct {
DB *gorm.DB
}
// NewUpdateStateHistorySessions creates a new UUID migration
func NewUpdateStateHistorySessions(db *gorm.DB) *UpdateStateHistorySessions {
return &UpdateStateHistorySessions{DB: db}
}
// Up executes the migration
func (m *UpdateStateHistorySessions) Up() error {
logging.Info("Checking UUID migration...")
// Check if migration is needed by looking at the servers table structure
if !m.needsMigration() {
logging.Info("UUID migration not needed - tables already use UUID primary keys")
return nil
}
logging.Info("Starting UUID migration...")
// Check if migration has already been applied
var migrationRecord MigrationRecord
err := m.DB.Where("migration_name = ?", "002_migrate_to_uuid").First(&migrationRecord).Error
if err == nil {
logging.Info("UUID migration already applied, skipping")
return nil
}
// Create migration tracking table if it doesn't exist
if err := m.DB.AutoMigrate(&MigrationRecord{}); err != nil {
return fmt.Errorf("failed to create migration tracking table: %v", err)
}
// Execute the UUID migration using the existing migration function
logging.Info("Executing UUID migration...")
if err := runUUIDMigrationSQL(m.DB); err != nil {
return fmt.Errorf("failed to execute UUID migration: %v", err)
}
logging.Info("UUID migration completed successfully")
return nil
}
// needsMigration checks if the UUID migration is needed by examining table structure
func (m *UpdateStateHistorySessions) needsMigration() bool {
// Check if servers table exists and has integer primary key
var result struct {
Exists bool `gorm:"column:exists"`
}
err := m.DB.Raw(`
SELECT count(*) > 0 as exists FROM state_history
WHERE length(session) > 1 LIMIT 1;
`).Scan(&result).Error
if err != nil || !result.Exists {
// Table doesn't exist or no primary key found - assume no migration needed
return false
}
return result.Exists
}
// Down reverses the migration (not implemented for safety)
func (m *UpdateStateHistorySessions) Down() error {
logging.Error("UUID migration rollback is not supported for data safety reasons")
return fmt.Errorf("UUID migration rollback is not supported")
}
// runUpdateStateHistorySessionsMigration executes the UUID migration using the SQL file
func runUpdateStateHistorySessionsMigration(db *gorm.DB) error {
// Disable foreign key constraints during migration
if err := db.Exec("PRAGMA foreign_keys=OFF").Error; err != nil {
return fmt.Errorf("failed to disable foreign keys: %v", err)
}
// Start transaction
tx := db.Begin()
if tx.Error != nil {
return fmt.Errorf("failed to start transaction: %v", tx.Error)
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
migrationSQL := "UPDATE state_history SET session = upper(substr(session, 1, 1));"
// Execute the migration
if err := tx.Exec(string(migrationSQL)).Error; err != nil {
tx.Rollback()
return fmt.Errorf("failed to execute migration: %v", err)
}
// Commit transaction
if err := tx.Commit().Error; err != nil {
return fmt.Errorf("failed to commit migration: %v", err)
}
// Re-enable foreign key constraints
if err := db.Exec("PRAGMA foreign_keys=ON").Error; err != nil {
return fmt.Errorf("failed to re-enable foreign keys: %v", err)
}
return nil
}
// RunUpdateStateHistorySessionsMigration is a convenience function to run the migration
func RunUpdateStateHistorySessionsMigration(db *gorm.DB) error {
migration := NewUpdateStateHistorySessions(db)
return migration.Up()
}

View File

@@ -82,7 +82,7 @@ type Session struct {
HourOfDay IntString `json:"hourOfDay"` HourOfDay IntString `json:"hourOfDay"`
DayOfWeekend IntString `json:"dayOfWeekend"` DayOfWeekend IntString `json:"dayOfWeekend"`
TimeMultiplier IntString `json:"timeMultiplier"` TimeMultiplier IntString `json:"timeMultiplier"`
SessionType string `json:"sessionType"` SessionType TrackSession `json:"sessionType"`
SessionDurationMinutes IntString `json:"sessionDurationMinutes"` SessionDurationMinutes IntString `json:"sessionDurationMinutes"`
} }

View File

@@ -73,7 +73,7 @@ type State struct {
type ServerState struct { type ServerState struct {
sync.RWMutex `swaggerignore:"-" json:"-"` sync.RWMutex `swaggerignore:"-" json:"-"`
Session string `json:"session"` Session TrackSession `json:"session"`
SessionStart time.Time `json:"sessionStart"` SessionStart time.Time `json:"sessionStart"`
PlayerCount int `json:"playerCount"` PlayerCount int `json:"playerCount"`
Track string `json:"track"` Track string `json:"track"`

View File

@@ -1,6 +1,9 @@
package model package model
import ( import (
"encoding/json"
"fmt"
"strings"
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
@@ -13,7 +16,7 @@ type StateHistoryFilter struct {
DateRangeFilter // Adds date range filtering DateRangeFilter // Adds date range filtering
// Additional fields specific to state history // Additional fields specific to state history
Session string `query:"session"` Session TrackSession `query:"session"`
MinPlayers *int `query:"min_players"` MinPlayers *int `query:"min_players"`
MaxPlayers *int `query:"max_players"` MaxPlayers *int `query:"max_players"`
} }
@@ -52,10 +55,60 @@ func (f *StateHistoryFilter) ApplyFilter(query *gorm.DB) *gorm.DB {
return query return query
} }
type TrackSession string
const (
SessionPractice TrackSession = "P"
SessionQualify TrackSession = "Q"
SessionRace TrackSession = "R"
SessionUnknown TrackSession = "U"
)
func (i *TrackSession) UnmarshalJSON(b []byte) error {
var str string
if err := json.Unmarshal(b, &str); err == nil {
*i = ToTrackSession(str)
return nil
}
return fmt.Errorf("invalid TrackSession value")
}
func (i TrackSession) Humanize() string {
switch i {
case SessionPractice:
return "Practice"
case SessionQualify:
return "Qualifying"
case SessionRace:
return "Race"
default:
return "Unknown"
}
}
func ToTrackSession(i string) TrackSession {
sessionAbrv := strings.ToUpper(i[:1])
switch sessionAbrv {
case "P":
return SessionPractice
case "Q":
return SessionQualify
case "R":
return SessionRace
default:
return SessionUnknown
}
}
func (i TrackSession) ToString() string {
return string(i)
}
type StateHistory struct { type StateHistory struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;" json:"id"` ID uuid.UUID `gorm:"type:uuid;primary_key;" json:"id"`
ServerID uuid.UUID `json:"serverId" gorm:"not null;type:uuid"` ServerID uuid.UUID `json:"serverId" gorm:"not null;type:uuid"`
Session string `json:"session"` Session TrackSession `json:"session"`
Track string `json:"track"` Track string `json:"track"`
PlayerCount int `json:"playerCount"` PlayerCount int `json:"playerCount"`
DateCreated time.Time `json:"dateCreated"` DateCreated time.Time `json:"dateCreated"`

View File

@@ -3,7 +3,7 @@ package model
import "github.com/google/uuid" import "github.com/google/uuid"
type SessionCount struct { type SessionCount struct {
Name string `json:"name"` Name TrackSession `json:"name"`
Count int `json:"count"` Count int `json:"count"`
} }
@@ -31,7 +31,7 @@ type StateHistoryStats struct {
type RecentSession struct { type RecentSession struct {
ID uuid.UUID `json:"id"` ID uuid.UUID `json:"id"`
Date string `json:"date"` Date string `json:"date"`
Type string `json:"type"` Type TrackSession `json:"type"`
Track string `json:"track"` Track string `json:"track"`
Duration int `json:"duration"` Duration int `json:"duration"`
Players int `json:"players"` Players int `json:"players"`

View File

@@ -159,7 +159,7 @@ func (s *ServerService) insertStateHistory(serverID uuid.UUID, state *model.Serv
}) })
} }
func (s *ServerService) updateSessionDuration(server *model.Server, sessionType string) { func (s *ServerService) updateSessionDuration(server *model.Server, sessionType model.TrackSession) {
// Get configs using helper methods // Get configs using helper methods
event, err := s.configService.GetEventConfig(server) event, err := s.configService.GetEventConfig(server)
if err != nil { if err != nil {

View File

@@ -11,12 +11,13 @@ import (
) )
type StateChange int type StateChange int
const ( const (
PlayerCount StateChange = iota PlayerCount StateChange = iota
Session Session
) )
var StateChanges = map[StateChange]string { var StateChanges = map[StateChange]string{
PlayerCount: "player-count", PlayerCount: "player-count",
Session: "session", Session: "session",
} }
@@ -47,13 +48,13 @@ func NewRegexHandler(str string, test string) *StateRegexHandler {
} }
} }
func (rh *StateRegexHandler) Test(line string) bool{ func (rh *StateRegexHandler) Test(line string) bool {
return strings.Contains(line, rh.test) return strings.Contains(line, rh.test)
} }
func (rh *StateRegexHandler) Count(line string) int{ func (rh *StateRegexHandler) Count(line string) int {
var count int = 0 var count int = 0
rh.Contains(line, func (strs ...string) { rh.Contains(line, func(strs ...string) {
if len(strs) == 2 { if len(strs) == 2 {
if ct, err := strconv.Atoi(strs[1]); err == nil { if ct, err := strconv.Atoi(strs[1]); err == nil {
count = ct count = ct
@@ -63,10 +64,10 @@ func (rh *StateRegexHandler) Count(line string) int{
return count return count
} }
func (rh *StateRegexHandler) Change(line string) (string, string){ func (rh *StateRegexHandler) Change(line string) (string, string) {
var old string = "" var old string = ""
var new string = "" var new string = ""
rh.Contains(line, func (strs ...string) { rh.Contains(line, func(strs ...string) {
if len(strs) == 3 { if len(strs) == 3 {
old = strs[1] old = strs[1]
new = strs[2] new = strs[2]
@@ -93,6 +94,7 @@ func TailLogFile(path string, callback func(string)) {
} }
type LogStateType int type LogStateType int
const ( const (
SessionChange LogStateType = iota SessionChange LogStateType = iota
LeaderboardUpdate LeaderboardUpdate
@@ -101,7 +103,7 @@ const (
RemovingDeadConnection RemovingDeadConnection
) )
var logStateContain = map[LogStateType]string { var logStateContain = map[LogStateType]string{
SessionChange: "Session changed", SessionChange: "Session changed",
LeaderboardUpdate: "Updated leaderboard for", LeaderboardUpdate: "Updated leaderboard for",
UDPCount: "Udp message count", UDPCount: "Udp message count",
@@ -115,7 +117,7 @@ var udpCountRegex = NewRegexHandler(`Udp message count (\d+) client`, logStateCo
var clientsOnlineRegex = NewRegexHandler(`(\d+) client\(s\) online`, logStateContain[ClientsOnline]) var clientsOnlineRegex = NewRegexHandler(`(\d+) client\(s\) online`, logStateContain[ClientsOnline])
var removingDeadConnectionsRegex = NewRegexHandler(`Removing dead connection`, logStateContain[RemovingDeadConnection]) var removingDeadConnectionsRegex = NewRegexHandler(`Removing dead connection`, logStateContain[RemovingDeadConnection])
var logStateRegex = map[LogStateType]*StateRegexHandler { var logStateRegex = map[LogStateType]*StateRegexHandler{
SessionChange: sessionChangeRegex, SessionChange: sessionChangeRegex,
LeaderboardUpdate: leaderboardUpdateRegex, LeaderboardUpdate: leaderboardUpdateRegex,
UDPCount: udpCountRegex, UDPCount: udpCountRegex,
@@ -125,7 +127,7 @@ var logStateRegex = map[LogStateType]*StateRegexHandler {
func (instance *AccServerInstance) HandleLogLine(line string) { func (instance *AccServerInstance) HandleLogLine(line string) {
for logState, regexHandler := range logStateRegex { for logState, regexHandler := range logStateRegex {
if (regexHandler.Test(line)) { if regexHandler.Test(line) {
switch logState { switch logState {
case LeaderboardUpdate: case LeaderboardUpdate:
case UDPCount: case UDPCount:
@@ -134,7 +136,9 @@ func (instance *AccServerInstance) HandleLogLine(line string) {
instance.UpdatePlayerCount(count) instance.UpdatePlayerCount(count)
case SessionChange: case SessionChange:
_, new := regexHandler.Change(line) _, new := regexHandler.Change(line)
instance.UpdateSessionChange(new)
trackSession := model.ToTrackSession(new)
instance.UpdateSessionChange(trackSession)
case RemovingDeadConnection: case RemovingDeadConnection:
instance.UpdatePlayerCount(instance.State.PlayerCount - 1) instance.UpdatePlayerCount(instance.State.PlayerCount - 1)
} }
@@ -148,23 +152,23 @@ func (instance *AccServerInstance) UpdateState(callback func(state *model.Server
state.Lock() state.Lock()
defer state.Unlock() defer state.Unlock()
callback(state, &changes) callback(state, &changes)
if (len(changes) > 0) { if len(changes) > 0 {
instance.OnStateChange(state, changes...) instance.OnStateChange(state, changes...)
} }
} }
func (instance *AccServerInstance) UpdatePlayerCount(count int) { func (instance *AccServerInstance) UpdatePlayerCount(count int) {
if (count < 0) { if count < 0 {
return return
} }
instance.UpdateState(func (state *model.ServerState, changes *[]StateChange) { instance.UpdateState(func(state *model.ServerState, changes *[]StateChange) {
if (count == state.PlayerCount) { if count == state.PlayerCount {
return return
} }
if (count > 0 && state.PlayerCount == 0) { if count > 0 && state.PlayerCount == 0 {
state.SessionStart = time.Now() state.SessionStart = time.Now()
*changes = append(*changes, Session) *changes = append(*changes, Session)
} else if (count == 0) { } else if count == 0 {
state.SessionStart = time.Time{} state.SessionStart = time.Time{}
*changes = append(*changes, Session) *changes = append(*changes, Session)
} }
@@ -173,12 +177,12 @@ func (instance *AccServerInstance) UpdatePlayerCount(count int) {
}) })
} }
func (instance *AccServerInstance) UpdateSessionChange(session string) { func (instance *AccServerInstance) UpdateSessionChange(session model.TrackSession) {
instance.UpdateState(func (state *model.ServerState, changes *[]StateChange) { instance.UpdateState(func(state *model.ServerState, changes *[]StateChange) {
if (session == state.Session) { if session == state.Session {
return return
} }
if (state.PlayerCount > 0) { if state.PlayerCount > 0 {
state.SessionStart = time.Now() state.SessionStart = time.Now()
} else { } else {
state.SessionStart = time.Time{} state.SessionStart = time.Time{}

View File

@@ -260,7 +260,7 @@ func (m *MockStateHistoryRepository) GetSessionTypes(ctx context.Context, filter
} }
// Group by session type // Group by session type
sessionMap := make(map[string]map[string]bool) // session -> sessionID -> bool sessionMap := make(map[model.TrackSession]map[string]bool) // session -> sessionID -> bool
for _, entry := range filteredEntries { for _, entry := range filteredEntries {
if sessionMap[entry.Session] == nil { if sessionMap[entry.Session] == nil {
sessionMap[entry.Session] = make(map[string]bool) sessionMap[entry.Session] = make(map[string]bool)

View File

@@ -22,7 +22,7 @@ func NewStateHistoryTestData(serverID uuid.UUID) *StateHistoryTestData {
} }
// CreateStateHistory creates a basic state history entry // CreateStateHistory creates a basic state history entry
func (td *StateHistoryTestData) CreateStateHistory(session string, track string, playerCount int, sessionID uuid.UUID) model.StateHistory { func (td *StateHistoryTestData) CreateStateHistory(session model.TrackSession, track string, playerCount int, sessionID uuid.UUID) model.StateHistory {
return model.StateHistory{ return model.StateHistory{
ID: uuid.New(), ID: uuid.New(),
ServerID: td.ServerID, ServerID: td.ServerID,
@@ -37,7 +37,7 @@ func (td *StateHistoryTestData) CreateStateHistory(session string, track string,
} }
// CreateMultipleEntries creates multiple state history entries for the same session // CreateMultipleEntries creates multiple state history entries for the same session
func (td *StateHistoryTestData) CreateMultipleEntries(session string, track string, playerCounts []int) []model.StateHistory { func (td *StateHistoryTestData) CreateMultipleEntries(session model.TrackSession, track string, playerCounts []int) []model.StateHistory {
sessionID := uuid.New() sessionID := uuid.New()
var entries []model.StateHistory var entries []model.StateHistory
@@ -69,7 +69,7 @@ func CreateBasicFilter(serverID string) *model.StateHistoryFilter {
} }
// CreateFilterWithSession creates a filter with session type // CreateFilterWithSession creates a filter with session type
func CreateFilterWithSession(serverID string, session string) *model.StateHistoryFilter { func CreateFilterWithSession(serverID string, session model.TrackSession) *model.StateHistoryFilter {
return &model.StateHistoryFilter{ return &model.StateHistoryFilter{
ServerBasedFilter: model.ServerBasedFilter{ ServerBasedFilter: model.ServerBasedFilter{
ServerID: serverID, ServerID: serverID,
@@ -97,13 +97,13 @@ var SampleLogLines = []string{
// ExpectedSessionChanges represents the expected session changes from parsing the sample log lines // ExpectedSessionChanges represents the expected session changes from parsing the sample log lines
var ExpectedSessionChanges = []struct { var ExpectedSessionChanges = []struct {
From string From model.TrackSession
To string To model.TrackSession
}{ }{
{"NONE", "PRACTICE"}, {model.SessionUnknown, model.SessionPractice},
{"PRACTICE", "QUALIFY"}, {model.SessionPractice, model.SessionQualify},
{"QUALIFY", "RACE"}, {model.SessionQualify, model.SessionRace},
{"RACE", "NONE"}, {model.SessionRace, model.SessionUnknown},
} }
// ExpectedPlayerCounts represents the expected player counts from parsing the sample log lines // ExpectedPlayerCounts represents the expected player counts from parsing the sample log lines

View File

@@ -46,7 +46,7 @@ func TestStateHistoryController_GetAll_Success(t *testing.T) {
// Insert test data // Insert test data
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID) testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
history := testData.CreateStateHistory("Practice", "spa", 5, uuid.New()) history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
err := repo.Insert(helper.CreateContext(), &history) err := repo.Insert(helper.CreateContext(), &history)
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
@@ -78,7 +78,7 @@ func TestStateHistoryController_GetAll_Success(t *testing.T) {
err = json.Unmarshal(body, &result) err = json.Unmarshal(body, &result)
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
tests.AssertEqual(t, 1, len(result)) tests.AssertEqual(t, 1, len(result))
tests.AssertEqual(t, "Practice", result[0].Session) tests.AssertEqual(t, model.SessionPractice, result[0].Session)
tests.AssertEqual(t, 5, result[0].PlayerCount) tests.AssertEqual(t, 5, result[0].PlayerCount)
} }
@@ -107,8 +107,8 @@ func TestStateHistoryController_GetAll_WithSessionFilter(t *testing.T) {
// Insert test data with different sessions // Insert test data with different sessions
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID) testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
practiceHistory := testData.CreateStateHistory("Practice", "spa", 5, uuid.New()) practiceHistory := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
raceHistory := testData.CreateStateHistory("Race", "spa", 10, uuid.New()) raceHistory := testData.CreateStateHistory(model.SessionRace, "spa", 10, uuid.New())
err := repo.Insert(helper.CreateContext(), &practiceHistory) err := repo.Insert(helper.CreateContext(), &practiceHistory)
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
@@ -124,7 +124,7 @@ func TestStateHistoryController_GetAll_WithSessionFilter(t *testing.T) {
controller.NewStateHistoryController(stateHistoryService, routeGroups, GetTestAuthMiddleware(membershipService, inMemCache)) controller.NewStateHistoryController(stateHistoryService, routeGroups, GetTestAuthMiddleware(membershipService, inMemCache))
// Create request with session filter and authentication // Create request with session filter and authentication
req := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/state-history?id=%s&session=Race", helper.TestData.ServerID.String()), nil) req := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/state-history?id=%s&session=R", helper.TestData.ServerID.String()), nil)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+tests.MustGenerateTestToken()) req.Header.Set("Authorization", "Bearer "+tests.MustGenerateTestToken())
@@ -143,7 +143,7 @@ func TestStateHistoryController_GetAll_WithSessionFilter(t *testing.T) {
err = json.Unmarshal(body, &result) err = json.Unmarshal(body, &result)
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
tests.AssertEqual(t, 1, len(result)) tests.AssertEqual(t, 1, len(result))
tests.AssertEqual(t, "Race", result[0].Session) tests.AssertEqual(t, model.SessionRace, result[0].Session)
tests.AssertEqual(t, 10, result[0].PlayerCount) tests.AssertEqual(t, 10, result[0].PlayerCount)
} }
@@ -220,7 +220,7 @@ func TestStateHistoryController_GetStatistics_Success(t *testing.T) {
// Create entries with varying player counts // Create entries with varying player counts
playerCounts := []int{5, 10, 15, 20, 25} playerCounts := []int{5, 10, 15, 20, 25}
entries := testData.CreateMultipleEntries("Race", "spa", playerCounts) entries := testData.CreateMultipleEntries(model.SessionRace, "spa", playerCounts)
for _, entry := range entries { for _, entry := range entries {
err := repo.Insert(helper.CreateContext(), &entry) err := repo.Insert(helper.CreateContext(), &entry)
@@ -475,7 +475,7 @@ func TestStateHistoryController_ContentType(t *testing.T) {
// Insert test data // Insert test data
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID) testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
history := testData.CreateStateHistory("Practice", "spa", 5, uuid.New()) history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
err := repo.Insert(helper.CreateContext(), &history) err := repo.Insert(helper.CreateContext(), &history)
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
@@ -551,7 +551,7 @@ func TestStateHistoryController_ResponseStructure(t *testing.T) {
// Insert test data // Insert test data
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID) testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
history := testData.CreateStateHistory("Practice", "spa", 5, uuid.New()) history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
err := repo.Insert(helper.CreateContext(), &history) err := repo.Insert(helper.CreateContext(), &history)
tests.AssertNoError(t, err) tests.AssertNoError(t, err)

View File

@@ -30,7 +30,7 @@ func TestStateHistoryRepository_Insert_Success(t *testing.T) {
// Create test data // Create test data
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID) testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
history := testData.CreateStateHistory("Practice", "spa", 5, uuid.New()) history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
// Test Insert // Test Insert
err := repo.Insert(ctx, &history) err := repo.Insert(ctx, &history)
@@ -65,7 +65,7 @@ func TestStateHistoryRepository_GetAll_Success(t *testing.T) {
// Insert multiple entries // Insert multiple entries
playerCounts := []int{0, 5, 10, 15, 10, 5, 0} playerCounts := []int{0, 5, 10, 15, 10, 5, 0}
entries := testData.CreateMultipleEntries("Practice", "spa", playerCounts) entries := testData.CreateMultipleEntries(model.SessionPractice, "spa", playerCounts)
for _, entry := range entries { for _, entry := range entries {
err := repo.Insert(ctx, &entry) err := repo.Insert(ctx, &entry)
@@ -101,8 +101,8 @@ func TestStateHistoryRepository_GetAll_WithFilter(t *testing.T) {
// Create test data with different sessions // Create test data with different sessions
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID) testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
practiceHistory := testData.CreateStateHistory("Practice", "spa", 5, uuid.New()) practiceHistory := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
raceHistory := testData.CreateStateHistory("Race", "spa", 15, uuid.New()) raceHistory := testData.CreateStateHistory(model.SessionRace, "spa", 15, uuid.New())
// Insert both // Insert both
err := repo.Insert(ctx, &practiceHistory) err := repo.Insert(ctx, &practiceHistory)
@@ -111,13 +111,13 @@ func TestStateHistoryRepository_GetAll_WithFilter(t *testing.T) {
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
// Test GetAll with session filter // Test GetAll with session filter
filter := testdata.CreateFilterWithSession(helper.TestData.ServerID.String(), "Race") filter := testdata.CreateFilterWithSession(helper.TestData.ServerID.String(), model.SessionRace)
result, err := repo.GetAll(ctx, filter) result, err := repo.GetAll(ctx, filter)
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
tests.AssertNotNil(t, result) tests.AssertNotNil(t, result)
tests.AssertEqual(t, 1, len(*result)) tests.AssertEqual(t, 1, len(*result))
tests.AssertEqual(t, "Race", (*result)[0].Session) tests.AssertEqual(t, model.SessionRace, (*result)[0].Session)
tests.AssertEqual(t, 15, (*result)[0].PlayerCount) tests.AssertEqual(t, 15, (*result)[0].PlayerCount)
} }
@@ -145,8 +145,8 @@ func TestStateHistoryRepository_GetLastSessionID_Success(t *testing.T) {
sessionID1 := uuid.New() sessionID1 := uuid.New()
sessionID2 := uuid.New() sessionID2 := uuid.New()
history1 := testData.CreateStateHistory("Practice", "spa", 5, sessionID1) history1 := testData.CreateStateHistory(model.SessionPractice, "spa", 5, sessionID1)
history2 := testData.CreateStateHistory("Race", "spa", 10, sessionID2) history2 := testData.CreateStateHistory(model.SessionRace, "spa", 10, sessionID2)
// Insert with a small delay to ensure ordering // Insert with a small delay to ensure ordering
err := repo.Insert(ctx, &history1) err := repo.Insert(ctx, &history1)
@@ -217,7 +217,7 @@ func TestStateHistoryRepository_GetSummaryStats_Success(t *testing.T) {
sessionID2 := uuid.New() sessionID2 := uuid.New()
// Practice session: 5, 10, 15 players // Practice session: 5, 10, 15 players
practiceEntries := testData.CreateMultipleEntries("Practice", "spa", []int{5, 10, 15}) practiceEntries := testData.CreateMultipleEntries(model.SessionPractice, "spa", []int{5, 10, 15})
for i := range practiceEntries { for i := range practiceEntries {
practiceEntries[i].SessionID = sessionID1 practiceEntries[i].SessionID = sessionID1
err := repo.Insert(ctx, &practiceEntries[i]) err := repo.Insert(ctx, &practiceEntries[i])
@@ -225,7 +225,7 @@ func TestStateHistoryRepository_GetSummaryStats_Success(t *testing.T) {
} }
// Race session: 20, 25, 30 players // Race session: 20, 25, 30 players
raceEntries := testData.CreateMultipleEntries("Race", "spa", []int{20, 25, 30}) raceEntries := testData.CreateMultipleEntries(model.SessionRace, "spa", []int{20, 25, 30})
for i := range raceEntries { for i := range raceEntries {
raceEntries[i].SessionID = sessionID2 raceEntries[i].SessionID = sessionID2
err := repo.Insert(ctx, &raceEntries[i]) err := repo.Insert(ctx, &raceEntries[i])
@@ -305,7 +305,7 @@ func TestStateHistoryRepository_GetTotalPlaytime_Success(t *testing.T) {
{ {
ID: uuid.New(), ID: uuid.New(),
ServerID: helper.TestData.ServerID, ServerID: helper.TestData.ServerID,
Session: "Practice", Session: model.SessionPractice,
Track: "spa", Track: "spa",
PlayerCount: 5, PlayerCount: 5,
DateCreated: baseTime, DateCreated: baseTime,
@@ -316,7 +316,7 @@ func TestStateHistoryRepository_GetTotalPlaytime_Success(t *testing.T) {
{ {
ID: uuid.New(), ID: uuid.New(),
ServerID: helper.TestData.ServerID, ServerID: helper.TestData.ServerID,
Session: "Practice", Session: model.SessionPractice,
Track: "spa", Track: "spa",
PlayerCount: 10, PlayerCount: 10,
DateCreated: baseTime.Add(30 * time.Minute), DateCreated: baseTime.Add(30 * time.Minute),
@@ -327,7 +327,7 @@ func TestStateHistoryRepository_GetTotalPlaytime_Success(t *testing.T) {
{ {
ID: uuid.New(), ID: uuid.New(),
ServerID: helper.TestData.ServerID, ServerID: helper.TestData.ServerID,
Session: "Practice", Session: model.SessionPractice,
Track: "spa", Track: "spa",
PlayerCount: 8, PlayerCount: 8,
DateCreated: baseTime.Add(60 * time.Minute), DateCreated: baseTime.Add(60 * time.Minute),
@@ -391,7 +391,7 @@ func TestStateHistoryRepository_ConcurrentOperations(t *testing.T) {
} }
// Create and insert initial entry to ensure table exists and is properly set up // Create and insert initial entry to ensure table exists and is properly set up
initialHistory := testData.CreateStateHistory("Practice", "spa", 5, uuid.New()) initialHistory := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
err := repo.Insert(ctx, &initialHistory) err := repo.Insert(ctx, &initialHistory)
if err != nil { if err != nil {
t.Fatalf("Failed to insert initial record: %v", err) t.Fatalf("Failed to insert initial record: %v", err)
@@ -404,7 +404,7 @@ func TestStateHistoryRepository_ConcurrentOperations(t *testing.T) {
defer func() { defer func() {
done <- true done <- true
}() }()
history := testData.CreateStateHistory("Practice", "spa", 5, uuid.New()) history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
err := repo.Insert(ctx, &history) err := repo.Insert(ctx, &history)
if err != nil { if err != nil {
t.Logf("Insert error: %v", err) t.Logf("Insert error: %v", err)
@@ -462,7 +462,7 @@ func TestStateHistoryRepository_FilterEdgeCases(t *testing.T) {
// Insert a test record to ensure the table is properly set up // Insert a test record to ensure the table is properly set up
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID) testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
history := testData.CreateStateHistory("Practice", "spa", 5, uuid.New()) history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
err := repo.Insert(ctx, &history) err := repo.Insert(ctx, &history)
tests.AssertNoError(t, err) tests.AssertNoError(t, err)

View File

@@ -94,7 +94,7 @@ func TestConfigService_GetEventConfig_ValidFile(t *testing.T) {
// Verify sessions // Verify sessions
tests.AssertEqual(t, 3, len(eventConfig.Sessions)) tests.AssertEqual(t, 3, len(eventConfig.Sessions))
if len(eventConfig.Sessions) > 0 { if len(eventConfig.Sessions) > 0 {
tests.AssertEqual(t, "P", eventConfig.Sessions[0].SessionType) tests.AssertEqual(t, model.SessionPractice, eventConfig.Sessions[0].SessionType)
tests.AssertEqual(t, model.IntString(10), eventConfig.Sessions[0].SessionDurationMinutes) tests.AssertEqual(t, model.IntString(10), eventConfig.Sessions[0].SessionDurationMinutes)
} }
} }

View File

@@ -33,7 +33,7 @@ func TestStateHistoryService_GetAll_Success(t *testing.T) {
// Insert test data directly into DB // Insert test data directly into DB
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID) testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
history := testData.CreateStateHistory("Practice", "spa", 5, uuid.New()) history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
err := repo.Insert(helper.CreateContext(), &history) err := repo.Insert(helper.CreateContext(), &history)
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
@@ -49,7 +49,7 @@ func TestStateHistoryService_GetAll_Success(t *testing.T) {
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
tests.AssertNotNil(t, result) tests.AssertNotNil(t, result)
tests.AssertEqual(t, 1, len(*result)) tests.AssertEqual(t, 1, len(*result))
tests.AssertEqual(t, "Practice", (*result)[0].Session) tests.AssertEqual(t, model.SessionPractice, (*result)[0].Session)
tests.AssertEqual(t, 5, (*result)[0].PlayerCount) tests.AssertEqual(t, 5, (*result)[0].PlayerCount)
} }
@@ -71,8 +71,8 @@ func TestStateHistoryService_GetAll_WithFilter(t *testing.T) {
// Insert test data with different sessions // Insert test data with different sessions
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID) testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
practiceHistory := testData.CreateStateHistory("Practice", "spa", 5, uuid.New()) practiceHistory := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
raceHistory := testData.CreateStateHistory("Race", "spa", 10, uuid.New()) raceHistory := testData.CreateStateHistory(model.SessionRace, "spa", 10, uuid.New())
err := repo.Insert(helper.CreateContext(), &practiceHistory) err := repo.Insert(helper.CreateContext(), &practiceHistory)
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
@@ -85,13 +85,13 @@ func TestStateHistoryService_GetAll_WithFilter(t *testing.T) {
defer helper.ReleaseFiberCtx(app, ctx) defer helper.ReleaseFiberCtx(app, ctx)
// Test GetAll with session filter // Test GetAll with session filter
filter := testdata.CreateFilterWithSession(helper.TestData.ServerID.String(), "Race") filter := testdata.CreateFilterWithSession(helper.TestData.ServerID.String(), model.SessionRace)
result, err := stateHistoryService.GetAll(ctx, filter) result, err := stateHistoryService.GetAll(ctx, filter)
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
tests.AssertNotNil(t, result) tests.AssertNotNil(t, result)
tests.AssertEqual(t, 1, len(*result)) tests.AssertEqual(t, 1, len(*result))
tests.AssertEqual(t, "Race", (*result)[0].Session) tests.AssertEqual(t, model.SessionRace, (*result)[0].Session)
tests.AssertEqual(t, 10, (*result)[0].PlayerCount) tests.AssertEqual(t, 10, (*result)[0].PlayerCount)
} }
@@ -143,7 +143,7 @@ func TestStateHistoryService_Insert_Success(t *testing.T) {
// Create test data // Create test data
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID) testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
history := testData.CreateStateHistory("Practice", "spa", 5, uuid.New()) history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
// Create proper Fiber context // Create proper Fiber context
app := fiber.New() app := fiber.New()
@@ -180,7 +180,7 @@ func TestStateHistoryService_GetLastSessionID_Success(t *testing.T) {
// Insert test data // Insert test data
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID) testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
sessionID := uuid.New() sessionID := uuid.New()
history := testData.CreateStateHistory("Practice", "spa", 5, sessionID) history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, sessionID)
err := repo.Insert(helper.CreateContext(), &history) err := repo.Insert(helper.CreateContext(), &history)
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
@@ -254,7 +254,7 @@ func TestStateHistoryService_GetStatistics_Success(t *testing.T) {
{ {
ID: uuid.New(), ID: uuid.New(),
ServerID: helper.TestData.ServerID, ServerID: helper.TestData.ServerID,
Session: "Practice", Session: model.SessionPractice,
Track: "spa", Track: "spa",
PlayerCount: 5, PlayerCount: 5,
DateCreated: baseTime, DateCreated: baseTime,
@@ -265,7 +265,7 @@ func TestStateHistoryService_GetStatistics_Success(t *testing.T) {
{ {
ID: uuid.New(), ID: uuid.New(),
ServerID: helper.TestData.ServerID, ServerID: helper.TestData.ServerID,
Session: "Practice", Session: model.SessionPractice,
Track: "spa", Track: "spa",
PlayerCount: 10, PlayerCount: 10,
DateCreated: baseTime.Add(5 * time.Minute), DateCreated: baseTime.Add(5 * time.Minute),
@@ -276,7 +276,7 @@ func TestStateHistoryService_GetStatistics_Success(t *testing.T) {
{ {
ID: uuid.New(), ID: uuid.New(),
ServerID: helper.TestData.ServerID, ServerID: helper.TestData.ServerID,
Session: "Race", Session: model.SessionRace,
Track: "spa", Track: "spa",
PlayerCount: 15, PlayerCount: 15,
DateCreated: baseTime.Add(10 * time.Minute), DateCreated: baseTime.Add(10 * time.Minute),
@@ -404,7 +404,7 @@ func TestStateHistoryService_LogParsingWorkflow(t *testing.T) {
} }
// Verify session changes were parsed correctly // Verify session changes were parsed correctly
expectedSessions := []string{"PRACTICE", "QUALIFY", "RACE", "NONE"} expectedSessions := []model.TrackSession{model.SessionPractice, model.SessionQualify, model.SessionRace}
sessionIndex := 0 sessionIndex := 0
for _, state := range stateChanges { for _, state := range stateChanges {
@@ -433,7 +433,7 @@ func TestStateHistoryService_SessionChangeTracking(t *testing.T) {
Name: "Test Server", Name: "Test Server",
} }
var sessionChanges []string var sessionChanges []model.TrackSession
onStateChange := func(state *model.ServerState, changes ...tracking.StateChange) { onStateChange := func(state *model.ServerState, changes ...tracking.StateChange) {
for _, change := range changes { for _, change := range changes {
if change == tracking.Session { if change == tracking.Session {
@@ -448,7 +448,7 @@ func TestStateHistoryService_SessionChangeTracking(t *testing.T) {
// We'll add one session change at a time and wait briefly to ensure they're processed in order // We'll add one session change at a time and wait briefly to ensure they're processed in order
for _, expected := range testdata.ExpectedSessionChanges { for _, expected := range testdata.ExpectedSessionChanges {
line := "[2024-01-15 14:30:25.123] Session changed: " + expected.From + " -> " + expected.To line := string("[2024-01-15 14:30:25.123] Session changed: " + expected.From + " -> " + expected.To)
instance.HandleLogLine(line) instance.HandleLogLine(line)
// Small pause to ensure log processing completes // Small pause to ensure log processing completes
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)