Skip to content
This repository was archived by the owner on Sep 21, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
*
!accounts
!assets
!dancingtogether
!frontend
!main
!radio
!static
!templates
!manage.py
!Pipfile
!Pipfile.lock
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Python
*pyc
.mypy_cache
.python-version

## Django
staticfiles
Expand Down
16 changes: 16 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
exclude: ^(accounts/migrations/|radio/migrations)
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-toml
- id: check-yaml
- id: debug-statements
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: stable
hooks:
- id: black
language_version: python3.8
exclude: migrations
27 changes: 17 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
FROM python:3.7
FROM python:3.8.1
ENV PYTHONUNBUFFERED 1

RUN curl -sL https://deb.nodesource.com/setup_10.x | bash -
RUN apt-get install -y nodejs
RUN npm install -g webpack webpack-bundle-tracker typescript ts-loader
RUN ["/bin/bash", "-c", "set -o pipefail && curl -sL https://deb.nodesource.com/setup_10.x | bash -"]
# Update and install dependencies, and remove the package manager cache. Do
# this in a single step for better caching.
RUN apt-get --yes update && apt-get install --yes --no-install-recommends \
libpq-dev \
nodejs \
&& rm -rf /var/lib/apt/lists/*
RUN pip install pipenv
RUN npm install --global \
ts-loader \
typescript \
webpack \
webpack-bundle-tracker

RUN set -ex && mkdir /app
RUN mkdir /app
COPY Pipfile Pipfile.lock /app/
WORKDIR /app

ADD Pipfile /app
ADD Pipfile.lock /app
RUN set -ex && pip install pipenv --upgrade
RUN set -ex && pipenv install --dev --system --ignore-pipfile --deploy

ADD . /app
COPY . /app
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build:
.PHONY: check
check:
cd frontend && npx tslint --project .
pipenv run yapf --recursive . --exclude '*/migrations/*' --in-place --parallel
pipenv run black --check .
bash -c "pipenv run mypy --ignore-missing-imports \$$(find . -name \*.py ! -path '*/migrations/*')"
find . -name '*.py' ! -path '*/migrations/*' -print0 | xargs pipenv run pylint --load-plugins pylint_django

Expand Down Expand Up @@ -64,7 +64,7 @@ test-client-watch:

.PHONY: test-server
test-server:
DJANGO_SETTINGS_MODULE=dancingtogether.settings.test pipenv run python3 manage.py test
tools/scripts/test_backend

.PHONY: deploy
deploy:
Expand Down
10 changes: 7 additions & 3 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,21 @@ django-webpack-loader = "*"
djangorestframework = "*"
drf-nested-routers = "*"
raven = "*"
"psycopg2-binary" = "*"
psycopg2 = "*"

[requires]
python_version = "3.7"
python_version = "3.8"

[dev-packages]
pycodestyle = "*"
mypy = "*"
yapf = "*"
pylint = "*"
pytest-django = "*"
pytest-asyncio = "*"
python-dateutil = "*"
pylint-django = "*"
pre-commit = "*"
black = "*"

[pipenv]
allow_prereleases = true
861 changes: 526 additions & 335 deletions Pipfile.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ hit play. Each person listens to music using their own Spotify account and
Dancing Together keeps the jammin' in sync. Pause it, skip it, rewind it,
enjoy it!

It has a Python 3.8 Django backend with a JavaScript React frontend.

## Features

- Create your own radio station and invite friends via email
Expand Down
2 changes: 1 addition & 1 deletion accounts/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@


class AccountsConfig(AppConfig):
name = 'accounts'
name = "accounts"
2 changes: 1 addition & 1 deletion accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

class User(AbstractUser):
def __str__(self):
return f'user-{self.id}'
return f"user-{self.id}"
135 changes: 72 additions & 63 deletions accounts/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
from django.test import TestCase
import pytest

MOCK_USERNAME = 'MockUsername'
MOCK_USERNAME2 = 'MockUsername2'
MOCK_PASSWORD = 'MockPassword'
MOCK_PASSWORD2 = 'MockPassword2'
MOCK_USERNAME = "MockUsername"
MOCK_USERNAME2 = "MockUsername2"
MOCK_PASSWORD = "MockPassword"
MOCK_PASSWORD2 = "MockPassword2"


class AccountsViewsTests(TestCase):
Expand All @@ -18,12 +18,14 @@ def tearDown(self):

def test_user_sign_up(self):
response = self.client.post(
'/join/', {
'username': MOCK_USERNAME,
'password1': MOCK_PASSWORD,
'password2': MOCK_PASSWORD,
})
self.assertRedirects(response, '/stations/')
"/join/",
{
"username": MOCK_USERNAME,
"password1": MOCK_PASSWORD,
"password2": MOCK_PASSWORD,
},
)
self.assertRedirects(response, "/stations/")

user = auth.get_user(self.client)
assert user.is_authenticated
Expand All @@ -32,118 +34,125 @@ def test_user_sign_up_already_logged_in(self):
user = create_user()
self.client.force_login(user)

response = self.client.get('/join/')
self.assertRedirects(response, '/')
response = self.client.get("/join/")
self.assertRedirects(response, "/")

response = self.client.post(
'/join/', {
'username': MOCK_USERNAME,
'password1': MOCK_PASSWORD,
'password2': MOCK_PASSWORD
})
self.assertRedirects(response, '/')
"/join/",
{
"username": MOCK_USERNAME,
"password1": MOCK_PASSWORD,
"password2": MOCK_PASSWORD,
},
)
self.assertRedirects(response, "/")

def test_user_login(self):
user = create_user()
response = self.client.post('/login/', {
'username': MOCK_USERNAME,
'password': MOCK_PASSWORD,
})
self.assertRedirects(response, '/stations/')
response = self.client.post(
"/login/", {"username": MOCK_USERNAME, "password": MOCK_PASSWORD,}
)
self.assertRedirects(response, "/stations/")

assert user.is_authenticated

def test_user_login_already_logged_in(self):
user = create_user()
self.client.force_login(user)

response = self.client.get('/login/')
self.assertRedirects(response, '/')
response = self.client.get("/login/")
self.assertRedirects(response, "/")

response = self.client.post('/login/', {
'username': MOCK_USERNAME,
'password': MOCK_PASSWORD,
})
self.assertRedirects(response, '/')
response = self.client.post(
"/login/", {"username": MOCK_USERNAME, "password": MOCK_PASSWORD,}
)
self.assertRedirects(response, "/")

def test_user_can_see_profile(self):
user = create_user()
self.client.force_login(user)

response = self.client.get(f'/accounts/{user.id}/')
response = self.client.get(f"/accounts/{user.id}/")
assert response.status_code == HTTPStatus.OK.value

def test_user_can_only_see_their_profile(self):
user = create_user()
self.client.force_login(user)

response = self.client.get('/accounts/2/')
self.assertRedirects(response, '/')
response = self.client.get("/accounts/2/")
self.assertRedirects(response, "/")

def test_user_needs_to_be_logged_in_to_see_profile(self):
response = self.client.get('/accounts/1/')
self.assertRedirects(response, '/accounts/login/?next=/accounts/1/')
response = self.client.get("/accounts/1/")
self.assertRedirects(response, "/accounts/login/?next=/accounts/1/")

def test_user_can_change_password(self):
user = create_user()
self.client.force_login(user)

response = self.client.post(
f'/accounts/{user.id}/', {
'old_password': MOCK_PASSWORD,
'new_password1': MOCK_PASSWORD2,
'new_password2': MOCK_PASSWORD2,
})
self.assertRedirects(response, f'/accounts/{user.id}/')
f"/accounts/{user.id}/",
{
"old_password": MOCK_PASSWORD,
"new_password1": MOCK_PASSWORD2,
"new_password2": MOCK_PASSWORD2,
},
)
self.assertRedirects(response, f"/accounts/{user.id}/")

self.client.logout()
assert self.client.login(username=MOCK_USERNAME,
password=MOCK_PASSWORD2)
assert self.client.login(username=MOCK_USERNAME, password=MOCK_PASSWORD2)

def test_user_can_only_change_their_password(self):
user = create_user()
self.client.force_login(user)

response = self.client.post(
f'/accounts/2/', {
'old_password': MOCK_PASSWORD,
'new_password1': MOCK_PASSWORD2,
'new_password2': MOCK_PASSWORD2,
})
self.assertRedirects(response, '/')
f"/accounts/2/",
{
"old_password": MOCK_PASSWORD,
"new_password1": MOCK_PASSWORD2,
"new_password2": MOCK_PASSWORD2,
},
)
self.assertRedirects(response, "/")

def test_user_needs_to_be_logged_in_to_change_password(self):
response = self.client.post(
f'/accounts/1/', {
'old_password': MOCK_PASSWORD,
'new_password1': MOCK_PASSWORD2,
'new_password2': MOCK_PASSWORD2,
})
self.assertRedirects(response, '/accounts/login/?next=/accounts/1/')
f"/accounts/1/",
{
"old_password": MOCK_PASSWORD,
"new_password1": MOCK_PASSWORD2,
"new_password2": MOCK_PASSWORD2,
},
)
self.assertRedirects(response, "/accounts/login/?next=/accounts/1/")

def test_user_can_delete_account(self):
user = create_user()
self.client.force_login(user)

response = self.client.post(f'/accounts/{user.id}/delete/')
self.assertRedirects(response, '/')
response = self.client.post(f"/accounts/{user.id}/delete/")
self.assertRedirects(response, "/")
with pytest.raises(auth.get_user_model().DoesNotExist):
auth.get_user_model().objects.get(id=user.id)

def test_user_can_delete_only_their_account(self):
user = create_user()
self.client.force_login(user)

response = self.client.post(f'/accounts/2/delete/')
self.assertRedirects(response, '/')
response = self.client.post(f"/accounts/2/delete/")
self.assertRedirects(response, "/")

def test_user_needs_to_be_logged_in_to_delete_account(self):
user = create_user()
response = self.client.post(f'/accounts/{user.id}/delete/')
response = self.client.post(f"/accounts/{user.id}/delete/")
self.assertRedirects(
response, f'/accounts/login/?next=/accounts/{user.id}/delete/')
response, f"/accounts/login/?next=/accounts/{user.id}/delete/"
)


def create_user(username=MOCK_USERNAME) -> User:
return auth.get_user_model().objects.create_user(username=MOCK_USERNAME,
password=MOCK_PASSWORD)
return auth.get_user_model().objects.create_user(
username=MOCK_USERNAME, password=MOCK_PASSWORD
)
10 changes: 4 additions & 6 deletions accounts/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
from . import views

urlpatterns = [
path('join/', views.JoinView.as_view(), name='join'),
path('<int:pk>/', views.UserDetailView.as_view(), name='account-detail'),
path('<int:pk>/delete/',
views.UserDeleteView.as_view(),
name='account-delete'),
path('', include('django.contrib.auth.urls')),
path("join/", views.JoinView.as_view(), name="join"),
path("<int:pk>/", views.UserDetailView.as_view(), name="account-detail"),
path("<int:pk>/delete/", views.UserDeleteView.as_view(), name="account-delete"),
path("", include("django.contrib.auth.urls")),
]
Loading