Skip to content

Commit

Permalink
more api tests
Browse files Browse the repository at this point in the history
  • Loading branch information
backface committed Aug 28, 2024
1 parent e24a03d commit 23e7b5a
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 57 deletions.
6 changes: 4 additions & 2 deletions TODO
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
* strip linebreaks from project names
* MIGRATION:
strip linebreaks and empty values from project names.
needs also fix for linked items (likes, comments)

* more unit tests

* tags suggestions?

* meta tags
* meta tags?


more:
Expand Down
50 changes: 22 additions & 28 deletions api/api_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from ninja.errors import HttpError
from allauth.account.forms import ResetPasswordForm
from allauth.account.utils import send_email_confirmation
from allauth.account.forms import SignupForm
from allauth.account.adapter import DefaultAccountAdapter

from apps.projects.models import Project
from apps.classrooms.models import Group, SelectedProject
Expand All @@ -33,25 +35,8 @@
)

# TODO
# * API rate lmiting/api/v1/projects/mash/test
# * API rate lmiting?

# TODO
# @api.post("/users/{username}?email=&password=&password_repeat="))
# @api.post("/users/{username}/newpassword?oldpwassword=&password_repeat=&newpassword=")


# questions:
# - what is ?updatenotes (loading projects) XXX
# - what is ?persist
# - what is ?upublished

# - what is "media" in snap-data / project file format

# - password hashing?
# - why image in base64?

# - rate limiting
# - securing emailing endpoints?


