Skip to content

Commit ea36e69

Browse files
refactor: split up tracking functions for testability
1 parent 17a9f68 commit ea36e69

File tree

4 files changed

+122
-99
lines changed

4 files changed

+122
-99
lines changed

cmd/init_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package cmd_test
22

33
import (
4-
"fmt"
54
"log"
5+
"log/slog"
66
"os"
77
"testing"
88

@@ -52,7 +52,7 @@ func TestMain(m *testing.M) {
5252
nil,
5353
)
5454
testSuite.trackingHandler.SetEventEmitter(func(eventName string, optionalData ...interface{}) {
55-
log.Println(fmt.Sprintf("[EVENT] %s", eventName), optionalData[0])
55+
slog.Info("[FE]", slog.String("event", eventName), slog.Any("data", optionalData))
5656
})
5757
os.Exit(m.Run())
5858
}

cmd/tracking.go

Lines changed: 98 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -57,170 +57,176 @@ func NewTrackingHandler(
5757
}
5858
}
5959

60-
func (ch *TrackingHandler) SetEventEmitter(eventEmitter EventEmitFn) {
61-
ch.eventEmitter = eventEmitter
60+
func (t *TrackingHandler) SetEventEmitter(eventEmitter EventEmitFn) {
61+
t.eventEmitter = eventEmitter
6262
}
6363

64-
func (ch *TrackingHandler) StartTracking(userCode string, restore bool) error {
65-
slog.Info("started tracking", slog.String("user_code", userCode), slog.Bool("restoring", restore))
64+
func (t *TrackingHandler) CreateSession(userCode string, restore bool) (*model.Session, error) {
65+
ctx := context.Background()
6666

67-
ctx, cancel := context.WithCancel(context.Background())
68-
ch.cancelPolling = cancel
67+
user, err := t.gameTracker.GetUser(ctx, userCode)
68+
if err != nil {
69+
return nil, model.WrapError(model.ErrGetUser, err)
70+
}
71+
if err := t.sqlDb.SaveUser(ctx, *user); err != nil {
72+
return nil, model.WrapError(model.ErrSaveUser, err)
73+
}
6974

7075
var session *model.Session
7176
if restore {
72-
sesh, err := ch.sqlDb.GetLatestSession(ctx, userCode)
77+
sesh, err := t.sqlDb.GetLatestSession(ctx, userCode)
7378
if err != nil {
74-
return model.WrapError(model.ErrGetLatestSession, err)
79+
return nil, model.WrapError(model.ErrGetLatestSession, err)
7580
}
7681
session = sesh
7782
} else {
78-
sesh, err := ch.sqlDb.CreateSession(ctx, userCode)
83+
sesh, err := t.sqlDb.CreateSession(ctx, userCode)
7984
if err != nil {
80-
return model.WrapError(model.ErrCreateSession, err)
85+
return nil, model.WrapError(model.ErrCreateSession, err)
8186
}
8287
session = sesh
8388
}
84-
if session == nil {
85-
return model.ErrCreateSession
86-
}
8789

88-
user, err := ch.gameTracker.GetUser(ctx, userCode)
89-
if err != nil {
90-
return model.WrapError(model.ErrGetUser, err)
91-
}
92-
if err := ch.sqlDb.SaveUser(ctx, *user); err != nil {
93-
return model.WrapError(model.ErrSaveUser, err)
94-
}
9590
session.LP = user.LP
9691
session.MR = user.MR
9792
session.UserName = user.DisplayName
93+
if err := t.sqlDb.UpdateSession(ctx, session); err != nil {
94+
slog.Error("update session:", slog.Any("error", err))
95+
}
96+
return session, nil
97+
}
9898

99-
ch.eventEmitter("match", model.Match{
100-
UserName: session.UserName,
101-
LP: session.LP,
102-
MR: session.MR,
103-
SessionId: session.Id,
104-
UserId: session.UserId,
105-
})
99+
func (t *TrackingHandler) StartTracking(sessionId string) error {
100+
slog.Info("started tracking", slog.String("session_id", sessionId))
106101

107-
ticker := time.NewTicker(30 * time.Second)
108-
ch.forcePollChan = make(chan struct{})
109-
defer func() {
110-
ch.eventEmitter("stopped-tracking")
111-
ticker.Stop()
112-
cancel()
113-
close(ch.forcePollChan)
114-
ch.forcePollChan = nil
115-
}()
116-
117-
matchChan := make(chan model.Match)
102+
ctx, cancel := context.WithCancel(context.Background())
103+
t.cancelPolling = cancel
118104

119-
onNewMatch := func(match *model.Match) {
120-
if match == nil {
121-
return
122-
}
123-
matchChan <- *match
124-
for _, mc := range ch.matchChans {
125-
if mc != nil {
126-
mc <- *match
127-
}
128-
}
105+
session, err := t.sqlDb.GetSession(ctx, sessionId)
106+
if err != nil {
107+
return model.WrapError(model.ErrCreateSession, err)
129108
}
130109

110+
// update the ui with the latest match
111+
// or with the user's base stats
112+
var initMatch *model.Match
131113
if len(session.Matches) > 0 {
132-
match := *session.Matches[0]
133-
ch.eventEmitter("match", match)
134-
for _, mc := range ch.matchChans {
135-
if mc != nil {
136-
mc <- match
137-
}
114+
initMatch = session.Matches[0]
115+
} else {
116+
initMatch = &model.Match{
117+
UserName: session.UserName,
118+
LP: session.LP,
119+
MR: session.MR,
120+
SessionId: session.Id,
121+
UserId: session.UserId,
138122
}
139123
}
124+
t.onNewMatch(ctx, session, initMatch, true)
125+
126+
ticker := time.NewTicker(30 * time.Second)
127+
t.forcePollChan = make(chan struct{})
128+
defer func() {
129+
t.eventEmitter("stopped-tracking")
130+
ticker.Stop()
131+
cancel()
132+
close(t.forcePollChan)
133+
t.forcePollChan = nil
134+
}()
140135

141136
go func() {
142137
slog.Info("polling")
143-
match, err := ch.gameTracker.Poll(ctx, session)
138+
match, err := t.gameTracker.Poll(ctx, session)
144139
if err != nil {
145140
cancel()
146141
return
147142
}
148-
onNewMatch(match)
143+
t.onNewMatch(ctx, session, match, false)
149144
for {
150145
select {
151-
case <-ch.forcePollChan:
146+
case <-t.forcePollChan:
152147
slog.Info("forced poll")
153-
match, err := ch.gameTracker.Poll(ctx, session)
148+
match, err := t.gameTracker.Poll(ctx, session)
154149
if err != nil {
155150
cancel()
156151
return
157152
}
158-
onNewMatch(match)
153+
t.onNewMatch(ctx, session, match, false)
159154
case <-ticker.C:
160155
slog.Info("polling")
161-
match, err := ch.gameTracker.Poll(ctx, session)
156+
match, err := t.gameTracker.Poll(ctx, session)
162157
if err != nil {
163158
cancel()
164159
return
165160
}
166-
onNewMatch(match)
161+
t.onNewMatch(ctx, session, match, false)
167162
case <-ctx.Done():
168-
close(matchChan)
169163
return
170164
}
171165
}
172166
}()
173167

174-
for match := range matchChan {
175-
ch.eventEmitter("match", match)
176-
177-
session.LP = match.LP
178-
session.MR = match.MR
179-
session.Matches = append([]*model.Match{&match}, session.Matches...)
168+
return nil
169+
}
180170

181-
if err := ch.sqlDb.UpdateSession(ctx, session); err != nil {
182-
slog.Error("update session:", slog.Any("error", err))
183-
break
184-
}
185-
if err := ch.sqlDb.SaveMatch(ctx, match); err != nil {
186-
slog.Error("save match to database", slog.Any("error", err))
187-
break
188-
}
189-
if err := ch.txtDb.SaveMatch(match); err != nil {
190-
slog.Error("save to text files:", slog.Any("error", err))
191-
break
171+
func (t *TrackingHandler) onNewMatch(ctx context.Context, session *model.Session, match *model.Match, dry bool) {
172+
if match == nil {
173+
return
174+
}
175+
t.eventEmitter("match", *match)
176+
for _, mc := range t.matchChans {
177+
if mc != nil {
178+
mc <- *match
192179
}
193180
}
194-
return nil
181+
if dry {
182+
return
183+
}
184+
185+
session.LP = match.LP
186+
session.MR = match.MR
187+
session.Matches = append([]*model.Match{match}, session.Matches...)
188+
189+
if err := t.sqlDb.UpdateSession(ctx, session); err != nil {
190+
slog.Error("update session:", slog.Any("error", err))
191+
return
192+
}
193+
if err := t.sqlDb.SaveMatch(ctx, *match); err != nil {
194+
slog.Error("save match to database", slog.Any("error", err))
195+
return
196+
}
197+
if err := t.txtDb.SaveMatch(*match); err != nil {
198+
slog.Error("save to text files:", slog.Any("error", err))
199+
return
200+
}
195201
}
196202

197-
func (ch *TrackingHandler) StopTracking() {
198-
ch.cancelPolling()
203+
func (t *TrackingHandler) StopTracking() {
204+
t.cancelPolling()
199205
}
200206

201-
func (ch *TrackingHandler) SelectGame(game model.GameType) error {
207+
func (t *TrackingHandler) SelectGame(game model.GameType) error {
202208
var username, password string
203209
switch game {
204210
case model.GameTypeT8:
205-
ch.gameTracker = t8.NewT8Tracker(ch.wavuClient)
211+
t.gameTracker = t8.NewT8Tracker(t.wavuClient)
206212
case model.GameTypeSF6:
207-
ch.gameTracker = sf6.NewSF6Tracker(ch.cfnClient)
208-
username = ch.cfg.CapIDEmail
209-
password = ch.cfg.CapIDPassword
213+
t.gameTracker = sf6.NewSF6Tracker(t.cfnClient)
214+
username = t.cfg.CapIDEmail
215+
password = t.cfg.CapIDPassword
210216
default:
211217
return model.WrapError(model.ErrSelectGame, fmt.Errorf("game does not exist"))
212218
}
213219

214220
authChan := make(chan tracker.AuthStatus)
215221
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
216222
defer cancel()
217-
go ch.gameTracker.Authenticate(ctx, username, password, authChan)
223+
go t.gameTracker.Authenticate(ctx, username, password, authChan)
218224
for status := range authChan {
219225
if status.Err != nil {
220226
return model.WrapError(model.ErrAuth, status.Err)
221227
}
222228

223-
ch.eventEmitter("auth-progress", status.Progress)
229+
t.eventEmitter("auth-progress", status.Progress)
224230

225231
if status.Progress >= 100 {
226232
close(authChan)
@@ -230,8 +236,8 @@ func (ch *TrackingHandler) SelectGame(game model.GameType) error {
230236
return nil
231237
}
232238

233-
func (ch *TrackingHandler) ForcePoll() {
234-
if ch.forcePollChan != nil {
235-
ch.forcePollChan <- struct{}{}
239+
func (t *TrackingHandler) ForcePoll() {
240+
if t.forcePollChan != nil {
241+
t.forcePollChan <- struct{}{}
236242
}
237243
}

main.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,11 @@ func main() {
123123
Error: err.Error(),
124124
}
125125
if err := tmpl.Execute(&b, params); err != nil {
126-
log.Println("write error template: ", err)
126+
slog.Error("write error template: ", slog.Any("error", err))
127127
}
128128
_, err := w.Write(b.Bytes())
129129
if err != nil {
130-
log.Println("write error page: ", err)
130+
slog.Error("write error page: ", slog.Any("error", err))
131131
}
132132
})
133133
}),
@@ -276,7 +276,7 @@ func handleAutoUpdate(versionStr string) {
276276
slog.Info("deleting old exe...")
277277
currentExePath, err := os.Executable()
278278
if err != nil {
279-
slog.Error(fmt.Sprintf("get current exe path: %v", err))
279+
slog.Error("get current exe path", slog.Any("error", err))
280280
}
281281

282282
err = os.Remove(currentExePath + ".old")
@@ -305,12 +305,12 @@ func handleAutoUpdate(versionStr string) {
305305
// an error.
306306
p, err := os.FindProcess(prevInstancePid)
307307
if err != nil {
308-
slog.Warn(fmt.Sprintf("failed (err received) to find previous instance process, it's probably shut down...: %v'", err))
308+
slog.Warn("failed to find previous instance process, it's probably shut down...", slog.Any("error", err))
309309
deleteOldExe()
310310
break
311311
}
312312
if p == nil {
313-
slog.Info("find previous instance process, it's probably shut down...'")
313+
slog.Info("find previous instance process, it's probably shut down...")
314314
deleteOldExe()
315315
break
316316
}

pkg/storage/sql/session.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
type SessionStorage interface {
1414
CreateSession(ctx context.Context, userId string) error
15+
GetSession(ctx context.Context, sessionId string) (*model.Session, error)
1516
GetSessions(ctx context.Context, userId string, date string, limit uint8, offset uint16) ([]*model.Session, error)
1617
GetSessionsStatistics(ctx context.Context, userId string) (*model.SessionsStatistics, error)
1718
UpdateSession(ctx context.Context, session *model.Session) error
@@ -51,6 +52,22 @@ func (s *Storage) CreateSession(ctx context.Context, userId string) (*model.Sess
5152
return &sesh, nil
5253
}
5354

55+
func (s *Storage) GetSession(ctx context.Context, sessionId string) (*model.Session, error) {
56+
query, args, err := sqlx.In(`
57+
SELECT * FROM sessions
58+
WHERE id = (?)
59+
LIMIT 1
60+
`, sessionId)
61+
if err != nil {
62+
return nil, fmt.Errorf("prepare get user clause: %w", err)
63+
}
64+
var session *model.Session
65+
if err := s.db.GetContext(ctx, session, query, args...); err != nil {
66+
return nil, fmt.Errorf("get session: %w", err)
67+
}
68+
return session, nil
69+
}
70+
5471
type monthlySessionCount struct {
5572
Month string `db:"month"`
5673
Count uint16 `db:"count"`

0 commit comments

Comments
 (0)