Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/testing #181

Merged
merged 24 commits into from
Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d7f33af
sms driver
rathorevaibhav Jun 7, 2021
d06b62b
test cases init
rathorevaibhav Jun 8, 2021
f7ea078
some test cases
rathorevaibhav Jun 8, 2021
e817b42
Merge branch 'feature/testing' of https://github.com/avantifellows/pl…
rathorevaibhav Jun 8, 2021
dfe23aa
default for allowed hosts
rathorevaibhav Jun 8, 2021
fb7c21d
running only for python 3.8
rathorevaibhav Jun 8, 2021
7cb6557
running only for python 3.8
rathorevaibhav Jun 8, 2021
9eb5b2f
secret key
rathorevaibhav Jun 8, 2021
5dd1c85
db host
rathorevaibhav Jun 8, 2021
10406e6
some changes to db creds
rathorevaibhav Jun 8, 2021
5388f43
changes to postgres setup
rathorevaibhav Jun 8, 2021
05d9d3b
coverage and coveralls
rathorevaibhav Jun 8, 2021
5b1f35e
coveralls service github
rathorevaibhav Jun 8, 2021
f7a8dd7
code coverage integration
rathorevaibhav Jun 8, 2021
9add161
coverage in requirements, readme badge and run_tests upload to codecov
rathorevaibhav Jun 9, 2021
a7fcbdd
name to job
rathorevaibhav Jun 9, 2021
db98c36
single ci file and multiple jobs
rathorevaibhav Jun 9, 2021
7701fca
codecov token
rathorevaibhav Jun 9, 2021
b7dc9c4
comments as doc
rathorevaibhav Jun 9, 2021
dc95032
Update users/tests.py
rathorevaibhav Jun 9, 2021
ac22886
review feedback
rathorevaibhav Jun 9, 2021
f49a31b
Merge branch 'feature/testing' of https://github.com/avantifellows/pl…
rathorevaibhav Jun 9, 2021
ad3a0bf
invalid otp fails test
rathorevaibhav Jun 9, 2021
0881884
Apply suggestions from code review
rathorevaibhav Jun 9, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,8 @@ EMAIL_HOST_USER= # sender's email-id
# passwords. More on that here:
# https://dev.to/abderrahmanemustapha/how-to-send-email-with-django-and-gmail-in-production-the-right-way-24ab
EMAIL_HOST_PASSWORD=

# The driver for sending SMSs. Possible values are `sns` or `log`.
# Use `sns` to have AWS SNS support. The AWS credentials must be present for this.
# Use `log` to log SMSs into a file instead. Recommended for development mode.
SMS_DRIVER='sns'
rathorevaibhav marked this conversation as resolved.
Show resolved Hide resolved
62 changes: 62 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Plio CI

on:
pull_request:
push:
branches: [master]
dalmia marked this conversation as resolved.
Show resolved Hide resolved

jobs:
pre-commit:
name: Pre-commit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: pre-commit/action@v2.0.0

test:
name: Test cases
runs-on: ubuntu-latest
services:
# Creates a postgres docker where migrations will run.
db:
image: postgres:12.3-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: github_actions_testing
ports:
- 5432:5432
# needed because the postgres container does not provide a healthcheck
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- uses: actions/checkout@v2

# Install Python. This matches the Python version in Dockerfile.
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8

- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
dalmia marked this conversation as resolved.
Show resolved Hide resolved

