Skip to content

Commit a3efc47

Browse files
authored
Increase test coverage to 97% (#108)
* OPT: added tests for middlewares * OPT: added tests for models * OPT: added tests for controllers * OPT: encryption is now mocked during tests, significantly speeding up the test suite * OPT: election metadata for unit tests is now loaded from a static zip rather than the local filesystem * OPT: tests can now also run in a container * NEW: added test workflow for pull requests
1 parent d9e404f commit a3efc47

31 files changed

+2060
-13
lines changed

.github/workflows/test.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: Run tests
2+
on:
3+
pull_request:
4+
5+
jobs:
6+
test:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v4
10+
- run: make test-docker-notty

Dockerfile

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
1-
FROM python:3.11-bookworm
1+
FROM python:3.11-bookworm AS base
22

33
WORKDIR /app
44

5-
ADD . /app
6-
5+
ADD requirements.txt /app
76
RUN pip install -r requirements.txt
87

8+
ADD . /app
9+
910
USER 10017
1011

11-
ENTRYPOINT ./entrypoint.sh
12+
ENTRYPOINT ["./entrypoint.sh"]
13+
14+
FROM base AS test
15+
16+
RUN mkdir /tmp/app
17+
RUN touch /tmp/app/test.db
18+
ENV DB_PATH=/tmp/app/test.db
19+
20+
CMD ["./test-entrypoint.sh"]

Makefile

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ PIP:=venv/bin/pip
33
PYTEST:=venv/bin/py.test
44
COV:=venv/bin/coverage
55

6-
.PHONY: clean venv run test cov
6+
.PHONY: clean venv run test cov test-build test-docker
77
clean:
88
rm -rf venv
99

@@ -21,3 +21,12 @@ cov:
2121
$(COV) run -m pytest test || true
2222
$(COV) html
2323
open htmlcov/index.html
24+
25+
test-build:
26+
docker build . -t elekto-test --target test
27+
28+
test-docker: test-build
29+
docker run -it --rm --entrypoint=./test-entrypoint.sh elekto-test
30+
31+
test-docker-notty: test-build
32+
docker run --rm --entrypoint=./test-entrypoint.sh elekto-test

docker-compose.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
services:
2+
db:
3+
image: postgres
4+
restart: always
5+
environment:
6+
POSTGRES_DB: elekto
7+
POSTGRES_USER: root
8+
POSTGRES_PASSWORD: root
9+
volumes:
10+
- pgdata:/var/lib/postgresql/data
11+
ports:
12+
- '5432:5432'
13+
14+
volumes:
15+
pgdata:

elekto/controllers/authentication.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,12 @@ def oauth_github_redirect():
103103
if resp.status_code != 200:
104104
F.g.user = None
105105
F.g.auth = False
106-
F.session.pop(constants.AUTH_STATE)
106+
107+
# If GitHub couldn't fetch the user (who just now granted Elekto access to their GitHub profile), try to flush
108+
# the auth state from the session. Likely this isn't set, so a membership test is performed before doing the
109+
# pop(...) call.
110+
if constants.AUTH_STATE in F.session.keys():
111+
F.session.pop(constants.AUTH_STATE)
107112
else:
108113
data = resp.json()
109114
expries = datetime.now() + timedelta(days=1)

elekto/controllers/elections.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,8 @@ def elections_admin_review(eid, rid):
292292
.filter(Request.id == rid)
293293
.first()
294294
)
295+
# TODO: Check if a Request was found to prevent rendering errors. If the `rid` does not lead to a Request for this
296+
# Election, send the user to another page (the exact location has to be discussed).
295297

296298
if F.request.method == "POST":
297299
req.reviewed = False if req.reviewed else True

elekto/controllers/webhook.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ def webhook_sync():
3131
backend.clone()
3232
else:
3333
backend.pull()
34+
# FIXME: sync(...) returns a string, not a response.
3435
return sync(SESSION, meta.Election.all())

elekto/middlewares/__init__.py

Whitespace-only changes.

elekto/middlewares/auth.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,3 @@ def decorated_function(*args, **kwargs):
6666
return F.redirect(F.request.url)
6767
return f(*args, **kwargs)
6868
return decorated_function
69-
70-

elekto/models/meta.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ def voters(self):
136136
return utils.parse_yaml(os.path.join(self.path, Election.VOT))
137137

138138
def showfields(self):
139+
# FIXME: show_candidate_fields could be None (as is the case in the name_the_app example meta), leading to an
140+
# error if showfields() is called. See: https://github.com/elekto-io/elekto/issues/98
139141
return dict.fromkeys(self.election['show_candidate_fields'], '')
140142

141143
def candidates(self):

0 commit comments

Comments
 (0)