Skip to content

Commit 03a6fd7

Browse files
committed
working towards database tests
1 parent 8221fff commit 03a6fd7

File tree

4 files changed

+725
-7
lines changed

4 files changed

+725
-7
lines changed

docker-compose.test.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
services:
2+
postgres-test:
3+
image: postgres:15
4+
container_name: postgres_db_test
5+
environment:
6+
POSTGRES_USER: postgres
7+
POSTGRES_PASSWORD: postgres
8+
POSTGRES_DB: clusterdev
9+
ports:
10+
- "5433:5432"
11+
tmpfs:
12+
- /var/lib/postgresql/data
13+
healthcheck:
14+
test: ["CMD-SHELL", "pg_isready -U postgres -d clusterdev"]
15+
interval: 5s
16+
timeout: 3s
17+
retries: 10
18+
19+
migrate-test:
20+
image: python:3.11-slim
21+
depends_on:
22+
postgres-test:
23+
condition: service_healthy
24+
volumes:
25+
- ./src/migrations:/migrations
26+
command: >
27+
sh -c "
28+
pip install yoyo-migrations psycopg2-binary &&
29+
yoyo apply -b -d 'postgresql://postgres:postgres@postgres-test:5432/clusterdev' -v /migrations/ &&
30+
yoyo list -d 'postgresql://postgres:postgres@postgres-test:5432/clusterdev' /migrations/
31+
"
32+
restart: "no"

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ relative_files = true
4444
exclude_lines = [
4545
"pragma: no cover",
4646
"raise NotImplementedError",
47+
# For now, don't require coverage of db errors
48+
"except psycopg2.Error"
4749
]
4850

4951
[tool.pytest.ini_options]

src/libkernelbot/leaderboard_db.py

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ def create_leaderboard(
8787
forum_id: int,
8888
gpu_types: list | str,
8989
) -> int:
90+
# to prevent surprises, ensure we have specified a timezone
91+
assert deadline.tzinfo is not None
9092
try:
9193
task = definition.task
9294
self.cursor.execute(
@@ -127,10 +129,10 @@ def create_leaderboard(
127129
self.name_cache.invalidate() # Invalidate autocomplete cache
128130
return leaderboard_id
129131
except psycopg2.Error as e:
130-
logger.exception("Error in leaderboard creation.", e)
132+
logger.exception("Error in leaderboard creation.", exc_info=e)
131133
if isinstance(e, psycopg2.errors.UniqueViolation):
132134
raise KernelBotError(
133-
"Error: Tried to create a leaderboard " f'"{name}" that already exists.'
135+
f"Error: Tried to create a leaderboard '{name}' that already exists."
134136
) from e
135137
self.connection.rollback() # Ensure rollback if error occurs
136138
raise KernelBotError("Error in leaderboard creation.") from e
@@ -260,7 +262,7 @@ def create_submission(
260262

261263
code_id = None
262264
for candidate in self.cursor.fetchall():
263-
if candidate[1] == code:
265+
if bytes(candidate[1]).decode("utf-8") == code:
264266
code_id = candidate[0]
265267
break
266268

@@ -357,6 +359,24 @@ def create_submission_run(
357359
if compilation is not None:
358360
compilation = json.dumps(dataclasses.asdict(compilation))
359361

362+
# check validity
363+
self.cursor.execute(
364+
"""
365+
SELECT done FROM leaderboard.submission WHERE id = %s
366+
""",
367+
(submission,),
368+
)
369+
if self.cursor.fetchone()[0]:
370+
logger.error(
371+
"Submission '%s' is already marked as done when trying to add %s run.",
372+
submission,
373+
mode,
374+
)
375+
raise KernelBotError(
376+
"Internal error: Attempted to add run, "
377+
"but submission was already marked as done."
378+
)
379+
360380
meta = {
361381
k: result.__dict__[k]
362382
for k in ["stdout", "stderr", "success", "exit_code", "command", "duration"]
@@ -461,7 +481,7 @@ def get_leaderboard_gpu_types(self, leaderboard_name: str) -> List[str]:
461481

462482
return [x[0] for x in self.cursor.fetchall()]
463483

464-
def get_leaderboard_templates(self, leaderboard_name: str) -> Dict[str, str]:
484+
def get_leaderboard_id(self, leaderboard_name: str) -> int:
465485
self.cursor.execute(
466486
"""
467487
SELECT id
@@ -473,14 +493,18 @@ def get_leaderboard_templates(self, leaderboard_name: str) -> Dict[str, str]:
473493
lb_id = self.cursor.fetchone()
474494
if lb_id is None:
475495
raise LeaderboardDoesNotExist(leaderboard_name)
496+
return lb_id[0]
497+
498+
def get_leaderboard_templates(self, leaderboard_name: str) -> Dict[str, str]:
499+
lb_id = self.get_leaderboard_id(leaderboard_name)
476500

477501
self.cursor.execute(
478502
"""
479503
SELECT lang, code
480504
FROM leaderboard.templates
481505
WHERE leaderboard_id = %s
482506
""",
483-
(lb_id[0],),
507+
(lb_id,),
484508
)
485509

486510
return {x[0]: x[1] for x in self.cursor.fetchall()}
@@ -585,7 +609,7 @@ def get_leaderboard_submissions(
585609

586610
self.cursor.execute(query, args)
587611

588-
return [
612+
result = [
589613
LeaderboardRankedEntry(
590614
submission_name=submission[0],
591615
submission_id=submission[1],
@@ -599,6 +623,19 @@ def get_leaderboard_submissions(
599623
)
600624
for submission in self.cursor.fetchall()
601625
]
626+
if len(result) == 0:
627+
# try to diagnose why we didn't get anything
628+
# this will raise if the LB does not exist at all.
629+
self.get_leaderboard_id(leaderboard_name)
630+
631+
# did we specify a valid GPU?
632+
gpus = self.get_leaderboard_gpu_types(leaderboard_name)
633+
if gpu_name not in gpus:
634+
raise KernelBotError(
635+
f"Invalid GPU type '{gpu_name}' for leaderboard '{leaderboard_name}'"
636+
)
637+
638+
return result
602639

603640
def generate_stats(self, last_day: bool):
604641
try:
@@ -824,7 +861,20 @@ def get_leaderboard_submission_count(
824861
args = (leaderboard_name, gpu_name)
825862

826863
self.cursor.execute(query, args)
827-
return self.cursor.fetchone()[0]
864+
count = self.cursor.fetchone()[0]
865+
if count == 0:
866+
# try to diagnose why we didn't get anything
867+
# this will raise if the LB does not exist at all.
868+
self.get_leaderboard_id(leaderboard_name)
869+
870+
# did we specify a valid GPU?
871+
gpus = self.get_leaderboard_gpu_types(leaderboard_name)
872+
if gpu_name not in gpus:
873+
raise KernelBotError(
874+
f"Invalid GPU type '{gpu_name}' for leaderboard '{leaderboard_name}'"
875+
)
876+
877+
return count
828878

829879
def init_user_from_cli(self, cli_id: str, auth_provider: str):
830880
"""

0 commit comments

Comments
 (0)