update state history session type
This commit is contained in:
124
local/migrations/003_update_state_history_sessions.go
Normal file
124
local/migrations/003_update_state_history_sessions.go
Normal 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()
|
||||
}
|
||||
@@ -79,11 +79,11 @@ type EventConfig struct {
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
HourOfDay IntString `json:"hourOfDay"`
|
||||
DayOfWeekend IntString `json:"dayOfWeekend"`
|
||||
TimeMultiplier IntString `json:"timeMultiplier"`
|
||||
SessionType string `json:"sessionType"`
|
||||
SessionDurationMinutes IntString `json:"sessionDurationMinutes"`
|
||||
HourOfDay IntString `json:"hourOfDay"`
|
||||
DayOfWeekend IntString `json:"dayOfWeekend"`
|
||||
TimeMultiplier IntString `json:"timeMultiplier"`
|
||||
SessionType TrackSession `json:"sessionType"`
|
||||
SessionDurationMinutes IntString `json:"sessionDurationMinutes"`
|
||||
}
|
||||
|
||||
type AssistRules struct {
|
||||
|
||||
@@ -73,12 +73,12 @@ type State struct {
|
||||
|
||||
type ServerState struct {
|
||||
sync.RWMutex `swaggerignore:"-" json:"-"`
|
||||
Session string `json:"session"`
|
||||
SessionStart time.Time `json:"sessionStart"`
|
||||
PlayerCount int `json:"playerCount"`
|
||||
Track string `json:"track"`
|
||||
MaxConnections int `json:"maxConnections"`
|
||||
SessionDurationMinutes int `json:"sessionDurationMinutes"`
|
||||
Session TrackSession `json:"session"`
|
||||
SessionStart time.Time `json:"sessionStart"`
|
||||
PlayerCount int `json:"playerCount"`
|
||||
Track string `json:"track"`
|
||||
MaxConnections int `json:"maxConnections"`
|
||||
SessionDurationMinutes int `json:"sessionDurationMinutes"`
|
||||
// Players map[int]*PlayerState
|
||||
// etc.
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -13,9 +16,9 @@ type StateHistoryFilter struct {
|
||||
DateRangeFilter // Adds date range filtering
|
||||
|
||||
// Additional fields specific to state history
|
||||
Session string `query:"session"`
|
||||
MinPlayers *int `query:"min_players"`
|
||||
MaxPlayers *int `query:"max_players"`
|
||||
Session TrackSession `query:"session"`
|
||||
MinPlayers *int `query:"min_players"`
|
||||
MaxPlayers *int `query:"max_players"`
|
||||
}
|
||||
|
||||
// ApplyFilter implements the Filterable interface
|
||||
@@ -52,16 +55,66 @@ func (f *StateHistoryFilter) ApplyFilter(query *gorm.DB) *gorm.DB {
|
||||
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 {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;" json:"id"`
|
||||
ServerID uuid.UUID `json:"serverId" gorm:"not null;type:uuid"`
|
||||
Session string `json:"session"`
|
||||
Track string `json:"track"`
|
||||
PlayerCount int `json:"playerCount"`
|
||||
DateCreated time.Time `json:"dateCreated"`
|
||||
SessionStart time.Time `json:"sessionStart"`
|
||||
SessionDurationMinutes int `json:"sessionDurationMinutes"`
|
||||
SessionID uuid.UUID `json:"sessionId" gorm:"not null;type:uuid"` // Unique identifier for each session/event
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;" json:"id"`
|
||||
ServerID uuid.UUID `json:"serverId" gorm:"not null;type:uuid"`
|
||||
Session TrackSession `json:"session"`
|
||||
Track string `json:"track"`
|
||||
PlayerCount int `json:"playerCount"`
|
||||
DateCreated time.Time `json:"dateCreated"`
|
||||
SessionStart time.Time `json:"sessionStart"`
|
||||
SessionDurationMinutes int `json:"sessionDurationMinutes"`
|
||||
SessionID uuid.UUID `json:"sessionId" gorm:"not null;type:uuid"` // Unique identifier for each session/event
|
||||
}
|
||||
|
||||
// BeforeCreate is a GORM hook that runs before creating new state history entries
|
||||
|
||||
@@ -3,8 +3,8 @@ package model
|
||||
import "github.com/google/uuid"
|
||||
|
||||
type SessionCount struct {
|
||||
Name string `json:"name"`
|
||||
Count int `json:"count"`
|
||||
Name TrackSession `json:"name"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
type DailyActivity struct {
|
||||
@@ -29,10 +29,10 @@ type StateHistoryStats struct {
|
||||
}
|
||||
|
||||
type RecentSession struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Date string `json:"date"`
|
||||
Type string `json:"type"`
|
||||
Track string `json:"track"`
|
||||
Duration int `json:"duration"`
|
||||
Players int `json:"players"`
|
||||
ID uuid.UUID `json:"id"`
|
||||
Date string `json:"date"`
|
||||
Type TrackSession `json:"type"`
|
||||
Track string `json:"track"`
|
||||
Duration int `json:"duration"`
|
||||
Players int `json:"players"`
|
||||
}
|
||||
|
||||
@@ -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
|
||||
event, err := s.configService.GetEventConfig(server)
|
||||
if err != nil {
|
||||
|
||||
@@ -11,102 +11,104 @@ import (
|
||||
)
|
||||
|
||||
type StateChange int
|
||||
|
||||
const (
|
||||
PlayerCount StateChange = iota
|
||||
Session
|
||||
PlayerCount StateChange = iota
|
||||
Session
|
||||
)
|
||||
|
||||
var StateChanges = map[StateChange]string {
|
||||
PlayerCount: "player-count",
|
||||
Session: "session",
|
||||
var StateChanges = map[StateChange]string{
|
||||
PlayerCount: "player-count",
|
||||
Session: "session",
|
||||
}
|
||||
|
||||
type AccServerInstance struct {
|
||||
Model *model.Server
|
||||
State *model.ServerState
|
||||
OnStateChange func(*model.ServerState, ...StateChange)
|
||||
Model *model.Server
|
||||
State *model.ServerState
|
||||
OnStateChange func(*model.ServerState, ...StateChange)
|
||||
}
|
||||
|
||||
func NewAccServerInstance(server *model.Server, onStateChange func(*model.ServerState, ...StateChange)) *AccServerInstance {
|
||||
return &AccServerInstance{
|
||||
Model: server,
|
||||
State: &model.ServerState{PlayerCount: 0},
|
||||
OnStateChange: onStateChange,
|
||||
}
|
||||
return &AccServerInstance{
|
||||
Model: server,
|
||||
State: &model.ServerState{PlayerCount: 0},
|
||||
OnStateChange: onStateChange,
|
||||
}
|
||||
}
|
||||
|
||||
type StateRegexHandler struct {
|
||||
*regex_handler.RegexHandler
|
||||
test string
|
||||
*regex_handler.RegexHandler
|
||||
test string
|
||||
}
|
||||
|
||||
func NewRegexHandler(str string, test string) *StateRegexHandler {
|
||||
return &StateRegexHandler{
|
||||
RegexHandler: regex_handler.New(str),
|
||||
test: test,
|
||||
RegexHandler: regex_handler.New(str),
|
||||
test: test,
|
||||
}
|
||||
}
|
||||
|
||||
func (rh *StateRegexHandler) Test(line string) bool{
|
||||
return strings.Contains(line, rh.test)
|
||||
func (rh *StateRegexHandler) Test(line string) bool {
|
||||
return strings.Contains(line, rh.test)
|
||||
}
|
||||
|
||||
func (rh *StateRegexHandler) Count(line string) int{
|
||||
var count int = 0
|
||||
rh.Contains(line, func (strs ...string) {
|
||||
if len(strs) == 2 {
|
||||
if ct, err := strconv.Atoi(strs[1]); err == nil {
|
||||
count = ct
|
||||
}
|
||||
}
|
||||
})
|
||||
return count
|
||||
func (rh *StateRegexHandler) Count(line string) int {
|
||||
var count int = 0
|
||||
rh.Contains(line, func(strs ...string) {
|
||||
if len(strs) == 2 {
|
||||
if ct, err := strconv.Atoi(strs[1]); err == nil {
|
||||
count = ct
|
||||
}
|
||||
}
|
||||
})
|
||||
return count
|
||||
}
|
||||
|
||||
func (rh *StateRegexHandler) Change(line string) (string, string){
|
||||
var old string = ""
|
||||
var new string = ""
|
||||
rh.Contains(line, func (strs ...string) {
|
||||
if len(strs) == 3 {
|
||||
old = strs[1]
|
||||
new = strs[2]
|
||||
}
|
||||
})
|
||||
return old, new
|
||||
func (rh *StateRegexHandler) Change(line string) (string, string) {
|
||||
var old string = ""
|
||||
var new string = ""
|
||||
rh.Contains(line, func(strs ...string) {
|
||||
if len(strs) == 3 {
|
||||
old = strs[1]
|
||||
new = strs[2]
|
||||
}
|
||||
})
|
||||
return old, new
|
||||
}
|
||||
|
||||
func TailLogFile(path string, callback func(string)) {
|
||||
file, _ := os.Open(path)
|
||||
defer file.Close()
|
||||
file, _ := os.Open(path)
|
||||
defer file.Close()
|
||||
|
||||
file.Seek(0, os.SEEK_END) // Start at end of file
|
||||
reader := bufio.NewReader(file)
|
||||
file.Seek(0, os.SEEK_END) // Start at end of file
|
||||
reader := bufio.NewReader(file)
|
||||
|
||||
for {
|
||||
line, err := reader.ReadString('\n')
|
||||
if err == nil {
|
||||
callback(line)
|
||||
} else {
|
||||
time.Sleep(500 * time.Millisecond) // wait for new data
|
||||
}
|
||||
}
|
||||
for {
|
||||
line, err := reader.ReadString('\n')
|
||||
if err == nil {
|
||||
callback(line)
|
||||
} else {
|
||||
time.Sleep(500 * time.Millisecond) // wait for new data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type LogStateType int
|
||||
|
||||
const (
|
||||
SessionChange LogStateType = iota
|
||||
LeaderboardUpdate
|
||||
UDPCount
|
||||
ClientsOnline
|
||||
RemovingDeadConnection
|
||||
SessionChange LogStateType = iota
|
||||
LeaderboardUpdate
|
||||
UDPCount
|
||||
ClientsOnline
|
||||
RemovingDeadConnection
|
||||
)
|
||||
|
||||
var logStateContain = map[LogStateType]string {
|
||||
SessionChange: "Session changed",
|
||||
LeaderboardUpdate: "Updated leaderboard for",
|
||||
UDPCount: "Udp message count",
|
||||
ClientsOnline: "client(s) online",
|
||||
RemovingDeadConnection: "Removing dead connection",
|
||||
var logStateContain = map[LogStateType]string{
|
||||
SessionChange: "Session changed",
|
||||
LeaderboardUpdate: "Updated leaderboard for",
|
||||
UDPCount: "Udp message count",
|
||||
ClientsOnline: "client(s) online",
|
||||
RemovingDeadConnection: "Removing dead connection",
|
||||
}
|
||||
|
||||
var sessionChangeRegex = NewRegexHandler(`Session changed: (\w+) -> (\w+)`, logStateContain[SessionChange])
|
||||
@@ -115,75 +117,77 @@ var udpCountRegex = NewRegexHandler(`Udp message count (\d+) client`, logStateCo
|
||||
var clientsOnlineRegex = NewRegexHandler(`(\d+) client\(s\) online`, logStateContain[ClientsOnline])
|
||||
var removingDeadConnectionsRegex = NewRegexHandler(`Removing dead connection`, logStateContain[RemovingDeadConnection])
|
||||
|
||||
var logStateRegex = map[LogStateType]*StateRegexHandler {
|
||||
SessionChange: sessionChangeRegex,
|
||||
LeaderboardUpdate: leaderboardUpdateRegex,
|
||||
UDPCount: udpCountRegex,
|
||||
ClientsOnline: clientsOnlineRegex,
|
||||
RemovingDeadConnection: removingDeadConnectionsRegex,
|
||||
var logStateRegex = map[LogStateType]*StateRegexHandler{
|
||||
SessionChange: sessionChangeRegex,
|
||||
LeaderboardUpdate: leaderboardUpdateRegex,
|
||||
UDPCount: udpCountRegex,
|
||||
ClientsOnline: clientsOnlineRegex,
|
||||
RemovingDeadConnection: removingDeadConnectionsRegex,
|
||||
}
|
||||
|
||||
func (instance *AccServerInstance) HandleLogLine(line string) {
|
||||
for logState, regexHandler := range logStateRegex {
|
||||
if (regexHandler.Test(line)) {
|
||||
switch logState {
|
||||
case LeaderboardUpdate:
|
||||
case UDPCount:
|
||||
case ClientsOnline:
|
||||
count := regexHandler.Count(line)
|
||||
instance.UpdatePlayerCount(count)
|
||||
case SessionChange:
|
||||
_, new := regexHandler.Change(line)
|
||||
instance.UpdateSessionChange(new)
|
||||
case RemovingDeadConnection:
|
||||
instance.UpdatePlayerCount(instance.State.PlayerCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
for logState, regexHandler := range logStateRegex {
|
||||
if regexHandler.Test(line) {
|
||||
switch logState {
|
||||
case LeaderboardUpdate:
|
||||
case UDPCount:
|
||||
case ClientsOnline:
|
||||
count := regexHandler.Count(line)
|
||||
instance.UpdatePlayerCount(count)
|
||||
case SessionChange:
|
||||
_, new := regexHandler.Change(line)
|
||||
|
||||
trackSession := model.ToTrackSession(new)
|
||||
instance.UpdateSessionChange(trackSession)
|
||||
case RemovingDeadConnection:
|
||||
instance.UpdatePlayerCount(instance.State.PlayerCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (instance *AccServerInstance) UpdateState(callback func(state *model.ServerState, changes *[]StateChange)) {
|
||||
state := instance.State
|
||||
changes := []StateChange{}
|
||||
state.Lock()
|
||||
defer state.Unlock()
|
||||
callback(state, &changes)
|
||||
if (len(changes) > 0) {
|
||||
instance.OnStateChange(state, changes...)
|
||||
}
|
||||
state := instance.State
|
||||
changes := []StateChange{}
|
||||
state.Lock()
|
||||
defer state.Unlock()
|
||||
callback(state, &changes)
|
||||
if len(changes) > 0 {
|
||||
instance.OnStateChange(state, changes...)
|
||||
}
|
||||
}
|
||||
|
||||
func (instance *AccServerInstance) UpdatePlayerCount(count int) {
|
||||
if (count < 0) {
|
||||
return
|
||||
}
|
||||
instance.UpdateState(func (state *model.ServerState, changes *[]StateChange) {
|
||||
if (count == state.PlayerCount) {
|
||||
return
|
||||
}
|
||||
if (count > 0 && state.PlayerCount == 0) {
|
||||
state.SessionStart = time.Now()
|
||||
*changes = append(*changes, Session)
|
||||
} else if (count == 0) {
|
||||
state.SessionStart = time.Time{}
|
||||
*changes = append(*changes, Session)
|
||||
}
|
||||
state.PlayerCount = count
|
||||
*changes = append(*changes, PlayerCount)
|
||||
})
|
||||
if count < 0 {
|
||||
return
|
||||
}
|
||||
instance.UpdateState(func(state *model.ServerState, changes *[]StateChange) {
|
||||
if count == state.PlayerCount {
|
||||
return
|
||||
}
|
||||
if count > 0 && state.PlayerCount == 0 {
|
||||
state.SessionStart = time.Now()
|
||||
*changes = append(*changes, Session)
|
||||
} else if count == 0 {
|
||||
state.SessionStart = time.Time{}
|
||||
*changes = append(*changes, Session)
|
||||
}
|
||||
state.PlayerCount = count
|
||||
*changes = append(*changes, PlayerCount)
|
||||
})
|
||||
}
|
||||
|
||||
func (instance *AccServerInstance) UpdateSessionChange(session string) {
|
||||
instance.UpdateState(func (state *model.ServerState, changes *[]StateChange) {
|
||||
if (session == state.Session) {
|
||||
return
|
||||
}
|
||||
if (state.PlayerCount > 0) {
|
||||
state.SessionStart = time.Now()
|
||||
} else {
|
||||
state.SessionStart = time.Time{}
|
||||
}
|
||||
state.Session = session
|
||||
*changes = append(*changes, Session)
|
||||
})
|
||||
func (instance *AccServerInstance) UpdateSessionChange(session model.TrackSession) {
|
||||
instance.UpdateState(func(state *model.ServerState, changes *[]StateChange) {
|
||||
if session == state.Session {
|
||||
return
|
||||
}
|
||||
if state.PlayerCount > 0 {
|
||||
state.SessionStart = time.Now()
|
||||
} else {
|
||||
state.SessionStart = time.Time{}
|
||||
}
|
||||
state.Session = session
|
||||
*changes = append(*changes, Session)
|
||||
})
|
||||
}
|
||||
@@ -260,7 +260,7 @@ func (m *MockStateHistoryRepository) GetSessionTypes(ctx context.Context, filter
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if sessionMap[entry.Session] == nil {
|
||||
sessionMap[entry.Session] = make(map[string]bool)
|
||||
|
||||
18
tests/testdata/state_history_data.go
vendored
18
tests/testdata/state_history_data.go
vendored
@@ -22,7 +22,7 @@ func NewStateHistoryTestData(serverID uuid.UUID) *StateHistoryTestData {
|
||||
}
|
||||
|
||||
// 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{
|
||||
ID: uuid.New(),
|
||||
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
|
||||
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()
|
||||
var entries []model.StateHistory
|
||||
|
||||
@@ -69,7 +69,7 @@ func CreateBasicFilter(serverID string) *model.StateHistoryFilter {
|
||||
}
|
||||
|
||||
// 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{
|
||||
ServerBasedFilter: model.ServerBasedFilter{
|
||||
ServerID: serverID,
|
||||
@@ -97,13 +97,13 @@ var SampleLogLines = []string{
|
||||
|
||||
// ExpectedSessionChanges represents the expected session changes from parsing the sample log lines
|
||||
var ExpectedSessionChanges = []struct {
|
||||
From string
|
||||
To string
|
||||
From model.TrackSession
|
||||
To model.TrackSession
|
||||
}{
|
||||
{"NONE", "PRACTICE"},
|
||||
{"PRACTICE", "QUALIFY"},
|
||||
{"QUALIFY", "RACE"},
|
||||
{"RACE", "NONE"},
|
||||
{model.SessionUnknown, model.SessionPractice},
|
||||
{model.SessionPractice, model.SessionQualify},
|
||||
{model.SessionQualify, model.SessionRace},
|
||||
{model.SessionRace, model.SessionUnknown},
|
||||
}
|
||||
|
||||
// ExpectedPlayerCounts represents the expected player counts from parsing the sample log lines
|
||||
|
||||
@@ -46,7 +46,7 @@ func TestStateHistoryController_GetAll_Success(t *testing.T) {
|
||||
|
||||
// Insert test data
|
||||
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)
|
||||
tests.AssertNoError(t, err)
|
||||
|
||||
@@ -78,7 +78,7 @@ func TestStateHistoryController_GetAll_Success(t *testing.T) {
|
||||
err = json.Unmarshal(body, &result)
|
||||
tests.AssertNoError(t, err)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ func TestStateHistoryController_GetAll_WithSessionFilter(t *testing.T) {
|
||||
// Insert test data with different sessions
|
||||
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
|
||||
|
||||
practiceHistory := testData.CreateStateHistory("Practice", "spa", 5, uuid.New())
|
||||
raceHistory := testData.CreateStateHistory("Race", "spa", 10, uuid.New())
|
||||
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)
|
||||
@@ -124,7 +124,7 @@ func TestStateHistoryController_GetAll_WithSessionFilter(t *testing.T) {
|
||||
controller.NewStateHistoryController(stateHistoryService, routeGroups, GetTestAuthMiddleware(membershipService, inMemCache))
|
||||
|
||||
// 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("Authorization", "Bearer "+tests.MustGenerateTestToken())
|
||||
|
||||
@@ -143,7 +143,7 @@ func TestStateHistoryController_GetAll_WithSessionFilter(t *testing.T) {
|
||||
err = json.Unmarshal(body, &result)
|
||||
tests.AssertNoError(t, err)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ func TestStateHistoryController_GetStatistics_Success(t *testing.T) {
|
||||
|
||||
// Create entries with varying player counts
|
||||
playerCounts := []int{5, 10, 15, 20, 25}
|
||||
entries := testData.CreateMultipleEntries("Race", "spa", playerCounts)
|
||||
entries := testData.CreateMultipleEntries(model.SessionRace, "spa", playerCounts)
|
||||
|
||||
for _, entry := range entries {
|
||||
err := repo.Insert(helper.CreateContext(), &entry)
|
||||
@@ -475,7 +475,7 @@ func TestStateHistoryController_ContentType(t *testing.T) {
|
||||
|
||||
// Insert test data
|
||||
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)
|
||||
tests.AssertNoError(t, err)
|
||||
|
||||
@@ -551,7 +551,7 @@ func TestStateHistoryController_ResponseStructure(t *testing.T) {
|
||||
|
||||
// Insert test data
|
||||
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)
|
||||
tests.AssertNoError(t, err)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestStateHistoryRepository_Insert_Success(t *testing.T) {
|
||||
|
||||
// Create test data
|
||||
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
|
||||
err := repo.Insert(ctx, &history)
|
||||
@@ -65,7 +65,7 @@ func TestStateHistoryRepository_GetAll_Success(t *testing.T) {
|
||||
|
||||
// Insert multiple entries
|
||||
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 {
|
||||
err := repo.Insert(ctx, &entry)
|
||||
@@ -101,8 +101,8 @@ func TestStateHistoryRepository_GetAll_WithFilter(t *testing.T) {
|
||||
// Create test data with different sessions
|
||||
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
|
||||
|
||||
practiceHistory := testData.CreateStateHistory("Practice", "spa", 5, uuid.New())
|
||||
raceHistory := testData.CreateStateHistory("Race", "spa", 15, uuid.New())
|
||||
practiceHistory := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
|
||||
raceHistory := testData.CreateStateHistory(model.SessionRace, "spa", 15, uuid.New())
|
||||
|
||||
// Insert both
|
||||
err := repo.Insert(ctx, &practiceHistory)
|
||||
@@ -111,13 +111,13 @@ func TestStateHistoryRepository_GetAll_WithFilter(t *testing.T) {
|
||||
tests.AssertNoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
tests.AssertNoError(t, err)
|
||||
tests.AssertNotNil(t, 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)
|
||||
}
|
||||
|
||||
@@ -145,8 +145,8 @@ func TestStateHistoryRepository_GetLastSessionID_Success(t *testing.T) {
|
||||
sessionID1 := uuid.New()
|
||||
sessionID2 := uuid.New()
|
||||
|
||||
history1 := testData.CreateStateHistory("Practice", "spa", 5, sessionID1)
|
||||
history2 := testData.CreateStateHistory("Race", "spa", 10, sessionID2)
|
||||
history1 := testData.CreateStateHistory(model.SessionPractice, "spa", 5, sessionID1)
|
||||
history2 := testData.CreateStateHistory(model.SessionRace, "spa", 10, sessionID2)
|
||||
|
||||
// Insert with a small delay to ensure ordering
|
||||
err := repo.Insert(ctx, &history1)
|
||||
@@ -217,7 +217,7 @@ func TestStateHistoryRepository_GetSummaryStats_Success(t *testing.T) {
|
||||
sessionID2 := uuid.New()
|
||||
|
||||
// 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 {
|
||||
practiceEntries[i].SessionID = sessionID1
|
||||
err := repo.Insert(ctx, &practiceEntries[i])
|
||||
@@ -225,7 +225,7 @@ func TestStateHistoryRepository_GetSummaryStats_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
raceEntries[i].SessionID = sessionID2
|
||||
err := repo.Insert(ctx, &raceEntries[i])
|
||||
@@ -305,7 +305,7 @@ func TestStateHistoryRepository_GetTotalPlaytime_Success(t *testing.T) {
|
||||
{
|
||||
ID: uuid.New(),
|
||||
ServerID: helper.TestData.ServerID,
|
||||
Session: "Practice",
|
||||
Session: model.SessionPractice,
|
||||
Track: "spa",
|
||||
PlayerCount: 5,
|
||||
DateCreated: baseTime,
|
||||
@@ -316,7 +316,7 @@ func TestStateHistoryRepository_GetTotalPlaytime_Success(t *testing.T) {
|
||||
{
|
||||
ID: uuid.New(),
|
||||
ServerID: helper.TestData.ServerID,
|
||||
Session: "Practice",
|
||||
Session: model.SessionPractice,
|
||||
Track: "spa",
|
||||
PlayerCount: 10,
|
||||
DateCreated: baseTime.Add(30 * time.Minute),
|
||||
@@ -327,7 +327,7 @@ func TestStateHistoryRepository_GetTotalPlaytime_Success(t *testing.T) {
|
||||
{
|
||||
ID: uuid.New(),
|
||||
ServerID: helper.TestData.ServerID,
|
||||
Session: "Practice",
|
||||
Session: model.SessionPractice,
|
||||
Track: "spa",
|
||||
PlayerCount: 8,
|
||||
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
|
||||
initialHistory := testData.CreateStateHistory("Practice", "spa", 5, uuid.New())
|
||||
initialHistory := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
|
||||
err := repo.Insert(ctx, &initialHistory)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to insert initial record: %v", err)
|
||||
@@ -404,7 +404,7 @@ func TestStateHistoryRepository_ConcurrentOperations(t *testing.T) {
|
||||
defer func() {
|
||||
done <- true
|
||||
}()
|
||||
history := testData.CreateStateHistory("Practice", "spa", 5, uuid.New())
|
||||
history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, uuid.New())
|
||||
err := repo.Insert(ctx, &history)
|
||||
if err != nil {
|
||||
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
|
||||
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)
|
||||
tests.AssertNoError(t, err)
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ func TestConfigService_GetEventConfig_ValidFile(t *testing.T) {
|
||||
// Verify sessions
|
||||
tests.AssertEqual(t, 3, len(eventConfig.Sessions))
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestStateHistoryService_GetAll_Success(t *testing.T) {
|
||||
|
||||
// Insert test data directly into DB
|
||||
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)
|
||||
tests.AssertNoError(t, err)
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestStateHistoryService_GetAll_Success(t *testing.T) {
|
||||
tests.AssertNoError(t, err)
|
||||
tests.AssertNotNil(t, 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)
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ func TestStateHistoryService_GetAll_WithFilter(t *testing.T) {
|
||||
|
||||
// Insert test data with different sessions
|
||||
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
|
||||
practiceHistory := testData.CreateStateHistory("Practice", "spa", 5, uuid.New())
|
||||
raceHistory := testData.CreateStateHistory("Race", "spa", 10, uuid.New())
|
||||
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)
|
||||
@@ -85,13 +85,13 @@ func TestStateHistoryService_GetAll_WithFilter(t *testing.T) {
|
||||
defer helper.ReleaseFiberCtx(app, ctx)
|
||||
|
||||
// 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)
|
||||
|
||||
tests.AssertNoError(t, err)
|
||||
tests.AssertNotNil(t, 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)
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ func TestStateHistoryService_Insert_Success(t *testing.T) {
|
||||
|
||||
// Create test data
|
||||
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
|
||||
app := fiber.New()
|
||||
@@ -180,7 +180,7 @@ func TestStateHistoryService_GetLastSessionID_Success(t *testing.T) {
|
||||
// Insert test data
|
||||
testData := testdata.NewStateHistoryTestData(helper.TestData.ServerID)
|
||||
sessionID := uuid.New()
|
||||
history := testData.CreateStateHistory("Practice", "spa", 5, sessionID)
|
||||
history := testData.CreateStateHistory(model.SessionPractice, "spa", 5, sessionID)
|
||||
err := repo.Insert(helper.CreateContext(), &history)
|
||||
tests.AssertNoError(t, err)
|
||||
|
||||
@@ -254,7 +254,7 @@ func TestStateHistoryService_GetStatistics_Success(t *testing.T) {
|
||||
{
|
||||
ID: uuid.New(),
|
||||
ServerID: helper.TestData.ServerID,
|
||||
Session: "Practice",
|
||||
Session: model.SessionPractice,
|
||||
Track: "spa",
|
||||
PlayerCount: 5,
|
||||
DateCreated: baseTime,
|
||||
@@ -265,7 +265,7 @@ func TestStateHistoryService_GetStatistics_Success(t *testing.T) {
|
||||
{
|
||||
ID: uuid.New(),
|
||||
ServerID: helper.TestData.ServerID,
|
||||
Session: "Practice",
|
||||
Session: model.SessionPractice,
|
||||
Track: "spa",
|
||||
PlayerCount: 10,
|
||||
DateCreated: baseTime.Add(5 * time.Minute),
|
||||
@@ -276,7 +276,7 @@ func TestStateHistoryService_GetStatistics_Success(t *testing.T) {
|
||||
{
|
||||
ID: uuid.New(),
|
||||
ServerID: helper.TestData.ServerID,
|
||||
Session: "Race",
|
||||
Session: model.SessionRace,
|
||||
Track: "spa",
|
||||
PlayerCount: 15,
|
||||
DateCreated: baseTime.Add(10 * time.Minute),
|
||||
@@ -404,7 +404,7 @@ func TestStateHistoryService_LogParsingWorkflow(t *testing.T) {
|
||||
}
|
||||
|
||||
// Verify session changes were parsed correctly
|
||||
expectedSessions := []string{"PRACTICE", "QUALIFY", "RACE", "NONE"}
|
||||
expectedSessions := []model.TrackSession{model.SessionPractice, model.SessionQualify, model.SessionRace}
|
||||
sessionIndex := 0
|
||||
|
||||
for _, state := range stateChanges {
|
||||
@@ -433,7 +433,7 @@ func TestStateHistoryService_SessionChangeTracking(t *testing.T) {
|
||||
Name: "Test Server",
|
||||
}
|
||||
|
||||
var sessionChanges []string
|
||||
var sessionChanges []model.TrackSession
|
||||
onStateChange := func(state *model.ServerState, changes ...tracking.StateChange) {
|
||||
for _, change := range changes {
|
||||
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
|
||||
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)
|
||||
// Small pause to ensure log processing completes
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
Reference in New Issue
Block a user