From 53d34ae82ab8f74cb6281c701d8deca2cf440a9a Mon Sep 17 00:00:00 2001 From: Patrick Fairbank Date: Sun, 27 Aug 2017 16:27:02 -0700 Subject: [PATCH] Refactor partner API clients into separate package. --- arena_test.go | 3 +- game/{test_data.go => test_helpers.go} | 2 +- main.go | 5 + match_play.go | 6 +- match_play_test.go | 4 +- model/database_test.go | 9 +- model/test_data.go | 26 ---- model/test_helpers.go | 43 ++++++ partner/stemtv.go | 35 +++++ stemtv_test.go => partner/stemtv_test.go | 12 +- tba.go => partner/tba.go | 172 +++++++++++------------ tba_test.go => partner/tba_test.go | 68 +++++---- setup_alliance_selection.go | 4 +- setup_alliance_selection_test.go | 2 +- setup_schedule.go | 8 +- setup_schedule_test.go | 2 +- setup_settings.go | 5 + setup_teams.go | 8 +- setup_teams_test.go | 41 +++--- stemtv.go | 28 ---- main_test.go => test_helpers.go | 12 +- 21 files changed, 267 insertions(+), 228 deletions(-) rename game/{test_data.go => test_helpers.go} (91%) delete mode 100644 model/test_data.go create mode 100644 model/test_helpers.go create mode 100644 partner/stemtv.go rename stemtv_test.go => partner/stemtv_test.go (82%) rename tba.go => partner/tba.go (78%) rename tba_test.go => partner/tba_test.go (63%) delete mode 100644 stemtv.go rename main_test.go => test_helpers.go (50%) diff --git a/arena_test.go b/arena_test.go index 5193fd2..92105e4 100644 --- a/arena_test.go +++ b/arena_test.go @@ -60,12 +60,13 @@ func TestArenaMatchFlow(t *testing.T) { db.CreateTeam(&model.Team{Id: 254}) err := mainArena.AssignTeam(254, "B3") + assert.Nil(t, err) dummyDs := &DriverStationConnection{TeamId: 254} mainArena.AllianceStations["B3"].DsConn = dummyDs - assert.Nil(t, err) // Check pre-match state and packet timing. assert.Equal(t, preMatch, mainArena.MatchState) + mainArena.lastDsPacketTime = mainArena.lastDsPacketTime.Add(-300 * time.Millisecond) mainArena.Update() assert.Equal(t, true, mainArena.AllianceStations["B3"].DsConn.Auto) assert.Equal(t, false, mainArena.AllianceStations["B3"].DsConn.Enabled) diff --git a/game/test_data.go b/game/test_helpers.go similarity index 91% rename from game/test_data.go rename to game/test_helpers.go index 91e67dc..522f3c7 100644 --- a/game/test_data.go +++ b/game/test_helpers.go @@ -1,7 +1,7 @@ // Copyright 2017 Team 254. All Rights Reserved. // Author: pat@patfairbank.com (Patrick Fairbank) // -// Data for use in tests in this package and others. +// Helper methods for use in tests in this package and others. package game diff --git a/main.go b/main.go index 32462a0..0874a4c 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ package main import ( "github.com/Team254/cheesy-arena/model" + "github.com/Team254/cheesy-arena/partner" "log" "math/rand" "time" @@ -14,11 +15,15 @@ const eventDbPath = "./event.db" var db *model.Database var eventSettings *model.EventSettings +var tbaClient *partner.TbaClient +var stemTvClient *partner.StemTvClient // Main entry point for the application. func main() { rand.Seed(time.Now().UnixNano()) initDb() + tbaClient = partner.NewTbaClient(eventSettings.TbaEventCode, eventSettings.TbaSecretId, eventSettings.TbaSecret) + stemTvClient = partner.NewStemTvClient(eventSettings.StemTvEventCode) // Run the webserver and DS packet listener in goroutines and use the main one for the arena state machine. go ServeWebInterface() diff --git a/match_play.go b/match_play.go index 6b95d0e..a9f179d 100644 --- a/match_play.go +++ b/match_play.go @@ -508,12 +508,12 @@ func CommitMatchScore(match *model.Match, matchResult *model.MatchResult, loadTo if eventSettings.TbaPublishingEnabled && match.Type != "practice" { // Publish asynchronously to The Blue Alliance. go func() { - err = PublishMatches() + err = tbaClient.PublishMatches(db) if err != nil { log.Printf("Failed to publish matches: %s", err.Error()) } if match.Type == "qualification" { - err = PublishRankings() + err = tbaClient.PublishRankings(db) if err != nil { log.Printf("Failed to publish rankings: %s", err.Error()) } @@ -524,7 +524,7 @@ func CommitMatchScore(match *model.Match, matchResult *model.MatchResult, loadTo if eventSettings.StemTvPublishingEnabled && match.Type != "practice" { // Publish asynchronously to STEMtv. go func() { - err = PublishMatchVideoSplit(match, time.Now()) + err = stemTvClient.PublishMatchVideoSplit(match, time.Now()) if err != nil { log.Printf("Failed to publish match video split to STEMtv: %s", err.Error()) } diff --git a/match_play_test.go b/match_play_test.go index 978492d..3d4464b 100644 --- a/match_play_test.go +++ b/match_play_test.go @@ -156,8 +156,8 @@ func TestCommitMatch(t *testing.T) { assert.Equal(t, "T", match.Winner) // Verify TBA and STEMtv publishing by checking the log for the expected failure messages. - tbaBaseUrl = "fakeurl" - stemTvBaseUrl = "fakeurl" + tbaClient.BaseUrl = "fakeUrl" + stemTvClient.BaseUrl = "fakeUrl" eventSettings.TbaPublishingEnabled = true eventSettings.StemTvPublishingEnabled = true var writer bytes.Buffer diff --git a/model/database_test.go b/model/database_test.go index ee05eb5..66dbee2 100644 --- a/model/database_test.go +++ b/model/database_test.go @@ -5,21 +5,14 @@ package model import ( "github.com/stretchr/testify/assert" - "os" - "path/filepath" "testing" ) -const testDbPath = "test.db" - func TestOpenUnreachableDatabase(t *testing.T) { _, err := OpenDatabase("..", "nonexistentdir/test.db") assert.NotNil(t, err) } func setupTestDb(t *testing.T) *Database { - os.Remove(filepath.Join("..", testDbPath)) - db, err := OpenDatabase("..", testDbPath) - assert.Nil(t, err) - return db + return SetupTestDb(t, "model", "..") } diff --git a/model/test_data.go b/model/test_data.go deleted file mode 100644 index 2cda2a2..0000000 --- a/model/test_data.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2017 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) -// -// Data for use in tests in this package and others. - -package model - -import "github.com/Team254/cheesy-arena/game" - -func BuildTestMatchResult(matchId int, playNumber int) *MatchResult { - matchResult := &MatchResult{MatchId: matchId, PlayNumber: playNumber, MatchType: "qualification"} - matchResult.RedScore = game.TestScore1() - matchResult.BlueScore = game.TestScore2() - matchResult.RedCards = map[string]string{"1868": "yellow"} - matchResult.BlueCards = map[string]string{} - return matchResult -} - -func BuildTestAlliances(db *Database) { - db.CreateAllianceTeam(&AllianceTeam{0, 2, 0, 1718}) - db.CreateAllianceTeam(&AllianceTeam{0, 1, 3, 74}) - db.CreateAllianceTeam(&AllianceTeam{0, 1, 1, 469}) - db.CreateAllianceTeam(&AllianceTeam{0, 1, 0, 254}) - db.CreateAllianceTeam(&AllianceTeam{0, 1, 2, 2848}) - db.CreateAllianceTeam(&AllianceTeam{0, 2, 1, 2451}) -} diff --git a/model/test_helpers.go b/model/test_helpers.go new file mode 100644 index 0000000..6647ed0 --- /dev/null +++ b/model/test_helpers.go @@ -0,0 +1,43 @@ +// Copyright 2017 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) +// +// Helper methods for use in tests in this package and others. + +package model + +import ( + "fmt" + "github.com/Team254/cheesy-arena/game" + "github.com/stretchr/testify/assert" + "os" + "path/filepath" + "testing" +) + +const testDbPath = "%s_test.db" + +func SetupTestDb(t *testing.T, uniqueName, baseDir string) *Database { + dbPath := fmt.Sprintf(testDbPath, uniqueName) + os.Remove(filepath.Join(baseDir, dbPath)) + database, err := OpenDatabase(baseDir, dbPath) + assert.Nil(t, err) + return database +} + +func BuildTestMatchResult(matchId int, playNumber int) *MatchResult { + matchResult := &MatchResult{MatchId: matchId, PlayNumber: playNumber, MatchType: "qualification"} + matchResult.RedScore = game.TestScore1() + matchResult.BlueScore = game.TestScore2() + matchResult.RedCards = map[string]string{"1868": "yellow"} + matchResult.BlueCards = map[string]string{} + return matchResult +} + +func BuildTestAlliances(database *Database) { + database.CreateAllianceTeam(&AllianceTeam{0, 2, 0, 1718}) + database.CreateAllianceTeam(&AllianceTeam{0, 1, 3, 74}) + database.CreateAllianceTeam(&AllianceTeam{0, 1, 1, 469}) + database.CreateAllianceTeam(&AllianceTeam{0, 1, 0, 254}) + database.CreateAllianceTeam(&AllianceTeam{0, 1, 2, 2848}) + database.CreateAllianceTeam(&AllianceTeam{0, 2, 1, 2451}) +} diff --git a/partner/stemtv.go b/partner/stemtv.go new file mode 100644 index 0000000..f8d96af --- /dev/null +++ b/partner/stemtv.go @@ -0,0 +1,35 @@ +// Copyright 2016 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) +// +// Methods for publishing match video split information to STEMtv. + +package partner + +import ( + "fmt" + "github.com/Team254/cheesy-arena/model" + "net/http" + "time" +) + +type StemTvClient struct { + BaseUrl string + eventCode string +} + +const ( + stemTvBaseUrl = "http://stemtv.io" + preMatchPaddingSec = 5 + postScoreDisplayPaddingSec = 10 +) + +func NewStemTvClient(eventCode string) *StemTvClient { + return &StemTvClient{stemTvBaseUrl, eventCode} +} + +func (client *StemTvClient) PublishMatchVideoSplit(match *model.Match, scoreDisplayTime time.Time) error { + url := fmt.Sprintf("%s/event/api/v1.0/%s/%s/split/%d,%d", client.BaseUrl, client.eventCode, match.TbaCode(), + match.StartedAt.Unix()-preMatchPaddingSec, scoreDisplayTime.Unix()+postScoreDisplayPaddingSec) + _, err := http.Get(url) + return err +} diff --git a/stemtv_test.go b/partner/stemtv_test.go similarity index 82% rename from stemtv_test.go rename to partner/stemtv_test.go index f570cc0..f7e4320 100644 --- a/stemtv_test.go +++ b/partner/stemtv_test.go @@ -1,7 +1,7 @@ // Copyright 2016 Team 254. All Rights Reserved. // Author: pat@patfairbank.com (Patrick Fairbank) -package main +package partner import ( "github.com/Team254/cheesy-arena/model" @@ -13,19 +13,17 @@ import ( ) func TestPublishMatchVideoSplit(t *testing.T) { - setupTest(t) - - eventSettings.StemTvEventCode = "my_event_code" - // Mock the STEMtv server. stemTvServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, "/event/api/v1.0/my_event_code/qm254/split/981187501,981187690", r.URL.String()) })) defer stemTvServer.Close() - stemTvBaseUrl = stemTvServer.URL + + client := NewStemTvClient("my_event_code") + client.BaseUrl = stemTvServer.URL matchStartedTime, _ := time.Parse("2006-01-02 15:04:05 -0700", "2001-02-03 04:05:06 -0400") match := &model.Match{Type: "qualification", DisplayName: "254", StartedAt: matchStartedTime} scoreDisplayTime, _ := time.Parse("2006-01-02 15:04:05 -0700", "2001-02-03 04:08:00 -0400") - assert.Nil(t, PublishMatchVideoSplit(match, scoreDisplayTime)) + assert.Nil(t, client.PublishMatchVideoSplit(match, scoreDisplayTime)) } diff --git a/tba.go b/partner/tba.go similarity index 78% rename from tba.go rename to partner/tba.go index 7e7a962..260098b 100644 --- a/tba.go +++ b/partner/tba.go @@ -3,7 +3,7 @@ // // Methods for publishing data to and retrieving data from The Blue Alliance. -package main +package partner import ( "bytes" @@ -17,17 +17,15 @@ import ( "strconv" ) -// Distinct endpoints are necessary for testing. -var tbaBaseUrl = "https://www.thebluealliance.com" -var tbaTeamBaseUrl = tbaBaseUrl -var tbaTeamRobotsBaseUrl = tbaBaseUrl -var tbaTeamAwardsBaseUrl = tbaBaseUrl -var tbaEventBaseUrl = tbaBaseUrl +type TbaClient struct { + BaseUrl string + eventCode string + secretId string + secret string + eventNamesCache map[string]string +} -// Cache of event codes to names. -var tbaEventNames = make(map[string]string) - -// MODELS +const tbaBaseUrl = "https://www.thebluealliance.com" type TbaMatch struct { CompLevel string `json:"comp_level"` @@ -114,10 +112,14 @@ type TbaEvent struct { Name string `json:"name"` } -// DATA RETRIEVAL -func getTeamFromTba(teamNumber int) (*TbaTeam, error) { - url := fmt.Sprintf("%s/api/v2/team/%s", tbaTeamBaseUrl, getTbaTeam(teamNumber)) - resp, err := getTbaRequest(url) +func NewTbaClient(eventCode, secretId, secret string) *TbaClient { + return &TbaClient{BaseUrl: tbaBaseUrl, eventCode: eventCode, secretId: secretId, secret: secret, + eventNamesCache: make(map[string]string)} +} + +func (client *TbaClient) GetTeam(teamNumber int) (*TbaTeam, error) { + path := fmt.Sprintf("/api/v2/team/%s", getTbaTeam(teamNumber)) + resp, err := client.getRequest(path) if err != nil { return nil, err } @@ -135,9 +137,9 @@ func getTeamFromTba(teamNumber int) (*TbaTeam, error) { return &teamData, err } -func getRobotNameFromTba(teamNumber int, year int) (string, error) { - url := fmt.Sprintf("%s/api/v2/team/frc%d/history/robots", tbaTeamRobotsBaseUrl, teamNumber) - resp, err := getTbaRequest(url) +func (client *TbaClient) GetRobotName(teamNumber int, year int) (string, error) { + path := fmt.Sprintf("/api/v2/team/%s/history/robots", getTbaTeam(teamNumber)) + resp, err := client.getRequest(path) if err != nil { return "", err } @@ -160,9 +162,9 @@ func getRobotNameFromTba(teamNumber int, year int) (string, error) { return "", nil } -func getTeamAwardsFromTba(teamNumber int) ([]*TbaAward, error) { - url := fmt.Sprintf("%s/api/v2/team/%s/history/awards", tbaTeamAwardsBaseUrl, getTbaTeam(teamNumber)) - resp, err := getTbaRequest(url) +func (client *TbaClient) GetTeamAwards(teamNumber int) ([]*TbaAward, error) { + path := fmt.Sprintf("/api/v2/team/%s/history/awards", getTbaTeam(teamNumber)) + resp, err := client.getRequest(path) if err != nil { return nil, err } @@ -181,46 +183,21 @@ func getTeamAwardsFromTba(teamNumber int) ([]*TbaAward, error) { } for _, award := range awards { - if _, ok := tbaEventNames[award.EventKey]; !ok { - tbaEventNames[award.EventKey], err = getEventNameFromTba(award.EventKey) + if _, ok := client.eventNamesCache[award.EventKey]; !ok { + client.eventNamesCache[award.EventKey], err = client.getEventName(award.EventKey) if err != nil { return nil, err } } - award.EventName = tbaEventNames[award.EventKey] + award.EventName = client.eventNamesCache[award.EventKey] } return awards, nil } -func getEventNameFromTba(eventCode string) (string, error) { - url := fmt.Sprintf("%s/api/v2/event/%s", tbaEventBaseUrl, eventCode) - resp, err := getTbaRequest(url) - if err != nil { - return "", err - } - - // Get the response and handle errors - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", err - } - - var event TbaEvent - err = json.Unmarshal(body, &event) - if err != nil { - return "", err - } - - return event.Name, err -} - -// PUBLISHING - // Uploads the event team list to The Blue Alliance. -func PublishTeams() error { - teams, err := db.GetAllTeams() +func (client *TbaClient) PublishTeams(database *model.Database) error { + teams, err := database.GetAllTeams() if err != nil { return err } @@ -235,7 +212,7 @@ func PublishTeams() error { return err } - resp, err := postTbaRequest("team_list", "update", jsonBody) + resp, err := client.postRequest("team_list", "update", jsonBody) if err != nil { return err } @@ -248,12 +225,12 @@ func PublishTeams() error { } // Uploads the qualification and elimination match schedule and results to The Blue Alliance. -func PublishMatches() error { - qualMatches, err := db.GetMatchesByType("qualification") +func (client *TbaClient) PublishMatches(database *model.Database) error { + qualMatches, err := database.GetMatchesByType("qualification") if err != nil { return err } - elimMatches, err := db.GetMatchesByType("elimination") + elimMatches, err := database.GetMatchesByType("elimination") if err != nil { return err } @@ -271,7 +248,7 @@ func PublishMatches() error { // Fill in scores if the match has been played. if match.Status == "complete" { - matchResult, err := db.GetMatchResultForMatch(match.Id) + matchResult, err := database.GetMatchResultForMatch(match.Id) if err != nil { return err } @@ -298,7 +275,7 @@ func PublishMatches() error { return err } - resp, err := postTbaRequest("matches", "update", jsonBody) + resp, err := client.postRequest("matches", "update", jsonBody) if err != nil { return err } @@ -311,8 +288,8 @@ func PublishMatches() error { } // Uploads the team standings to The Blue Alliance. -func PublishRankings() error { - rankings, err := db.GetAllRankings() +func (client *TbaClient) PublishRankings(database *model.Database) error { + rankings, err := database.GetAllRankings() if err != nil { return err } @@ -332,7 +309,7 @@ func PublishRankings() error { return err } - resp, err := postTbaRequest("rankings", "update", jsonBody) + resp, err := client.postRequest("rankings", "update", jsonBody) if err != nil { return err } @@ -345,8 +322,8 @@ func PublishRankings() error { } // Uploads the alliances selection results to The Blue Alliance. -func PublishAlliances() error { - alliances, err := db.GetAllAlliances() +func (client *TbaClient) PublishAlliances(database *model.Database) error { + alliances, err := database.GetAllAlliances() if err != nil { return err } @@ -363,7 +340,7 @@ func PublishAlliances() error { return err } - resp, err := postTbaRequest("alliance_selections", "update", jsonBody) + resp, err := client.postRequest("alliance_selections", "update", jsonBody) if err != nil { return err } @@ -376,8 +353,8 @@ func PublishAlliances() error { } // Clears out the existing match data on The Blue Alliance for the event. -func DeletePublishedMatches() error { - resp, err := postTbaRequest("matches", "delete_all", []byte(eventSettings.TbaEventCode)) +func (client *TbaClient) DeletePublishedMatches() error { + resp, err := client.postRequest("matches", "delete_all", []byte(client.eventCode)) if err != nil { return err } @@ -389,38 +366,61 @@ func DeletePublishedMatches() error { return nil } +func (client *TbaClient) getEventName(eventCode string) (string, error) { + path := fmt.Sprintf("/api/v2/event/%s", eventCode) + resp, err := client.getRequest(path) + if err != nil { + return "", err + } + + // Get the response and handle errors + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + var event TbaEvent + err = json.Unmarshal(body, &event) + if err != nil { + return "", err + } + + return event.Name, err +} + // Converts an integer team number into the "frcXXXX" format TBA expects. func getTbaTeam(team int) string { return fmt.Sprintf("frc%d", team) } -// HELPERS +// Sends a GET request to the TBA API. +func (client *TbaClient) getRequest(path string) (*http.Response, error) { + url := client.BaseUrl + path -// Signs the request and sends it to the TBA API. -func postTbaRequest(resource string, action string, body []byte) (*http.Response, error) { - path := fmt.Sprintf("/api/trusted/v1/event/%s/%s/%s", eventSettings.TbaEventCode, resource, action) - signature := fmt.Sprintf("%x", md5.Sum(append([]byte(eventSettings.TbaSecret+path), body...))) - - client := &http.Client{} - request, err := http.NewRequest("POST", fmt.Sprintf("%s%s", tbaBaseUrl, path), bytes.NewReader(body)) - if err != nil { - return nil, err - } - request.Header.Add("X-TBA-Auth-Id", eventSettings.TbaSecretId) - request.Header.Add("X-TBA-Auth-Sig", signature) - return client.Do(request) -} - -// Sends a GET request to the TBA API -func getTbaRequest(url string) (*http.Response, error) { - // Make an HTTP GET request with the TBA auth headers - client := &http.Client{} + // Make an HTTP GET request with the TBA auth headers. + httpClient := &http.Client{} req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } req.Header.Set("X-TBA-App-Id", "cheesy-arena:cheesy-fms:v0.1") - return client.Do(req) + return httpClient.Do(req) +} + +// Signs the request and sends it to the TBA API. +func (client *TbaClient) postRequest(resource string, action string, body []byte) (*http.Response, error) { + path := fmt.Sprintf("/api/trusted/v1/event/%s/%s/%s", client.eventCode, resource, action) + signature := fmt.Sprintf("%x", md5.Sum(append([]byte(client.secret+path), body...))) + + httpClient := &http.Client{} + request, err := http.NewRequest("POST", client.BaseUrl+path, bytes.NewReader(body)) + if err != nil { + return nil, err + } + request.Header.Add("X-TBA-Auth-Id", client.secretId) + request.Header.Add("X-TBA-Auth-Sig", signature) + return httpClient.Do(request) } func createTbaScoringBreakdown(match *model.Match, matchResult *model.MatchResult, alliance string) *TbaScoreBreakdown { diff --git a/tba_test.go b/partner/tba_test.go similarity index 63% rename from tba_test.go rename to partner/tba_test.go index 932658e..b8fa9e2 100644 --- a/tba_test.go +++ b/partner/tba_test.go @@ -1,7 +1,7 @@ // Copyright 2014 Team 254. All Rights Reserved. // Author: pat@patfairbank.com (Patrick Fairbank) -package main +package partner import ( "bytes" @@ -17,13 +17,10 @@ import ( ) func TestPublishTeams(t *testing.T) { - setupTest(t) + database := setupTestDb(t) - eventSettings.TbaEventCode = "my_event_code" - eventSettings.TbaSecretId = "my_secret_id" - eventSettings.TbaSecret = "my_secret" - db.CreateTeam(&model.Team{Id: 254}) - db.CreateTeam(&model.Team{Id: 1114}) + database.CreateTeam(&model.Team{Id: 254}) + database.CreateTeam(&model.Team{Id: 1114}) // Mock the TBA server. tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -35,21 +32,22 @@ func TestPublishTeams(t *testing.T) { assert.Equal(t, "f5c022fde6d1186ea0719fe28ab6cc63", r.Header["X-Tba-Auth-Sig"][0]) })) defer tbaServer.Close() - tbaBaseUrl = tbaServer.URL + client := NewTbaClient("my_event_code", "my_secret_id", "my_secret") + client.BaseUrl = tbaServer.URL - assert.Nil(t, PublishTeams()) + assert.Nil(t, client.PublishTeams(database)) } func TestPublishMatches(t *testing.T) { - setupTest(t) + database := setupTestDb(t) match1 := model.Match{Type: "qualification", DisplayName: "2", Time: time.Unix(600, 0), Red1: 7, Red2: 8, Red3: 9, Blue1: 10, Blue2: 11, Blue3: 12, Status: "complete"} match2 := model.Match{Type: "elimination", DisplayName: "SF2-2", ElimRound: 2, ElimGroup: 2, ElimInstance: 2} - db.CreateMatch(&match1) - db.CreateMatch(&match2) + database.CreateMatch(&match1) + database.CreateMatch(&match2) matchResult1 := model.BuildTestMatchResult(match1.Id, 1) - db.CreateMatchResult(matchResult1) + database.CreateMatchResult(matchResult1) // Mock the TBA server. tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -61,16 +59,17 @@ func TestPublishMatches(t *testing.T) { assert.Equal(t, "sf", matches[1].CompLevel) })) defer tbaServer.Close() - tbaBaseUrl = tbaServer.URL + client := NewTbaClient("my_event_code", "my_secret_id", "my_secret") + client.BaseUrl = tbaServer.URL - assert.Nil(t, PublishMatches()) + assert.Nil(t, client.PublishMatches(database)) } func TestPublishRankings(t *testing.T) { - setupTest(t) + database := setupTestDb(t) - db.CreateRanking(game.TestRanking2()) - db.CreateRanking(game.TestRanking1()) + database.CreateRanking(game.TestRanking2()) + database.CreateRanking(game.TestRanking1()) // Mock the TBA server. tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -82,15 +81,16 @@ func TestPublishRankings(t *testing.T) { assert.Equal(t, "frc1114", response.Rankings[1].TeamKey) })) defer tbaServer.Close() - tbaBaseUrl = tbaServer.URL + client := NewTbaClient("my_event_code", "my_secret_id", "my_secret") + client.BaseUrl = tbaServer.URL - assert.Nil(t, PublishRankings()) + assert.Nil(t, client.PublishRankings(database)) } func TestPublishAlliances(t *testing.T) { - setupTest(t) + database := setupTestDb(t) - model.BuildTestAlliances(db) + model.BuildTestAlliances(database) // Mock the TBA server. tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -100,25 +100,31 @@ func TestPublishAlliances(t *testing.T) { reader.String()) })) defer tbaServer.Close() - tbaBaseUrl = tbaServer.URL + client := NewTbaClient("my_event_code", "my_secret_id", "my_secret") + client.BaseUrl = tbaServer.URL - assert.Nil(t, PublishAlliances()) + assert.Nil(t, client.PublishAlliances(database)) } func TestPublishingErrors(t *testing.T) { - setupTest(t) + database := setupTestDb(t) - model.BuildTestAlliances(db) + model.BuildTestAlliances(database) // Mock the TBA server. tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "oh noes", 500) })) defer tbaServer.Close() - tbaBaseUrl = tbaServer.URL + client := NewTbaClient("my_event_code", "my_secret_id", "my_secret") + client.BaseUrl = tbaServer.URL - assert.NotNil(t, PublishTeams()) - assert.NotNil(t, PublishMatches()) - assert.NotNil(t, PublishRankings()) - assert.NotNil(t, PublishAlliances()) + assert.NotNil(t, client.PublishTeams(database)) + assert.NotNil(t, client.PublishMatches(database)) + assert.NotNil(t, client.PublishRankings(database)) + assert.NotNil(t, client.PublishAlliances(database)) +} + +func setupTestDb(t *testing.T) *model.Database { + return model.SetupTestDb(t, "partner", "..") } diff --git a/setup_alliance_selection.go b/setup_alliance_selection.go index c7318d4..41797d0 100644 --- a/setup_alliance_selection.go +++ b/setup_alliance_selection.go @@ -210,12 +210,12 @@ func AllianceSelectionFinalizeHandler(w http.ResponseWriter, r *http.Request) { if eventSettings.TbaPublishingEnabled { // Publish alliances and schedule to The Blue Alliance. - err = PublishAlliances() + err = tbaClient.PublishAlliances(db) if err != nil { renderAllianceSelection(w, r, fmt.Sprintf("Failed to publish alliances: %s", err.Error())) return } - err = PublishMatches() + err = tbaClient.PublishMatches(db) if err != nil { renderAllianceSelection(w, r, fmt.Sprintf("Failed to publish matches: %s", err.Error())) return diff --git a/setup_alliance_selection_test.go b/setup_alliance_selection_test.go index ac4d8a8..1d0b553 100644 --- a/setup_alliance_selection_test.go +++ b/setup_alliance_selection_test.go @@ -136,7 +136,7 @@ func TestSetupAllianceSelectionErrors(t *testing.T) { assert.Contains(t, recorder.Body.String(), "valid start time") // Finalize for real and check that TBA publishing is triggered. - tbaBaseUrl = "fakeurl" + tbaClient.BaseUrl = "fakeurl" eventSettings.TbaPublishingEnabled = true recorder = postHttpResponse("/setup/alliance_selection/finalize", "startTime=2014-01-01 01:00:00 PM") assert.Equal(t, 200, recorder.Code) diff --git a/setup_schedule.go b/setup_schedule.go index 5b04595..505277e 100644 --- a/setup_schedule.go +++ b/setup_schedule.go @@ -99,12 +99,12 @@ func ScheduleGeneratePostHandler(w http.ResponseWriter, r *http.Request) { func ScheduleRepublishPostHandler(w http.ResponseWriter, r *http.Request) { if eventSettings.TbaPublishingEnabled { // Publish schedule to The Blue Alliance. - err := DeletePublishedMatches() + err := tbaClient.DeletePublishedMatches() if err != nil { http.Error(w, "Failed to delete published matches: "+err.Error(), 500) return } - err = PublishMatches() + err = tbaClient.PublishMatches(db) if err != nil { http.Error(w, "Failed to publish matches: "+err.Error(), 500) return @@ -151,12 +151,12 @@ func ScheduleSavePostHandler(w http.ResponseWriter, r *http.Request) { if eventSettings.TbaPublishingEnabled && cachedMatchType != "practice" { // Publish schedule to The Blue Alliance. - err = DeletePublishedMatches() + err = tbaClient.DeletePublishedMatches() if err != nil { http.Error(w, "Failed to delete published matches: "+err.Error(), 500) return } - err = PublishMatches() + err = tbaClient.PublishMatches(db) if err != nil { http.Error(w, "Failed to publish matches: "+err.Error(), 500) return diff --git a/setup_schedule_test.go b/setup_schedule_test.go index 4494885..55d5d84 100644 --- a/setup_schedule_test.go +++ b/setup_schedule_test.go @@ -34,7 +34,7 @@ func TestSetupSchedule(t *testing.T) { assert.Contains(t, recorder.Body.String(), "2014-01-03 16:54:00") // Last match of third block. // Save schedule and check that it is published to TBA. - tbaBaseUrl = "fakeurl" + tbaClient.BaseUrl = "fakeUrl" eventSettings.TbaPublishingEnabled = true recorder = postHttpResponse("/setup/schedule/save", "") matches, err := db.GetMatchesByType("qualification") diff --git a/setup_settings.go b/setup_settings.go index 6db5a98..cf92d07 100644 --- a/setup_settings.go +++ b/setup_settings.go @@ -8,6 +8,7 @@ package main import ( "fmt" "github.com/Team254/cheesy-arena/model" + "github.com/Team254/cheesy-arena/partner" "html/template" "io" "io/ioutil" @@ -81,6 +82,10 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) { return } + // Refresh the partner clients in case they changed. + tbaClient = partner.NewTbaClient(eventSettings.TbaEventCode, eventSettings.TbaSecretId, eventSettings.TbaSecret) + stemTvClient = partner.NewStemTvClient(eventSettings.StemTvEventCode) + http.Redirect(w, r, "/setup/settings", 302) } diff --git a/setup_teams.go b/setup_teams.go index bfcc519..b6884be 100644 --- a/setup_teams.go +++ b/setup_teams.go @@ -193,7 +193,7 @@ func TeamsPublishHandler(w http.ResponseWriter, r *http.Request) { return } - err := PublishTeams() + err := tbaClient.PublishTeams(db) if err != nil { http.Error(w, "Failed to publish teams: "+err.Error(), 500) return @@ -267,7 +267,7 @@ func getOfficialTeamInfo(teamId int) (*model.Team, error) { // If team info download is enabled, download the current teams data (caching isn't easy with the new paging system in the api) if eventSettings.TBADownloadEnabled { - tbaTeam, err := getTeamFromTba(teamId) + tbaTeam, err := tbaClient.GetTeam(teamId) if err != nil { return nil, err } @@ -276,12 +276,12 @@ func getOfficialTeamInfo(teamId int) (*model.Team, error) { if tbaTeam.TeamNumber == 0 { team = model.Team{Id: teamId} } else { - robotName, err := getRobotNameFromTba(teamId, time.Now().Year()) + robotName, err := tbaClient.GetRobotName(teamId, time.Now().Year()) if err != nil { return nil, err } - recentAwards, err := getTeamAwardsFromTba(teamId) + recentAwards, err := tbaClient.GetTeamAwards(teamId) if err != nil { return nil, err } diff --git a/setup_teams_test.go b/setup_teams_test.go index faa338e..79e106e 100644 --- a/setup_teams_test.go +++ b/setup_teams_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "net/http" "net/http/httptest" + "strings" "testing" ) @@ -33,12 +34,14 @@ func TestSetupTeams(t *testing.T) { "country_name": "USA", "nickname": "The Cheesy Poofs" }` - teamInfoServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, teamInfoBody) - })) - defer teamInfoServer.Close() - tbaTeamBaseUrl = teamInfoServer.URL - + teamRobotsBody := `{ + "2017": { + "team_key": "frc33", + "name": "Buzz 22", + "key": "frc33_2017", + "year": 2017 + } + }` teamAwardsBody := `[{ "event_key": "2014cmp", "award_type": 1, @@ -63,18 +66,22 @@ func TestSetupTeams(t *testing.T) { ], "year": 2014 }]` - teamAwardsServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, teamAwardsBody) - })) - defer teamAwardsServer.Close() - tbaTeamAwardsBaseUrl = teamAwardsServer.URL - eventBody := `{ "name": "Championship" }` - eventServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, eventBody) + tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.Contains(r.RequestURI, "history/robots") { + fmt.Fprintln(w, teamRobotsBody) + } else if strings.Contains(r.RequestURI, "history/awards") { + fmt.Fprintln(w, teamAwardsBody) + } else if strings.Contains(r.RequestURI, "team") { + fmt.Fprintln(w, teamInfoBody) + } else if strings.Contains(r.RequestURI, "event") { + fmt.Fprintln(w, eventBody) + } else { + http.Error(w, "Unexpected request during test", 500) + } })) - defer eventServer.Close() - tbaEventBaseUrl = eventServer.URL + defer tbaServer.Close() + tbaClient.BaseUrl = tbaServer.URL // Add some teams. recorder = postHttpResponse("/setup/teams", "teamNumbers=254\r\nnotateam\r\n1114\r\n") @@ -195,7 +202,7 @@ func TestSetupTeamsWpaKeys(t *testing.T) { func TestSetupTeamsPublish(t *testing.T) { setupTest(t) - tbaBaseUrl = "fakeurl" + tbaClient.BaseUrl = "fakeurl" eventSettings.TbaPublishingEnabled = true recorder := postHttpResponse("/setup/teams/publish", "") diff --git a/stemtv.go b/stemtv.go deleted file mode 100644 index 4b595b4..0000000 --- a/stemtv.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2016 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) -// -// Methods for publishing match video split information to STEMtv. - -package main - -import ( - "fmt" - "github.com/Team254/cheesy-arena/model" - "net/http" - "time" -) - -const ( - preMatchPaddingSec = 5 - postScoreDisplayPaddingSec = 10 -) - -var stemTvBaseUrl = "http://stemtv.io" - -func PublishMatchVideoSplit(match *model.Match, scoreDisplayTime time.Time) error { - url := fmt.Sprintf("%s/event/api/v1.0/%s/%s/split/%d,%d", stemTvBaseUrl, eventSettings.StemTvEventCode, - match.TbaCode(), match.StartedAt.Unix()-preMatchPaddingSec, - scoreDisplayTime.Unix()+postScoreDisplayPaddingSec) - _, err := http.Get(url) - return err -} diff --git a/main_test.go b/test_helpers.go similarity index 50% rename from main_test.go rename to test_helpers.go index ff6a844..747e0e5 100644 --- a/main_test.go +++ b/test_helpers.go @@ -1,23 +1,23 @@ // Copyright 2017 Team 254. All Rights Reserved. // Author: pat@patfairbank.com (Patrick Fairbank) +// +// Helper methods for use in tests in this package and others. package main import ( "github.com/Team254/cheesy-arena/model" + "github.com/Team254/cheesy-arena/partner" "github.com/stretchr/testify/assert" - "os" "testing" ) -const testDbPath = "test.db" - func setupTest(t *testing.T) { - os.Remove(testDbPath) + db = model.SetupTestDb(t, "main", ".") var err error - db, err = model.OpenDatabase(".", testDbPath) - assert.Nil(t, err) eventSettings, err = db.GetEventSettings() assert.Nil(t, err) + tbaClient = partner.NewTbaClient(eventSettings.TbaEventCode, eventSettings.TbaSecretId, eventSettings.TbaSecret) + stemTvClient = partner.NewStemTvClient(eventSettings.StemTvEventCode) mainArena.Setup() }