Fakultas Ilmu Komputer UI

Commit ab73ae0f authored by Rayza Arasj Mahardhika's avatar Rayza Arasj Mahardhika
Browse files

Exercise 8

parent e649ec84
......@@ -108,4 +108,22 @@ def test_apps(self):
Dengan test tersebut jumlah mutant yang survived berkurang menjadi 44 karena terdapat 2 mutant yang berhasil di kill. (hasil baru dapat dilihat di file `mutation_test_after.result`)
## Exercise - 7 : Spiking & De-Spiking
Setelah saya melakukan subbab 18.1-18.5, pengertian mengenai Spiking yang saya dapat adalah, Spiking itu sendiri adalah suatu cara agar kita dapat melakukan proof of concept terhadap ide fitur yang kita miliki tanpa menmbuat test terlebih dahulu. Memang code akan tidak ditest namun code yang dibuat ini memang tidak dibuat untuk ditest/karena test namun code dibuat agar ide fitur yang kita miliki dapat terrealisasikan. Sedangkan De-Spiking adalah proses dimana kita membuang semua code spiking kita (karena concept ide kita sudah proven), lalu mengerjakan kembali fitur tersebut dengan menggunakan prinsip TDD dengan benar.
\ No newline at end of file
Setelah saya melakukan subbab 18.1-18.5, pengertian mengenai Spiking yang saya dapat adalah, Spiking itu sendiri adalah suatu cara agar kita dapat melakukan proof of concept terhadap ide fitur yang kita miliki tanpa menmbuat test terlebih dahulu. Memang code akan tidak ditest namun code yang dibuat ini memang tidak dibuat untuk ditest/karena test namun code dibuat agar ide fitur yang kita miliki dapat terrealisasikan. Sedangkan De-Spiking adalah proses dimana kita membuang semua code spiking kita (karena concept ide kita sudah proven), lalu mengerjakan kembali fitur tersebut dengan menggunakan prinsip TDD dengan benar.
## Exercise - 8 : Mocking
Setelah melakukan subbab 19.1-19.5, saya mengambil pengertian mengenai perbedaan manual mocking dan mocking menggunakan mock library adalah dengan menggunakan mock library, mocking akan sangat lebih mudah. Hal ini dikarenakan dengan manual mock kita harus membuat fungsi palsu telebih dahulu sesuai dengan parameter yang ada, serta harus mengganti fungsi yang ingin dimock dengan fungsi tersebut serta mengembalikannya ke keadaan semula agar tidak merusak test lain.
Dengan menggunakan mock, program dapat menjadi tightly coupled karena dengan mock kita akan melakukan test terhadap menggunakan mock, test akan lebih condong melihat bagaimana code berajalan dibandingkan dengan hanya melihat apa yang terjadi dengan code yang ada. Contoh mock yang digunakan:
```
@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'])
```
\ No newline at end of file
from accounts.models import User, Token
class PasswordlessAuthenticationBackend(object):
def authenticate(self, request, 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:
return None
def get_user(self, email):
try:
return User.objects.get(email=email)
except User.DoesNotExist:
return None
from django.db import models
from django.contrib import auth
import uuid
auth.signals.user_logged_in.disconnect(auth.models.update_last_login)
class User(models.Model):
email = models.EmailField(primary_key=True)
......
from django.test import TestCase
from django.contrib.auth import get_user_model
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(
None,
'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(None, token.uid)
new_user = User.objects.get(email=email)
self.assertEqual(user, new_user)
def test_returns_existing_user_with_correct_email_if_token_exists(self):
email = 'edith@example.com'
existing_user = User.objects.create(email=email)
token = Token.objects.create(email=email)
user = PasswordlessAuthenticationBackend().authenticate(None, 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')
found_user = PasswordlessAuthenticationBackend().get_user(
'edith@example.com'
)
self.assertEqual(found_user, desired_user)
def test_returns_None_if_no_user_with_that_email(self):
self.assertIsNone(
PasswordlessAuthenticationBackend().get_user('edith@example.com')
)
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.contrib import auth
from accounts.models import Token
User = get_user_model()
User = auth.get_user_model()
class UserModelTest(TestCase):
......@@ -15,6 +13,12 @@ class UserModelTest(TestCase):
user = User(email='a@b.com')
self.assertEqual(user.pk, 'a@b.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) # should not raise
class TokenModelTest(TestCase):
def test_links_user_with_auto_generated_uid(self):
......
from django.test import TestCase
import accounts.views
from unittest.mock import patch, call
from unittest.mock import patch
from accounts.models import Token
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, '/')
@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'])
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)
@patch('accounts.views.auth')
class LoginViewTest(TestCase):
def test_redirects_to_home_page(self, mock_auth):
response = self.client.get('/accounts/login?token=abcd123')
self.assertRedirects(response, '/')
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')
)
def test_calls_auth_login_with_user_if_there_is_one(self, mock_auth):
response = self.client.get('/accounts/login?token=abcd123')
self.assertEqual(
mock_auth.login.call_args,
call(response.wsgi_request, mock_auth.authenticate.return_value)
)
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.assertEqual(mock_auth.login.called, False)
from django.urls import path
from accounts.views import send_login_email, login
from django.contrib.auth.views import LogoutView
urlpatterns = [
path('send_login_email', send_login_email, name='send_login_email'),
path('login', login, name='login'),
path('logout', LogoutView.as_view(next_page='/'), name='logout'),
]
from django.shortcuts import render
from django.core.mail import send_mail
from django.shortcuts import redirect
from django.contrib import auth, messages
from accounts.models import Token
from django.urls import reverse
# Create your views here.
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}'
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."
)
return redirect('/')
def login(request):
user = auth.authenticate(uid=request.GET.get('token'))
if user:
auth.login(request, user)
return redirect('/')
......@@ -12,7 +12,6 @@ SUBJECT = 'Your login link for Superlists'
class LoginTest(FunctionalTest):
@skip
def test_can_get_email_link_to_log_in(self):
# Edith goes to the awesome superlists site
# and notices a "Log in" section in the navbar for the first time
......@@ -49,4 +48,14 @@ class LoginTest(FunctionalTest):
lambda: self.browser.find_element_by_link_text('Log out')
)
navbar = self.browser.find_element_by_css_selector('.navbar')
self.assertIn(TEST_EMAIL, navbar.text)
\ No newline at end of file
self.assertIn(TEST_EMAIL, navbar.text)
# Now she logs out
self.browser.find_element_by_link_text('Log out').click()
# She is logged out
self.wait_for(
lambda: self.browser.find_element_by_name('email')
)
navbar = self.browser.find_element_by_css_selector('.navbar')
self.assertNotIn(TEST_EMAIL, navbar.text)
......@@ -16,14 +16,37 @@
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<a class="navbar-brand" href="/">Superlists</a>
<form class="navbar-form navbar-right" method="POST" action="#">
<span>Enter email to log in:</span>
<input class="form-control" name="email" type="text" />
{% csrf_token %}
</form>
{% if user.email %}
<ul class="nav navbar-nav navbar-right">
<li class="navbar-text">Logged in as {{ user.email }}</li>
<li><a href="{% url 'logout' %}">Log out</a></li>
</ul>
{% else %}
<form class="navbar-form navbar-right"
method="POST"
action="{% url 'send_login_email' %}">
<span>Enter email to log in:</span>
<input class="form-control" name="email" type="text" />
{% csrf_token %}
</form>
{% endif %}
</div>
</nav>
​{% if messages %}
<div class="row">
<div class="col-md-8">
​{% for message in messages %}
​{% if message.level_tag == 'success' %}
<div class="alert alert-success">{{ message }}</div>
​{% else %}
<div class="alert alert-warning">{{ message }}</div>
​{% endif %}
​{% endfor %}
</div>
</div>
​{% endif %}
<div class="row">
<div class="col-md-6 col-md-offset-3 jumbotron">
<div class="text-center" id="centering-div">
......
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.core.mail import send_mail
from django.core.exceptions import ValidationError
from lists.models import Item, List
......
......@@ -30,6 +30,11 @@ ALLOWED_HOSTS = [
'*'
]
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'throwawayram@gmail.com'
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_PASSWORD')
EMAIL_PORT = 587
EMAIL_USE_TLS = True
# Application definition
......@@ -46,6 +51,10 @@ INSTALLED_APPS = [
]
AUTH_USER_MODEL = 'accounts.User'
AUTHENTICATION_BACKENDS = [
'accounts.authentication.PasswordlessAuthenticationBackend',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
......
......@@ -17,10 +17,12 @@ from django.contrib import admin
from django.urls import path, include
from lists import views as lists_views
from lists import urls as lists_urls
from accounts import urls as accounts_urls
urlpatterns = [
# path('admin/', admin.site.urls),
path('', lists_views.home_page, name='home'),
path('lists/', include(lists_urls)),
path('about/', lists_views.about_page, name='about')
path('about/', lists_views.about_page, name='about'),
path('accounts/', include('accounts.urls')),
]
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment