Fakultas Ilmu Komputer UI

Commit 3ea50fba authored by Dwi Nanda Susanto's avatar Dwi Nanda Susanto
Browse files

Merge branch 'fix_uas_take_home' into 'master'

Fix uas take home

See merge request !16
parents f0da274c dfb64154
Pipeline #27634 passed with stages
in 9 minutes and 30 seconds
......@@ -3,4 +3,6 @@ __pycache__
*.pyc
.vscode
Pipfile
debug.log
\ No newline at end of file
debug.log
accounts/tests/__pycache__/*
lists/tests/__pycache__/*
stages:
- unitTest
- test
- test_functional
- deploy
- functionalTest
Test:
image: python:3.6.5
stage: unitTest
image: python:3.7
stage: test
before_script:
- pip3 install --upgrade pip
- pip3 install -r requirements.txt
- python3 manage.py collectstatic --no-input
- python3 manage.py collectstatic --no-input
script:
- python3 manage.py test lists
- coverage run manage.py test accounts/tests lists
- coverage report
tags:
- test
Deploy:
TestFunctional:
image: python:3.7
stage: test_functional
before_script:
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
- echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list
- pip3 install -r requirements.txt
- python3 manage.py makemigrations
- python3 manage.py migrate
- python3 manage.py collectstatic --no-input
- apt-get update -qq && apt-get install -y -qq unzip
- apt-get install -y google-chrome-stable
- apt-get install -y xvfb
- wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.zip
- unzip chromedriver_linux64.zip
- ls -al
- python3 manage.py runserver 8000 &
when: on_success
script:
- coverage run --source=lists,accounts,functional_test manage.py test functional_test lists accounts
- coverage report -m
Deployment:
image: ruby:2.4
stage: deploy
before_script:
......@@ -24,32 +47,10 @@ Deploy:
script:
- dpl --provider=heroku --app=$HEROKU_APPNAME --api-key=$HEROKU_APIKEY
- export HEROKU_API_KEY=$HEROKU_APIKEY
- heroku run --app $HEROKU_APPNAME python manage.py makemigrations
- heroku run --app $HEROKU_APPNAME python manage.py migrate
environment:
name: production
url: $HEROKU_APP_HOST
only:
- master
FunctionalTest:
image: python:3.6.5
stage: functionalTest
dependencies:
- Deploy
before_script:
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
- echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list
- pip3 install -r requirements.txt
- apt-get update -qq && apt-get install -y -qq unzip
- apt-get install -y google-chrome-stable
- apt-get install -y xvfb
- wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.zip
- unzip chromedriver_linux64.zip
- python3 manage.py collectstatic --noinput
when: on_success
script:
- echo $HEROKU_APP_HOST
- python3 manage.py test functional_tests
only:
- master
- master
......@@ -5,6 +5,10 @@
The **deployed** simple home page can be accessed here :
[http://dwi-simplehomepage.herokuapp.com/](http://dwi-simplehomepage.herokuapp.com/)
[![pipeline status](https://gitlab.cs.ui.ac.id/pmpl/practice-collection/2019/1506722720-practice/badges/fix_uas_take_home/pipeline.svg)](https://gitlab.cs.ui.ac.id/pmpl/practice-collection/2019/1506722720-practice/commits/fix_uas_take_home)
[![coverage report](https://gitlab.cs.ui.ac.id/pmpl/practice-collection/2019/1506722720-practice/badges/fix_uas_take_home/coverage.svg)](https://gitlab.cs.ui.ac.id/pmpl/practice-collection/2019/1506722720-practice/commits/fix_uas_take_home)
# Exercise 3
Pada exercise 3 kali ini dibuat sebuah integration test yang akan mengautomasi waktu menunggu hingga terjadi perubahan pada rows.
Pada integration test ini sudah bisa melakukan listening terhadap request dari url LiveServer yang sedang aktif. Jadi tidak perlu melihat lagi url apa yang sedang berjalan.
......
import sys
from accounts.models import ListUser, Token
from accounts.models import User, Token
class PasswordlessAuthenticationBackend(object):
def authenticate(self, uid):
try:
token = Token.objects.get(uid=uid)
return User.objects.get(email=token.email)
except User.DoesNotExist:
return User.objects.create(email=token.email)
except Token.DoesNotExist:
def authenticate(self, request, uid):
print('uid', uid, file=sys.stderr)
if not Token.objects.filter(uid=uid).exists():
print('no token found', file=sys.stderr)
return None
token = Token.objects.get(uid=uid)
print('got token', file=sys.stderr)
try:
user = ListUser.objects.get(email=token.email)
print('got user', file=sys.stderr)
return user
except ListUser.DoesNotExist:
print('new user', file=sys.stderr)
return ListUser.objects.create(email=token.email)
def get_user(self, email):
try:
return User.objects.get(email=email)
except User.DoesNotExist:
return ListUser.objects.get(email=email)
except ListUser.DoesNotExist:
return None
# Generated by Django 2.0.2 on 2019-11-28 04:26
# Generated by Django 2.2.5 on 2019-12-21 06:15
from django.db import migrations, models
import uuid
......@@ -9,10 +9,24 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
('auth', '0011_update_proxy_permissions'),
]
operations = [
migrations.CreateModel(
name='Token',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.EmailField(max_length=254)),
('uid', models.CharField(default=uuid.uuid4, max_length=200)),
],
),
migrations.CreateModel(
name='User',
fields=[
('email', models.EmailField(max_length=254, primary_key=True, serialize=False)),
],
),
migrations.CreateModel(
name='ListUser',
fields=[
......@@ -27,18 +41,4 @@ class Migration(migrations.Migration):
'abstract': False,
},
),
migrations.CreateModel(
name='Token',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.EmailField(max_length=254)),
('uid', models.CharField(default=uuid.uuid4, max_length=40)),
],
),
migrations.CreateModel(
name='User',
fields=[
('email', models.EmailField(max_length=254, primary_key=True, serialize=False)),
],
),
]
......@@ -5,12 +5,14 @@ from django.contrib import auth
from django.db import models
auth.signals.user_logged_in.disconnect(auth.models.update_last_login)
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager, PermissionsMixin
AbstractBaseUser, BaseUserManager, PermissionsMixin
)
from django.db import models
class Token(models.Model):
email = models.EmailField()
uid = models.CharField(default=uuid.uuid4, max_length=40)
uid = models.CharField(default=uuid.uuid4, max_length=200)
class ListUserManager(BaseUserManager):
......@@ -23,11 +25,13 @@ class ListUserManager(BaseUserManager):
class ListUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(primary_key=True)
USERNAME_FIELD = 'email'
#REQUIRED_FIELDS = ['email', 'height']
# REQUIRED_FIELDS = ['email', 'height']
objects = ListUserManager()
@property
def is_staff(self):
return self.email == 'harry.percival@example.com'
@property
def is_active(self):
return True
......
<html>
<h1>Email sent</h1>
<p>Check your email, you'll find a message with a link that will log you into
the site.</p>
<p>Please Check</p>
</html>
\ No newline at end of file
from django.test import TestCase
# Create your tests here.
......@@ -4,17 +4,18 @@ from accounts.authentication import PasswordlessAuthenticationBackend
from accounts.models import Token
User = get_user_model()
class AuthenticateTest(TestCase):
def test_returns_None_if_no_such_token(self):
result = PasswordlessAuthenticationBackend().authenticate(
'no-such-token'
'no-such-token', 'no-such-token'
)
self.assertIsNone(result)
def test_returns_new_user_with_correct_email_if_token_exists(self):
email = 'edith@example.com'
token = Token.objects.create(email=email)
user = PasswordlessAuthenticationBackend().authenticate(token.uid)
user = PasswordlessAuthenticationBackend().authenticate(token.uid, token.uid)
new_user = User.objects.get(email=email)
self.assertEqual(user, new_user)
......@@ -22,10 +23,12 @@ class AuthenticateTest(TestCase):
email = 'edith@example.com'
existing_user = User.objects.create(email=email)
token = Token.objects.create(email=email)
user = PasswordlessAuthenticationBackend().authenticate(token.uid)
user = PasswordlessAuthenticationBackend().authenticate(token.uid, token.uid)
self.assertEqual(user, existing_user)
class GetUserTest(TestCase):
def test_gets_user_by_email(self):
User.objects.create(email='another@example.com')
desired_user = User.objects.create(email='edith@example.com')
......@@ -35,6 +38,7 @@ class GetUserTest(TestCase):
self.assertEqual(found_user, desired_user)
def test_returns_None_if_no_user_with_that_email(self):
User.objects.create(email='another@example.com')
self.assertIsNone(
PasswordlessAuthenticationBackend().get_user('edith@example.com')
PasswordlessAuthenticationBackend().get_user('email@gmail.com')
)
from django.test import TestCase
from django.contrib.auth import get_user_model
from accounts.models import Token
from django.test import TestCase
from django.contrib import auth
from accounts.models import Token
User = auth.get_user_model()
User = get_user_model()
class TokenModelTest(TestCase):
def test_links_user_with_auto_generated_uid(self):
token1 = Token.objects.create(email='a@b.com')
token2 = Token.objects.create(email='a@b.com')
self.assertNotEqual(token1.uid, token2.uid)
class UserModelTest(TestCase):
def test_email_is_primary_key(self):
user = User(email='a@b.com')
self.assertEqual(user.pk, 'a@b.com')
user = User(email='email@gmail.com')
self.assertEqual(user.pk, 'email@gmail.com')
def test_no_problem_with_auth_login(self):
user = User.objects.create(email='edith@example.com')
user.backend = ''
request = self.client.request().wsgi_request
auth.login(request, user)
class TokenModelTest(TestCase):
def test_links_user_with_auto_generated_uid(self):
token1 = Token.objects.create(email='email@gmail.com')
token2 = Token.objects.create(email='email@gmail.com')
self.assertEqual(token1.uid, token1.uid)
from django.test import TestCase
from unittest.mock import patch, call
import accounts.views
from accounts.models import Token
from django.test import TestCase
from unittest.mock import patch
class SendLoginEmailViewTest(TestCase):
def test_redirects_to_home_page(self):
response = self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
self.assertRedirects(response, '/')
from unittest.mock import call
import mock
def test_sends_mail_to_address_from_post(self):
self.send_mail_called = False
def fake_send_mail(subject, body, from_email, to_list):
self.send_mail_called = True
self.subject = subject
self.body = body
self.from_email = from_email
self.to_list = to_list
accounts.views.send_mail = fake_send_mail
self.client.post('/accounts/send_login_email', data={
class SendLoginEmailViewTest(TestCase):
@mock.patch('accounts.views.send_mail')
def test_redirects_to_home_page(self, mock_send_mail):
response = self.client.post('/accounts/send_email', data={
'email': 'edith@example.com'
})
self.assertTrue(self.send_mail_called)
self.assertEqual(self.subject, 'Your login link for Superlists')
self.assertEqual(self.from_email, 'noreply@superlists')
self.assertEqual(self.to_list, ['edith@example.com'])
@patch('accounts.views.send_mail')
def test_sends_mail_to_address_from_post(self, mock_send_mail):
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
self.assertEqual(mock_send_mail.called, True)
(subject, body, from_email, to_list), kwargs = mock_send_mail.call_args
self.assertEqual(subject, 'Your login link for Superlists')
self.assertEqual(from_email, 'noreply@superlists')
self.assertEqual(to_list, ['edith@example.com'])
self.assertEqual(to_list, ['schmedith@example.com'])
self.assertRedirects(response, '/')
@patch('accounts.views.send_mail')
@mock.patch('accounts.views.send_mail')
def test_sends_mail_to_address_from_post(self, mock_send_mail):
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
self.client.post('/accounts/send_email', data={
'email': 'edith@example.com'
})
self.assertEqual(mock_send_mail.called, True)
(subject, body, from_email, to_list), kwargs = mock_send_mail.call_args
self.assertEqual(subject, 'Your login link for Superlists')
self.assertEqual(from_email, 'noreply@superlists')
self.assertEqual(subject, 'Your Link for Superlists')
self.assertIn('obeythetestinggoat@gmail.com', from_email,)
self.assertEqual(to_list, ['edith@example.com'])
def test_adds_success_message(self):
response = self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
}, follow=True)
message = list(response.context['messages'])[0]
self.assertEqual(
message.message,
"Check your email, we've sent you a link you can use to log in."
)
self.assertEqual(message.tags, "success")
def test_creates_token_associated_with_email(self):
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
token = Token.objects.first()
self.assertEqual(token.email, 'edith@example.com')
@patch('accounts.views.send_mail')
def test_sends_link_to_login_using_token_uid(self, mock_send_mail):
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
token = Token.objects.first()
expected_url = f'http://testserver/accounts/login?token={token.uid}'
(subject, body, from_email, to_list), kwargs = mock_send_mail.call_args
self.assertIn(expected_url, body)
class LoginViewTest(TestCase):
@mock.patch('accounts.views.send_mail')
def test_redirects_to_home_page(self, mock_send_mail):
response = self.client.get('/accounts/login?token=abcd123')
self.assertRedirects(response, '/')
@patch('accounts.views.auth')
def test_calls_authenticate_with_uid_from_get_request(self, mock_auth):
self.client.get('/accounts/login?token=abcd123')
self.assertEqual(
mock_auth.authenticate.call_args,
call(uid='abcd123')
)
@patch('accounts.views.auth')
@mock.patch('accounts.views.auth')
def test_calls_auth_login_with_user_if_there_is_one(self, mock_auth):
response = self.client.get('/accounts/login?token=abcd123')
response = self.client.get('/accounts/login?uid=abcd123')
self.assertEqual(
mock_auth.login.call_args,
call(response.wsgi_request, mock_auth.authenticate.return_value)
)
class LoginViewTest(TestCase):
def test_redirects_to_home_page(self):
response = self.client.get('/accounts/login?token=abcd123')
self.assertRedirects(response, '/')
@mock.patch('accounts.views.auth')
def test_calls_authenticate_with_uid_from_get_request(self, mock_auth):
self.client.get('/accounts/login?uid=abcd123')
args, kwargs = mock_auth.authenticate.call_args
self.assertEqual(kwargs, {'uid': 'abcd123'})
@mock.patch('accounts.views.auth')
def test_does_not_login_if_user_is_not_authenticated(self, mock_auth):
mock_auth.authenticate.return_value = None
self.client.get('/accounts/login?token=abcd123')
self.client.get('accounts/login?uid=abcd123')
self.assertEqual(mock_auth.login.called, False)
......@@ -2,8 +2,9 @@ from django.conf.urls import url
from accounts import views
from django.contrib.auth import logout, login
urlpatterns = [
url(r'^send_login_email$', views.send_login_email, name='send_login_email'),
url(r'^login$', views.login, name='login'),
url(r'^logout$', logout, {'next_page': '/'}, name='logout'),
url(r'^send_email$', views.send_login_email, name='send_login_email'),
url(r'^login$', views.login, name='login'),
url(r'^logout$', views.logout, name='logout'),
]
import uuid
import sys
from django.shortcuts import redirect
from django.contrib import messages
from django.contrib import auth, messages
from accounts.models import Token
from django.contrib.auth import authenticate
from django.contrib.auth import login as auth_login
from django.core.mail import send_mail
from django.shortcuts import redirect, render
from django.contrib.auth import login as auth_login, logout as auth_logout
from django.core.mail import send_mail
from django.shortcuts import redirect
# from django.contrib.auth import authenticate
# from django.contrib.auth import login as auth_login, logout as auth_logout
from django.contrib import auth
from accounts.models import Token
from django.conf import settings
# def send_login_email(request):
# email = request.POST['email']
# uid = str(uuid.uuid4())
# Token.objects.create(email=email, uid=uid)
# print('saving uid', uid, 'for email', email, file=sys.stderr)
# url = request.build_absolute_uri(f'/accounts/login?uid={uid}')
# send_mail(
# 'Your login link for Superlists',
# f'Use this link to log in:\n\n{url}',
# 'noreply@superlists',
# [email],
# )
# return render(request, 'login_email_sent.html')
def send_login_email(request):
email = request.POST['email']
token = Token.objects.create(email=email)
url = request.build_absolute_uri(
reverse('login') + '?token=' + str(token.uid)
)
message_body = f'Use this link to log in:\n\n{url}'
uid = str(uuid.uuid4())
Token.objects.create(email=email, uid=uid)
url = request.build_absolute_uri(f'/accounts/login?uid={uid}')
send_mail(
'Your login link for Superlists',
message_body,
'noreply@superlists',
[email]
)
messages.success( request,
"Check your email, we've sent you a link you can use to log in."
'Your Link for Superlists',
f'Use this link to log in:\n\n{url}',
settings.EMAIL_HOST_USER,
[email],
fail_silently=False,
)
messages.add_message(
request,
messages.SUCCESS,
"Check your email, we've sent you a link you can use to log in.")
return redirect('/')
def login(request):
user = auth.authenticate(uid=request.GET.get('token'))
if user:
print('login view', file=sys.stderr)
uid = request.GET.get('uid')
print(uid)
user = auth.authenticate(uid=uid)
print(user)
if user is not None:
auth.login(request, user)
return redirect('/')
def logout(request):
auth_logout(request)
auth.logout(request)
return redirect('/')
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="a">
<rect width="99" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#a)">
<path fill="#555" d="M0 0h63v20H0z"/>
<path fill="#fe7d37" d="M63 0h36v20H63z"/>
<path fill="url(#b)" d="M0 0h99v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
<text x="31.5" y="14">coverage</text>
<text x="80" y="15" fill="#010101" fill-opacity=".3">54%</text>
<text x="80" y="14">54%</text>
</g>
</svg>
No preview for this file type
......@@ -44,7 +44,7 @@ class FunctionalTest(StaticLiveServerTestCase):
# table = self.browser.find_element_by_css_selector(
# '#id_list_table')
# wait = WebDriverWait(driver, 10)
wait = WebDriverWait(self.browser, 10)
wait = WebDriverWait(self.browser, 30)
table = wait.until(EC.presence_of_element_located(
(By.CSS_SELECTOR, "#id_list_table")))
rows = table.find_elements_by_tag_name('tr')
......
......@@ -2,18 +2,11 @@ from .base import FunctionalTest
from selenium.webdriver.common.keys import Keys
class LayoutAndStylingTest(FunctionalTest):
def test_layout_and_styling(self):
# Edith goes to the home page
def test_name_in_header_and_footer(self):
self.browser.get(self.live_server_url)