###################################
Expand Down Expand Up @@ -173,12 +158,7 @@ def signup_user(

try:
user = User.objects.get(username=username)
print(user)
return 400, Errors(
errors=[
f"User {username} already exists",
]
)
return 400, Errors(errors=[f"User {username} already exists",])
except User.DoesNotExist:
pass

Expand All @@ -188,6 +168,17 @@ def signup_user(
except User.DoesNotExist:
pass

# form = SignupForm(
# {
# "username": username,
# "email": email,
# "password1": password1,
# "password2": password2,
# })
# if not form.is_valid():
# return 400, Errors(errors=form.errors)
# form.save(request)

user = User(username=username, email=email, password=password)
user.set_password(password)
user.save()
Expand Down Expand Up @@ -291,7 +282,8 @@ def logout_user(request):

@api.get("/projects/{username}", response=ProjectList, summary="Get a user's projects.")
def get_users_projects(request, username: str):
"""
""")
# for project in
Get a user's projects.
"""
if request.user.is_authenticated and request.user.username == username:
Expand Down Expand Up @@ -383,10 +375,11 @@ def save_project(request, username: str, projectname: str):
notes = data["notes"]
thumbnail = data["thumbnail"]
soup = BeautifulSoup(contents, "xml")

# # we look for the first instances (less elegant but it might be nested)
thumbnail = (
soup.find_all("pentrails")[0].text if soup.find_all("thumbnail") else None
)
# thumbnail = soup.find_all("thumbnail")[0].text if soup.find_all("thumbnail") else thumbnail
thumbnail = soup.find_all("pentrails")[0].text if soup.find_all("pentrails") else thumbnail
# notes = soup.find_all("notes")[0].text if soup.find_all("notes") else None
# # tag = soup.find_all("tags")[0].text if soup.find_all("notes") else None

Expand All @@ -411,6 +404,7 @@ def save_project(request, username: str, projectname: str):
project.thumbnail.save(
f"{project.slug}.{ext}", ContentFile(base64.b64decode(imgstr)), save=True
)
project.save()

if "group" in request.session:
group = Group.objects.get(id=request.session["group"])
Expand Down
13 changes: 13 additions & 0 deletions api/test_api_legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ def test_set_project_visibility(self):
self.assertEqual(response.status_code, 200)
self.assertFalse(response.json()["ispublic"])

self.client.logout()
response = self.client.get(
reverse("api-legacy:get_users_projects", args=["testuser"])
)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json()), 0)

def test_delete_project(self):
self.client.login(username="testuser", password="testpassword")
response = self.client.get(
Expand All @@ -87,3 +94,9 @@ def test_save_project(self):
self.assertEqual(
response.json()["contents"], "<xml><notes>some notes #tagged</notes></xml>"
)

response = self.client.get(
reverse("api-legacy:get_users_projects", args=["testuser"])
)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json()), 2)
142 changes: 116 additions & 26 deletions api/test_api_v1.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,129 @@
import json
import base64
from django.test import TestCase, Client
from django.urls import reverse
from apps.users.models import User
from django.contrib.auth import get_user_model
from django.core.files.uploadedfile import SimpleUploadedFile
from apps.projects.models import Project
from apps.classrooms.models import Group, SelectedProject
from allauth.account.models import EmailAddress
User = get_user_model()


class APITest(TestCase):
class APITestCase(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
username="testuser", password="testpassword"
)
self.project = Project.objects.create(
user=self.user, name="testproject", slug="testproject", notes="testproject"
)
self.user = User.objects.create_user(username='testuser', email='[email protected]', password='snap$testpass')
self.user_email = EmailAddress.objects.create(user=self.user, email='[email protected]', primary=True, verified=True)
self.user2 = User.objects.create_user(username='testuse2r', email='[email protected]', password='snap$testpass')
self.project = Project.objects.create(user=self.user, name='testproject', is_public=True)

def test_init(self):
response = self.client.post('/api/v1/init')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {})

def test_current_user(self):
response = self.client.get(reverse("api-v1:current_user"))
self.client.login(username='testuser', password='snap$testpass')
response = self.client.get('/api/v1/users/c')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['username'], 'testuser')

def test_login_user(self):
response = self.client.post('/api/v1/users/testuser/login', data='testpass', content_type='text/plain')
self.assertEqual(response.status_code, 200)
self.assertIn('logged in', response.json()['message'])

def test_signup_user(self):
response = self.client.post('/api/v1/users/[email protected]&password=newpass&password_repeat=newpass',
content_type='application/json')
self.assertEqual(response.status_code, 200)
self.assertIn('Account created', response.json()['title'])

response = self.client.post('/api/v1/users/newuser/login', data='newpass', content_type='text/plain')
self.assertEqual(response.status_code, 200)
self.assertIn('logged in', response.json()['message'])


def test_resend_verification(self):
response = self.client.get('/api/v1/users/testuser/resendverification')
self.assertEqual(response.status_code, 200)

def test_request_password_change(self):
response = self.client.post('/api/v1/users/testuser/password_reset')
self.assertEqual(response.status_code, 200)
self.assertIn('password reset link', response.json()['message'])

def test_change_password(self):
self.client.login(username='testuser', password='snap$testpass')
response = self.client.post('/api/v1/users/testuser/newpassword?oldpassword=testpass&newpassword=newpass&password_repeat=newpass', content_type='application/json')
self.assertEqual(response.status_code, 200)
self.assertIn('Password changed', response.json()['title'])

def test_logout_user(self):
self.client.login(username='testuser', password='snap$testpass')
response = self.client.post('/api/v1/logout')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()["username"], "")
self.client.login(username="testuser", password="testpassword")
# response = self.client.get("/api/v1/users/c")
response = self.client.get(reverse("api-v1:current_user"))
self.assertEqual(response.json()['redirect'], '/')

def test_save_load_delete_project(self):
self.client.login(username='testuser', password='snap$testpass')
data = {
'xml': '<project name="testfile" app="Snap! 9.0, https://snap.berkeley.edu" version="2"><notes>a test note</notes><thumbnail>data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAAB4CAYAAAB1ovlvAAACqElEQVR4Xu3XMY7aUABF0e99QA/LAYkN0bAgEKuBjUBBRqSYYmaSKIWvZB3Xlp58/xHG0+vjGi4FogITgFF5s+8CAIKQFgAwzW8cQAbSAgCm+Y0DyEBaAMA0v3EAGUgLAJjmNw4gA2kBANP8xgFkIC0AYJrfOIAMpAUATPMbB5CBtACAaX7jADKQFgAwzW8cQAbSAgCm+Y0DyEBaAMA0v3EAGUgLAJjmNw4gA2kBANP8xgFkIC0AYJrfOIAMpAUATPMbB5CBtACAaX7jADKQFgAwzW8cQAbSAgCm+Y0DyEBaAMA0v3EAGUgLAJjmNw4gA2kBANP8xgFkIC0…7"></list></sounds><variables></variables><blocks></blocks><scripts></scripts><sprites select="1"><sprite name="Sprite" idx="1" x="0" y="0" heading="90" scale="1" volume="100" pan="0" rotation="1" draggable="true" costume="0" color="80,80,80,1" pen="tip" id="12"><costumes><list struct="atomic" id="13"></list></costumes><sounds><list struct="atomic" id="14"></list></sounds><blocks></blocks><variables></variables><scripts></scripts></sprite></sprites></stage><variables></variables></scene></scenes></project>',
'notes': 'a test notes',
'thumbnail': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAAB4CAYAAAB1ovlvAAACqElEQVR4Xu3XMY7aUABF0e99QA/LAYkN0bAgEKuBjUBBRqSYYmaSKIWvZB3Xlp58/xHG0+vjGi4FogITgFF5s+8CAIKQFgAwzW8cQAbSAgCm+Y0DyEBaAMA0v3EAGUgLAJjmNw4gA2kBANP8xgFkIC0AYJrfOIAMpAUATPMbB5CBtACAaX7jADKQFgAwzW8cQAbSAgCm+Y0DyEBaAMA0v3EAGUgLAJjmNw4gA2kBANP8xgFkIC0AYJrfOIAMpAUATPMbB5CBtACAaX7jADKQFgAwzW8cQAbSAgCm+Y0DyEBaAMA0v3EAGUgLAJjmNw4gA2kBANP8xgFkIC0AYJrfOIAMpAUATPMbB5CBtACAaX7jADKQFgAwzW8cQAbSAgCm+Y0DyEBaAMA0v3EAGUgLAPhD/u12O47H4zgcDukBLX0cwD+c8H6/H6/X633H7XYb1+t1rNfrpZuY9fkA/Evu3W735Y5pmsbz+Rzn83nWw1riGID/CfDxeIzL5bJEE7M+E4D/+Aq+3+/vV/BqtZr1gJY+BuAPJ7zZbMbpdBrfvYKXjmLO5wNwztq2vv6f/vjK+/2Z51IgKOAXMIhu8rMAgDSkBQBM8xsHkIG0AIBpfuMAMpAWADDNbxxABtICAKb5jQPIQFoAwDS/cQAZSAsAmOY3DiADaQEA0/zGAWQgLQBgmt84gAykBQBM8xsHkIG0AIBpfuMAMpAWADDNbxxABtICAKb5jQPIQFoAwDS/cQAZSAsAmOY3DiADaQEA0/zGAWQgLQBgmt84gAykBQBM8xsHkIG0AIBpfuMAMpAWADDNbxxABtICAKb5jQPIQFoAwDS/cQAZSAsAmOY3DiADaQEA0/zGAWQgLQBgmt84gAykBQBM8xsHkIG0AIBpfuMAMpAWADDNb/wXK14Ct+2fpIIAAAAASUVORK5CYII='
}
response = self.client.post('/api/v1/projects/testuser/newproject', data=json.dumps(data), content_type='application/json')
self.assertEqual(response.status_code, 200)
self.assertIn('project newproject saved', response.json()['message'])

response = self.client.get('/api/v1/projects/testuser')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json()['projects']), 2)

response = self.client.get('/api/v1/projects/testuser/newproject/thumbnail')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()["username"], "testuser")
self.assertIn('data:image/png;base64,', response.content.decode())

def test_logout(self):
self.client.login(username="testuser", password="testpassword")
response = self.client.post(reverse("api-v1:logout"))
response = self.client.delete('/api/v1/projects/testuser/newproject')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()["redirect"], "/")
self.assertIn('project newproject has been deleted', response.json()['message'])

def test_get_users_projects(self):
self.client.login(username="testuser", password="testpassword")
response = self.client.get(
reverse("api-v1:get_users_projects", args=["testuser"])
)
self.client.login(username='testuser', password='snap$testpass')
response = self.client.get('/api/v1/projects/testuser')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json()['projects']), 1)

# def test_get_project_thumbnail(self):
# response = self.client.get('/api/v1/projects/testuser/newproject/thumbnail')
# self.assertEqual(response.status_code, 200)
# self.assertIn('data:image/png;base64,', response.content.decode())

# def test_get_project(self):
# response = self.client.get('/api/v1/projects/testuser/newproject')
# self.assertEqual(response.status_code, 200)
# self.assertIn('<snapdata>', response.content.decode())

def test_get_project_versions(self):
response = self.client.get('/api/v1/projects/testuser/testproject/versions')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json()["projects"]), 1)
self.assertEqual(response.json(), [])

def test_set_project_visibility(self):
self.client.login(username='testuser', password='snap$testpass')
response = self.client.post('/api/v1/projects/testuser/testproject/metadata?ispublic=false&ispublished=false')
self.assertEqual(response.status_code, 200)
self.assertIn('project testproject updated', response.json()['message'])

response = self.client.post('/api/v1/logout')
self.assertEqual(response.status_code, 200)

self.client.login(username='testuser2', password='snap$testpass')
response = self.client.get('/api/v1/projects/testuser')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json()['projects']), 0)

def test_delete_project(self):
self.client.login(username='testuser', password='snap$testpass')
response = self.client.delete('/api/v1/projects/testuser/testproject')
self.assertEqual(response.status_code, 200)
self.assertIn('project testproject has been deleted', response.json()['message'])

1 change: 0 additions & 1 deletion apps/classrooms/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ def test_leave_view(self):
self.assertEqual(response.status_code, 302) # Check for redirect
self.assertFalse(self.group.members.filter(username='member').exists()) # Check if member left


def tearDown(self):
User.objects.all().delete()
Group.objects.all().delete()
1 change: 1 addition & 0 deletions apps/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def thumbs_upload_handler(instance, filename):
notes = models.TextField(
blank=True,
null=True,
default="",
verbose_name="Project notes",
help_text="You can add hashtags here!",
)
Expand Down

0 comments on commit 23e7b5a

Please sign in to comment.