Skip to content

Commit

Permalink
Implement voting
Browse files Browse the repository at this point in the history
Fixes #200
  • Loading branch information
1-alex98 committed Jun 5, 2018
1 parent 0101c8f commit 824f445
Show file tree
Hide file tree
Showing 27 changed files with 1,684 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ before_install:
install:
- git clone https://github.com/FAForever/faf-stack.git faf-stack
&& pushd faf-stack
&& git checkout 1a37706
&& git checkout 6bdb8b3
&& cp -r config.template config
&& popd
- docker-compose -f faf-stack/docker-compose.yml up -d faf-db
Expand Down
225 changes: 225 additions & 0 deletions src/inttest/java/com/faforever/api/data/VotingElideTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
package com.faforever.api.data;

import com.faforever.api.AbstractIntegrationTest;
import com.faforever.api.data.domain.VotingChoice;
import com.faforever.api.data.domain.VotingQuestion;
import com.faforever.api.security.OAuthScope;
import com.faforever.api.voting.VotingQuestionRepository;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;

import java.util.List;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:sql/prepDefaultUser.sql")
@Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:sql/prepVotingData.sql")
@Sql(executionPhase = ExecutionPhase.AFTER_TEST_METHOD, scripts = "classpath:sql/cleanVotingData.sql")
public class VotingElideTest extends AbstractIntegrationTest {
/*
{
"data": {
"type": "votingSubject",
"id": "2",
"attributes": {
"revealWinner": true
}
}
}
*/
private static final String PATCH_VOTING_SUBJECT_REVEAL_ID_2 = "{\n" +
" \"data\": {\n" +
" \"type\": \"votingSubject\",\n" +
" \"id\": \"2\",\n" +
" \"attributes\": {\n" +
" \"revealWinner\": true\n" +
" }\n" +
" }\n" +
"}";

/*
{
"data": {
"type": "votingSubject",
"id": "1",
"attributes": {
"revealWinner": true
}
}
}
*/
private static final String PATCH_VOTING_SUBJECT_REVEAL_ID_1 = "{\n" +
" \"data\": {\n" +
" \"type\": \"votingSubject\",\n" +
" \"id\": \"1\",\n" +
" \"attributes\": {\n" +
" \"revealWinner\": true\n" +
" }\n" +
" }\n" +
"}";
/*
{
"votingSubject":{
"id":1
},
"votingAnswers":[
{
"votingQuestion":{
"id":1
},
"alternativeOrdinal":0,
"votingChoice":{
"id":1
}
}
]
}
*/
private static final String POST_VOTE_SUBJECT1 =
"{\n" +
"\n" +
"\"votingSubject\":{\n" +
"\"id\":1\n" +
"},\n" +
"\"votingAnswers\":[\n" +
"{\n" +
"\"votingQuestion\":{\n" +
"\"id\":1\n" +
"},\n" +
"\"alternativeOrdinal\":0,\n" +
"\"votingChoice\":{\n" +
"\"id\":1\n" +
"}\n" +
"}\n" +
"]\n" +
"\n" +
"}";

/*
{
"votingSubject":{
"id":2
},
"votingAnswers":[
{
"votingQuestion":{
"id":2
},
"alternativeOrdinal":0,
"votingChoice":{
"id":3
}
}
]
}
*/
private static final String POST_VOTE_SUBJECT2 = "{\n" +
"\n" +
" \t\"votingSubject\":{\n" +
" \t\t\"id\":2\n" +
" \t},\n" +
" \t\"votingAnswers\":[\n" +
" \t\t{\n" +
" \t\t\t\"votingQuestion\":{\n" +
" \t\t\t\t\"id\":2\n" +
" \t\t\t},\n" +
" \t\t\t\"alternativeOrdinal\":0,\n" +
" \t\t\t\"votingChoice\":{\n" +
" \t\t\t\t\"id\":3\n" +
" \t\t\t}\n" +
" \t\t}\n" +
" \t]\n" +
"\n" +
"}";

@Autowired
VotingQuestionRepository votingQuestionRepository;


@Test
@WithUserDetails(AUTH_MODERATOR)
public void noBodyCanSeeOtherPeoplesVote() throws Exception {
mockMvc.perform(get("/data/vote"))
.andExpect(status().isOk())
.andExpect(content().string("{\"data\":[]}"));
}

@Test
@WithUserDetails(AUTH_USER)
public void everyBodySeesVotingSubjects() throws Exception {
mockMvc.perform(get("/data/votingSubject"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data", hasSize(2)));
}

@Test
@WithUserDetails(AUTH_USER)
public void everyBodySeesVotingQuestions() throws Exception {
mockMvc.perform(get("/data/votingQuestion"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data", hasSize(2)));
}

@Test
@WithUserDetails(AUTH_USER)
public void everyBodySeesVotingChoices() throws Exception {
mockMvc.perform(get("/data/votingChoice"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data", hasSize(3)));
}

@Test
@WithUserDetails(AUTH_MODERATOR)
public void testRevealWinnerOnEndedSubjectWorks() throws Exception {
mockMvc.perform(patch("/data/votingSubject/2").content(PATCH_VOTING_SUBJECT_REVEAL_ID_2))
.andExpect(status().isNoContent());
VotingQuestion question = votingQuestionRepository.getOne(2);
List<VotingChoice> winners = question.getWinners();
assertThat(winners, hasSize(1));
assertThat(winners.get(0).getId(), is(3));
}

@Test
@WithUserDetails(AUTH_MODERATOR)
public void testRevealWinnerOnNoneEndedSubjectFails() throws Exception {
mockMvc.perform(patch("/data/votingSubject/1").content(PATCH_VOTING_SUBJECT_REVEAL_ID_1))
.andExpect(status().is(500));
}

@Test
@WithUserDetails(AUTH_MODERATOR)
public void postVote() throws Exception {
mockMvc.perform(post("/voting/vote").contentType(MediaType.APPLICATION_JSON).content(POST_VOTE_SUBJECT1).with(getOAuthToken(OAuthScope._VOTE)))
.andExpect(status().isOk());
}

@Test
@WithUserDetails(AUTH_USER)
public void postVoteWhereVoteAlreadyExists() throws Exception {
mockMvc.perform(post("/voting/vote").contentType(MediaType.APPLICATION_JSON).content(POST_VOTE_SUBJECT1).with(getOAuthToken(OAuthScope._VOTE)))
.andExpect(status().is(422));
}

@Test
@WithUserDetails(AUTH_USER)
public void postVoteOnEndedSubject() throws Exception {
mockMvc.perform(post("/voting/vote").contentType(MediaType.APPLICATION_JSON).content(POST_VOTE_SUBJECT2).with(getOAuthToken(OAuthScope._VOTE)))
.andExpect(status().is(422));
}
}
6 changes: 6 additions & 0 deletions src/inttest/resources/sql/cleanVotingData.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DELETE FROM winner_for_voting_question;
DELETE FROM voting_answer;
DELETE FROM vote;
DELETE FROM voting_choice;
DELETE FROM voting_question;
DELETE FROM voting_subject;
9 changes: 6 additions & 3 deletions src/inttest/resources/sql/prepDefaultUser.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
DELETE FROM winner_for_voting_question;
DELETE FROM voting_answer;
DELETE FROM vote;
DELETE FROM unique_id_users;
DELETE FROM uniqueid;
DELETE FROM global_rating;
Expand Down Expand Up @@ -36,12 +39,12 @@ DELETE FROM login;
INSERT INTO oauth_clients (id, name, client_secret, client_type, redirect_uris, default_redirect_uri, default_scope)
VALUES
('test', 'test', 'test', 'public', 'http://localhost https://www.getpostman.com/oauth2/callback ', 'http://localhost',
'read_events read_achievements upload_map upload_mod upload_avatar write_account_data');
'read_events read_achievements upload_map upload_mod upload_avatar write_account_data vote');

INSERT INTO login (id, login, email, password, steamid)
VALUES (1, 'USER', 'user@faforever.com', '92b7b421992ef490f3b75898ec0e511f1a5c02422819d89719b20362b023ee4f', NULL),
(2, 'MODERATOR', 'moderator@faforever.com', '778ac5b81fa251b450f827846378739caee510c31b01cfa9d31822b88bed8441', 1234),
(3, 'ADMIN', 'admin@faforever.com', '835d6dc88b708bc646d6db82c853ef4182fabbd4a8de59c213f2b5ab3ae7d9be', NULL);
(2, 'MODERATOR', 'moderator@faforever.com', '778ac5b81fa251b450f827846378739caee510c31b01cfa9d31822b88bed8441', 1234),
(3, 'ADMIN', 'admin@faforever.com', '835d6dc88b708bc646d6db82c853ef4182fabbd4a8de59c213f2b5ab3ae7d9be', NULL);

INSERT INTO lobby_admin (user_id, `group`) VALUES
(2, 1),
Expand Down
40 changes: 40 additions & 0 deletions src/inttest/resources/sql/prepVotingData.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
DELETE FROM winner_for_voting_question;
DELETE FROM voting_answer;
DELETE FROM vote;
DELETE FROM voting_choice;
DELETE FROM voting_question;
DELETE FROM voting_subject;

INSERT INTO voting_subject (id, subject_key, begin_of_vote_time, end_of_vote_time, min_games_to_vote, description_key, topic_url)
VALUES
(1, 'subject', NOW(), DATE_ADD(NOW(), INTERVAL 1 YEAR), 0, 'des', 'www.google.de');

INSERT INTO voting_subject (id, subject_key, reveal_winner, begin_of_vote_time, end_of_vote_time, min_games_to_vote, description_key, topic_url)
VALUES
(2, 'subject', 0, DATE_SUB(NOW(), INTERVAL 1 YEAR), DATE_SUB(NOW(), INTERVAL 45 DAY), 0, 'des', 'www.google.de');

INSERT INTO voting_question (id, max_answers, question_key, voting_subject_id, description_key, ordinal, alternative_voting)
VALUES
(1, 2, 'question', 1, 'des', 1, 1);

INSERT INTO voting_choice (id, choice_text_key, voting_question_id, description_key, ordinal) VALUES
(1, 'text', 1, 'des', 1);

INSERT INTO voting_choice (id, choice_text_key, voting_question_id, description_key, ordinal) VALUES
(2, 'text', 1, 'des', 2);

INSERT INTO vote (id, player_id, voting_subject_id) VALUES
(1, 1, 1);

INSERT INTO voting_question (id, max_answers, question_key, voting_subject_id, description_key, ordinal, alternative_voting)
VALUES
(2, 2, 'question', 2, 'des', 1, 1);

INSERT INTO voting_choice (id, choice_text_key, voting_question_id, description_key, ordinal) VALUES
(3, 'text', 2, 'des', 2);

INSERT INTO vote (id, player_id, voting_subject_id) VALUES
(2, 3, 2);

INSERT INTO voting_answer (id, vote_id, voting_choice_id, alternative_ordinal) VALUES
(1, 2, 3, 0);
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ private Predicate<String> paths() {
regex("/users/.*"),
regex("/mods/.*"),
regex("/maps/.*"),
regex("/leaderboards/.*"));
regex("/leaderboards/.*"),
regex("/voting/.*"));
}
}
54 changes: 54 additions & 0 deletions src/main/java/com/faforever/api/data/domain/Vote.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.faforever.api.data.domain;

import com.faforever.api.data.checks.IsEntityOwner;
import com.yahoo.elide.annotation.Include;
import com.yahoo.elide.annotation.ReadPermission;
import com.yahoo.elide.annotation.UpdatePermission;
import lombok.Setter;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.util.Set;

@Entity
@Table(name = "vote")
@Include(type = Vote.TYPE_NAME, rootLevel = true)
@ReadPermission(expression = IsEntityOwner.EXPRESSION)
@UpdatePermission(expression = "Prefab.Role.None")
@Setter
public class Vote extends AbstractEntity implements OwnableEntity {
public static final String TYPE_NAME = "vote";

private Player player;
private VotingSubject votingSubject;
private Set<VotingAnswer> votingAnswers;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "player_id")
public Player getPlayer() {
return player;
}

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "voting_subject_id")
public VotingSubject getVotingSubject() {
return votingSubject;
}

@OneToMany(mappedBy = "vote", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<VotingAnswer> getVotingAnswers() {
return votingAnswers;
}

@Transient
@Override
public Login getEntityOwner() {
return getPlayer();
}
}

0 comments on commit 824f445

Please sign in to comment.