Skip to content

Commit 4d1fc13

Browse files
committed
add some tests.
1 parent ca19e57 commit 4d1fc13

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed

tests/conftest.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
import pytest
3+
from unittest.mock import MagicMock, patch
4+
from main import app as flask_app
5+
6+
@pytest.fixture
7+
def app():
8+
"""Create and configure a new app instance for each test."""
9+
# Overwrite the GRL value to True so we can bypass some of the checks
10+
# that are not relevant for testing.
11+
with patch('ide.auth.GRL', True):
12+
flask_app.config.update({
13+
"TESTING": True,
14+
})
15+
yield flask_app
16+
17+
@pytest.fixture
18+
def client(app):
19+
"""A test client for the app."""
20+
return app.test_client()
21+
22+
@pytest.fixture(autouse=True)
23+
def mock_google_oauth(mocker):
24+
"""Mock Google OAuth2."""
25+
mocker.patch('ide.auth.is_logged_in', return_value=True)
26+
mocker.patch('ide.auth.get_user_info', return_value={'email': 'test@example.com'})
27+
28+
@pytest.fixture(autouse=True)
29+
def mock_datastore(mocker):
30+
"""Mock Google Cloud Datastore."""
31+
mocker.patch('google.cloud.ndb.Client', return_value=MagicMock())
32+
mocker.patch('google.cloud.ndb.Key', return_value=MagicMock())

