blob: e3650ff13acf3a1533ce1b7ca81af79580ec98d6 [file] [log] [blame]
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08001package requests
2
3import (
4 "bytes"
5 "io"
6 "net/http"
Philipp Schradercbf5c6a2022-02-27 23:25:19 -08007 "reflect"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -08008 "testing"
9
Philipp Schrader8747f1b2022-02-23 23:56:22 -080010 "github.com/frc971/971-Robot-Code/scouting/db"
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080011 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/debug"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080012 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response"
Philipp Schradercbf5c6a2022-02-27 23:25:19 -080013 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches"
14 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_all_matches_response"
Philipp Schraderacf96232022-03-01 22:03:30 -080015 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting"
16 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_data_scouting_response"
Philipp Schraderd1c4bef2022-02-28 22:51:30 -080017 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team"
18 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/request_matches_for_team_response"
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080019 "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting"
20 _ "github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/submit_data_scouting_response"
21 "github.com/frc971/971-Robot-Code/scouting/webserver/server"
22 flatbuffers "github.com/google/flatbuffers/go"
23)
24
25// Validates that an unhandled address results in a 404.
26func Test404(t *testing.T) {
Philipp Schrader8747f1b2022-02-23 23:56:22 -080027 db := MockDatabase{}
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080028 scoutingServer := server.NewScoutingServer()
Philipp Schrader8747f1b2022-02-23 23:56:22 -080029 HandleRequests(&db, scoutingServer)
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080030 scoutingServer.Start(8080)
31 defer scoutingServer.Stop()
32
33 resp, err := http.Get("http://localhost:8080/requests/foo")
34 if err != nil {
35 t.Fatalf("Failed to get data: %v", err)
36 }
37 if resp.StatusCode != http.StatusNotFound {
38 t.Fatalf("Expected error code 404, but got %d instead", resp.Status)
39 }
40}
41
42// Validates that we can submit new data scouting data.
43func TestSubmitDataScoutingError(t *testing.T) {
Philipp Schrader8747f1b2022-02-23 23:56:22 -080044 db := MockDatabase{}
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080045 scoutingServer := server.NewScoutingServer()
Philipp Schrader8747f1b2022-02-23 23:56:22 -080046 HandleRequests(&db, scoutingServer)
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080047 scoutingServer.Start(8080)
48 defer scoutingServer.Stop()
49
50 resp, err := http.Post("http://localhost:8080/requests/submit/data_scouting", "application/octet-stream", bytes.NewReader([]byte("")))
51 if err != nil {
52 t.Fatalf("Failed to send request: %v", err)
53 }
54 if resp.StatusCode != http.StatusBadRequest {
55 t.Fatal("Unexpected status code. Got", resp.Status)
56 }
57
58 responseBytes, err := io.ReadAll(resp.Body)
59 if err != nil {
60 t.Fatal("Failed to read response bytes:", err)
61 }
62 errorResponse := error_response.GetRootAsErrorResponse(responseBytes, 0)
63
64 errorMessage := string(errorResponse.ErrorMessage())
65 if errorMessage != "Failed to parse SubmitDataScouting: runtime error: index out of range [3] with length 0" {
66 t.Fatal("Got mismatched error message:", errorMessage)
67 }
68}
69
70// Validates that we can submit new data scouting data.
71func TestSubmitDataScouting(t *testing.T) {
Philipp Schrader8747f1b2022-02-23 23:56:22 -080072 db := MockDatabase{}
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080073 scoutingServer := server.NewScoutingServer()
Philipp Schrader8747f1b2022-02-23 23:56:22 -080074 HandleRequests(&db, scoutingServer)
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080075 scoutingServer.Start(8080)
76 defer scoutingServer.Stop()
77
78 builder := flatbuffers.NewBuilder(1024)
79 builder.Finish((&submit_data_scouting.SubmitDataScoutingT{
Sabina Leavere66c2fc2022-02-24 16:56:15 -080080 Team: 971,
81 Match: 1,
82 MissedShotsAuto: 9971,
83 UpperGoalAuto: 9971,
84 LowerGoalAuto: 9971,
85 MissedShotsTele: 9971,
86 UpperGoalTele: 9971,
87 LowerGoalTele: 9971,
88 DefenseRating: 9971,
89 Climbing: 9971,
Philipp Schradercdb5cfc2022-02-20 14:57:07 -080090 }).Pack(builder))
91
92 resp, err := http.Post("http://localhost:8080/requests/submit/data_scouting", "application/octet-stream", bytes.NewReader(builder.FinishedBytes()))
93 if err != nil {
94 t.Fatalf("Failed to send request: %v", err)
95 }
96 if resp.StatusCode != http.StatusNotImplemented {
97 t.Fatal("Unexpected status code. Got", resp.Status)
98 }
99 // TODO(phil): We have nothing to validate yet. Fix that.
Philipp Schraderd9096a32022-02-24 17:53:09 -0800100 // TODO(phil): Can we use scouting/webserver/requests/debug here?
Philipp Schradercdb5cfc2022-02-20 14:57:07 -0800101}
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800102
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800103// Validates that we can request the full match list.
104func TestRequestAllMatches(t *testing.T) {
105 db := MockDatabase{
106 matches: []db.Match{
107 {
108 MatchNumber: 1, Round: 1, CompLevel: "qual",
109 R1: 5, R2: 42, R3: 600, B1: 971, B2: 400, B3: 200,
110 },
111 {
112 MatchNumber: 2, Round: 1, CompLevel: "qual",
113 R1: 6, R2: 43, R3: 601, B1: 972, B2: 401, B3: 201,
114 },
115 {
116 MatchNumber: 3, Round: 1, CompLevel: "qual",
117 R1: 7, R2: 44, R3: 602, B1: 973, B2: 402, B3: 202,
118 },
119 },
120 }
121 scoutingServer := server.NewScoutingServer()
122 HandleRequests(&db, scoutingServer)
123 scoutingServer.Start(8080)
124 defer scoutingServer.Stop()
125
126 builder := flatbuffers.NewBuilder(1024)
127 builder.Finish((&request_all_matches.RequestAllMatchesT{}).Pack(builder))
128
129 response, err := debug.RequestAllMatches("http://localhost:8080", builder.FinishedBytes())
130 if err != nil {
131 t.Fatal("Failed to request all matches: ", err)
132 }
133
134 expected := request_all_matches_response.RequestAllMatchesResponseT{
135 MatchList: []*request_all_matches_response.MatchT{
136 // MatchNumber, Round, CompLevel
137 // R1, R2, R3, B1, B2, B3
138 {
139 1, 1, "qual",
140 5, 42, 600, 971, 400, 200,
141 },
142 {
143 2, 1, "qual",
144 6, 43, 601, 972, 401, 201,
145 },
146 {
147 3, 1, "qual",
148 7, 44, 602, 973, 402, 202,
149 },
150 },
151 }
152 if len(expected.MatchList) != len(response.MatchList) {
153 t.Fatal("Expected ", expected, ", but got ", *response)
154 }
155 for i, match := range expected.MatchList {
156 if !reflect.DeepEqual(*match, *response.MatchList[i]) {
157 t.Fatal("Expected for match", i, ":", *match, ", but got:", *response.MatchList[i])
158 }
159 }
160}
161
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800162// Validates that we can request the full match list.
163func TestRequestMatchesForTeam(t *testing.T) {
164 db := MockDatabase{
165 matches: []db.Match{
166 {
167 MatchNumber: 1, Round: 1, CompLevel: "qual",
168 R1: 5, R2: 42, R3: 600, B1: 971, B2: 400, B3: 200,
169 },
170 {
171 MatchNumber: 2, Round: 1, CompLevel: "qual",
172 R1: 6, R2: 43, R3: 601, B1: 972, B2: 401, B3: 201,
173 },
174 },
175 }
176 scoutingServer := server.NewScoutingServer()
177 HandleRequests(&db, scoutingServer)
178 scoutingServer.Start(8080)
179 defer scoutingServer.Stop()
180
181 builder := flatbuffers.NewBuilder(1024)
182 builder.Finish((&request_matches_for_team.RequestMatchesForTeamT{
183 Team: 971,
184 }).Pack(builder))
185
186 response, err := debug.RequestMatchesForTeam("http://localhost:8080", builder.FinishedBytes())
187 if err != nil {
188 t.Fatal("Failed to request all matches: ", err)
189 }
190
191 expected := request_matches_for_team_response.RequestMatchesForTeamResponseT{
192 MatchList: []*request_matches_for_team_response.MatchT{
193 // MatchNumber, Round, CompLevel
194 // R1, R2, R3, B1, B2, B3
195 {
196 1, 1, "qual",
197 5, 42, 600, 971, 400, 200,
198 },
199 },
200 }
201 if len(expected.MatchList) != len(response.MatchList) {
202 t.Fatal("Expected ", expected, ", but got ", *response)
203 }
204 for i, match := range expected.MatchList {
205 if !reflect.DeepEqual(*match, *response.MatchList[i]) {
206 t.Fatal("Expected for match", i, ":", *match, ", but got:", *response.MatchList[i])
207 }
208 }
209}
210
Philipp Schraderacf96232022-03-01 22:03:30 -0800211// Validates that we can request the stats.
212func TestRequestDataScouting(t *testing.T) {
213 db := MockDatabase{
214 stats: []db.Stats{
215 {
216 TeamNumber: 971, MatchNumber: 1,
217 ShotsMissed: 1, UpperGoalShots: 2, LowerGoalShots: 3,
218 ShotsMissedAuto: 4, UpperGoalAuto: 5, LowerGoalAuto: 6,
219 PlayedDefense: 7, Climbing: 8,
220 },
221 {
222 TeamNumber: 972, MatchNumber: 1,
223 ShotsMissed: 2, UpperGoalShots: 3, LowerGoalShots: 4,
224 ShotsMissedAuto: 5, UpperGoalAuto: 6, LowerGoalAuto: 7,
225 PlayedDefense: 8, Climbing: 9,
226 },
227 },
228 }
229 scoutingServer := server.NewScoutingServer()
230 HandleRequests(&db, scoutingServer)
231 scoutingServer.Start(8080)
232 defer scoutingServer.Stop()
233
234 builder := flatbuffers.NewBuilder(1024)
235 builder.Finish((&request_data_scouting.RequestDataScoutingT{}).Pack(builder))
236
237 response, err := debug.RequestDataScouting("http://localhost:8080", builder.FinishedBytes())
238 if err != nil {
239 t.Fatal("Failed to request all matches: ", err)
240 }
241
242 expected := request_data_scouting_response.RequestDataScoutingResponseT{
243 StatsList: []*request_data_scouting_response.StatsT{
244 // Team, Match,
245 // MissedShotsAuto, UpperGoalAuto, LowerGoalAuto,
246 // MissedShotsTele, UpperGoalTele, LowerGoalTele,
247 // DefenseRating, Climbing,
248 {
249 971, 1,
250 4, 5, 6,
251 1, 2, 3,
252 7, 8,
253 },
254 {
255 972, 1,
256 5, 6, 7,
257 2, 3, 4,
258 8, 9,
259 },
260 },
261 }
262 if len(expected.StatsList) != len(response.StatsList) {
263 t.Fatal("Expected ", expected, ", but got ", *response)
264 }
265 for i, match := range expected.StatsList {
266 if !reflect.DeepEqual(*match, *response.StatsList[i]) {
267 t.Fatal("Expected for stats", i, ":", *match, ", but got:", *response.StatsList[i])
268 }
269 }
270}
271
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800272// A mocked database we can use for testing. Add functionality to this as
273// needed for your tests.
274
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800275type MockDatabase struct {
276 matches []db.Match
Philipp Schraderacf96232022-03-01 22:03:30 -0800277 stats []db.Stats
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800278}
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800279
280func (database *MockDatabase) AddToMatch(db.Match) error {
281 return nil
282}
283
284func (database *MockDatabase) AddToStats(db.Stats) error {
285 return nil
286}
287
288func (database *MockDatabase) ReturnMatches() ([]db.Match, error) {
Philipp Schradercbf5c6a2022-02-27 23:25:19 -0800289 return database.matches, nil
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800290}
291
292func (database *MockDatabase) ReturnStats() ([]db.Stats, error) {
Philipp Schraderacf96232022-03-01 22:03:30 -0800293 return database.stats, nil
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800294}
295
Philipp Schraderd1c4bef2022-02-28 22:51:30 -0800296func (database *MockDatabase) QueryMatches(requestedTeam int32) ([]db.Match, error) {
297 var matches []db.Match
298 for _, match := range database.matches {
299 for _, team := range []int32{match.R1, match.R2, match.R3, match.B1, match.B2, match.B3} {
300 if team == requestedTeam {
301 matches = append(matches, match)
302 break
303 }
304 }
305 }
306 return matches, nil
Philipp Schrader8747f1b2022-02-23 23:56:22 -0800307}
308
309func (database *MockDatabase) QueryStats(int) ([]db.Stats, error) {
310 return []db.Stats{}, nil
311}