Skip to content

Commit e90ced3

Browse files
committed
feat: add tests
1 parent 86f8b34 commit e90ced3

10 files changed

Lines changed: 318 additions & 14 deletions

File tree

config/settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class Config:
2020
GITHUB_USERNAME = os.getenv("GITHUB_USERNAME", "")
2121
GITHUB_EMAIL = os.getenv("GITHUB_EMAIL", "")
2222

23+
DEBUG = False
24+
2325
STORAGE_DIR = BASE_DIR / os.getenv("STORAGE_DIR", "storage")
2426
LOGS_DIR = BASE_DIR / os.getenv("LOGS_DIR", "logs")
2527

leetcode/client.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@ def post(self, query: str, variables: dict = None):
1717
json=payload,
1818
timeout=Config.REQUEST_TIMEOUT
1919
)
20+
if Config.DEBUG:
2021

21-
print("=" * 80)
22-
print("Status Code:", response.status_code)
23-
print(response.text)
24-
print("=" * 80)
22+
print("=" * 80)
23+
print("Status Code:", response.status_code)
24+
print(response.text)
25+
print("=" * 80)
26+
27+
2528

2629
return response.json()

sync/manager.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,28 +34,45 @@ def run(self):
3434
Run one synchronization cycle.
3535
"""
3636

37+
# Fetch recent accepted submissions
3738
submissions = self.api.get_recent_submissions()
3839

40+
# Detect which ones haven't been synced yet
3941
new_submissions = self.detector.find_new(submissions)
4042

4143
if not new_submissions:
4244
print("No new submissions found.")
4345
return
4446

45-
latest = new_submissions[0]
47+
print(f"Found {len(new_submissions)} new submission(s).\n")
4648

47-
detail = self.api.get_submission_detail(latest.id)
49+
for submission in new_submissions:
50+
try:
51+
# Fetch complete submission details
52+
detail = self.api.get_submission_detail(
53+
submission.id
54+
)
4855

49-
self.downloader.download(detail)
56+
# Download solution and metadata
57+
self.downloader.download(detail)
5058

51-
self.git.add()
59+
60+
self.git.add()
5261

53-
self.git.commit(
54-
generate_commit_message(detail)
55-
)
62+
63+
self.git.commit(
64+
generate_commit_message(detail)
65+
)
66+
67+
68+
self.git.push()
69+
70+
self.detector.update(submission)
5671

57-
self.git.push()
72+
print(f"✓ Synced: {submission.title}")
5873

59-
self.detector.update(latest)
74+
except Exception as e:
75+
print(f"✗ Failed: {submission.title}")
76+
print(e)
6077

61-
print(f"Successfully synced: {latest.title}")
78+
print("\nSynchronization complete.")
File renamed without changes.

tests/conftest.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from datetime import datetime
2+
3+
import pytest
4+
5+
from leetcode.models import Submission
6+
from leetcode.models import SubmissionDetail
7+
8+
9+
@pytest.fixture
10+
def sample_submission():
11+
12+
return Submission(
13+
id="2051842379",
14+
title="Find Median from Data Stream",
15+
slug="find-median-from-data-stream",
16+
timestamp=1782879665,
17+
)
18+
19+
20+
@pytest.fixture
21+
def sample_submission_detail():
22+
"""
23+
Sample submission detail.
24+
"""
25+
26+
return SubmissionDetail(
27+
submission_id="2051842379",
28+
question_id="295",
29+
title_slug="find-median-from-data-stream",
30+
31+
language="python3",
32+
language_verbose="Python3",
33+
34+
runtime=177,
35+
runtime_display="177 ms",
36+
37+
memory=41264000,
38+
memory_display="41.3 MB",
39+
40+
status_code=10,
41+
42+
code=(
43+
"class MedianFinder:\n"
44+
" pass\n"
45+
),
46+
47+
timestamp=1782879665,
48+
)
49+
50+
51+
@pytest.fixture
52+
def temp_solution_dir(tmp_path):
53+
"""
54+
Temporary directory for downloader tests.
55+
"""
56+
57+
return tmp_path / "solutions"

tests/test_api.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
from unittest.mock import MagicMock
2+
3+
from leetcode.api import LeetCodeAPI
4+
5+
6+
def test_get_profile():
7+
8+
client = MagicMock()
9+
10+
client.post.return_value = {
11+
"data": {
12+
"matchedUser": {
13+
"username": "arnav-on-code"
14+
}
15+
}
16+
}
17+
18+
api = LeetCodeAPI(client)
19+
20+
profile = api.get_profile()
21+
22+
assert profile["username"] == "arnav-on-code"
23+
24+
25+
def test_get_recent_submissions():
26+
27+
client = MagicMock()
28+
29+
client.post.return_value = {
30+
"data": {
31+
"recentAcSubmissionList": [
32+
{
33+
"id": "1",
34+
"title": "Two Sum",
35+
"titleSlug": "two-sum",
36+
"timestamp": "1780000000",
37+
}
38+
]
39+
}
40+
}
41+
42+
api = LeetCodeAPI(client)
43+
44+
submissions = api.get_recent_submissions()
45+
46+
assert len(submissions) == 1
47+
48+
assert submissions[0].id == "1"
49+
50+
assert submissions[0].title == "Two Sum"
51+
52+
assert submissions[0].slug == "two-sum"
53+
54+
55+
def test_get_submission_detail():
56+
57+
client = MagicMock()
58+
59+
client.post.return_value = {
60+
"data": {
61+
"submissionDetails": {
62+
"question": {
63+
"questionId": "1",
64+
"titleSlug": "two-sum",
65+
},
66+
"lang": {
67+
"name": "python3",
68+
"verboseName": "Python3",
69+
},
70+
"runtime": 45,
71+
"runtimeDisplay": "45 ms",
72+
"memory": 17000000,
73+
"memoryDisplay": "17 MB",
74+
"statusCode": 10,
75+
"code": "print('hello')",
76+
"timestamp": "1780000000",
77+
}
78+
}
79+
}
80+
81+
api = LeetCodeAPI(client)
82+
83+
detail = api.get_submission_detail("123")
84+
85+
assert detail.submission_id == "123"
86+
87+
assert detail.question_id == "1"
88+
89+
assert detail.title_slug == "two-sum"
90+
91+
assert detail.language == "python3"
92+
93+
assert detail.status_code == 10

tests/test_parser.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from leetcode.parser import (
2+
parse_submissions,
3+
parse_submission_detail,
4+
)
5+
6+
7+
def test_parse_submissions():
8+
9+
data = [
10+
{
11+
"id": "123",
12+
"title": "Two Sum",
13+
"titleSlug": "two-sum",
14+
"timestamp": "1780000000",
15+
}
16+
]
17+
18+
submissions = parse_submissions(data)
19+
20+
assert len(submissions) == 1
21+
22+
submission = submissions[0]
23+
24+
assert submission.id == "123"
25+
assert submission.title == "Two Sum"
26+
assert submission.slug == "two-sum"
27+
assert submission.timestamp == 1780000000
28+
29+
30+
def test_parse_submission_detail():
31+
32+
data = {
33+
"question": {
34+
"questionId": "1",
35+
"titleSlug": "two-sum",
36+
},
37+
"lang": {
38+
"name": "python3",
39+
"verboseName": "Python3",
40+
},
41+
"runtime": 45,
42+
"runtimeDisplay": "45 ms",
43+
"memory": 17000000,
44+
"memoryDisplay": "17 MB",
45+
"statusCode": 10,
46+
"code": "print('hello')",
47+
"timestamp": "1780000000",
48+
}
49+
50+
detail = parse_submission_detail(
51+
"999",
52+
data,
53+
)
54+
55+
assert detail.submission_id == "999"
56+
57+
assert detail.question_id == "1"
58+
59+
assert detail.title_slug == "two-sum"
60+
61+
assert detail.language == "python3"
62+
63+
assert detail.language_verbose == "Python3"
64+
65+
assert detail.runtime == 45
66+
67+
assert detail.runtime_display == "45 ms"
68+
69+
assert detail.memory == 17000000
70+
71+
assert detail.memory_display == "17 MB"
72+
73+
assert detail.status_code == 10
74+
75+
assert detail.code == "print('hello')"
76+
77+
assert detail.timestamp == 1780000000

tests/test_storage.py

Whitespace-only changes.

tests/test_sync.py

Whitespace-only changes.

utils/logger.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import logging
2+
from pathlib import Path
3+
4+
from config.settings import Config
5+
6+
7+
class Logger:
8+
"""
9+
Application logger.
10+
"""
11+
12+
_logger = None
13+
14+
@classmethod
15+
def get_logger(cls) -> logging.Logger:
16+
17+
if cls._logger is not None:
18+
return cls._logger
19+
20+
log_file = Config.LOGS_DIR / "sync.log"
21+
22+
logger = logging.getLogger("leetcode_sync")
23+
24+
logger.setLevel(logging.INFO)
25+
26+
logger.propagate = False
27+
28+
if logger.handlers:
29+
return logger
30+
31+
formatter = logging.Formatter(
32+
"%(asctime)s | %(levelname)-8s | %(message)s",
33+
"%Y-%m-%d %H:%M:%S",
34+
)
35+
36+
# Log to file
37+
file_handler = logging.FileHandler(
38+
log_file,
39+
encoding="utf-8",
40+
)
41+
42+
file_handler.setFormatter(formatter)
43+
44+
logger.addHandler(file_handler)
45+
46+
# Log to console
47+
console_handler = logging.StreamHandler()
48+
49+
console_handler.setFormatter(formatter)
50+
51+
logger.addHandler(console_handler)
52+
53+
cls._logger = logger
54+
55+
return logger

0 commit comments

Comments
 (0)