Newer
Older
package rootmux
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/http/httptest"
"net/url"
"os"
"regexp"
"testing"
"forge.grandlyon.com/apoyen/elections/internal/auth"
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
"forge.grandlyon.com/apoyen/sdk-go/pkg/tester"
"forge.grandlyon.com/apoyen/sdk-go/pkg/tokens"
"forge.grandlyon.com/apoyen/elections/internal/mocks"
)
var (
reg, _ = regexp.Compile("[\n \t]+")
initialUsersBuff, _ = ioutil.ReadFile("../../configs/users.json")
initialUsers = reg.ReplaceAllString(string(initialUsersBuff), "")
newUser = `{"id":"3","login":"new_user","memberOf":["USERS"],"password":"test"}`
noH = tester.Header{Key: "", Value: ""}
)
func init() {
tokens.Init("../../configs/tokenskey.json", true)
}
func TestAll(t *testing.T) {
os.Mkdir("data", os.ModePerm)
os.Create("./data/test.db")
os.Link("../../data/users.db", "./data/users.db")
// Create the mock OAuth2 server
oAuth2Server := httptest.NewServer(mocks.CreateMockOAuth2())
defer oAuth2Server.Close()
// Create the mock API server
go http.ListenAndServe(":8091", mocks.CreateMockAPI())
// Set the constants with environment variables
os.Setenv("HOSTNAME", "sdk-go.io")
os.Setenv("ADMIN_GROUP", "ADMINS")
os.Setenv("CLIENT_ID", "clientid")
os.Setenv("CLIENT_SECRET", "clientsecret")
os.Setenv("TOKEN_URL", oAuth2Server.URL+"/token")
os.Setenv("USERINFO_URL", oAuth2Server.URL+"/userinfo")
os.Setenv("LOGOUT_URL", oAuth2Server.URL+"/logout")
// Set up testers
os.Setenv("AUTH_URL", oAuth2Server.URL+"/auth-wrong-state") // Set the server to access failing OAuth2 endpoints
Oauth2Tests(t)
os.Setenv("AUTH_URL", oAuth2Server.URL+"/auth") // Set the server to access the correct OAuth2Endpoint
os.Setenv("USERINFO_URL", oAuth2Server.URL+"/admininfo")
deletionInCascadeGenericElectionTest(t)
resetDataWithData(t)
deletionInCascadeRoundTest(t)
resetDataWithData(t)
deletionInCascadePartyTest(t)
resetDataWithData(t)
removeRoundRemoveDeskRoundsTest(t)
resetDataWithData(t)
CapturerTests(t)
resetDataWithData(t)
VisualizerTests(t)
os.RemoveAll("./data")
}
/**
SECURITY TESTS (this tests are to check that the security protections works)
**/
func Oauth2Tests(t *testing.T) {
// Create the tester
ts, do, _ := createTester(t)
defer ts.Close() // Close the tester
// Try to login (must fail)
do("GET", "/OAuth2Login", noH, "", 500, "invalid oauth state")
}
func appTests(t *testing.T) {
ts, do, _ := createTester(t)
defer ts.Close() // Close the tester
tests := func() {
// Get the XSRF Token
response := do("GET", "/api/common/WhoAmI", noH, "", 200, "")
token := auth.TokenData{}
json.Unmarshal([]byte(response), &token)
xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
// Add a capturer to an already bind UserID should fail
do("POST", "/api/Capturer", xsrfHeader, `{"UserID":2,"Name":"Capturer"}`, 500, `UserID is already bind to a Capturer`)
// Verify that RoundDesks have been created on Round Creation
do("GET", "/api/Round/1", xsrfHeader, ``, 200, `{"ID":1,"ElectionID":1,"Parameter":{"ID":0,"CountBlankAndNull":false,"ShowOnlyCompleted":false,"ShowMap":false},"Date":"2020-06-28","Round":1,"DeskRounds":[{"ID":1,"RoundID":1,"DeskID":1,"Capturers":null,"Completed":false,"DateCompletion":"0001-01-01T00:00:00Z","Validated":false,"Votes":null},{"ID":2,"RoundID":1,"DeskID":2,"Capturers":null,"Completed":false,"DateCompletion":"0001-01-01T00:00:00Z","Validated":false,"Votes":null}],"CandidateLists":[{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":null,"Votes":null}]}`)
do("GET", "/api/Desk/1", xsrfHeader, ``, 200, `{"ID":1,"SectionID":1,"Name":"Desk 1","WitnessDesk":true,"Subscribed":9587,"DeskRounds":[{"ID":1,"RoundID":1,"DeskID":1,"Capturers":null,"Completed":false,"DateCompletion":"0001-01-01T00:00:00Z","Validated":false,"Votes":null}]}`)
// Verify that a DeskRound can't be validated witout being completed
do("PUT", "/api/DeskRound/1", xsrfHeader, `{"ID":1,"Validated":true}`, 500, `Le bureau doit être complété avant de le valider`)
do("POST", "/api/Vote", xsrfHeader, `{"DeskRoundID":1,"CandidateListID":null,"VoiceNumber":3,"Blank":true}`, 200, `{"ID":3,"DeskRoundID":1,"CandidateListID":0,"VoiceNumber":3,"Blank":true,"NullVote":false}`)
do("POST", "/api/Vote", xsrfHeader, `{"DeskRoundID":1,"CandidateListID":null,"VoiceNumber":5,"NullVote":true}`, 200, `{"ID":4,"DeskRoundID":1,"CandidateListID":0,"VoiceNumber":5,"Blank":false,"NullVote":true}`)
do("GET", "/api/DeskRound/1", xsrfHeader, ``, 200, `{"ID":1,"RoundID":1,"DeskID":1,"Capturers":[{"ID":1,"UserID":2,"Name":"Capturer","DeskRounds":null}],"Completed":true,"DateCompletion":"20`)
// Check to update the good vote
do("PUT", "/api/Vote/1", xsrfHeader, `{"DeskRoundID":1,"CandidateListID":1,"VoiceNumber":258,"Blank":false,"NullVote":false}`, 200, `{"ID":1,"DeskRoundID":1,"CandidateListID":1,"VoiceNumber":258,"Blank":false,"NullVote":false}`)
do("PUT", "/api/Vote/1", xsrfHeader, `{"DeskRoundID":1,"CandidateListID":null,"VoiceNumber":158,"Blank":true,"NullVote":false}`, 200, `{"ID":3,"DeskRoundID":1,"CandidateListID":0,"VoiceNumber":158,"Blank":true,"NullVote":false}`)
do("PUT", "/api/Vote/1", xsrfHeader, `{"DeskRoundID":1,"CandidateListID":null,"VoiceNumber":158,"Blank":false,"NullVote":true}`, 200, `{"ID":4,"DeskRoundID":1,"CandidateListID":0,"VoiceNumber":158,"Blank":false,"NullVote":true}`)
// Can't add the same vote several time
do("POST", "/api/Vote", xsrfHeader, `{"DeskRoundID":1,"CandidateListID":1,"VoiceNumber":158}`, 500, `Error the vote have already been captured`)
do("POST", "/api/Vote", xsrfHeader, `{"DeskRoundID":1,"CandidateListID":null,"VoiceNumber":3,"Blank":true}`, 500, `Error the vote have already been captured`)
do("POST", "/api/Vote", xsrfHeader, `{"DeskRoundID":1,"CandidateListID":null,"VoiceNumber":5,"NullVote":true}`, 500, `Error the vote have already been captured`)
// Update a DeskRound to Validated=true can only be done when votes are captured
do("PUT", "/api/DeskRound/1", xsrfHeader, `{"ID":1,"Validated":true}`, 200, `{"ID":1,"RoundID":1,"DeskID":1,"Capturers":[{"ID":1,"UserID":2,"Name":"Capturer","DeskRounds":null}],"Completed":true,"DateCompletion":"20`)
// If DeskRound is validated, votes can't be updated or deleted
do("PUT", "/api/Vote/1", xsrfHeader, `{"DeskRoundID":1,"CandidateListID":1,"VoiceNumber":358,"Blank":false,"NullVote":false}`, 500, `Error the vote have already been validated and can't be updated`)
do("DELETE", "/api/Vote/1", xsrfHeader, ``, 500, `Error the vote have already been validated and can't be updated`)
// //Check that on Vote deletion, deskRound is updated
do("PUT", "/api/DeskRound/1", xsrfHeader, `{"ID":1,"Validated":false}`, 200, `{"ID":1,"RoundID":1,"DeskID":1,"Capturers":[{"ID":1,"UserID":2,"Name":"Capturer","DeskRounds":null}],"Completed":true,"DateCompletion":"20`)
do("DELETE", "/api/Vote/1", xsrfHeader, ``, 200, ``)
do("GET", "/api/DeskRound/1", xsrfHeader, ``, 200, `{"ID":1,"RoundID":1,"DeskID":1,"Capturers":[{"ID":1,"UserID":2,"Name":"Capturer","DeskRounds":null}],"Completed":false,"DateCompletion":"0001-01-01T00:00:00Z","Validated":false,"Votes":[{"ID":3,"DeskRoundID":1,"CandidateListID":0,"VoiceNumber":158,"Blank":true,"NullVote":false},{"ID":4,"DeskRoundID":1,"CandidateListID":0,"VoiceNumber":158,"Blank":false,"NullVote":true}]}`)
// Verify that on Desk deletion deskRounds are deleted
do("GET", "/api/Desk/1", xsrfHeader, ``, 200, `{"ID":1,"SectionID":1,"Name":"Desk 1","WitnessDesk":true,"Subscribed":9587,"DeskRounds":[{"ID":1,"RoundID":1,"DeskID":1,"Capturers":null,"Completed":false,"DateCompletion":"0001-01-01T00:00:00Z","Validated":false,"Votes":null}]}`)
do("DELETE", "/api/Desk/1", xsrfHeader, ``, 200, ``)
do("GET", "/api/DeskRound/1", xsrfHeader, ``, 404, `id is missing`)
}
// Do an OAuth2 login with an known admin
do("GET", "/OAuth2Login", noH, "", 200, "<!DOCTYPE html>")
tests()
// Try to logout (must pass)
do("GET", "/Logout", noH, "", 200, "Logout OK")
}
func deletionInCascadeGenericElectionTest(t *testing.T) {
ts, do, _ := createTester(t)
defer ts.Close() // Close the tester
tests := func() {
// Get the XSRF Token
response := do("GET", "/api/common/WhoAmI", noH, "", 200, "")
token := auth.TokenData{}
json.Unmarshal([]byte(response), &token)
xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
// Test deletion in cascade for generic election
do("DELETE", "/api/Election/1", xsrfHeader, ``, 200, ``)
do("GET", "/api/Area/1", xsrfHeader, ``, 404, `id is missing`)
do("GET", "/api/Section/1", xsrfHeader, ``, 404, `id is missing`)
do("GET", "/api/Desk/1", xsrfHeader, ``, 404, `id is missing`)
do("GET", "/api/Round/1", xsrfHeader, ``, 404, `id is missing`)
}
// Do an OAuth2 login with an known admin
do("GET", "/OAuth2Login", noH, "", 200, "<!DOCTYPE html>")
tests()
// Try to logout (must pass)
do("GET", "/Logout", noH, "", 200, "Logout OK")
func deletionInCascadePartyTest(t *testing.T) {
ts, do, _ := createTester(t)
defer ts.Close() // Close the tester
tests := func() {
// Get the XSRF Token
response := do("GET", "/api/common/WhoAmI", noH, "", 200, "")
token := auth.TokenData{}
json.Unmarshal([]byte(response), &token)
xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
// Check that Candidate and candidateLists are deleted in cascade on Party deletion.
do("DELETE", "/api/Party/1", xsrfHeader, ``, 200, ``)
do("GET", "/api/CandidateList/1", xsrfHeader, ``, 404, `id is missing`)
do("GET", "/api/Candidate/1", xsrfHeader, ``, 404, `id is missing`)
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
}
// Do an OAuth2 login with an known admin
do("GET", "/OAuth2Login", noH, "", 200, "<!DOCTYPE html>")
tests()
// Try to logout (must pass)
do("GET", "/Logout", noH, "", 200, "Logout OK")
}
func deletionInCascadeRoundTest(t *testing.T) {
ts, do, _ := createTester(t)
defer ts.Close() // Close the tester
tests := func() {
// Get the XSRF Token
response := do("GET", "/api/common/WhoAmI", noH, "", 200, "")
token := auth.TokenData{}
json.Unmarshal([]byte(response), &token)
xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
// Test deletion in cascade for generic election
do("DELETE", "/api/Round/1", xsrfHeader, ``, 200, ``)
do("GET", "/api/CandidateList/1", xsrfHeader, ``, 404, `id is missing`)
do("GET", "/api/Candidate/1", xsrfHeader, ``, 404, `id is missing`)
}
// Do an OAuth2 login with an known admin
do("GET", "/OAuth2Login", noH, "", 200, "<!DOCTYPE html>")
tests()
// Try to logout (must pass)
do("GET", "/Logout", noH, "", 200, "Logout OK")
}
func removeRoundRemoveDeskRoundsTest(t *testing.T) {
ts, do, _ := createTester(t)
defer ts.Close() // Close the tester
tests := func() {
// Get the XSRF Token
response := do("GET", "/api/common/WhoAmI", noH, "", 200, "")
token := auth.TokenData{}
json.Unmarshal([]byte(response), &token)
xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
// Remove a Round should remove all DeskRounds
// Verify DeskRounds creation
do("GET", "/api/Desk/1", xsrfHeader, ``, 200, `{"ID":1,"SectionID":1,"Name":"Desk 1","WitnessDesk":true,"Subscribed":9587,"DeskRounds":[{"ID":1,"RoundID":1,"DeskID":1,"Capturers":null,"Completed":false,"DateCompletion":"0001-01-01T00:00:00Z","Validated":false,"Votes":null}]}`)
// Delete a Round
do("DELETE", "/api/Round/1", xsrfHeader, ``, 200, ``)
// Verify DeskRounds doesnt exist anymore
do("GET", "/api/Desk/1", xsrfHeader, ``, 200, `{"ID":1,"SectionID":1,"Name":"Desk 1","WitnessDesk":true,"Subscribed":9587,"DeskRounds":[]}`)
}
// Do an OAuth2 login with an known admin
do("GET", "/OAuth2Login", noH, "", 200, "<!DOCTYPE html>")
tests()
// Try to logout (must pass)
do("GET", "/Logout", noH, "", 200, "Logout OK")
}
func resetData(t *testing.T) {
os.RemoveAll("./data")
os.Mkdir("data", os.ModePerm)
os.Create("./data/test.db")
os.Link("../../data/users.db", "./data/users.db")
ts, do, _ := createTester(t)
defer ts.Close() // Close the tester
// Init Data in DB tests
init := func() {
response := do("GET", "/api/common/WhoAmI", noH, "", 200, "")
token := auth.TokenData{}
json.Unmarshal([]byte(response), &token)
xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
// Create a capturer
do("POST", "/api/Capturer", xsrfHeader, `{"UserID":2,"Name":"Capturer"}`, 200, `{"ID":1,"UserID":2,"Name":"Capturer","DeskRounds":null}`)
do("POST", "/api/Capturer", xsrfHeader, `{"UserID":3,"Name":"Capturer"}`, 200, `{"ID":2,"UserID":3,"Name":"Capturer","DeskRounds":null}`)
}
do("POST", "/Login", noH, `{"login": "admin","password": "password"}`, 200, "")
init()
do("GET", "/Logout", noH, "", 200, "Logout OK")
}
func resetDataWithData(t *testing.T) {
os.RemoveAll("./data")
os.Mkdir("data", os.ModePerm)
os.Create("./data/test.db")
os.Link("../../data/users.db", "./data/users.db")
ts, do, _ := createTester(t)
defer ts.Close() // Close the tester
// Init Data in DB tests
init := func() {
response := do("GET", "/api/common/WhoAmI", noH, "", 200, "")
token := auth.TokenData{}
json.Unmarshal([]byte(response), &token)
xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
// Create a capturer
do("POST", "/api/Capturer", xsrfHeader, `{"UserID":2,"Name":"Capturer"}`, 200, `{"ID":1,"UserID":2,"Name":"Capturer","DeskRounds":null}`)
do("POST", "/api/Capturer", xsrfHeader, `{"UserID":3,"Name":"Capturer"}`, 200, `{"ID":2,"UserID":3,"Name":"Capturer","DeskRounds":null}`)
do("POST", "/api/Election", xsrfHeader, `{"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct"}`, 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","Areas":null,"Rounds":null}`)
do("POST", "/api/Area", xsrfHeader, `{"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1"}`, 200, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}`)
do("POST", "/api/Section", xsrfHeader, `{"AreaID":1,"Name":"Section 1","MapID":"1"}`, 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":null}`)
do("POST", "/api/Desk", xsrfHeader, `{"SectionID":1,"Name":"Desk 1","WitnessDesk":true,"Subscribed":9587}`, 200, `{"ID":1,"SectionID":1,"Name":"Desk 1","WitnessDesk":true,"Subscribed":9587,"DeskRounds":null}`)
do("POST", "/api/Desk", xsrfHeader, `{"SectionID":1,"Name":"Desk 2","WitnessDesk":false,"Subscribed":3784}`, 200, `{"ID":2,"SectionID":1,"Name":"Desk 2","WitnessDesk":false,"Subscribed":3784,"DeskRounds":null}`)
do("POST", "/api/Round", xsrfHeader, `{"ElectionID":1,"Date":"2020-06-28","Round":1}`, 200, `{"ID":1,"ElectionID":1,"Parameter":{"ID":0,"CountBlankAndNull":false,"ShowOnlyCompleted":false,"ShowMap":false},"Date":"2020-06-28","Round":1,"DeskRounds":null,"CandidateLists":null}`)
do("POST", "/api/Party", xsrfHeader, `{"Name":"MyGreatParty","Color":"#FFFFFF"}`, 200, `{"ID":1,"Name":"MyGreatParty","Color":"#FFFFFF","CandidateLists":null}`)
do("POST", "/api/CandidateList", xsrfHeader, `{"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1}`, 200, `{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":null,"Votes":null}`)
do("POST", "/api/Candidate", xsrfHeader, `{"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`, 200, `{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`)
do("POST", "/api/Vote", xsrfHeader, `{"DeskRoundID":1,"CandidateListID":1,"VoiceNumber":158}`, 200, `{"ID":1,"DeskRoundID":1,"CandidateListID":1,"VoiceNumber":158,"Blank":false,"NullVote":false}`)
do("POST", "/api/Vote", xsrfHeader, `{"DeskRoundID":2,"CandidateListID":1,"VoiceNumber":103}`, 200, `{"ID":2,"DeskRoundID":2,"CandidateListID":1,"VoiceNumber":103,"Blank":false,"NullVote":false}`)
do("POST", "/api/CapturerDeskRound", xsrfHeader, `{"CapturerID":1,"DeskRoundID":1}`, 200, `{"ID":1,"UserID":2,"Name":"Capturer","DeskRounds":[{"ID":1,"RoundID":1,"DeskID":1,"Capturers":null,"Completed":false,"DateCompletion":"0001-01-01T00:00:00Z","Validated":false,"Votes":null}]}`)
}
do("POST", "/Login", noH, `{"login": "admin","password": "password"}`, 200, "")
init()
do("GET", "/Logout", noH, "", 200, "Logout OK")
}
func createTester(t *testing.T) (*httptest.Server, func(method string, url string, header tester.Header, payload string, expectedStatus int, expectedBody string) string, func(method string, url string, header tester.Header, payload string, expectedStatus int, expectedBody string) string) {
// Create the server
mux := CreateRootMux(1443, "../../web")
ts := httptest.NewServer(mux.Mux)
url, _ := url.Parse(ts.URL)
port := url.Port()
mux.Manager.Config.RedirectURL = "http://" + os.Getenv("HOSTNAME") + ":" + port + "/OAuth2Callback"
mux.Manager.Hostname = "http://" + os.Getenv("HOSTNAME") + ":" + port
// Create the cookie jar
jar, _ := cookiejar.New(nil)
// wrap the testing function
return ts, tester.CreateServerTester(t, port, os.Getenv("HOSTNAME"), jar), tester.CreateServerTester(t, port, os.Getenv("HOSTNAME"), nil)
}