tests/test_api.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
2+
import pytest
3+
from unittest.mock import patch, MagicMock
4+
5+
def test_api_login_not_logged_in(client):
6+
with patch('ide.auth.is_logged_in', return_value=False):
7+
response = client.get('/api/login')
8+
assert response.status_code == 200
9+
assert response.json == {'state': 'not_logged_in', 'login_url': '/google/login'}
10+
11+
def test_api_login_logged_in_existing_user(client):
12+
with patch('ide.models.User.query') as mock_query:
13+
mock_user = MagicMock()
14+
mock_user.key.id.return_value = 'testuser'
15+
mock_user.secret = 'secret'
16+
mock_query.return_value.filter.return_value.get.return_value = mock_user
17+
response = client.get('/api/login')
18+
assert response.status_code == 200
19+
assert response.json == {'state': 'logged_in', 'username': 'testuser', 'secret': 'secret', 'logout_url': '/google/logout'}
20+
21+
def test_api_login_logged_in_new_user(client):
22+
with patch('ide.models.User.query') as mock_query:
23+
mock_query.return_value.filter.return_value.get.return_value = None
24+
response = client.get('/api/login')
25+
assert response.status_code == 200
26+
assert response.json == {'state': 'new_user', 'suggested_name': 'test'}
27+
28+
def test_api_user_get_existing(client):
29+
with patch('ide.routes.parseUrlPath') as mock_parse:
30+
mock_parse.return_value = (['testuser'], MagicMock(), 'test@example.com')
31+
response = client.get('/api/user/testuser')
32+
assert response.status_code == 200
33+
assert response.json == {}
34+
35+
def test_api_user_get_non_existing(client):
36+
with patch('ide.routes.parseUrlPath') as mock_parse:
37+
mock_parse.return_value = (['testuser'], None, 'test@example.com')
38+
response = client.get('/api/user/testuser')
39+
assert response.status_code == 404
40+
41+
def test_api_user_put_new(client):
42+
with patch('ide.routes.parseUrlPath') as mock_parse, \
43+
patch('ide.routes.User') as mock_user_model, \
44+
patch('ide.routes.Folder') as mock_folder_model:
45+
mock_parse.return_value = (['testuser'], None, 'test@example.com')
46+
response = client.put('/api/user/testuser')
47+
assert response.status_code == 200
48+
mock_user_model.assert_called_once()
49+
assert mock_folder_model.call_count == 2
50+
51+
52+
def test_api_user_put_existing(client):
53+
with patch('ide.routes.parseUrlPath') as mock_parse:
54+
mock_parse.return_value = (['testuser'], MagicMock(), 'test@example.com')
55+
response = client.put('/api/user/testuser')
56+
assert response.status_code == 403
57+
58+
def test_api_user_folders_get(client):
59+
with patch('ide.routes.parseUrlPath') as mock_parse, \
60+
patch('ide.routes.Folder.query') as mock_query:
61+
mock_parse.return_value = (['testuser'], MagicMock(), 'test@example.com')
62+
mock_folder = MagicMock()
63+
mock_folder.key.id.return_value = 'MyPrograms'
64+
mock_folder.isPublic = True
65+
mock_query.return_value = [mock_folder]
66+
response = client.get('/api/user/testuser/folder/')
67+
assert response.status_code == 200
68+
assert response.json == {'user': 'testuser', 'folders': ['MyPrograms'], 'publics': [True]}
69+
70+
def test_api_user_folder_put(client):
71+
with patch('ide.routes.parseUrlPath') as mock_parse, \
72+
patch('ide.routes.authorize_user', return_value=True), \
73+
patch('ide.routes.Folder') as mock_folder_model:
74+
mock_parse.return_value = (['testuser', 'MyNewFolder'], MagicMock(), 'test@example.com')
75+
response = client.put('/api/user/testuser/folder/MyNewFolder')
76+
assert response.status_code == 200
77+
mock_folder_model.assert_called_once()
78+
79+
def test_api_user_folder_delete_non_empty(client):
80+
with patch('ide.routes.parseUrlPath') as mock_parse, \
81+
patch('ide.routes.ndb.Key') as mock_key, \
82+
patch('ide.routes.Program.query') as mock_program_query:
83+
mock_parse.return_value = (['testuser', 'MyPrograms'], MagicMock(), 'test@example.com')
84+
mock_key.return_value.get.return_value = MagicMock()
85+
mock_program_query.return_value.__iter__.return_value = [MagicMock()]
86+
response = client.delete('/api/user/testuser/folder/MyPrograms')
87+
assert response.status_code == 409
88+
89+
def test_api_user_folder_delete_empty(client):
90+
with patch('ide.routes.parseUrlPath') as mock_parse, \
91+
patch('ide.routes.ndb.Key') as mock_key, \
92+
patch('ide.routes.Program.query') as mock_program_query:
93+
mock_parse.return_value = (['testuser', 'MyPrograms'], MagicMock(), 'test@example.com')
94+
mock_folder = MagicMock()
95+
mock_key.return_value.get.return_value = mock_folder
96+
mock_program_query.return_value.__iter__.return_value = []
97+
response = client.delete('/api/user/testuser/folder/MyPrograms')
98+
assert response.status_code == 200
99+
mock_folder.key.delete.assert_called_once()
100+
101+
def test_api_user_folder_programs_get(client):
102+
with patch('ide.routes.parseUrlPath') as mock_parse, \
103+
patch('ide.routes.ndb.Key') as mock_key, \
104+
patch('ide.routes.Program.query') as mock_program_query:
105+
mock_parse.return_value = (['testuser', 'MyPrograms'], MagicMock(), 'test@example.com')
106+
mock_folder = MagicMock()
107+
mock_folder.isPublic = True
108+
mock_key.return_value.get.return_value = mock_folder
109+
mock_program = MagicMock()
110+
mock_program.key.id.return_value = 'test_program'
111+
mock_program.screenshot = b''
112+
mock_program.datetime = '2025-07-10'
113+
mock_program_query.return_value = [mock_program]
114+
response = client.get('/api/user/testuser/folder/MyPrograms/program/')
115+
assert response.status_code == 200
116+
assert response.json['programs'][0]['name'] == 'test_program'
117+
118+
def test_api_user_folder_program_get(client):
119+
with patch('ide.routes.parseUrlPath') as mock_parse, \
120+
patch('ide.routes.ndb.Key') as mock_key:
121+
mock_parse.return_value = (['testuser', 'MyPrograms', 'test_program'], MagicMock(), 'test@example.com')
122+
mock_folder = MagicMock()
123+
mock_folder.isPublic = True
124+
mock_program = MagicMock()
125+
mock_program.source = 'print("Hello, World!")'
126+
mock_program.screenshot = b''
127+
mock_program.datetime = '2025-07-10'
128+
mock_key.return_value.get.side_effect = [mock_folder, mock_program]
129+
response = client.get('/api/user/testuser/folder/MyPrograms/program/test_program')
130+
assert response.status_code == 200
131+
assert response.json['source'] == 'print("Hello, World!")'
132+
133+
def test_api_user_folder_program_put(client):
134+
with patch('ide.routes.parseUrlPath') as mock_parse, \
135+
patch('ide.routes.authorize_user', return_value=True), \
136+
patch('ide.routes.ndb.Key') as mock_key, \
137+
patch('ide.routes.Program') as mock_program_model:
138+
mock_parse.return_value = (['testuser', 'MyPrograms', 'test_program'], MagicMock(), 'test@example.com')
139+
mock_key.return_value.get.side_effect = [None, MagicMock()]
140+
response = client.put('/api/user/testuser/folder/MyPrograms/program/test_program', data={'program': '{}'})
141+
assert response.status_code == 200
142+
mock_program_model.assert_called_once()
143+
144+
def test_api_user_folder_program_delete(client):
145+
with patch('ide.routes.parseUrlPath') as mock_parse, \
146+
patch('ide.routes.authorize_user', return_value=True), \
147+
patch('ide.routes.ndb.Key') as mock_key:
148+
mock_parse.return_value = (['testuser', 'MyPrograms', 'test_program'], MagicMock(), 'test@example.com')
149+
mock_program = MagicMock()
150+
mock_key.return_value.get.return_value = mock_program
151+
response = client.delete('/api/user/testuser/folder/MyPrograms/program/test_program')
152+
assert response.status_code == 200
153+
mock_program.key.delete.assert_called_once()

0 commit comments

Comments
 (0)