Insert election with uuid instead of auto-generated id
This commit is contained in:
@ -145,7 +145,7 @@ func (r *createVotesRequestWithValidator) isValid() bool {
|
||||
return r.Valid()
|
||||
}
|
||||
|
||||
func (app *application) CreateVotes(w http.ResponseWriter, r *http.Request, id int) {
|
||||
func (app *application) CreateVotes(w http.ResponseWriter, r *http.Request, id string) {
|
||||
var request createVotesRequestWithValidator
|
||||
|
||||
if err := app.unmarshalRequest(r, &request); err != nil {
|
||||
@ -284,7 +284,7 @@ func (app *application) createVotesHandleUnknownVotersElection(w http.ResponseWr
|
||||
return voterIdentity, nil
|
||||
}
|
||||
|
||||
func (app *application) GetElectionResults(w http.ResponseWriter, r *http.Request, id int) {
|
||||
func (app *application) GetElectionResults(w http.ResponseWriter, r *http.Request, id string) {
|
||||
votes, err := app.votes.GetByElection(id)
|
||||
if err != nil {
|
||||
if errors.Is(sql.ErrNoRows, err) {
|
||||
@ -325,7 +325,7 @@ func getResultsFromVotes(votes *[]models.Vote) []api.VotesForChoice {
|
||||
return results
|
||||
}
|
||||
|
||||
func (app *application) GetElection(w http.ResponseWriter, r *http.Request, id int) {
|
||||
func (app *application) GetElection(w http.ResponseWriter, r *http.Request, id string) {
|
||||
election, err := app.elections.GetById(id)
|
||||
if err != nil {
|
||||
if errors.Is(sql.ErrNoRows, err) {
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"net/http"
|
||||
@ -21,10 +22,11 @@ func TestCreateElection(t *testing.T) {
|
||||
server := newTestServer(t, app.routes())
|
||||
defer server.Close()
|
||||
|
||||
id, _ := uuid.NewV7()
|
||||
mockElections := app.elections.(*mockElectionModel)
|
||||
mockElections.
|
||||
On("Insert", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(1, nil)
|
||||
Return(id.String(), nil)
|
||||
|
||||
path := baseUri + "election"
|
||||
|
||||
@ -197,10 +199,11 @@ func TestCreateElection_KnownVoters(t *testing.T) {
|
||||
server := newTestServer(t, app.routes())
|
||||
defer server.Close()
|
||||
|
||||
electionId, _ := uuid.NewV7()
|
||||
mockElections := app.elections.(*mockElectionModel)
|
||||
mockElections.
|
||||
On("Insert", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(1, nil)
|
||||
Return(electionId.String(), nil)
|
||||
|
||||
mockVoters := app.voters.(*mockVoterModel)
|
||||
mockVoters.
|
||||
@ -270,11 +273,12 @@ func TestCreateVotes_UnknownVotersElection(t *testing.T) {
|
||||
server := newTestServer(t, app.routes())
|
||||
defer server.Close()
|
||||
|
||||
id, _ := uuid.NewV7()
|
||||
mockElections := app.elections.(*mockElectionModel)
|
||||
mockElections.
|
||||
On("GetById", mock.Anything).
|
||||
Return(&models.Election{
|
||||
ID: 1,
|
||||
ID: id.String(),
|
||||
Name: "Guy of the year",
|
||||
Tokens: 100,
|
||||
AreVotersKnown: false,
|
||||
@ -373,11 +377,12 @@ func TestCreateVotes_KnownVotersElection(t *testing.T) {
|
||||
server := newTestServer(t, app.routes())
|
||||
defer server.Close()
|
||||
|
||||
id, _ := uuid.NewV7()
|
||||
mockElections := app.elections.(*mockElectionModel)
|
||||
mockElections.
|
||||
On("GetById", mock.Anything).
|
||||
Return(&models.Election{
|
||||
ID: 1,
|
||||
ID: id.String(),
|
||||
Name: "Guy of the year",
|
||||
Tokens: 100,
|
||||
AreVotersKnown: true,
|
||||
@ -534,7 +539,7 @@ func TestCreateVotes_NonExistingElection(t *testing.T) {
|
||||
assert.Equal(t, http.StatusNotFound, code)
|
||||
}
|
||||
|
||||
func TestCreateVotes_NonNumberElectionID(t *testing.T) {
|
||||
func TestCreateVotes_NonUuidElectionID(t *testing.T) {
|
||||
app := newTestApplication(t)
|
||||
server := newTestServer(t, app.routes())
|
||||
defer server.Close()
|
||||
@ -562,7 +567,7 @@ func TestCreateVotes_NonNumberElectionID(t *testing.T) {
|
||||
|
||||
code, _, _ := server.post(t, path, bytes.NewReader(requestBodyJson))
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
assert.Equal(t, http.StatusNotFound, code)
|
||||
}
|
||||
|
||||
func TestCreateVotes_AlreadyVoted(t *testing.T) {
|
||||
@ -570,8 +575,9 @@ func TestCreateVotes_AlreadyVoted(t *testing.T) {
|
||||
server := newTestServer(t, app.routes())
|
||||
defer server.Close()
|
||||
|
||||
unknownVotersElectionId, _ := uuid.NewV7()
|
||||
unknownVotersElection := models.Election{
|
||||
ID: 1,
|
||||
ID: unknownVotersElectionId.String(),
|
||||
Name: "Guy of the year",
|
||||
Tokens: 100,
|
||||
AreVotersKnown: false,
|
||||
@ -580,15 +586,18 @@ func TestCreateVotes_AlreadyVoted(t *testing.T) {
|
||||
ExpiresAt: time.Now().Add(24 * time.Hour),
|
||||
Choices: []string{"Gandhi", "Buddha"},
|
||||
}
|
||||
|
||||
knownVotersElection := unknownVotersElection
|
||||
knownVotersElectionId, _ := uuid.NewV7()
|
||||
knownVotersElection.ID = knownVotersElectionId.String()
|
||||
knownVotersElection.AreVotersKnown = true
|
||||
|
||||
mockElections := app.elections.(*mockElectionModel)
|
||||
mockElections.
|
||||
On("GetById", 1).
|
||||
On("GetById", knownVotersElectionId.String()).
|
||||
Return(&knownVotersElection, nil)
|
||||
mockElections.
|
||||
On("GetById", 2).
|
||||
On("GetById", unknownVotersElectionId.String()).
|
||||
Return(&unknownVotersElection, nil)
|
||||
|
||||
mockVotes := app.votes.(*mockVoteModel)
|
||||
@ -601,8 +610,9 @@ func TestCreateVotes_AlreadyVoted(t *testing.T) {
|
||||
On("Exists", mock.Anything, mock.Anything).
|
||||
Return(true, nil)
|
||||
|
||||
knownVotersElectionPath := baseUri + "election/1/votes"
|
||||
unknownVotersElectionPath := baseUri + "election/2/votes"
|
||||
layout := baseUri + "election/%v/votes"
|
||||
knownVotersElectionPath := fmt.Sprintf(layout, unknownVotersElectionId)
|
||||
unknownVotersElectionPath := fmt.Sprintf(layout, knownVotersElectionId)
|
||||
voterIdentity := "anything"
|
||||
|
||||
tests := []struct {
|
||||
@ -661,11 +671,12 @@ func TestCreateVotes_UnknownVotersElectionMaxVotersReached(t *testing.T) {
|
||||
server := newTestServer(t, app.routes())
|
||||
defer server.Close()
|
||||
|
||||
id, _ := uuid.NewV7()
|
||||
mockElections := app.elections.(*mockElectionModel)
|
||||
mockElections.
|
||||
On("GetById", mock.Anything).
|
||||
Return(&models.Election{
|
||||
ID: 1,
|
||||
ID: id.String(),
|
||||
Name: "Guy of the year",
|
||||
Tokens: 100,
|
||||
AreVotersKnown: false,
|
||||
@ -712,11 +723,12 @@ func TestCreateVotes_ExpiredElection(t *testing.T) {
|
||||
server := newTestServer(t, app.routes())
|
||||
defer server.Close()
|
||||
|
||||
id, _ := uuid.NewV7()
|
||||
mockElections := app.elections.(*mockElectionModel)
|
||||
mockElections.
|
||||
On("GetById", mock.Anything).
|
||||
Return(&models.Election{
|
||||
ID: 1,
|
||||
ID: id.String(),
|
||||
Name: "Guy of the year",
|
||||
Tokens: 100,
|
||||
AreVotersKnown: false,
|
||||
@ -763,38 +775,39 @@ func TestGetElectionResults(t *testing.T) {
|
||||
server := newTestServer(t, app.routes())
|
||||
defer server.Close()
|
||||
|
||||
electionID, _ := uuid.NewV7()
|
||||
votes := []models.Vote{
|
||||
{
|
||||
VoterIdentity: "Voter1",
|
||||
ElectionID: 1,
|
||||
ElectionID: electionID.String(),
|
||||
ChoiceText: "Choice1",
|
||||
Tokens: 2,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
VoterIdentity: "Voter2",
|
||||
ElectionID: 1,
|
||||
ElectionID: electionID.String(),
|
||||
ChoiceText: "Choice2",
|
||||
Tokens: 4,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
VoterIdentity: "Voter3",
|
||||
ElectionID: 1,
|
||||
ElectionID: electionID.String(),
|
||||
ChoiceText: "Choice3",
|
||||
Tokens: 6,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
VoterIdentity: "Voter4",
|
||||
ElectionID: 1,
|
||||
ElectionID: electionID.String(),
|
||||
ChoiceText: "Choice1",
|
||||
Tokens: 8,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
VoterIdentity: "Voter5",
|
||||
ElectionID: 1,
|
||||
ElectionID: electionID.String(),
|
||||
ChoiceText: "Choice2",
|
||||
Tokens: 10,
|
||||
CreatedAt: time.Now(),
|
||||
@ -802,10 +815,10 @@ func TestGetElectionResults(t *testing.T) {
|
||||
}
|
||||
mockVotes := app.votes.(*mockVoteModel)
|
||||
mockVotes.
|
||||
On("GetByElection", mock.Anything).
|
||||
On("GetByElection", electionID.String()).
|
||||
Return(&votes, nil)
|
||||
|
||||
path := baseUri + "election/1/results"
|
||||
path := baseUri + fmt.Sprintf("election/%v/results", electionID)
|
||||
code, _, body := server.get(t, path)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
@ -857,11 +870,12 @@ func TestGetElection_Found(t *testing.T) {
|
||||
server := newTestServer(t, app.routes())
|
||||
defer server.Close()
|
||||
|
||||
id, _ := uuid.NewV7()
|
||||
mockElections := app.elections.(*mockElectionModel)
|
||||
mockElections.
|
||||
On("GetById", mock.Anything).
|
||||
Return(&models.Election{
|
||||
ID: 1,
|
||||
ID: id.String(),
|
||||
Name: "Guy of the year",
|
||||
Tokens: 100,
|
||||
AreVotersKnown: false,
|
||||
|
@ -1,5 +1,3 @@
|
||||
//go:build !integration
|
||||
|
||||
//go:generate oapi-codegen --config=oapi-codegen.yml openapi.yml
|
||||
|
||||
package main
|
||||
|
@ -1,281 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"code.dlmw.ch/dlmw/qv/internal/migrations"
|
||||
"code.dlmw.ch/dlmw/qv/internal/models"
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
. "github.com/Eun/go-hit"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var addr = ":8080"
|
||||
|
||||
func main() {
|
||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
|
||||
|
||||
err := os.Remove("./qv.integration.sqlite")
|
||||
if err != nil {
|
||||
logger.Error("couldn't delete qv.integration.sqlite")
|
||||
}
|
||||
db, err := openDb()
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
defer db.Close()
|
||||
err = migrations.Run(db)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
app := &application{
|
||||
logger: logger,
|
||||
elections: &models.ElectionModel{DB: db},
|
||||
voters: &models.VoterModel{DB: db},
|
||||
votes: &models.VoteModel{DB: db},
|
||||
}
|
||||
|
||||
logger.Info("Starting integration tests server", "addr", addr)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: app.routes(),
|
||||
ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError),
|
||||
IdleTimeout: time.Minute,
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
go func() {
|
||||
logger.Info("Starting integration test server", "addr", addr)
|
||||
|
||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
logger.Error("Server error", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(1 * time.Second) // wait until srv starts
|
||||
runTests()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
srv.Shutdown(ctx)
|
||||
}
|
||||
|
||||
func openDb() (*sql.DB, error) {
|
||||
db, err := sql.Open("sqlite3", "./qv.integration.sqlite?_foreign_keys=on")
|
||||
if err == nil {
|
||||
err = db.Ping()
|
||||
}
|
||||
|
||||
return db, err
|
||||
}
|
||||
|
||||
func runTests() {
|
||||
runCreateElectionTests()
|
||||
runCreateVotesTests()
|
||||
}
|
||||
|
||||
func runCreateElectionTests() {
|
||||
template := CombineSteps(
|
||||
Post("http://127.0.0.1:8080/api/election"),
|
||||
)
|
||||
|
||||
MustDo(
|
||||
template,
|
||||
Send().Body().String(`
|
||||
{
|
||||
"name": "Guy of the year",
|
||||
"tokens": 100,
|
||||
"areVotersKnown": false,
|
||||
"maxVoters": 10,
|
||||
"expiresAt": "2124-12-31T14:15:22Z",
|
||||
"choices": [
|
||||
"Gandhi",
|
||||
"Buddha"
|
||||
]
|
||||
}`),
|
||||
Expect().Status().Equal(http.StatusOK),
|
||||
Expect().Headers("Location").Contains("/election/1"),
|
||||
)
|
||||
|
||||
MustDo(
|
||||
template,
|
||||
Send().Body().String(`
|
||||
{
|
||||
"name": "Guy of the year",
|
||||
"tokens": 100,
|
||||
"areVotersKnown": false,
|
||||
"maxVoters": 0,
|
||||
"expiresAt": "2124-12-31T14:15:22Z",
|
||||
"choices": [
|
||||
"Gandhi",
|
||||
"Buddha"
|
||||
]
|
||||
}`),
|
||||
Expect().Status().Equal(http.StatusOK),
|
||||
Expect().Headers("Location").Contains("/election/2"),
|
||||
)
|
||||
|
||||
MustDo(
|
||||
template,
|
||||
Send().Body().String(`
|
||||
{
|
||||
"name": "Guy of the year",
|
||||
"tokens": 100,
|
||||
"areVotersKnown": true,
|
||||
"maxVoters": 10,
|
||||
"expiresAt": "2124-12-31T14:15:22Z",
|
||||
"choices": [
|
||||
"Gandhi",
|
||||
"Buddha"
|
||||
]
|
||||
}`),
|
||||
Expect().Status().Equal(http.StatusOK),
|
||||
Expect().Headers("Location").Contains("/election/3"),
|
||||
Expect().Body().JSON().JQ(".voterIdentities").Len().Equal(10),
|
||||
)
|
||||
|
||||
MustDo(
|
||||
template,
|
||||
Send().Body().String(`
|
||||
{
|
||||
"name": "Guy of the year",
|
||||
"tokens": 100,
|
||||
"areVotersKnown": true,
|
||||
"maxVoters": -1,
|
||||
"expiresAt": "2124-12-31T14:15:22Z",
|
||||
"choices": [
|
||||
"Gandhi",
|
||||
"Buddha"
|
||||
]
|
||||
}`),
|
||||
Expect().Status().Equal(http.StatusUnprocessableEntity),
|
||||
)
|
||||
|
||||
MustDo(
|
||||
template,
|
||||
Send().Body().String(`
|
||||
{
|
||||
"name": "Guy of the year",
|
||||
"tokens": 100,
|
||||
"areVotersKnown": false,
|
||||
"maxVoters": -1,
|
||||
"expiresAt": "2124-12-31T14:15:22Z",
|
||||
"choices": [
|
||||
"Gandhi"
|
||||
]
|
||||
}`),
|
||||
Expect().Status().Equal(http.StatusUnprocessableEntity),
|
||||
)
|
||||
|
||||
MustDo(
|
||||
template,
|
||||
Send().Body().String(`
|
||||
{
|
||||
"name": "Guy of the year",
|
||||
"tokens": 100,
|
||||
"areVotersKnown": false,
|
||||
"maxVoters": 10,
|
||||
"expiresAt": "2018-12-31T14:15:22Z",
|
||||
"choices": [
|
||||
"Gandhi",
|
||||
"Buddha"
|
||||
]
|
||||
}`),
|
||||
Expect().Status().Equal(http.StatusUnprocessableEntity),
|
||||
)
|
||||
}
|
||||
|
||||
func runCreateVotesTests() {
|
||||
uri := "http://127.0.0.1:8080/api/election/%v/votes"
|
||||
|
||||
MustDo(
|
||||
Post(fmt.Sprintf(uri, "1")),
|
||||
Send().Body().String(`
|
||||
{
|
||||
"choices": [
|
||||
{
|
||||
"choiceText": "Buddha",
|
||||
"tokens": 90
|
||||
},
|
||||
{
|
||||
"choiceText": "Gandhi",
|
||||
"tokens": 10
|
||||
}
|
||||
]
|
||||
}`),
|
||||
Expect().Status().Equal(http.StatusCreated),
|
||||
)
|
||||
|
||||
MustDo(
|
||||
Post(fmt.Sprintf(uri, "3")),
|
||||
Send().Body().String(`
|
||||
{
|
||||
"choices": [
|
||||
{
|
||||
"choiceText": "Buddha",
|
||||
"tokens": 90
|
||||
},
|
||||
{
|
||||
"choiceText": "Gandhi",
|
||||
"tokens": 10
|
||||
}
|
||||
]
|
||||
}`),
|
||||
Expect().Status().Equal(http.StatusUnprocessableEntity),
|
||||
)
|
||||
|
||||
voterIdentities := make([]string, 0)
|
||||
locationHeader := ""
|
||||
MustDo(
|
||||
Post("http://127.0.0.1:8080/api/election"),
|
||||
Send().Body().String(`
|
||||
{
|
||||
"name": "Guy of the year",
|
||||
"tokens": 100,
|
||||
"areVotersKnown": true,
|
||||
"maxVoters": 10,
|
||||
"expiresAt": "2124-12-31T14:15:22Z",
|
||||
"choices": [
|
||||
"Gandhi",
|
||||
"Buddha"
|
||||
]
|
||||
}`),
|
||||
Expect().Status().Equal(http.StatusOK),
|
||||
Store().Response().Body().JSON().JQ(".voterIdentities").In(&voterIdentities),
|
||||
Store().Response().Headers("Location").In(&locationHeader),
|
||||
)
|
||||
|
||||
electionId := strings.Split(locationHeader, "/")[2]
|
||||
MustDo(
|
||||
Post(fmt.Sprintf(uri, electionId)),
|
||||
Send().Body().String(fmt.Sprintf(`
|
||||
{
|
||||
"voterIdentity": "%v",
|
||||
"choices": [
|
||||
{
|
||||
"choiceText": "Buddha",
|
||||
"tokens": 90
|
||||
},
|
||||
{
|
||||
"choiceText": "Gandhi",
|
||||
"tokens": 10
|
||||
}
|
||||
]
|
||||
}`, voterIdentities[0])),
|
||||
Expect().Status().Equal(http.StatusCreated),
|
||||
)
|
||||
}
|
@ -34,7 +34,7 @@ paths:
|
||||
required: true
|
||||
description: The ID of the election
|
||||
schema:
|
||||
type: integer
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: Election returned
|
||||
@ -102,7 +102,7 @@ paths:
|
||||
required: true
|
||||
description: The ID of the election
|
||||
schema:
|
||||
type: integer
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
@ -148,7 +148,7 @@ paths:
|
||||
required: true
|
||||
description: The ID of the election
|
||||
schema:
|
||||
type: integer
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: Election results returned
|
||||
@ -190,7 +190,7 @@ components:
|
||||
- choices
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
tokens:
|
||||
|
@ -65,12 +65,12 @@ type mockElectionModel struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (e *mockElectionModel) Insert(name string, tokens int, areVotersKnown bool, maxVoters int, choices []string, expiresAt time.Time) (int, error) {
|
||||
func (e *mockElectionModel) Insert(name string, tokens int, areVotersKnown bool, maxVoters int, choices []string, expiresAt time.Time) (string, error) {
|
||||
args := e.Called(name, tokens, areVotersKnown, maxVoters, choices, expiresAt)
|
||||
return args.Int(0), args.Error(1)
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
|
||||
func (e *mockElectionModel) GetById(id int) (*models.Election, error) {
|
||||
func (e *mockElectionModel) GetById(id string) (*models.Election, error) {
|
||||
args := e.Called(id)
|
||||
return args.Get(0).(*models.Election), args.Error(1)
|
||||
}
|
||||
@ -79,17 +79,17 @@ type mockVoterModel struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (v *mockVoterModel) InsertMultiple(identities []string, electionID int) ([]int, error) {
|
||||
func (v *mockVoterModel) InsertMultiple(identities []string, electionID string) ([]int, error) {
|
||||
args := v.Called(identities, electionID)
|
||||
return args.Get(0).([]int), args.Error(1)
|
||||
}
|
||||
|
||||
func (v *mockVoterModel) CountByElection(electionID int) (int, error) {
|
||||
func (v *mockVoterModel) CountByElection(electionID string) (int, error) {
|
||||
args := v.Called(electionID)
|
||||
return args.Int(0), args.Error(1)
|
||||
}
|
||||
|
||||
func (v *mockVoterModel) Exists(voterIdentity string, electionID int) (bool, error) {
|
||||
func (v *mockVoterModel) Exists(voterIdentity string, electionID string) (bool, error) {
|
||||
args := v.Called(voterIdentity, electionID)
|
||||
return args.Bool(0), args.Error(1)
|
||||
}
|
||||
@ -98,17 +98,17 @@ type mockVoteModel struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (v *mockVoteModel) Insert(voterIdentity string, electionId int, choiceText string, tokens int) (int, error) {
|
||||
func (v *mockVoteModel) Insert(voterIdentity string, electionId string, choiceText string, tokens int) (int, error) {
|
||||
args := v.Called(voterIdentity, electionId, choiceText, tokens)
|
||||
return args.Int(0), args.Error(1)
|
||||
}
|
||||
|
||||
func (v *mockVoteModel) Exists(voterIdentity string, electionID int) (bool, error) {
|
||||
func (v *mockVoteModel) Exists(voterIdentity string, electionID string) (bool, error) {
|
||||
args := v.Called(voterIdentity, electionID)
|
||||
return args.Bool(0), args.Error(1)
|
||||
}
|
||||
|
||||
func (v *mockVoteModel) GetByElection(electionID int) (*[]models.Vote, error) {
|
||||
func (v *mockVoteModel) GetByElection(electionID string) (*[]models.Vote, error) {
|
||||
args := v.Called(electionID)
|
||||
return args.Get(0).(*[]models.Vote), args.Error(1)
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ type Election struct {
|
||||
Choices []string `json:"choices"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
Id int `json:"id"`
|
||||
Id string `json:"id"`
|
||||
MaxVoters int `json:"maxVoters"`
|
||||
Name string `json:"name"`
|
||||
Tokens int `json:"tokens"`
|
||||
@ -89,13 +89,13 @@ type ServerInterface interface {
|
||||
CreateElection(w http.ResponseWriter, r *http.Request)
|
||||
// Get an election
|
||||
// (GET /election/{id})
|
||||
GetElection(w http.ResponseWriter, r *http.Request, id int)
|
||||
GetElection(w http.ResponseWriter, r *http.Request, id string)
|
||||
// Get the results of an election
|
||||
// (GET /election/{id}/results)
|
||||
GetElectionResults(w http.ResponseWriter, r *http.Request, id int)
|
||||
GetElectionResults(w http.ResponseWriter, r *http.Request, id string)
|
||||
// Cast your votes for an election
|
||||
// (POST /election/{id}/votes)
|
||||
CreateVotes(w http.ResponseWriter, r *http.Request, id int)
|
||||
CreateVotes(w http.ResponseWriter, r *http.Request, id string)
|
||||
}
|
||||
|
||||
// ServerInterfaceWrapper converts contexts to parameters.
|
||||
@ -129,7 +129,7 @@ func (siw *ServerInterfaceWrapper) GetElection(w http.ResponseWriter, r *http.Re
|
||||
var err error
|
||||
|
||||
// ------------- Path parameter "id" -------------
|
||||
var id int
|
||||
var id string
|
||||
|
||||
err = runtime.BindStyledParameterWithOptions("simple", "id", r.PathValue("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
|
||||
if err != nil {
|
||||
@ -155,7 +155,7 @@ func (siw *ServerInterfaceWrapper) GetElectionResults(w http.ResponseWriter, r *
|
||||
var err error
|
||||
|
||||
// ------------- Path parameter "id" -------------
|
||||
var id int
|
||||
var id string
|
||||
|
||||
err = runtime.BindStyledParameterWithOptions("simple", "id", r.PathValue("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
|
||||
if err != nil {
|
||||
@ -181,7 +181,7 @@ func (siw *ServerInterfaceWrapper) CreateVotes(w http.ResponseWriter, r *http.Re
|
||||
var err error
|
||||
|
||||
// ------------- Path parameter "id" -------------
|
||||
var id int
|
||||
var id string
|
||||
|
||||
err = runtime.BindStyledParameterWithOptions("simple", "id", r.PathValue("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
|
||||
if err != nil {
|
||||
|
@ -2,6 +2,7 @@ package mappers
|
||||
|
||||
import (
|
||||
"code.dlmw.ch/dlmw/qv/internal/models"
|
||||
uuid2 "github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
@ -19,8 +20,9 @@ func TestElectionResponse(t *testing.T) {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
uuid, _ := uuid2.NewV7()
|
||||
election := models.Election{
|
||||
ID: 15,
|
||||
ID: uuid.String(),
|
||||
Name: "The best",
|
||||
Tokens: 100,
|
||||
AreVotersKnown: true,
|
||||
@ -32,7 +34,7 @@ func TestElectionResponse(t *testing.T) {
|
||||
|
||||
response := ElectionResponse(&election)
|
||||
|
||||
assert.Equal(t, 15, response.Id)
|
||||
assert.Equal(t, uuid.String(), response.Id)
|
||||
assert.Equal(t, "The best", response.Name)
|
||||
assert.Equal(t, 100, response.Tokens)
|
||||
assert.Equal(t, true, response.AreVotersKnown)
|
||||
|
@ -1,5 +1,5 @@
|
||||
CREATE TABLE elections (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT, -- TODO: try to generate a UUID
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
tokens INTEGER NOT NULL,
|
||||
are_voters_known INTEGER NOT NULL,
|
||||
|
@ -2,12 +2,13 @@ package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/google/uuid"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ElectionModelInterface interface {
|
||||
Insert(name string, tokens int, areVotersKnown bool, maxVoters int, Choices []string, ExpiresAt time.Time) (int, error)
|
||||
GetById(id int) (*Election, error)
|
||||
Insert(name string, tokens int, areVotersKnown bool, maxVoters int, Choices []string, ExpiresAt time.Time) (string, error)
|
||||
GetById(id string) (*Election, error)
|
||||
}
|
||||
|
||||
type ElectionModel struct {
|
||||
@ -15,7 +16,7 @@ type ElectionModel struct {
|
||||
}
|
||||
|
||||
type Election struct {
|
||||
ID int
|
||||
ID string
|
||||
Name string
|
||||
Tokens int
|
||||
AreVotersKnown bool
|
||||
@ -25,48 +26,48 @@ type Election struct {
|
||||
Choices []string
|
||||
}
|
||||
|
||||
func (e *ElectionModel) Insert(name string, tokens int, areVotersKnown bool, maxVoters int, choices []string, expiresAt time.Time) (int, error) {
|
||||
func (e *ElectionModel) Insert(name string, tokens int, areVotersKnown bool, maxVoters int, choices []string, expiresAt time.Time) (string, error) {
|
||||
tx, err := e.DB.Begin()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return "", err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
result, err := tx.Exec(`
|
||||
INSERT INTO elections (name, tokens, are_voters_known, max_voters, expires_at)
|
||||
VALUES (?, ?, ?, ?, ?)`, name, tokens, areVotersKnown, maxVoters, expiresAt)
|
||||
electionID, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
electionID, err := result.LastInsertId()
|
||||
_, err = tx.Exec(`
|
||||
INSERT INTO elections (id, name, tokens, are_voters_known, max_voters, expires_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`, electionID, name, tokens, areVotersKnown, maxVoters, expiresAt)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`
|
||||
INSERT INTO choices (text, election_id)
|
||||
VALUES (?, ?)`)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return "", err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
for _, choice := range choices {
|
||||
_, err = stmt.Exec(choice, electionID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return 0, err
|
||||
return "0", err
|
||||
}
|
||||
|
||||
return int(electionID), nil
|
||||
return electionID.String(), nil
|
||||
}
|
||||
|
||||
func (e *ElectionModel) GetById(id int) (*Election, error) {
|
||||
func (e *ElectionModel) GetById(id string) (*Election, error) {
|
||||
query := `
|
||||
SELECT id, name, tokens, are_voters_known, max_voters, created_at, expires_at
|
||||
FROM elections
|
||||
|
@ -6,16 +6,16 @@ import (
|
||||
)
|
||||
|
||||
type VoterModelInterface interface {
|
||||
InsertMultiple(identities []string, electionID int) ([]int, error)
|
||||
CountByElection(electionID int) (int, error)
|
||||
Exists(voterIdentity string, electionID int) (bool, error)
|
||||
InsertMultiple(identities []string, electionID string) ([]int, error)
|
||||
CountByElection(electionID string) (int, error)
|
||||
Exists(voterIdentity string, electionID string) (bool, error)
|
||||
}
|
||||
|
||||
type VoterModel struct {
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
func (v *VoterModel) InsertMultiple(identities []string, electionID int) ([]int, error) {
|
||||
func (v *VoterModel) InsertMultiple(identities []string, electionID string) ([]int, error) {
|
||||
tx, err := v.DB.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -53,7 +53,7 @@ func (v *VoterModel) InsertMultiple(identities []string, electionID int) ([]int,
|
||||
return voterIDs, nil
|
||||
}
|
||||
|
||||
func (v *VoterModel) CountByElection(electionID int) (int, error) {
|
||||
func (v *VoterModel) CountByElection(electionID string) (int, error) {
|
||||
// use a transaction to prevent race conditions
|
||||
tx, err := v.DB.Begin()
|
||||
if err != nil {
|
||||
@ -82,7 +82,7 @@ func (v *VoterModel) CountByElection(electionID int) (int, error) {
|
||||
return voterCount, nil
|
||||
}
|
||||
|
||||
func (v *VoterModel) Exists(voterIdentity string, electionID int) (bool, error) {
|
||||
func (v *VoterModel) Exists(voterIdentity string, electionID string) (bool, error) {
|
||||
query := `
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
|
@ -7,9 +7,9 @@ import (
|
||||
)
|
||||
|
||||
type VoteModelInterface interface {
|
||||
Insert(voterIdentity string, electionId int, choiceText string, tokens int) (int, error)
|
||||
Exists(voterIdentity string, electionID int) (bool, error)
|
||||
GetByElection(electionID int) (*[]Vote, error)
|
||||
Insert(voterIdentity string, electionId string, choiceText string, tokens int) (int, error)
|
||||
Exists(voterIdentity string, electionID string) (bool, error)
|
||||
GetByElection(electionID string) (*[]Vote, error)
|
||||
}
|
||||
|
||||
type VoteModel struct {
|
||||
@ -18,13 +18,13 @@ type VoteModel struct {
|
||||
|
||||
type Vote struct {
|
||||
VoterIdentity string
|
||||
ElectionID int
|
||||
ElectionID string
|
||||
ChoiceText string
|
||||
Tokens int
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func (v *VoteModel) Insert(voterIdentity string, electionId int, choiceText string, tokens int) (int, error) {
|
||||
func (v *VoteModel) Insert(voterIdentity string, electionId string, choiceText string, tokens int) (int, error) {
|
||||
tx, err := v.DB.Begin()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@ -47,7 +47,7 @@ func (v *VoteModel) Insert(voterIdentity string, electionId int, choiceText stri
|
||||
return int(voteId), nil
|
||||
}
|
||||
|
||||
func (v *VoteModel) Exists(voterIdentity string, electionID int) (bool, error) {
|
||||
func (v *VoteModel) Exists(voterIdentity string, electionID string) (bool, error) {
|
||||
var exists bool
|
||||
query := `
|
||||
SELECT EXISTS (
|
||||
@ -66,7 +66,7 @@ func (v *VoteModel) Exists(voterIdentity string, electionID int) (bool, error) {
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
func (v *VoteModel) GetByElection(electionID int) (*[]Vote, error) {
|
||||
func (v *VoteModel) GetByElection(electionID string) (*[]Vote, error) {
|
||||
query := `
|
||||
SELECT voter_identity, election_id, choice_text, tokens, created_at
|
||||
FROM votes
|
||||
|
@ -93,7 +93,7 @@
|
||||
<template x-if="createdElectionId > 0">
|
||||
<div class="mt-6 bg-green-100 p-4 rounded-md">
|
||||
<h2 class="text-green-700 font-bold">Election Created Successfully</h2>
|
||||
<p class="mt-2">Election ID: <span class="font-mono" x-text="createdElectionId"></span></p>
|
||||
<p class="mt-2">Election ID: <span class="font-mono" x-text="createdElectionId"></span></p> <!-- TODO: link to created election -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
Reference in New Issue
Block a user