- name: Run Test Cases
env:
DB_HOST: 127.0.0.1
DB_NAME: github_actions_testing
DB_USER: postgres
DB_PASSWORD: postgres
SECRET_KEY: wpurj&oym6m@kcp(m&z(q-g0bo-r*+!f_&j(94di8j&_j4m%2s # random secret key
# command to run tests and generate coverage metrics
run: coverage run manage.py test

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
# The codecov token to upload report. Can be retrieved from your Codecov dashboard.
# This should be set from `GitHub Repo > Settings > Environment Variables`.
rathorevaibhav marked this conversation as resolved.
Show resolved Hide resolved
token: ${{ secrets.CODECOV_TOKEN }}
rathorevaibhav marked this conversation as resolved.
Show resolved Hide resolved
fail_ci_if_error: true
14 changes: 0 additions & 14 deletions .github/workflows/pre-commit.yml

This file was deleted.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?&style=flat-square)](https://github.com/psf/black)
![GitHub issues](https://img.shields.io/github/issues-raw/avantifellows/plio-backend?style=flat-square)
![Pre-commit checks](https://img.shields.io/github/workflow/status/avantifellows/plio-backend/pre-commit/master?label=Pre-commit%20checks&style=flat-square)
[![codecov](https://codecov.io/gh/avantifellows/plio-backend/branch/master/graph/badge.svg?token=5CIBHZ6FRB)](https://codecov.io/gh/avantifellows/plio-backend)
![Staging Build](https://img.shields.io/github/workflow/status/avantifellows/plio-backend/Deploy%20to%20ECS%20-%20staging?label=Staging%20Build&style=flat-square)
![Production Build](https://img.shields.io/github/workflow/status/avantifellows/plio-backend/Deploy%20to%20ECS%20-%20production?label=Production%20Build&style=flat-square)
[![Discord](https://img.shields.io/discord/717975833226248303.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2&style=flat-square)](https://discord.gg/29qYD7fZtZ)
Expand Down
3 changes: 3 additions & 0 deletions docs/ENV.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ AWS secret access key.
#### `AWS_REGION`
Region of the AWS IAM user.

#### `SMS_DRIVER`
The driver to send sms. The only supported value is `sns` right now for AWS SNS. When in development mode, use an empty string to avoid SMS triggers while debugging/testing.

### Redis
#### `REDIS_HOSTNAME`
Hostname of your Redis instance
Expand Down
9 changes: 5 additions & 4 deletions plio/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
DEBUG = os.environ.get("DEBUG", False)

# allowed hosts that can access the Django app
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS").split(" ")
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(" ")

# Application definition

Expand Down Expand Up @@ -112,7 +112,7 @@
]

MIDDLEWARE = [
"django_tenants.middleware.main.TenantMainMiddleware",
"organizations.middleware.OrganizationTenantMiddleware",
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.security.SecurityMiddleware",
Expand All @@ -123,7 +123,6 @@
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"request_logging.middleware.LoggingMiddleware",
"organizations.middleware.OrganizationTenantMiddleware",
]

ROOT_URLCONF = "plio.urls"
Expand Down Expand Up @@ -231,7 +230,7 @@
]

CMS_URL = "https://cms.peerlearning.com/api"
CMS_TOKEN = os.environ["CMS_TOKEN"]
CMS_TOKEN = os.environ.get("CMS_TOKEN")
GET_CMS_PROBLEM_URL = "/problems"

DATABASES = {
Expand Down Expand Up @@ -308,3 +307,5 @@
DEFAULT_FROM_EMAIL = os.environ.get("DEFAULT_FROM_EMAIL")
EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD")

SMS_DRIVER = os.environ.get("SMS_DRIVER")
4 changes: 2 additions & 2 deletions plio/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@
urlpatterns = [
path("admin/", admin.site.urls),
# API routes
path("api/v1/otp/request/", request_otp),
path("api/v1/otp/verify/", verify_otp),
path("api/v1/otp/request/", request_otp, name="request_otp"),
path("api/v1/otp/verify/", verify_otp, name="verify_otp"),
path("api/v1/users/token/", get_by_access_token),
path("api/v1/", include(api_router.urls)),
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
Expand Down
1 change: 1 addition & 0 deletions plio/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import shutil

from rest_framework import viewsets, status, filters
from rest_framework.response import Response
from rest_framework.decorators import action
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ drf-yasg==1.20.0
django-safedelete==1.0.0
django-oauth2-provider==0.2.6.1
django-rest-framework-social-oauth2==1.1.0
coverage==5.5
63 changes: 62 additions & 1 deletion users/tests.py
Original file line number Diff line number Diff line change
@@ -1 +1,62 @@
# Create your tests here.
from rest_framework.test import APIClient
from rest_framework.test import APITestCase
from rest_framework import status
from oauth2_provider.models import Application
from django.urls import reverse
from users.models import OneTimePassword
from plio.settings import API_APPLICATION_NAME


class BaseTestCase(APITestCase):
rathorevaibhav marked this conversation as resolved.
Show resolved Hide resolved
"""Base class that set up generic pre-requisites for all further test classes"""
rathorevaibhav marked this conversation as resolved.
Show resolved Hide resolved

def setUp(self):
self.client = APIClient()

# User access and refresh tokens require an OAuth Provider application to be set up and use it as a foreign key.
# As the test database is empty, we create an application instance before running the test cases.
Application.objects.create(
name=API_APPLICATION_NAME,
redirect_uris="",
client_type=Application.CLIENT_CONFIDENTIAL,
authorization_grant_type=Application.GRANT_AUTHORIZATION_CODE,
)


class OtpAuthTestCase(BaseTestCase):
"""Tests the OTP functionality."""

def setUp(self):
super().setUp()
self.user_mobile = "+919876543210"
rathorevaibhav marked this conversation as resolved.
Show resolved Hide resolved

def test_guest_can_request_for_otp(self):
response = self.client.post(
reverse("request_otp"), {"mobile": self.user_mobile}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)

otp_exists = OneTimePassword.objects.filter(mobile=self.user_mobile).exists()
self.assertTrue(otp_exists)
rathorevaibhav marked this conversation as resolved.
Show resolved Hide resolved

def test_invalid_otp_should_fail(self):
# request otp
self.client.post(reverse("request_otp"), {"mobile": self.user_mobile})

# invalid otp
otp = "000000"
response = self.client.post(
reverse("verify_otp"), {"mobile": self.user_mobile, "otp": otp}
)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

def test_valid_otp_should_pass(self):
# request otp
self.client.post(reverse("request_otp"), {"mobile": self.user_mobile})

# verify valid otp
otp = OneTimePassword.objects.filter(mobile=self.user_mobile).first()
response = self.client.post(
reverse("verify_otp"), {"mobile": self.user_mobile, "otp": otp.otp}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
rathorevaibhav marked this conversation as resolved.
Show resolved Hide resolved
12 changes: 7 additions & 5 deletions users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
OTP_EXPIRE_SECONDS,
DEFAULT_FROM_EMAIL,
ANALYTICS_IDP,
SMS_DRIVER,
)

from users.models import User, OneTimePassword, OrganizationUser
Expand Down Expand Up @@ -127,11 +128,12 @@ def request_otp(request):
)
otp.save()

sms = SnsService()
sms.publish(
otp.mobile,
f"Hello! Your OTP for Plio login is {otp.otp}. It is valid for the next 5 minutes. Please do not share it with anyone.",
)
if SMS_DRIVER == "sns":
sms = SnsService()
sms.publish(
otp.mobile,
f"Hello! Your OTP for Plio login is {otp.otp}. It is valid for the next 5 minutes. Please do not share it with anyone.",
)

return Response(OtpSerializer(otp).data)

Expand Down