aurweb/test/test_trusted_user_routes.py
2022-08-22 22:40:45 +02:00

970 lines
31 KiB
Python

import re
from http import HTTPStatus
from io import StringIO
from typing import Tuple
import lxml.etree
import pytest
from fastapi.testclient import TestClient
from aurweb import config, db, filters, time
from aurweb.models.account_type import DEVELOPER_ID, TRUSTED_USER_ID, AccountType
from aurweb.models.tu_vote import TUVote
from aurweb.models.tu_voteinfo import TUVoteInfo
from aurweb.models.user import User
from aurweb.testing.requests import Request
DATETIME_REGEX = r"^[0-9]{4}-[0-9]{2}-[0-9]{2} \(.+\)$"
PARTICIPATION_REGEX = r"^1?[0-9]{2}[%]$" # 0% - 100%
def parse_root(html):
parser = lxml.etree.HTMLParser(recover=True)
tree = lxml.etree.parse(StringIO(html), parser)
return tree.getroot()
def get_table(root, class_name):
table = root.xpath(f'//table[contains(@class, "{class_name}")]')[0]
return table
def get_table_rows(table):
tbody = table.xpath("./tbody")[0]
return tbody.xpath("./tr")
def get_pkglist_directions(table):
stats = table.getparent().xpath("./div[@class='pkglist-stats']")[0]
nav = stats.xpath("./p[@class='pkglist-nav']")[0]
return nav.xpath("./a")
def get_a(node):
return node.xpath("./a")[0].text.strip()
def get_span(node):
return node.xpath("./span")[0].text.strip()
def assert_current_vote_html(row, expected):
columns = row.xpath("./td")
proposal, start, end, user, voted = columns
p, s, e, u, v = expected # Column expectations.
assert re.match(p, get_a(proposal)) is not None
assert re.match(s, start.text) is not None
assert re.match(e, end.text) is not None
assert re.match(u, get_a(user)) is not None
assert re.match(v, get_span(voted)) is not None
def assert_past_vote_html(row, expected):
columns = row.xpath("./td")
proposal, start, end, user, yes, no, voted = columns # Real columns.
p, s, e, u, y, n, v = expected # Column expectations.
assert re.match(p, get_a(proposal)) is not None
assert re.match(s, start.text) is not None
assert re.match(e, end.text) is not None
assert re.match(u, get_a(user)) is not None
assert re.match(y, yes.text) is not None
assert re.match(n, no.text) is not None
assert re.match(v, get_span(voted)) is not None
@pytest.fixture(autouse=True)
def setup(db_test):
return
@pytest.fixture
def client():
from aurweb.asgi import app
yield TestClient(app=app)
@pytest.fixture
def tu_user():
tu_type = db.query(AccountType, AccountType.AccountType == "Trusted User").first()
with db.begin():
tu_user = db.create(
User,
Username="test_tu",
Email="test_tu@example.org",
RealName="Test TU",
Passwd="testPassword",
AccountType=tu_type,
)
yield tu_user
@pytest.fixture
def tu_user2():
with db.begin():
tu_user2 = db.create(
User,
Username="test_tu2",
Email="test_tu2@example.org",
RealName="Test TU 2",
Passwd="testPassword",
AccountTypeID=TRUSTED_USER_ID,
)
yield tu_user2
@pytest.fixture
def user():
user_type = db.query(AccountType, AccountType.AccountType == "User").first()
with db.begin():
user = db.create(
User,
Username="test",
Email="test@example.org",
RealName="Test User",
Passwd="testPassword",
AccountType=user_type,
)
yield user
@pytest.fixture
def proposal(user, tu_user):
ts = time.utcnow()
agenda = "Test proposal."
start = ts - 5
end = ts + 1000
with db.begin():
voteinfo = db.create(
TUVoteInfo,
Agenda=agenda,
Quorum=0.0,
User=user.Username,
Submitter=tu_user,
Submitted=start,
End=end,
)
yield (tu_user, user, voteinfo)
def test_tu_index_guest(client):
headers = {"referer": config.get("options", "aur_location") + "/tu"}
with client as request:
response = request.get("/tu", allow_redirects=False, headers=headers)
assert response.status_code == int(HTTPStatus.SEE_OTHER)
params = filters.urlencode({"next": "/tu"})
assert response.headers.get("location") == f"/login?{params}"
def test_tu_index_unauthorized(client: TestClient, user: User):
cookies = {"AURSID": user.login(Request(), "testPassword")}
with client as request:
# Login as a normal user, not a TU.
response = request.get("/tu", cookies=cookies, allow_redirects=False)
assert response.status_code == int(HTTPStatus.SEE_OTHER)
assert response.headers.get("location") == "/"
def test_tu_empty_index(client, tu_user):
"""Check an empty index when we don't create any records."""
# Make a default get request to /tu.
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
response = request.get("/tu", cookies=cookies, allow_redirects=False)
assert response.status_code == int(HTTPStatus.OK)
# Parse lxml root.
root = parse_root(response.text)
# Check that .current-votes does not exist.
tables = root.xpath('//table[contains(@class, "current-votes")]')
assert len(tables) == 0
# Check that .past-votes has does not exist.
tables = root.xpath('//table[contains(@class, "current-votes")]')
assert len(tables) == 0
def test_tu_index(client, tu_user):
ts = time.utcnow()
# Create some test votes: (Agenda, Start, End).
votes = [
("Test agenda 1", ts - 5, ts + 1000), # Still running.
("Test agenda 2", ts - 1000, ts - 5), # Not running anymore.
]
vote_records = []
with db.begin():
for vote in votes:
agenda, start, end = vote
vote_records.append(
db.create(
TUVoteInfo,
Agenda=agenda,
User=tu_user.Username,
Submitted=start,
End=end,
Quorum=0.0,
Submitter=tu_user,
)
)
with db.begin():
# Vote on an ended proposal.
vote_record = vote_records[1]
vote_record.Yes += 1
vote_record.ActiveTUs += 1
db.create(TUVote, VoteInfo=vote_record, User=tu_user)
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
# Pass an invalid cby and pby; let them default to "desc".
response = request.get(
"/tu",
cookies=cookies,
params={"cby": "BAD!", "pby": "blah"},
allow_redirects=False,
)
assert response.status_code == int(HTTPStatus.OK)
# Rows we expect to exist in HTML produced by /tu for current votes.
expected_rows = [
(
r"Test agenda 1",
DATETIME_REGEX,
DATETIME_REGEX,
tu_user.Username,
r"^(Yes|No)$",
)
]
# Assert that we are matching the number of current votes.
current_votes = [c for c in votes if c[2] > ts]
assert len(current_votes) == len(expected_rows)
# Parse lxml.etree root.
root = parse_root(response.text)
table = get_table(root, "current-votes")
rows = get_table_rows(table)
for i, row in enumerate(rows):
assert_current_vote_html(row, expected_rows[i])
# Assert that we are matching the number of past votes.
past_votes = [c for c in votes if c[2] <= ts]
assert len(past_votes) == len(expected_rows)
# Rows we expect to exist in HTML produced by /tu for past votes.
expected_rows = [
(
r"Test agenda 2",
DATETIME_REGEX,
DATETIME_REGEX,
tu_user.Username,
r"^\d+$",
r"^\d+$",
r"^(Yes|No)$",
)
]
table = get_table(root, "past-votes")
rows = get_table_rows(table)
for i, row in enumerate(rows):
assert_past_vote_html(row, expected_rows[i])
# Get the .last-votes table and check that our vote shows up.
table = get_table(root, "last-votes")
rows = get_table_rows(table)
assert len(rows) == 1
# Check to see the rows match up to our user and related vote.
username, vote_id = rows[0]
username = username.xpath("./a")[0]
vote_id = vote_id.xpath("./a")[0]
assert username.text.strip() == tu_user.Username
assert int(vote_id.text.strip()) == vote_records[1].ID
def test_tu_stats(client: TestClient, tu_user: User):
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
response = request.get("/tu", cookies=cookies, allow_redirects=False)
assert response.status_code == HTTPStatus.OK
root = parse_root(response.text)
stats = root.xpath('//table[@class="no-width"]')[0]
rows = stats.xpath("./tbody/tr")
# We have one trusted user.
total = rows[0]
label, count = total.xpath("./td")
assert int(count.text.strip()) == 1
# And we have one active TU.
active = rows[1]
label, count = active.xpath("./td")
assert int(count.text.strip()) == 1
with db.begin():
tu_user.InactivityTS = time.utcnow()
with client as request:
response = request.get("/tu", cookies=cookies, allow_redirects=False)
assert response.status_code == HTTPStatus.OK
root = parse_root(response.text)
stats = root.xpath('//table[@class="no-width"]')[0]
rows = stats.xpath("./tbody/tr")
# We have one trusted user.
total = rows[0]
label, count = total.xpath("./td")
assert int(count.text.strip()) == 1
# But we have no more active TUs.
active = rows[1]
label, count = active.xpath("./td")
assert int(count.text.strip()) == 0
def test_tu_index_table_paging(client, tu_user):
ts = time.utcnow()
with db.begin():
for i in range(25):
# Create 25 current votes.
db.create(
TUVoteInfo,
Agenda=f"Agenda #{i}",
User=tu_user.Username,
Submitted=(ts - 5),
End=(ts + 1000),
Quorum=0.0,
Submitter=tu_user,
)
for i in range(25):
# Create 25 past votes.
db.create(
TUVoteInfo,
Agenda=f"Agenda #{25 + i}",
User=tu_user.Username,
Submitted=(ts - 1000),
End=(ts - 5),
Quorum=0.0,
Submitter=tu_user,
)
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
response = request.get("/tu", cookies=cookies, allow_redirects=False)
assert response.status_code == int(HTTPStatus.OK)
# Parse lxml.etree root.
root = parse_root(response.text)
table = get_table(root, "current-votes")
rows = get_table_rows(table)
assert len(rows) == 10
def make_expectation(offset, i):
return [
f"Agenda #{offset + i}",
DATETIME_REGEX,
DATETIME_REGEX,
tu_user.Username,
r"^(Yes|No)$",
]
for i, row in enumerate(rows):
assert_current_vote_html(row, make_expectation(0, i))
# Parse out Back/Next buttons.
directions = get_pkglist_directions(table)
assert len(directions) == 1
assert "Next" in directions[0].text
# Now, get the next page of current votes.
offset = 10 # Specify coff=10
with client as request:
response = request.get(
"/tu", cookies=cookies, params={"coff": offset}, allow_redirects=False
)
assert response.status_code == int(HTTPStatus.OK)
old_rows = rows
root = parse_root(response.text)
table = get_table(root, "current-votes")
rows = get_table_rows(table)
assert rows != old_rows
for i, row in enumerate(rows):
assert_current_vote_html(row, make_expectation(offset, i))
# Parse out Back/Next buttons.
directions = get_pkglist_directions(table)
assert len(directions) == 2
assert "Back" in directions[0].text
assert "Next" in directions[1].text
# Make sure past-votes' Back/Next were not affected.
past_votes = get_table(root, "past-votes")
past_directions = get_pkglist_directions(past_votes)
assert len(past_directions) == 1
assert "Next" in past_directions[0].text
offset = 20 # Specify coff=10
with client as request:
response = request.get(
"/tu", cookies=cookies, params={"coff": offset}, allow_redirects=False
)
assert response.status_code == int(HTTPStatus.OK)
# Do it again, we only have five left.
old_rows = rows
root = parse_root(response.text)
table = get_table(root, "current-votes")
rows = get_table_rows(table)
assert rows != old_rows
for i, row in enumerate(rows):
assert_current_vote_html(row, make_expectation(offset, i))
# Parse out Back/Next buttons.
directions = get_pkglist_directions(table)
assert len(directions) == 1
assert "Back" in directions[0].text
# Make sure past-votes' Back/Next were not affected.
past_votes = get_table(root, "past-votes")
past_directions = get_pkglist_directions(past_votes)
assert len(past_directions) == 1
assert "Next" in past_directions[0].text
def test_tu_index_sorting(client, tu_user):
ts = time.utcnow()
with db.begin():
for i in range(2):
# Create 'Agenda #1' and 'Agenda #2'.
db.create(
TUVoteInfo,
Agenda=f"Agenda #{i + 1}",
User=tu_user.Username,
Submitted=(ts + 5),
End=(ts + 1000),
Quorum=0.0,
Submitter=tu_user,
)
# Let's order each vote one day after the other.
# This will allow us to test the sorting nature
# of the tables.
ts += 86405
# Make a default request to /tu.
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
response = request.get("/tu", cookies=cookies, allow_redirects=False)
assert response.status_code == int(HTTPStatus.OK)
# Get lxml handles of the document.
root = parse_root(response.text)
table = get_table(root, "current-votes")
rows = get_table_rows(table)
# The latest Agenda is at the top by default.
expected = ["Agenda #2", "Agenda #1"]
assert len(rows) == len(expected)
for i, row in enumerate(rows):
assert_current_vote_html(
row,
[
expected[i],
DATETIME_REGEX,
DATETIME_REGEX,
tu_user.Username,
r"^(Yes|No)$",
],
)
# Make another request; one that sorts the current votes
# in ascending order instead of the default descending order.
with client as request:
response = request.get(
"/tu", cookies=cookies, params={"cby": "asc"}, allow_redirects=False
)
assert response.status_code == int(HTTPStatus.OK)
# Get lxml handles of the document.
root = parse_root(response.text)
table = get_table(root, "current-votes")
rows = get_table_rows(table)
# Reverse our expectations and assert that the proposals got flipped.
rev_expected = list(reversed(expected))
assert len(rows) == len(rev_expected)
for i, row in enumerate(rows):
assert_current_vote_html(
row,
[
rev_expected[i],
DATETIME_REGEX,
DATETIME_REGEX,
tu_user.Username,
r"^(Yes|No)$",
],
)
def test_tu_index_last_votes(
client: TestClient, tu_user: User, tu_user2: User, user: User
):
ts = time.utcnow()
with db.begin():
# Create a proposal which has ended.
voteinfo = db.create(
TUVoteInfo,
Agenda="Test agenda",
User=user.Username,
Submitted=(ts - 1000),
End=(ts - 5),
Yes=1,
No=1,
ActiveTUs=1,
Quorum=0.0,
Submitter=tu_user,
)
# Create a vote on it from tu_user.
db.create(TUVote, VoteInfo=voteinfo, User=tu_user)
db.create(TUVote, VoteInfo=voteinfo, User=tu_user2)
# Now, check that tu_user got populated in the .last-votes table.
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
response = request.get("/tu", cookies=cookies)
assert response.status_code == int(HTTPStatus.OK)
root = parse_root(response.text)
table = get_table(root, "last-votes")
rows = get_table_rows(table)
assert len(rows) == 2
last_vote = rows[0]
user, vote_id = last_vote.xpath("./td/a")
assert user.text.strip() == tu_user.Username
assert int(vote_id.text.strip()) == voteinfo.ID
last_vote = rows[1]
user, vote_id = last_vote.xpath("./td/a")
assert int(vote_id.text.strip()) == voteinfo.ID
assert user.text.strip() == tu_user2.Username
def test_tu_proposal_not_found(client, tu_user):
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
response = request.get("/tu", params={"id": 1}, cookies=cookies)
assert response.status_code == int(HTTPStatus.NOT_FOUND)
def test_tu_proposal_unauthorized(
client: TestClient, user: User, proposal: Tuple[User, User, TUVoteInfo]
):
cookies = {"AURSID": user.login(Request(), "testPassword")}
endpoint = f"/tu/{proposal[2].ID}"
with client as request:
response = request.get(endpoint, cookies=cookies, allow_redirects=False)
assert response.status_code == int(HTTPStatus.SEE_OTHER)
assert response.headers.get("location") == "/tu"
with client as request:
response = request.post(
endpoint, cookies=cookies, data={"decision": False}, allow_redirects=False
)
assert response.status_code == int(HTTPStatus.SEE_OTHER)
assert response.headers.get("location") == "/tu"
def test_tu_running_proposal(
client: TestClient, proposal: Tuple[User, User, TUVoteInfo]
):
tu_user, user, voteinfo = proposal
with db.begin():
voteinfo.ActiveTUs = 1
# Initiate an authenticated GET request to /tu/{proposal_id}.
proposal_id = voteinfo.ID
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
response = request.get(f"/tu/{proposal_id}", cookies=cookies)
assert response.status_code == int(HTTPStatus.OK)
# Alright, now let's continue on to verifying some markup.
# First, let's verify that the proposal details match.
root = parse_root(response.text)
details = root.xpath('//div[@class="proposal details"]')[0]
vote_running = root.xpath('//p[contains(@class, "vote-running")]')[0]
assert vote_running.text.strip() == "This vote is still running."
# Verify User field.
username = details.xpath('./div[contains(@class, "user")]/strong/a/text()')[0]
assert username.strip() == user.Username
active = details.xpath('./div[contains(@class, "field")]')[1]
content = active.text.strip()
assert "Active Trusted Users assigned:" in content
assert "1" in content
submitted = details.xpath('./div[contains(@class, "submitted")]/text()')[0]
assert (
re.match(
r"^Submitted: \d{4}-\d{2}-\d{2} \d{2}:\d{2} \(.+\) by$", submitted.strip()
)
is not None
)
submitter = details.xpath('./div[contains(@class, "submitted")]/a')[0]
assert submitter.text.strip() == tu_user.Username
assert submitter.attrib["href"] == f"/account/{tu_user.Username}"
end = details.xpath('./div[contains(@class, "end")]')[0]
end_label = end.xpath("./text()")[0]
assert end_label.strip() == "End:"
end_datetime = end.xpath("./strong/text()")[0]
assert (
re.match(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2} \(.+\)$", end_datetime.strip())
is not None
)
# We have not voted yet. Assert that our voting form is shown.
form = root.xpath('//form[contains(@class, "action-form")]')[0]
fields = form.xpath("./fieldset")[0]
buttons = fields.xpath('./button[@name="decision"]')
assert len(buttons) == 3
# Check the button names and values.
yes, no, abstain = buttons
# Yes
assert yes.attrib["name"] == "decision"
assert yes.attrib["value"] == "Yes"
# No
assert no.attrib["name"] == "decision"
assert no.attrib["value"] == "No"
# Abstain
assert abstain.attrib["name"] == "decision"
assert abstain.attrib["value"] == "Abstain"
# Create a vote.
with db.begin():
db.create(TUVote, VoteInfo=voteinfo, User=tu_user)
voteinfo.ActiveTUs += 1
voteinfo.Yes += 1
# Make another request now that we've voted.
with client as request:
response = request.get("/tu", params={"id": voteinfo.ID}, cookies=cookies)
assert response.status_code == int(HTTPStatus.OK)
# Parse our new root.
root = parse_root(response.text)
# Check that we no longer have a voting form.
form = root.xpath('//form[contains(@class, "action-form")]')
assert not form
# Check that we're told we've voted.
status = root.xpath('//span[contains(@class, "status")]/text()')[0]
assert status == "You've already voted for this proposal."
def test_tu_ended_proposal(client, proposal):
tu_user, user, voteinfo = proposal
ts = time.utcnow()
with db.begin():
voteinfo.End = ts - 5 # 5 seconds ago.
# Initiate an authenticated GET request to /tu/{proposal_id}.
proposal_id = voteinfo.ID
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
response = request.get(f"/tu/{proposal_id}", cookies=cookies)
assert response.status_code == int(HTTPStatus.OK)
# Alright, now let's continue on to verifying some markup.
# First, let's verify that the proposal details match.
root = parse_root(response.text)
details = root.xpath('//div[@class="proposal details"]')[0]
vote_running = root.xpath('//p[contains(@class, "vote-running")]')
assert not vote_running
result_node = details.xpath('./div[contains(@class, "result")]')[0]
result_label = result_node.xpath("./text()")[0]
assert result_label.strip() == "Result:"
result = result_node.xpath("./span/text()")[0]
assert result.strip() == "unknown"
# Check that voting has ended.
form = root.xpath('//form[contains(@class, "action-form")]')
assert not form
# We should see a status about it.
status = root.xpath('//span[contains(@class, "status")]/text()')[0]
assert status == "Voting is closed for this proposal."
def test_tu_proposal_vote_not_found(client, tu_user):
"""Test POST request to a missing vote."""
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
data = {"decision": "Yes"}
response = request.post(
"/tu/1", cookies=cookies, data=data, allow_redirects=False
)
assert response.status_code == int(HTTPStatus.NOT_FOUND)
def test_tu_proposal_vote(client, proposal):
tu_user, user, voteinfo = proposal
# Store the current related values.
yes = voteinfo.Yes
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
data = {"decision": "Yes"}
response = request.post(f"/tu/{voteinfo.ID}", cookies=cookies, data=data)
assert response.status_code == int(HTTPStatus.OK)
# Check that the proposal record got updated.
assert voteinfo.Yes == yes + 1
# Check that the new TUVote exists.
vote = db.query(TUVote, TUVote.VoteInfo == voteinfo, TUVote.User == tu_user).first()
assert vote is not None
root = parse_root(response.text)
# Check that we're told we've voted.
status = root.xpath('//span[contains(@class, "status")]/text()')[0]
assert status == "You've already voted for this proposal."
def test_tu_proposal_vote_unauthorized(
client: TestClient, proposal: Tuple[User, User, TUVoteInfo]
):
tu_user, user, voteinfo = proposal
with db.begin():
tu_user.AccountTypeID = DEVELOPER_ID
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
data = {"decision": "Yes"}
response = request.post(
f"/tu/{voteinfo.ID}", cookies=cookies, data=data, allow_redirects=False
)
assert response.status_code == int(HTTPStatus.UNAUTHORIZED)
root = parse_root(response.text)
status = root.xpath('//span[contains(@class, "status")]/text()')[0]
assert status == "Only Trusted Users are allowed to vote."
with client as request:
data = {"decision": "Yes"}
response = request.get(
f"/tu/{voteinfo.ID}", cookies=cookies, data=data, allow_redirects=False
)
assert response.status_code == int(HTTPStatus.OK)
root = parse_root(response.text)
status = root.xpath('//span[contains(@class, "status")]/text()')[0]
assert status == "Only Trusted Users are allowed to vote."
def test_tu_proposal_vote_cant_self_vote(client, proposal):
tu_user, user, voteinfo = proposal
# Update voteinfo.User.
with db.begin():
voteinfo.User = tu_user.Username
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
data = {"decision": "Yes"}
response = request.post(
f"/tu/{voteinfo.ID}", cookies=cookies, data=data, allow_redirects=False
)
assert response.status_code == int(HTTPStatus.BAD_REQUEST)
root = parse_root(response.text)
status = root.xpath('//span[contains(@class, "status")]/text()')[0]
assert status == "You cannot vote in an proposal about you."
with client as request:
data = {"decision": "Yes"}
response = request.get(
f"/tu/{voteinfo.ID}", cookies=cookies, data=data, allow_redirects=False
)
assert response.status_code == int(HTTPStatus.OK)
root = parse_root(response.text)
status = root.xpath('//span[contains(@class, "status")]/text()')[0]
assert status == "You cannot vote in an proposal about you."
def test_tu_proposal_vote_already_voted(client, proposal):
tu_user, user, voteinfo = proposal
with db.begin():
db.create(TUVote, VoteInfo=voteinfo, User=tu_user)
voteinfo.Yes += 1
voteinfo.ActiveTUs += 1
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
data = {"decision": "Yes"}
response = request.post(
f"/tu/{voteinfo.ID}", cookies=cookies, data=data, allow_redirects=False
)
assert response.status_code == int(HTTPStatus.BAD_REQUEST)
root = parse_root(response.text)
status = root.xpath('//span[contains(@class, "status")]/text()')[0]
assert status == "You've already voted for this proposal."
with client as request:
data = {"decision": "Yes"}
response = request.get(
f"/tu/{voteinfo.ID}", cookies=cookies, data=data, allow_redirects=False
)
assert response.status_code == int(HTTPStatus.OK)
root = parse_root(response.text)
status = root.xpath('//span[contains(@class, "status")]/text()')[0]
assert status == "You've already voted for this proposal."
def test_tu_proposal_vote_invalid_decision(client, proposal):
tu_user, user, voteinfo = proposal
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
data = {"decision": "EVIL"}
response = request.post(f"/tu/{voteinfo.ID}", cookies=cookies, data=data)
assert response.status_code == int(HTTPStatus.BAD_REQUEST)
assert response.text == "Invalid 'decision' value."
def test_tu_addvote(client: TestClient, tu_user: User):
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
response = request.get("/addvote", cookies=cookies)
assert response.status_code == int(HTTPStatus.OK)
def test_tu_addvote_unauthorized(
client: TestClient, user: User, proposal: Tuple[User, User, TUVoteInfo]
):
cookies = {"AURSID": user.login(Request(), "testPassword")}
with client as request:
response = request.get("/addvote", cookies=cookies, allow_redirects=False)
assert response.status_code == int(HTTPStatus.SEE_OTHER)
assert response.headers.get("location") == "/tu"
with client as request:
response = request.post("/addvote", cookies=cookies, allow_redirects=False)
assert response.status_code == int(HTTPStatus.SEE_OTHER)
assert response.headers.get("location") == "/tu"
def test_tu_addvote_invalid_type(client: TestClient, tu_user: User):
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
response = request.get("/addvote", params={"type": "faketype"}, cookies=cookies)
assert response.status_code == int(HTTPStatus.OK)
root = parse_root(response.text)
error = root.xpath('//*[contains(@class, "error")]/text()')[0]
assert error.strip() == "Invalid type."
def test_tu_addvote_post(client: TestClient, tu_user: User, user: User):
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
data = {"user": user.Username, "type": "add_tu", "agenda": "Blah"}
with client as request:
response = request.post("/addvote", cookies=cookies, data=data)
assert response.status_code == int(HTTPStatus.SEE_OTHER)
voteinfo = db.query(TUVoteInfo, TUVoteInfo.Agenda == "Blah").first()
assert voteinfo is not None
def test_tu_addvote_post_cant_duplicate_username(
client: TestClient, tu_user: User, user: User
):
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
data = {"user": user.Username, "type": "add_tu", "agenda": "Blah"}
with client as request:
response = request.post("/addvote", cookies=cookies, data=data)
assert response.status_code == int(HTTPStatus.SEE_OTHER)
voteinfo = db.query(TUVoteInfo, TUVoteInfo.Agenda == "Blah").first()
assert voteinfo is not None
with client as request:
response = request.post("/addvote", cookies=cookies, data=data)
assert response.status_code == int(HTTPStatus.BAD_REQUEST)
def test_tu_addvote_post_invalid_username(client: TestClient, tu_user: User):
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
data = {"user": "fakeusername"}
with client as request:
response = request.post("/addvote", cookies=cookies, data=data)
assert response.status_code == int(HTTPStatus.NOT_FOUND)
def test_tu_addvote_post_invalid_type(client: TestClient, tu_user: User, user: User):
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
data = {"user": user.Username}
with client as request:
response = request.post("/addvote", cookies=cookies, data=data)
assert response.status_code == int(HTTPStatus.BAD_REQUEST)
def test_tu_addvote_post_invalid_agenda(client: TestClient, tu_user: User, user: User):
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
data = {"user": user.Username, "type": "add_tu"}
with client as request:
response = request.post("/addvote", cookies=cookies, data=data)
assert response.status_code == int(HTTPStatus.BAD_REQUEST)
def test_tu_addvote_post_bylaws(client: TestClient, tu_user: User):
# Bylaws votes do not need a user specified.
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
data = {"type": "bylaws", "agenda": "Blah blah!"}
with client as request:
response = request.post("/addvote", cookies=cookies, data=data)
assert response.status_code == int(HTTPStatus.SEE_OTHER)