From d5a0958fa3b17f6449e8ccff38a1fb847bd8e6b0 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 15 Mar 2021 15:40:30 +0700 Subject: [PATCH 01/29] [REFACTOR] fix incompatible code for django 2.2 and python 3 removed .method() method call because now urlsafe_base64_encode returns string not bytes files: - new_rest_api/tests.py - new_rest_api/views.py --- new_rest_api/tests.py | 2 +- new_rest_api/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/new_rest_api/tests.py b/new_rest_api/tests.py index abba0d7..4f566fb 100644 --- a/new_rest_api/tests.py +++ b/new_rest_api/tests.py @@ -122,7 +122,7 @@ class UserTests(APITestCase): def test_activation_function(self): user = BisaGoUser.objects.get(phone_number='089892218567').user - uid = urlsafe_base64_encode(force_bytes(user.pk)).decode() + uid = urlsafe_base64_encode(force_bytes(user.pk)) token = account_activation_token.make_token(user) url = reverse('activate', kwargs={'uidb64' : uid, 'token' : token}) response = self.client.get(url) diff --git a/new_rest_api/views.py b/new_rest_api/views.py index d6f5687..3a19e78 100644 --- a/new_rest_api/views.py +++ b/new_rest_api/views.py @@ -84,7 +84,7 @@ def register_user(request): message = render_to_string('acc_active_email.html', { 'user' : user, 'domain' : request.get_host, - 'uid' : urlsafe_base64_encode(force_bytes(user.pk)).decode(), + 'uid' : urlsafe_base64_encode(force_bytes(user.pk)), 'token' : account_activation_token.make_token(user), }) mail = EmailMessage(mail_subject, message, to=[email]) -- GitLab From 7fbe7018f7a4ace4db0ae13317c7bdfa237cd8ab Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Tue, 16 Mar 2021 14:13:02 +0700 Subject: [PATCH 02/29] change header from "multipart/form-data" to "application/x-www-form-urlencoded" at tests sometimes django cant receive data using request.data with multipart/form-data as the content-type header --- informasi_fasilitas/test_views_fasilitas.py | 2 +- informasi_fasilitas/test_views_lokasi.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/informasi_fasilitas/test_views_fasilitas.py b/informasi_fasilitas/test_views_fasilitas.py index 6089b32..194192e 100644 --- a/informasi_fasilitas/test_views_fasilitas.py +++ b/informasi_fasilitas/test_views_fasilitas.py @@ -133,5 +133,5 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): 'rating': 3, 'tag': 'KR'} response = client.put(urls_put, data=send_data, - content_type="multipart/form-data") + content_type="application/x-www-form-urlencoded") self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) diff --git a/informasi_fasilitas/test_views_lokasi.py b/informasi_fasilitas/test_views_lokasi.py index d9ffa05..d719a93 100644 --- a/informasi_fasilitas/test_views_lokasi.py +++ b/informasi_fasilitas/test_views_lokasi.py @@ -112,5 +112,5 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): urls = reverse('update-lokasi', kwargs={'nama_lokasi': self.lokasi_test_1["name"]}) response = client.put(urls, data={"latitude": 100}, - content_type="multipart/form-data") + content_type="application/x-www-form-urlencoded") self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) -- GitLab From b072b73e379ac9d1e1a0c3a3811ba0dfc6a5b0bd Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Wed, 17 Mar 2021 00:14:53 +0700 Subject: [PATCH 03/29] [REFACTOR] removed hardcoded variables, added python-dotenv - modified hardcoded variables DEBUG and ALLOWED_HOSTS, now using env vars - using python-dotenv for managing .env variables - change some variables from using os.environ.get to os.getenv (as the wrapper) --- pplbackend/settings.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/pplbackend/settings.py b/pplbackend/settings.py index 5dbd07e..f0d2d90 100644 --- a/pplbackend/settings.py +++ b/pplbackend/settings.py @@ -11,12 +11,13 @@ https://docs.djangoproject.com/en/3.0/ref/settings/ """ import os + from datetime import timedelta -import dj_database_url +from dotenv import load_dotenv # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - +load_dotenv(os.path.join(BASE_DIR, '.env')) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ @@ -26,11 +27,10 @@ SECRET_KEY = os.getenv("SECRET_KEY", 'akua') # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = FALSE +DEBUG = os.getenv('DEBUG') == 'True' -ALLOWED_HOSTS = ['localhost', '127.0.0.1', '10.119.105.26', 'bisago.cs.ui.ac.id'] +ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', 'localhost').strip(';').split(';') -#ALLOWED_HOSTS = ['*'] # Application definition INSTALLED_APPS = [ @@ -96,19 +96,21 @@ WSGI_APPLICATION = 'pplbackend.wsgi.application' DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': os.environ.get('DB_NAME', ''), - 'USER': os.environ.get('DB_USER', ''), - 'PASSWORD': os.environ.get('DB_PASSWORD', ''), - 'HOST': os.environ.get('DB_HOST', 'localhost'), - 'PORT': os.environ.get('DB_PORT', '5432'), + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } -#if os.environ.get("DATABASE_URL"): -# DATABASES['default'] = dj_database_url.config() - - +# custom database host, overriding the default +if os.getenv('DB_HOST') is not None: + DATABASES['default'] = { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': os.getenv('DB_NAME'), + 'USER': os.getenv('DB_USER'), + 'PASSWORD': os.getenv('DB_PASSWORD'), + 'HOST': os.getenv('DB_HOST'), + 'PORT': os.getenv('DB_PORT'), + } # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators -- GitLab From 6e6c5ec61ee8b765c6e54eede3a583ca21ae2f0c Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Thu, 18 Mar 2021 22:34:11 +0700 Subject: [PATCH 04/29] [REFACTOR] change default databases to sqlite3 when running test using sys.argv to check if doing test or not and change databases to default --- pplbackend/settings.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pplbackend/settings.py b/pplbackend/settings.py index f0d2d90..dc61e98 100644 --- a/pplbackend/settings.py +++ b/pplbackend/settings.py @@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/3.0/ref/settings/ """ import os +import sys from datetime import timedelta from dotenv import load_dotenv @@ -94,11 +95,13 @@ WSGI_APPLICATION = 'pplbackend.wsgi.application' # Database # https://docs.djangoproject.com/en/3.0/ref/settings/#databases +DEFAULT_DATABASE = { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), +} + DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } + 'default': DEFAULT_DATABASE } # custom database host, overriding the default @@ -112,6 +115,10 @@ if os.getenv('DB_HOST') is not None: 'PORT': os.getenv('DB_PORT'), } +# use default database when running test +if 'test' in sys.argv: + DATABASES['default'] = DEFAULT_DATABASE + # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators -- GitLab From e659f0f0edb0b48b48e5bd19360fe7a6005195e3 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Thu, 18 Mar 2021 23:38:42 +0700 Subject: [PATCH 05/29] [REFACTOR] remove unnecessary test, modified wrong tests --- informasi_fasilitas/test_base.py | 4 ++-- informasi_fasilitas/test_models.py | 2 +- informasi_fasilitas/test_views_lokasi.py | 20 +++++++------------- layanan_khusus/tests.py | 7 +++---- registrasi/tests.py | 2 +- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index e85ce9f..0f7e055 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -6,7 +6,7 @@ from django.urls import reverse, path, include from .models import Lokasi, Fasilitas, Komentar, KURSI_RODA, Likes, Dislikes -NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'null' +NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' REGISTER = '/api/register/' TOKEN_AUTH = '/api-token-auth/' @@ -37,7 +37,7 @@ class InformasiFasilitasTest(TestCase): 'phone_number': 000000000, 'password': "hahagotim"} - lokasi_test_1 = {'id': '1', + lokasi_test_1 = {'id': 1, 'name': 'Ma Homie', 'latitude': 0.1, 'longitude': 0.1, diff --git a/informasi_fasilitas/test_models.py b/informasi_fasilitas/test_models.py index f5c6603..7445c93 100644 --- a/informasi_fasilitas/test_models.py +++ b/informasi_fasilitas/test_models.py @@ -3,7 +3,7 @@ from django.db.utils import IntegrityError from .test_base import InformasiFasilitasTest from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes -NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'null' +NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' class InformasiFasilitasModelTest(InformasiFasilitasTest): diff --git a/informasi_fasilitas/test_views_lokasi.py b/informasi_fasilitas/test_views_lokasi.py index d719a93..cf525dd 100644 --- a/informasi_fasilitas/test_views_lokasi.py +++ b/informasi_fasilitas/test_views_lokasi.py @@ -28,7 +28,6 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): response = Client().get(reverse('lokasi-list')) content = json.loads(response.content.decode('utf-8')) expected_first_entry = copy.deepcopy(self.lokasi_test_1) - expected_first_entry["id"] = 45 expected_first_entry["image"] = None expected_json = [expected_first_entry] self.assertEqual(content, expected_json) @@ -48,7 +47,7 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): content = json.loads(response.content.decode('utf-8')) expected_json = copy.deepcopy(self.lokasi_test_1) expected_json["image"] = None - expected_json["id"] = 35 + # expected_json["id"] = 35 self.assertEqual(content, expected_json) def test_cannot_post_lokasi_details(self): @@ -68,7 +67,13 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): def test_can_post_add_lokasi(self): client = self.client_user_token() + Lokasi.objects.all().delete() response = client.post(reverse('add-lokasi'), self.lokasi_test_1) + response_json = json.loads(response.content.decode("utf-8")) + expected_json = copy.deepcopy(self.lokasi_test_1) + expected_json['id'] = 2 + expected_json['image'] = None + self.assertEqual(response_json, expected_json) self.assertEqual(response.status_code, HTTPStatus.CREATED) def test_post_add_lokasi_missing_key(self): @@ -87,17 +92,6 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): expected_json = {"name": ["This field is required."]} self.assertEqual(response_json, expected_json) - def test_can_post_add_lokasi_json(self): - client = self.client_user_token() - response = client.post(reverse('add-lokasi'), - self.lokasi_test_1) - response_json = json.loads(response.content.decode("utf-8")) - - expected_json = copy.deepcopy(self.lokasi_test_1) - expected_json["id"] = 40 - expected_json["image"] = None - self.assertEqual(response_json, expected_json) - def test_put_update_detail_lokasi_success(self): client = self.client_user_token() urls = reverse('update-lokasi', diff --git a/layanan_khusus/tests.py b/layanan_khusus/tests.py index 8a6038c..54d527a 100644 --- a/layanan_khusus/tests.py +++ b/layanan_khusus/tests.py @@ -7,7 +7,7 @@ from django.urls import path, include, reverse from .models import Sekolah, Penyandang, Komunitas from .serializers import SekolahSerializer, KomunitasSerializer -NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'null' +NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' ID = 'id' NAME = 'name' @@ -247,7 +247,6 @@ class LayananKhususViewsTest(TestCase): MOCK_PENYANDANG) content = json.loads(response.content.decode("utf-8")) expected_json = MOCK_PENYANDANG - expected_json[ID] = 3 self.assertEqual(content, expected_json) def test_cannot_get_register_penyandang(self): @@ -285,12 +284,12 @@ class LayananKhususViewsTest(TestCase): content = json.loads(response.content.decode('utf-8')) expected_json = MOCK_KOMUNITAS self.assertEqual(content, expected_json) - + def test_cannot_post_detail_komunitas(self): response = Client().post( reverse('detail-komunitas', kwargs={'id_komunitas': 1})) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) - + def test_get_details_komuniras_not_found(self): response = Client().get( reverse('detail-komunitas', kwargs={'id_komunitas': 1})) diff --git a/registrasi/tests.py b/registrasi/tests.py index 472b6a7..0bef2cb 100644 --- a/registrasi/tests.py +++ b/registrasi/tests.py @@ -17,4 +17,4 @@ class RegistrationTest(TestCase): with self.assertRaises(IntegrityError) as error: obj = BisaGoUser(user=None) obj.save() - self.assertFalse(str(error.exception).startswith('NOT NULL constraint failed')) + self.assertTrue(str(error.exception).startswith('NOT NULL constraint failed')) -- GitLab From 038c9eeca73a4f9ff092f240c813fc9204e3e5fc Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 20 Mar 2021 18:04:52 +0700 Subject: [PATCH 06/29] [REFACTOR] custom deployment to private server - removed variables and unnecessary postgres service (relates to commit 6e6c5ec) --- .gitlab-ci.yml | 59 ++++++++++---------------------------------------- 1 file changed, 12 insertions(+), 47 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9968488..6ffa0f4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,21 +1,8 @@ -variables: - POSTGRES_DB: $DB_NAME - POSTGRES_USER: $DB_USER - POSTGRES_PASSWORD: $DB_PASSWORD - POSTGRES_HOST_AUTH_METHOD: $DB_HOST_AUTH_METHOD - AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY - AWS_STORAGE_BUCKET_NAME: $AWS_STORAGE_BUCKET_NAME - -services: - - postgres:12.2-alpine - stages: - test - linter - sonarqube - - staging - - production + - deployment UnitTest: image: python:3.7 @@ -43,10 +30,11 @@ Pylint: - pip install -r requirements.txt when: on_success script: - - pylint --load-plugins pylint_django --rcfile=./.pylintrc informasi_fasilitas layanan_khusus new_rest_api oauth registrasi + - pylint --load-plugins pylint_django --rcfile=./.pylintrc \ + informasi_fasilitas layanan_khusus new_rest_api oauth registrasi SonarScanner: - image: + image: name: sonarsource/sonar-scanner-cli:latest entrypoint: [""] stage: sonarqube @@ -57,30 +45,13 @@ SonarScanner: -Dsonar.branch.name=$CI_COMMIT_REF_NAME -Dsonar.projectKey=$SONARQUBE_PROJECT_KEY -Staging: - image: ruby:2.4 - stage: staging - only: - refs: - - staging - before_script: - - gem install dpl - - wget -qO- https://cli-assets.heroku.com/install-ubuntu.sh | sh - script: - - dpl --provider=heroku --app=$HEROKU_APPNAME_STAGING --api-key=$HEROKU_APIKEY - - export HEROKU_API_KEY=$HEROKU_APIKEY - - heroku run --app $HEROKU_APPNAME_STAGING migrate - environment: - name: staging - url: $HEROKU_APP_HOST_STAGING - - Deployment: image: ruby:2.4 - stage: production + stage: deployment only: refs: - - master + - staging + - development before_script: - apt-get update -qq - apt-get install -qq git @@ -88,16 +59,10 @@ Deployment: - eval $(ssh-agent -s) - ssh-add <(echo "$SSH_PRIVATE_KEY" | base64 -d) - mkdir -p ~/.ssh - - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' + - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config script: - - > - ssh $TARGET_USER_AT_HOST - "cd bisago-be && - git checkout master && - git pull origin master && - bash stop.sh && - bash start.sh && - exit" + - result=$(ssh $TARGET "bash deployment/reload.sh $CI_COMMIT_REF_NAME") + - echo "$result" + - [ '$(echo "$result" | tail -n 1)' != "success" ] && exit 1 environment: - name: production - url: $HEROKU_APP_HOST \ No newline at end of file + name: deployment -- GitLab From aac729dd21cdadfcceb6a37b3394cf99ad2254ae Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 20 Mar 2021 18:26:48 +0700 Subject: [PATCH 07/29] gitlab ci cant detect character '[' or '&' --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6ffa0f4..533cd0e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -63,6 +63,6 @@ Deployment: script: - result=$(ssh $TARGET "bash deployment/reload.sh $CI_COMMIT_REF_NAME") - echo "$result" - - [ '$(echo "$result" | tail -n 1)' != "success" ] && exit 1 + - if [ '$(echo "$result" | tail -n 1)' != "success" ]; then exit 1; fi environment: name: deployment -- GitLab From d2f371f689f2de5287f3efcd0371cfc17e847b3d Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 20 Mar 2021 20:41:42 +0700 Subject: [PATCH 08/29] added allow failure to sonarqube script temporarily --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 533cd0e..e4b2bfb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,6 +48,7 @@ SonarScanner: Deployment: image: ruby:2.4 stage: deployment + allow_failure: true only: refs: - staging -- GitLab From 8997a58791f780e45d8ec5906ffae7dcdb1710ae Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 20 Mar 2021 20:46:02 +0700 Subject: [PATCH 09/29] Revert "added allow failure to sonarqube script temporarily" This reverts commit d2f371f689f2de5287f3efcd0371cfc17e847b3d. --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e4b2bfb..533cd0e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,7 +48,6 @@ SonarScanner: Deployment: image: ruby:2.4 stage: deployment - allow_failure: true only: refs: - staging -- GitLab From 2298e07cfa337f0c1e28d67a0b92e85695ef8cee Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 20 Mar 2021 20:46:51 +0700 Subject: [PATCH 10/29] added allow failure to sonarqube script temporarily --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 533cd0e..27273ac 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,6 +38,7 @@ SonarScanner: name: sonarsource/sonar-scanner-cli:latest entrypoint: [""] stage: sonarqube + allow_failure: true script: - sonar-scanner -Dsonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube-advprog -- GitLab From 4b5272865adaa14436116567dccf3fbffca9a8c9 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 20 Mar 2021 20:59:20 +0700 Subject: [PATCH 11/29] remove repeated code at base test, make more general method --- informasi_fasilitas/test_base.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index 0f7e055..cff9b58 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -141,29 +141,26 @@ class InformasiFasilitasTest(TestCase): dislike_test.save() return dislike_test - def client_user_token(self): - Client().post(REGISTER, self.user_post) - test_user = User.objects.get(username=self.user_post["email"]) + def get_client_from_user(self, user_dict): + Client().post(REGISTER, user_dict) + test_user = User.objects.get(username=user_dict['email']) test_user.is_active = True test_user.save() - token_response = Client().post(TOKEN_AUTH, {'username': self.user_post["email"], - 'password': self.user_post["password"]}) + token_response = Client().post(TOKEN_AUTH, {'username': user_dict["email"], + 'password': user_dict["password"]}) content = json.loads(token_response.content.decode('utf-8')) token = content['token'] client = Client(HTTP_AUTHORIZATION=TOKEN_SUFFIX+token) return client + def client_user_token(self): + return self.get_client_from_user(self.user_post) + def client_user_token2(self): - Client().post(REGISTER, self.user_post) - test_user = User.objects.get(username=self.default_username_email) - test_user.is_active = True - test_user.save() - token_response = Client().post(TOKEN_AUTH, {'username': self.default_username_email, - 'password': self.default_password}) - content = json.loads(token_response.content.decode('utf-8')) - token = content['token'] - client = Client(HTTP_AUTHORIZATION=TOKEN_SUFFIX+token) - return client + return self.get_client_from_user({'name': self.default_username_email, + 'email': self.default_username_email, + 'phone_number': self.default_no_telp, + 'password': self.default_password}) class InformasiFasilitasViewTest(InformasiFasilitasTest): -- GitLab From 8b913cf41b245f70d3b202f00e35669852792281 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 21 Mar 2021 17:21:13 +0700 Subject: [PATCH 12/29] fixing bug gitlab-ci script - remove single quote from if condition --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 27273ac..de9bf4b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -64,6 +64,6 @@ Deployment: script: - result=$(ssh $TARGET "bash deployment/reload.sh $CI_COMMIT_REF_NAME") - echo "$result" - - if [ '$(echo "$result" | tail -n 1)' != "success" ]; then exit 1; fi + - if [ $(echo "$result" | tail -n 1) != "success" ]; then exit 1; fi environment: name: deployment -- GitLab From ac04ad26a229350e0202fe39c2019f6ae9a0c319 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 21 Mar 2021 17:36:22 +0700 Subject: [PATCH 13/29] now can safely remove allow_failure option from gitlab ci yml file --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index de9bf4b..723c6ad 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,7 +38,6 @@ SonarScanner: name: sonarsource/sonar-scanner-cli:latest entrypoint: [""] stage: sonarqube - allow_failure: true script: - sonar-scanner -Dsonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube-advprog -- GitLab From 0f2e5ea3e11df1754c036275090f34a29463eeda Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 21 Mar 2021 23:22:07 +0700 Subject: [PATCH 14/29] remove unused if condition on views now using @api_view decorator, no need to check the request method --- informasi_fasilitas/views.py | 353 +++++++++++++++++------------------ 1 file changed, 171 insertions(+), 182 deletions(-) diff --git a/informasi_fasilitas/views.py b/informasi_fasilitas/views.py index 30d7ad8..d799fce 100644 --- a/informasi_fasilitas/views.py +++ b/informasi_fasilitas/views.py @@ -29,10 +29,9 @@ def missing_key_message(key): @authentication_classes([]) @permission_classes([]) def lokasi_list(request): - if request.method == 'GET': - lokasi_list = Lokasi.objects.all() - serializer = LokasiSerializer(lokasi_list, many=True) - return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) + lokasi_list = Lokasi.objects.all() + serializer = LokasiSerializer(lokasi_list, many=True) + return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) @api_view(['GET']) @@ -40,10 +39,9 @@ def lokasi_list(request): @permission_classes([]) def lokasi_details(request, nama_lokasi): try: - if request.method == 'GET': - lokasi = Lokasi.objects.get(name=nama_lokasi) - serializer = LokasiSerializer(lokasi) - return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) + lokasi = Lokasi.objects.get(name=nama_lokasi) + serializer = LokasiSerializer(lokasi) + return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) except ObjectDoesNotExist as err: return JsonResponse({'response': str(err)}, status=HTTPStatus.NOT_FOUND) @@ -53,14 +51,13 @@ def lokasi_details(request, nama_lokasi): @permission_classes([IsAuthenticated]) def add_lokasi(request): try: - if request.method == 'POST': - data = request.data - serializer = LokasiSerializer(data=data) - if serializer.is_valid(): - serializer.save() - return JsonResponse(serializer.data, status=HTTPStatus.CREATED) - else: - return JsonResponse(serializer.errors, status=HTTPStatus.BAD_REQUEST) + data = request.data + serializer = LokasiSerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, status=HTTPStatus.CREATED) + else: + return JsonResponse(serializer.errors, status=HTTPStatus.BAD_REQUEST) except Exception as err: return JsonResponse({'response': str(err)}, status=HTTPStatus.NOT_FOUND) @@ -69,13 +66,12 @@ def add_lokasi(request): @permission_classes([IsAuthenticated]) def update_lokasi_details(request, nama_lokasi): try: - if request.method == 'PUT': - lokasi = Lokasi.objects.get(name=nama_lokasi) - lokasi.no_telp = request.data['no_telp'] - lokasi.counter = F('counter') + 1 - lokasi.save() - return JsonResponse({'response': "phone changed to {}".format(lokasi.no_telp)}, - status=HTTPStatus.ACCEPTED) + lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi.no_telp = request.data['no_telp'] + lokasi.counter = F('counter') + 1 + lokasi.save() + return JsonResponse({'response': "phone changed to {}".format(lokasi.no_telp)}, + status=HTTPStatus.ACCEPTED) except KeyError as missing_key: return JsonResponse({'response': missing_key_message(str(missing_key))}, status=HTTPStatus.BAD_REQUEST) @@ -86,32 +82,31 @@ def update_lokasi_details(request, nama_lokasi): @permission_classes([]) def list_fasilitas(request, nama_lokasi): try: - if request.method == 'GET': - lokasi = Lokasi.objects.get(name=nama_lokasi) - list_fasilitas = Fasilitas.objects.filter(lokasi=lokasi) - return_json = {} - for fasilitas in list_fasilitas: - fasilitas.like = Likes.objects.filter( - fasilitas=fasilitas).count() - fasilitas.dislike = Dislikes.objects.filter( - fasilitas=fasilitas).count() - return_json[fasilitas.id] = {} - fasilitas_details = return_json[fasilitas.id] - fasilitas_details["id"] = fasilitas.id - fasilitas_details["nama_lokasi"] = fasilitas.lokasi.name - fasilitas_details["deskripsi"] = fasilitas.deskripsi - fasilitas_details["creator"] = fasilitas.user.last_name - fasilitas_details["date_time"] = fasilitas.date_time.strftime( - TIME_FORMAT) - fasilitas_details["like"] = fasilitas.like - fasilitas_details["dislike"] = fasilitas.dislike - fasilitas_details["rating"] = fasilitas.rating - fasilitas_details["tag"] = fasilitas.tag - fasilitas_details["disabilitas"] = fasilitas.disabilitas - fasilitas_details["jumlah"] = fasilitas.jumlah - fasilitas_details["image"] = '/media/'+str(fasilitas.image) - fasilitas_details["is_verified"] = fasilitas.is_verified - return JsonResponse(return_json, status=HTTPStatus.OK) + lokasi = Lokasi.objects.get(name=nama_lokasi) + list_fasilitas = Fasilitas.objects.filter(lokasi=lokasi) + return_json = {} + for fasilitas in list_fasilitas: + fasilitas.like = Likes.objects.filter( + fasilitas=fasilitas).count() + fasilitas.dislike = Dislikes.objects.filter( + fasilitas=fasilitas).count() + return_json[fasilitas.id] = {} + fasilitas_details = return_json[fasilitas.id] + fasilitas_details["id"] = fasilitas.id + fasilitas_details["nama_lokasi"] = fasilitas.lokasi.name + fasilitas_details["deskripsi"] = fasilitas.deskripsi + fasilitas_details["creator"] = fasilitas.user.last_name + fasilitas_details["date_time"] = fasilitas.date_time.strftime( + TIME_FORMAT) + fasilitas_details["like"] = fasilitas.like + fasilitas_details["dislike"] = fasilitas.dislike + fasilitas_details["rating"] = fasilitas.rating + fasilitas_details["tag"] = fasilitas.tag + fasilitas_details["disabilitas"] = fasilitas.disabilitas + fasilitas_details["jumlah"] = fasilitas.jumlah + fasilitas_details["image"] = '/media/'+str(fasilitas.image) + fasilitas_details["is_verified"] = fasilitas.is_verified + return JsonResponse(return_json, status=HTTPStatus.OK) except Exception as error: return JsonResponse({'response': str(error)}, status=HTTPStatus.NOT_FOUND) @@ -121,32 +116,31 @@ def list_fasilitas(request, nama_lokasi): @permission_classes([IsAuthenticated]) def add_fasilitas(request, nama_lokasi): try: - if request.method == 'POST': - lokasi = Lokasi.objects.get(name=nama_lokasi) - user = User.objects.get(email=str(request.user)) - deskripsi = request.POST['deskripsi'] - rating = request.POST['rating'] - tag = request.POST['tag'] - disabilitas = request.POST['disabilitas'] - jumlah = request.POST['jumlah'] - image = "" - if Fasilitas.objects.filter(lokasi=lokasi.pk, tag=tag).count() > 0: - return JsonResponse({'response': 'fasilitas already exist'}, - status=HTTPStatus.OK) - if tag == "": - return JsonResponse({'response': 'tag cannot be null'}, - status=HTTPStatus.BAD_REQUEST) - if 'image' in request.FILES.keys(): - image = request.FILES['image'] - fasilitas = Fasilitas.objects.create(lokasi=lokasi, - user=user, - deskripsi=deskripsi, - rating=rating, - tag=tag, - disabilitas=disabilitas, - jumlah=jumlah, - image=image) - return JsonResponse({'response': 'fasilitas added', 'id': fasilitas.id}, + lokasi = Lokasi.objects.get(name=nama_lokasi) + user = User.objects.get(email=str(request.user)) + deskripsi = request.POST['deskripsi'] + rating = request.POST['rating'] + tag = request.POST['tag'] + disabilitas = request.POST['disabilitas'] + jumlah = request.POST['jumlah'] + image = "" + if Fasilitas.objects.filter(lokasi=lokasi.pk, tag=tag).count() > 0: + return JsonResponse({'response': 'fasilitas already exist'}, + status=HTTPStatus.OK) + if tag == "": + return JsonResponse({'response': 'tag cannot be null'}, + status=HTTPStatus.BAD_REQUEST) + if 'image' in request.FILES.keys(): + image = request.FILES['image'] + fasilitas = Fasilitas.objects.create(lokasi=lokasi, + user=user, + deskripsi=deskripsi, + rating=rating, + tag=tag, + disabilitas=disabilitas, + jumlah=jumlah, + image=image) + return JsonResponse({'response': 'fasilitas added', 'id': fasilitas.id}, status=HTTPStatus.CREATED) except KeyError as missing_key: return JsonResponse({'response': missing_key_message(str(missing_key))}, @@ -160,28 +154,27 @@ def add_fasilitas(request, nama_lokasi): @permission_classes([]) def detail_fasilitas(request, nama_lokasi, id): try: - if request.method == 'GET': - lokasi = Lokasi.objects.get(name=nama_lokasi) - fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) - user = fasilitas.user - fasilitas.like = Likes.objects.filter(fasilitas=fasilitas).count() - fasilitas.dislike = Dislikes.objects.filter( - fasilitas=fasilitas).count() - return_json = {"id": fasilitas.id, - "nama_lokasi": lokasi.name, - "deskripsi": fasilitas.deskripsi, - "creator": user.last_name, - "creator_email": user.email, - "date_time": fasilitas.date_time, - "like": fasilitas.like, - "dislike": fasilitas.dislike, - "rating": fasilitas.rating, - "tag": fasilitas.tag, - "disabilitas": fasilitas.disabilitas, - "jumlah": fasilitas.jumlah, - "image": '/media/' + str(fasilitas.image), - "is_verified": fasilitas.is_verified} - return JsonResponse(return_json, status=HTTPStatus.OK) + lokasi = Lokasi.objects.get(name=nama_lokasi) + fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) + user = fasilitas.user + fasilitas.like = Likes.objects.filter(fasilitas=fasilitas).count() + fasilitas.dislike = Dislikes.objects.filter( + fasilitas=fasilitas).count() + return_json = {"id": fasilitas.id, + "nama_lokasi": lokasi.name, + "deskripsi": fasilitas.deskripsi, + "creator": user.last_name, + "creator_email": user.email, + "date_time": fasilitas.date_time, + "like": fasilitas.like, + "dislike": fasilitas.dislike, + "rating": fasilitas.rating, + "tag": fasilitas.tag, + "disabilitas": fasilitas.disabilitas, + "jumlah": fasilitas.jumlah, + "image": '/media/' + str(fasilitas.image), + "is_verified": fasilitas.is_verified} + return JsonResponse(return_json, status=HTTPStatus.OK) except KeyError as missing_key: return JsonResponse({'response': missing_key_message(str(missing_key))}, status=HTTPStatus.BAD_REQUEST) @@ -194,36 +187,35 @@ def detail_fasilitas(request, nama_lokasi, id): @permission_classes([IsAuthenticated]) def update_fasilitas(request, nama_lokasi, id): try: - if request.method == "PUT": - lokasi = Lokasi.objects.get(name=nama_lokasi) - fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) - user_creator = fasilitas.user - desc = fasilitas.deskripsi - tag = fasilitas.tag - jumlah = fasilitas.jumlah - image = fasilitas.image - disabilitas = fasilitas.disabilitas - if user_creator == request.user: - if 'deskripsi' in request.data.keys(): - desc = request.data['deskripsi'] - if 'tag' in request.data.keys(): - tag = request.data['tag'].split() - if 'jumlah' in request.data.keys(): - jumlah = request.data['jumlah'] - if 'image' in request.data.keys(): - image = request.data['image'] - if 'disabilitas' in request.data.keys(): - disabilitas = request.data['disabilitas'] - fasilitas.deskripsi = desc - fasilitas.tag = tag - fasilitas.jumlah = jumlah - fasilitas.image = image - fasilitas.disabilitas = disabilitas - fasilitas.save() - return JsonResponse({'response': '{} in fasilitas edited'.format(str(request.data.keys())), }, - status=HTTPStatus.ACCEPTED) - return JsonResponse({'response': 'Authentication failed'}, - status=HTTPStatus.UNAUTHORIZED) + lokasi = Lokasi.objects.get(name=nama_lokasi) + fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) + user_creator = fasilitas.user + desc = fasilitas.deskripsi + tag = fasilitas.tag + jumlah = fasilitas.jumlah + image = fasilitas.image + disabilitas = fasilitas.disabilitas + if user_creator == request.user: + if 'deskripsi' in request.data.keys(): + desc = request.data['deskripsi'] + if 'tag' in request.data.keys(): + tag = request.data['tag'].split() + if 'jumlah' in request.data.keys(): + jumlah = request.data['jumlah'] + if 'image' in request.data.keys(): + image = request.data['image'] + if 'disabilitas' in request.data.keys(): + disabilitas = request.data['disabilitas'] + fasilitas.deskripsi = desc + fasilitas.tag = tag + fasilitas.jumlah = jumlah + fasilitas.image = image + fasilitas.disabilitas = disabilitas + fasilitas.save() + return JsonResponse({'response': '{} in fasilitas edited'.format(str(request.data.keys())), }, + status=HTTPStatus.ACCEPTED) + return JsonResponse({'response': 'Authentication failed'}, + status=HTTPStatus.UNAUTHORIZED) except KeyError as missing_key: return JsonResponse({'response': missing_key_message(str(missing_key))}, status=HTTPStatus.BAD_REQUEST) @@ -236,37 +228,36 @@ def update_fasilitas(request, nama_lokasi, id): @permission_classes([IsAuthenticated]) def update_like_fasilitas(request, nama_lokasi, id, operation): try: - if request.method == "PUT": - lokasi = Lokasi.objects.get(name=nama_lokasi) - fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) - user = request.user + lokasi = Lokasi.objects.get(name=nama_lokasi) + fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) + user = request.user - try: - like = Likes.objects.get(fasilitas=fasilitas, user=user) - except Likes.DoesNotExist: - like = None - try: - dislike = Dislikes.objects.get(fasilitas=fasilitas, user=user) - except Dislikes.DoesNotExist: - dislike = None - if operation == "like": - if like != None: - return JsonResponse({'response': "You have already liked this facility"}, status=HTTPStatus.ACCEPTED) - else: - Likes.objects.create(fasilitas=fasilitas, user=user) - if dislike != None: - dislike.delete() - elif operation == "dislike": - if dislike != None: - return JsonResponse({'response': "You have already disliked this facility"}, status=HTTPStatus.ACCEPTED) - else: - Dislikes.objects.create(fasilitas=fasilitas, user=user) - if like != None: - like.delete() - fasilitas.like = Likes.objects.filter(fasilitas=fasilitas).count() - fasilitas.dislike = Dislikes.objects.filter( - fasilitas=fasilitas).count() - return JsonResponse({'response': "You have successfuly {}d this facility".format(operation)}, status=HTTPStatus.CREATED) + try: + like = Likes.objects.get(fasilitas=fasilitas, user=user) + except Likes.DoesNotExist: + like = None + try: + dislike = Dislikes.objects.get(fasilitas=fasilitas, user=user) + except Dislikes.DoesNotExist: + dislike = None + if operation == "like": + if like != None: + return JsonResponse({'response': "You have already liked this facility"}, status=HTTPStatus.ACCEPTED) + else: + Likes.objects.create(fasilitas=fasilitas, user=user) + if dislike != None: + dislike.delete() + elif operation == "dislike": + if dislike != None: + return JsonResponse({'response': "You have already disliked this facility"}, status=HTTPStatus.ACCEPTED) + else: + Dislikes.objects.create(fasilitas=fasilitas, user=user) + if like != None: + like.delete() + fasilitas.like = Likes.objects.filter(fasilitas=fasilitas).count() + fasilitas.dislike = Dislikes.objects.filter( + fasilitas=fasilitas).count() + return JsonResponse({'response': "You have successfuly {}d this facility".format(operation)}, status=HTTPStatus.CREATED) except KeyError as missing_key: return JsonResponse({'response': missing_key_message(str(missing_key))}, status=HTTPStatus.BAD_REQUEST) except Exception as error: @@ -278,17 +269,16 @@ def update_like_fasilitas(request, nama_lokasi, id, operation): @permission_classes([IsAuthenticated]) def add_komentar(request, nama_lokasi, id): try: - if request.method == 'POST': - lokasi = Lokasi.objects.get(name=nama_lokasi) - fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) - user = User.objects.get(email=str(request.user)) - deskripsi = request.POST['deskripsi'] - komentar = Komentar.objects.create(fasilitas=fasilitas, - user=user, - deskripsi=deskripsi) - return JsonResponse({'response': 'komentar added', - 'id': komentar.id, - "created_date": komentar.date_time.strftime(TIME_FORMAT)}, status=HTTPStatus.CREATED) + lokasi = Lokasi.objects.get(name=nama_lokasi) + fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) + user = User.objects.get(email=str(request.user)) + deskripsi = request.POST['deskripsi'] + komentar = Komentar.objects.create(fasilitas=fasilitas, + user=user, + deskripsi=deskripsi) + return JsonResponse({'response': 'komentar added', + 'id': komentar.id, + "created_date": komentar.date_time.strftime(TIME_FORMAT)}, status=HTTPStatus.CREATED) except KeyError as e: return JsonResponse({'response': missing_key_message(str(e))}, status=HTTPStatus.BAD_REQUEST) except Exception as e: @@ -300,20 +290,19 @@ def add_komentar(request, nama_lokasi, id): @permission_classes([]) def list_komentar(request, nama_lokasi, id): try: - if request.method == 'GET': - lokasi = Lokasi.objects.get(name=nama_lokasi) - fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) - list_komentar = Komentar.objects.filter(fasilitas=fasilitas) - return_json = {} - for komentar in list_komentar: - return_json[komentar.id] = {} - komentar_details = return_json[komentar.id] - komentar_details["id"] = komentar.id - komentar_details["deskripsi"] = komentar.deskripsi - komentar_details["creator"] = komentar.user.last_name - komentar_details["date_time"] = komentar.date_time.strftime( - TIME_FORMAT) - return JsonResponse(return_json, status=HTTPStatus.OK) + lokasi = Lokasi.objects.get(name=nama_lokasi) + fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) + list_komentar = Komentar.objects.filter(fasilitas=fasilitas) + return_json = {} + for komentar in list_komentar: + return_json[komentar.id] = {} + komentar_details = return_json[komentar.id] + komentar_details["id"] = komentar.id + komentar_details["deskripsi"] = komentar.deskripsi + komentar_details["creator"] = komentar.user.last_name + komentar_details["date_time"] = komentar.date_time.strftime( + TIME_FORMAT) + return JsonResponse(return_json, status=HTTPStatus.OK) except KeyError as key: return JsonResponse({'response': missing_key_message(str(key))}, status=HTTPStatus.BAD_REQUEST) -- GitLab From 4f910dee3001f3d2e99775c97541b1e54a730fd5 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 27 Mar 2021 11:00:14 +0700 Subject: [PATCH 15/29] Setup sonarqube with sonar-project.properties --- .gitlab-ci.yml | 8 +++----- sonar-project.properties | 17 ++++++++--------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 723c6ad..5bb47b6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,16 +34,14 @@ Pylint: informasi_fasilitas layanan_khusus new_rest_api oauth registrasi SonarScanner: + depenencies: + - UnitTest image: name: sonarsource/sonar-scanner-cli:latest entrypoint: [""] stage: sonarqube script: - - sonar-scanner - -Dsonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube-advprog - -Dsonar.login=$SONARQUBE_TOKEN - -Dsonar.branch.name=$CI_COMMIT_REF_NAME - -Dsonar.projectKey=$SONARQUBE_PROJECT_KEY + - sonar-scanner -Dsonar.sources=. Deployment: image: ruby:2.4 diff --git a/sonar-project.properties b/sonar-project.properties index 34071ac..a1910a8 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,27 +1,26 @@ # SonarScanner properties file ## Server -sonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube -sonar.login=$SONARQUBE_TOKEN +sonar.host.url=https://pmpl.cs.ui.ac.id/sonarqube-advprog +sonar.login=${env.SONARQUBE_PROJECT_TOKEN} ## Project configuration -sonar.projectKey=$SONARQUBE_PROJECT_KEY +sonar.projectKey=${env.SONARQUBE_PROJECT_KEY} ## Path to sources sonar.sources=. -sonar.exclusions=**/migrations/**, **/__init__.py, pplbackend/**, **/test.py, manage.py -#sonar.inclusions= +sonar.exclusions=**/migrations/**, **/__init__.py, pplbackend/**, manage.py, coverage.xml ## Path to tests sonar.tests=. -#sonar.test.exclusions= -sonar.test.inclusions=**/test.py +sonar.test.inclusions=**/test*.py ## Source encoding sonar.sourceEncoding=UTF-8 ## Branch analysis -sonar.branch.name=$CI_COMMIT_REF_NAME +sonar.branch.name=${env.CI_COMMIT_REF_NAME} ## Coverage Report -sonar.python.coverage.reportPaths=coverage.xml \ No newline at end of file +sonar.coverage.exclusions=**/apps.py +sonar.python.coverage.reportPaths=coverage.xml -- GitLab From 086389b2e5adcbecff94d35e936463bfdf46221d Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sat, 27 Mar 2021 11:23:44 +0700 Subject: [PATCH 16/29] change depenencies to dependencies at gitlab ci Setup sonarqube with sonar-project.properties --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5bb47b6..a5172b2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,7 @@ Pylint: informasi_fasilitas layanan_khusus new_rest_api oauth registrasi SonarScanner: - depenencies: + dependencies: - UnitTest image: name: sonarsource/sonar-scanner-cli:latest -- GitLab From 181d2c381f8349533ba0e39c3368b5ae96b12bb8 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 28 Mar 2021 18:33:56 +0700 Subject: [PATCH 17/29] [RED] added tests for new model and restructure current tests --- informasi_fasilitas/test_base.py | 411 ++++++++++-------- informasi_fasilitas/test_models.py | 32 +- informasi_fasilitas/test_views_fasilitas.py | 191 ++++---- informasi_fasilitas/test_views_komentar.py | 88 ++-- .../test_views_like_dislike.py | 186 +++----- informasi_fasilitas/test_views_lokasi.py | 104 +++-- 6 files changed, 502 insertions(+), 510 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index cff9b58..6ba0967 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -1,190 +1,251 @@ import json import tempfile + from django.test import TestCase, Client from django.contrib.auth.models import User from django.urls import reverse, path, include +from django.core.files.uploadedfile import SimpleUploadedFile +from django.core.files.base import ContentFile -from .models import Lokasi, Fasilitas, Komentar, KURSI_RODA, Likes, Dislikes - -NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' +from base64 import b64decode -REGISTER = '/api/register/' -TOKEN_AUTH = '/api-token-auth/' -TOKEN_SUFFIX = 'token ' +from .models import ( + Lokasi, + Fasilitas, + Komentar, + KURSI_RODA, + Likes, + Dislikes +) +from pplbackend.utils import get_client_login_with_user class InformasiFasilitasTest(TestCase): - default_username_email = "user_test@gmail.com" - default_last_name = "name" - default_password = "hahagotim" - default_lokasi_name = "Mall" - default_latitude = 0.0 - default_longitude = 0.0 - default_alamat = "Jl. Raya Bogor no.1 Jakarta" - default_no_telp = "08123456789" - default_image = tempfile.NamedTemporaryFile(suffix=".jpg").name - default_like = 0 - default_dislike = 0 - default_tag = KURSI_RODA - default_rating = 3 - default_deskripsi = "penjelasan" - default_disabilitas = "DF" - default_jumlah = 1 - default_counter = 0 - - user_post = {'name': 'name', - 'email': "self.user_post@gmail.com", - 'phone_number': 000000000, - 'password': "hahagotim"} - - lokasi_test_1 = {'id': 1, - 'name': 'Ma Homie', - 'latitude': 0.1, - 'longitude': 0.1, - 'alamat': 'Jl. Raya Bogor no.2, Jakarta', - 'no_telp': '081212123132', - 'counter': 0 - } - - def create_user_test(self, username=default_username_email, - last_name=default_last_name, - password=default_password): - user_test = User(username=username, - last_name=last_name, - email=username, - password=password) - user_test.save() - return user_test - - def create_lokasi_test(self, name=default_lokasi_name, - latitude=default_latitude, - longitude=default_longitude, - alamat=default_alamat, - no_telp=default_no_telp, - image=default_image, - counter=default_counter): - - lokasi_test = Lokasi(pk=1, - name=name, - latitude=latitude, - longitude=longitude, - alamat=alamat, - no_telp=no_telp, - image=image, - counter=counter) - lokasi_test.save() - return lokasi_test - - def create_fasilitas_test(self, deskripsi=default_deskripsi, - like=default_like, - dislike=default_dislike, - rating=default_rating, - tag=default_tag, - disabilitas=default_disabilitas, - jumlah=default_jumlah, - image=default_image, - lokasi=None, - user=None): - if (user == None): - user = self.create_user_test() - if (lokasi == None): - lokasi = self.create_lokasi_test() - fasilitas_test = Fasilitas(pk=1, - lokasi=lokasi, - user=user, - deskripsi=deskripsi, - like=like, - dislike=dislike, - rating=rating, - tag=tag, - disabilitas=disabilitas, - jumlah=jumlah, - image=image) - fasilitas_test.save() - return fasilitas_test - - def create_komentar_test(self, deskripsi=default_deskripsi, - user=None, - fasilitas=None): - if (user == None): - user = self.create_user_test() - if (fasilitas == None): - lokasi = self.create_lokasi_test() - fasilitas = self.create_fasilitas_test(user=user, lokasi=lokasi) - komentar_test = Komentar(deskripsi=deskripsi, - user=user, - fasilitas=fasilitas) - komentar_test.save() - return komentar_test - - def create_likes_test(self, user=None, - fasilitas=None): - if (user == None): - user = self.create_user_test() - if (fasilitas == None): - lokasi = self.create_lokasi_test(user.pk) - fasilitas = self.create_fasilitas_test(user=user, lokasi=lokasi) - like_test = Likes(user=user, - fasilitas=fasilitas) - like_test.save() - return like_test - - def create_dislikes_test(self, user=None, - fasilitas=None): - if (user == None): - user = self.create_user_test() - if (fasilitas == None): - lokasi = self.create_lokasi_test(user.pk) - fasilitas = self.create_fasilitas_test(user=user, lokasi=lokasi) - dislike_test = Dislikes(user=user, - fasilitas=fasilitas) - dislike_test.save() - return dislike_test - - def get_client_from_user(self, user_dict): - Client().post(REGISTER, user_dict) - test_user = User.objects.get(username=user_dict['email']) - test_user.is_active = True - test_user.save() - token_response = Client().post(TOKEN_AUTH, {'username': user_dict["email"], - 'password': user_dict["password"]}) - content = json.loads(token_response.content.decode('utf-8')) - token = content['token'] - client = Client(HTTP_AUTHORIZATION=TOKEN_SUFFIX+token) - return client - - def client_user_token(self): - return self.get_client_from_user(self.user_post) - - def client_user_token2(self): - return self.get_client_from_user({'name': self.default_username_email, - 'email': self.default_username_email, - 'phone_number': self.default_no_telp, - 'password': self.default_password}) + not_null_constraint_failed_message = 'NOT NULL constraint failed' + unique_constraint_failed_message = 'UNIQUE constraint failed' + put_content_type = "application/x-www-form-urlencoded" + + mock_user_test = { + 'username': 'mock username', + 'email': 'self.mock_user@test.com', + 'last_name': 'mock last_name', + } + + mock_lokasi_test = { + 'place_id': 'mock_place_id', + 'name': 'Ma Homie', + 'alamat': 'Jl. Raya Bogor no.2, Jakarta', + 'no_telp': '081212123132', + 'counter': 0, + } + + mock_fasilitas_test = { + 'deskripsi': 'kursi roda lipat', + 'rating': 3, + 'tag': 'KR', + 'disabilitas': 'DF', + 'jumlah': 1, + } + + mock_komentar_test = { + 'deskripsi': 'sangat membantu', + } + + def create_user_test(self, user_dict=mock_user_test): + return User.objects.create_user(**user_dict) + + def get_or_create_user_test(self, user=None, user_dict=mock_user_test): + if user is not None: + return user + try: + return User.objects.get(**user_dict) + except User.DoesNotExist as user_does_not_exist: + return self.create_user_test(user_dict=user_dict) + + def create_lokasi_test(self, lokasi_dict=mock_lokasi_test): + return Lokasi.objects.create(**lokasi_dict) + + def get_or_create_lokasi_test( + self, + lokasi=None, + lokasi_dict=mock_lokasi_test, + ): + if lokasi is not None: + return lokasi + try: + return Lokasi.objects.get(**lokasi_dict) + except Lokasi.DoesNotExist as _: + return self.create_lokasi_test(lokasi_dict=lokasi_dict) + + def create_fasilitas_test( + self, + lokasi=None, + lokasi_dict=mock_lokasi_test, + user=None, + user_dict=mock_user_test, + fasilitas_dict=mock_fasilitas_test + ): + user = self.get_or_create_user_test(user=user, user_dict=user_dict) + + lokasi = self.get_or_create_lokasi_test( + lokasi=lokasi, + lokasi_dict=lokasi_dict + ) + + return Fasilitas.objects.create( + **fasilitas_dict, + user=user, + lokasi=lokasi + ) + + def get_or_create_fasilitas_test( + self, + user=None, + user_dict=mock_user_test, + lokasi=None, + lokasi_dict=mock_lokasi_test, + fasilitas=None, + fasilitas_dict=mock_fasilitas_test, + ): + if fasilitas is not None: + return fasilitas + + user = self.get_or_create_user_test( + user=user, + user_dict=user_dict, + ) + + lokasi = self.get_or_create_lokasi_test( + lokasi=lokasi, + lokasi_dict=lokasi_dict, + ) + + try: + fasilitas = fasilitas if fasilitas is not None \ + else Fasilitas.objects.get( + **fasilitas_dict, + user=user, + lokasi=lokasi + ) + except Fasilitas.DoesNotExist as _: + return self.create_fasilitas_test( + fasilitas_dict=fasilitas_dict, + user=user, + lokasi=lokasi, + ) + + def create_komentar_test( + self, + user=None, + user_dict=mock_user_test, + lokasi=None, + lokasi_dict=mock_lokasi_test, + fasilitas=None, + fasilitas_dict=mock_fasilitas_test, + komentar_dict=mock_komentar_test, + ): + user = self.get_or_create_user_test( + user=user, + user_dict=user_dict, + ) + + lokasi = self.get_or_create_lokasi_test( + lokasi=lokasi, + lokasi_dict=lokasi_dict, + ) + + fasilitas = self.get_or_create_fasilitas_test( + user=user, + lokasi=lokasi, + fasilitas=fasilitas, + fasilitas_dict=fasilitas_dict, + ) + + return Komentar.objects.create( + **komentar_dict, + user=user, + fasilitas=fasilitas, + ) + + def create_likes_test( + self, + user=None, + user_dict=mock_user_test, + lokasi=None, + lokasi_dict=mock_lokasi_test, + fasilitas=None, + fasilitas_dict=mock_fasilitas_test, + ): + return self.create_likes_or_dislikes_test( + Likes, + user=user, + user_dict=user_dict, + lokasi=lokasi, + lokasi_dict=lokasi_dict, + fasilitas=fasilitas, + fasilitas_dict=fasilitas_dict, + ) + + def create_dislikes_test( + self, + user=None, + user_dict=mock_user_test, + lokasi=None, + lokasi_dict=mock_lokasi_test, + fasilitas=None, + fasilitas_dict=mock_fasilitas_test, + ): + return self.create_likes_or_dislikes_test( + Dislikes, + user=user, + user_dict=user_dict, + lokasi=lokasi, + lokasi_dict=lokasi_dict, + fasilitas=fasilitas, + fasilitas_dict=fasilitas_dict, + ) + + def create_likes_or_dislikes_test( + self, + like_or_dislike, + user=None, + user_dict=mock_user_test, + lokasi=None, + lokasi_dict=mock_lokasi_test, + fasilitas=None, + fasilitas_dict=mock_fasilitas_test, + ): + user = self.get_or_create_user_test( + user=user, + user_dict=user_dict, + ) + + lokasi = self.get_or_create_lokasi_test( + lokasi=lokasi, + lokasi_dict=lokasi_dict, + ) + + fasilitas = self.get_or_create_fasilitas_test( + user=user, + lokasi=lokasi, + fasilitas=fasilitas, + fasilitas_dict=fasilitas_dict, + ) + + return like_or_dislike.objects.create( + user=user, + fasilitas=fasilitas, + ) class InformasiFasilitasViewTest(InformasiFasilitasTest): - urlpatterns = [ - path('informasi-fasilitas/', include('informasi_fasilitas.urls')), - ] def setUp(self): - email = 'usersetup@gmail.com' - password = 'hahagotim' - Client().post(REGISTER, - {'name': 'name', - 'email': email, - 'phone_number': 1000000, - 'password': password}) - test_user = User.objects.get(username=email) - test_user.is_active = True - test_user.save() - token_response = Client().post(TOKEN_AUTH, - {'username': email, - 'password': password}) - content = json.loads(token_response.content.decode('utf-8')) - token = content['token'] - client = Client(HTTP_AUTHORIZATION=TOKEN_SUFFIX+token) - - client.post(reverse('add-lokasi'), - self.lokasi_test_1) + super().setUp() + self.user = self.create_user_test() + self.lokasi = self.create_lokasi_test() + self.fasilitas = self.create_fasilitas_test( + user=self.user, + lokasi=self.lokasi + ) + self.client = get_client_login_with_user(self.user) diff --git a/informasi_fasilitas/test_models.py b/informasi_fasilitas/test_models.py index 7445c93..c4798b3 100644 --- a/informasi_fasilitas/test_models.py +++ b/informasi_fasilitas/test_models.py @@ -3,17 +3,29 @@ from django.db.utils import IntegrityError from .test_base import InformasiFasilitasTest from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes -NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' - class InformasiFasilitasModelTest(InformasiFasilitasTest): - def test_models_lokasi_not_created(self): + def test_models_lokasi_not_created_attrs_should_not_null(self): with self.assertRaises(IntegrityError) as err_message: - obj = Lokasi(name=None) - obj.save() + Lokasi.objects.create( + name=None, + alamat=None, + no_telp=None, + ) + self.assertTrue(str(err_message.exception).startswith( + self.not_null_constraint_failed_message)) + + def test_models_lokasi_not_created_place_id_should_unique(self): + with self.assertRaises(IntegrityError) as err_message: + Lokasi.objects.create( + place_id=self.mock_lokasi_test['place_id'], + ) + Lokasi.objects.create( + place_id=self.mock_lokasi_test['place_id'], + ) self.assertTrue(str(err_message.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.unique_constraint_failed_message)) def test_models_create_new_lokasi(self): self.create_lokasi_test() @@ -29,7 +41,7 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): obj = Fasilitas(lokasi=None) obj.save() self.assertTrue(str(err_message.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.not_null_constraint_failed_message)) def test_models_create_new_fasilitas(self): self.create_fasilitas_test() @@ -41,7 +53,7 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): obj = Komentar(fasilitas=None) obj.save() self.assertTrue(str(err_message.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.not_null_constraint_failed_message)) def test_models_create_new_komentar(self): self.create_komentar_test() @@ -53,7 +65,7 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): obj = Dislikes(fasilitas=None) obj.save() self.assertTrue(str(err_message.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.not_null_constraint_failed_message)) def test_models_create_new_dislikes(self): self.create_dislikes_test() @@ -65,7 +77,7 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): obj = Likes(fasilitas=None) obj.save() self.assertTrue(str(err_message.exception).startswith( - NOT_NULL_CONSTRAINT_FAILED_MESSAGE)) + self.not_null_constraint_failed_message)) def test_models_create_new_likes(self): self.create_likes_test() diff --git a/informasi_fasilitas/test_views_fasilitas.py b/informasi_fasilitas/test_views_fasilitas.py index 194192e..7e9fc41 100644 --- a/informasi_fasilitas/test_views_fasilitas.py +++ b/informasi_fasilitas/test_views_fasilitas.py @@ -4,134 +4,149 @@ from django.test import Client from django.urls import reverse from .test_base import InformasiFasilitasViewTest - -PENJELASAN = 'penjelasan fasilitas' +from .models import Fasilitas, Lokasi +from pplbackend.utils import response_decode class FasilitasRelatedViewTest(InformasiFasilitasViewTest): + def setUp(self): + super().setUp() + + self.kwargs_place_id = {'place_id': self.lokasi.place_id} + self.get_list_fasilitas_url = \ + reverse('list-fasilitas', kwargs=self.kwargs_place_id) + self.get_detail_fasilitas_url = \ + reverse('detail-fasilitas', kwargs={ + 'place_id': self.lokasi.place_id, + 'id': self.fasilitas.id, + }) + self.add_fasilitas_url = \ + reverse('add-fasilitas', kwargs=self.kwargs_place_id) + + self.update_fasilitas_url = reverse('update-fasilitas', + kwargs={ + 'place_id': self.lokasi.place_id, + 'id': self.fasilitas.id, + } + ) + def test_can_get_list_fasilitas(self): - self.create_fasilitas_test() - response = Client().get( - reverse('list-fasilitas', kwargs={'nama_lokasi': self.default_lokasi_name})) + response = Client().get(self.get_list_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.OK) def test_can_get_list_fasilitas_json(self): - fasilitas = self.create_fasilitas_test() - response = Client().get( - reverse('list-fasilitas', kwargs={'nama_lokasi': self.default_lokasi_name})) + response = Client().get(self.get_list_fasilitas_url) content = json.loads(response.content.decode('utf-8')) - expected_json = {'1': {'id': 1, - 'nama_lokasi': self.default_lokasi_name, - 'deskripsi': self.default_deskripsi, - 'creator': self.default_last_name, - 'like': self.default_like, - 'dislike': self.default_dislike, - 'rating': self.default_rating, - 'tag': 'KR', - 'disabilitas': ['DF'], - 'jumlah': 1, - 'image': '/media/' + str(fasilitas.image), - 'is_verified': False} - } - - time = fasilitas.date_time.strftime("%d-%m-%Y %H:%M:%S") + expected_json = { + '1': { + 'id': self.fasilitas.id, + 'nama_lokasi': self.fasilitas.lokasi.name, + 'deskripsi': self.fasilitas.deskripsi, + 'creator': self.fasilitas.user.last_name, + 'like': self.fasilitas.like, + 'dislike': self.fasilitas.dislike, + 'rating': self.fasilitas.rating, + 'tag': 'KR', + 'disabilitas': ['DF'], + 'jumlah': 1, + 'image': '/media/' + str(self.fasilitas.image), + 'is_verified': False, + }, + } + + time = self.fasilitas.date_time.strftime("%d-%m-%Y %H:%M:%S") expected_json["1"]['date_time'] = time self.assertEqual(content, expected_json) def test_cannot_post_list_fasilitas(self): - response = Client().post( - reverse('list-fasilitas', kwargs={'nama_lokasi': self.lokasi_test_1["name"]})) + response = Client().post(self.get_list_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_get_list_fasilitas_not_found(self): - response = Client().get( - reverse('list-fasilitas', kwargs={'nama_lokasi': 'Mall'})) + Lokasi.objects.all().delete() + response = Client().get(self.get_list_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) def test_can_get_detail_fasilitas(self): - self.create_fasilitas_test() - response = Client().get(reverse('detail-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1})) + response = Client().get(self.get_detail_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.OK) def test_can_get_detail_fasilitas_json(self): - # TODO - self.create_fasilitas_test() - response = Client().get(reverse('detail-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1})) + response = Client().get(self.get_detail_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.OK) def test_cannot_post_detail_fasilitas(self): - fasilitas = self.create_fasilitas_test() - response = Client().post(reverse('detail-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': fasilitas.pk})) + response = Client().post(self.get_detail_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_get_detail_fasilitas_not_found(self): - response = Client().get(reverse('detail-fasilitas', - kwargs={'nama_lokasi': 'Mall', 'id': 0})) + Fasilitas.objects.all().delete() + response = Client().get(self.get_detail_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) def test_can_post_add_fasilitas(self): - client = self.client_user_token() - self.create_lokasi_test() - urls = reverse('add-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name}) - response = client.post(urls, {'deskripsi': PENJELASAN, - 'rating': 2, - 'tag': 'KR', - 'disabilitas': 'DM', - 'jumlah': 1, - 'image': 'test.jpg'}) + Fasilitas.objects.all().delete() + response = \ + self.client.post(self.add_fasilitas_url, self.mock_fasilitas_test) self.assertEqual(response.status_code, HTTPStatus.CREATED) + def test_add_fasilitas_error_tag_is_blank(self): + Fasilitas.objects.all().delete() + custom_mock_delete_tag = self.mock_fasilitas_test.copy() + custom_mock_delete_tag['tag'] = "" + + response = \ + self.client.post(self.add_fasilitas_url, custom_mock_delete_tag) + response_json = response_decode(response) + expected_json = {'response': 'tag cannot be null'} + + self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) + self.assertEqual(response_json, expected_json) + + def test_add_fasilitas_error_tag_is_null(self): + Fasilitas.objects.all().delete() + custom_mock_delete_tag = self.mock_fasilitas_test.copy() + del custom_mock_delete_tag['tag'] + + response = \ + self.client.post(self.add_fasilitas_url, custom_mock_delete_tag) + response_json = response_decode(response) + expected_json = {'response': "Bad Request. 'tag' key is needed"} + + self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) + self.assertEqual(response_json, expected_json) + + def test_post_fasilitas_already_exist(self): + response = \ + self.client.post(self.add_fasilitas_url, self.mock_fasilitas_test) + response_json = response_decode(response) + expected_json = {'response': 'fasilitas already exist'} + + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertEqual(response_json, expected_json) + def test_cannot_get_add_fasilitas(self): - client = self.client_user_token() - self.create_lokasi_test() - response = client.get( - reverse('add-fasilitas', kwargs={'nama_lokasi': self.default_lokasi_name})) + response = self.client.get(self.add_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) - def test_post_add_fasilitas_not_found(self): - client = self.client_user_token() - urls = reverse('add-fasilitas', kwargs={'nama_lokasi': 'Mall'}) - response = client.post(urls, {'deskripsi': PENJELASAN, - 'rating': 2}) + def test_post_add_fasilitas_but_lokasi_not_found(self): + Lokasi.objects.all().delete() + response = \ + self.client.post(self.add_fasilitas_url, self.mock_fasilitas_test) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) def test_post_add_fasilitas_cannot_authorized(self): - client = Client() - self.create_lokasi_test() - urls = reverse('add-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name}) - response = client.post(urls, {'deskripsi': PENJELASAN, - 'rating': 2, - 'tag': 'KR'}) + response = \ + Client().post(self.add_fasilitas_url, self.mock_fasilitas_test) self.assertEqual(response.status_code, HTTPStatus.UNAUTHORIZED) def test_put_update_detail_fasilitas_success(self): - client = self.client_user_token() - self.create_lokasi_test() - urls_post = reverse( - 'add-fasilitas', kwargs={'nama_lokasi': self.default_lokasi_name}) - data_post = {'deskripsi': PENJELASAN, - 'rating': 2, - 'tag': 'KR', - 'disabilitas': 'DI', - 'jumlah': 1} - response = client.post(urls_post, data=data_post) - - response_json = json.loads(response.content.decode("utf-8")) - fasilitas_id = response_json["id"] - urls_put = reverse('update-fasilitas', - kwargs={'nama_lokasi': 'Mall', "id": fasilitas_id}) - send_data = {'deskripsi': 'penjelasan fasilitases', - 'rating': 3, - 'tag': 'KR'} - response = client.put(urls_put, data=send_data, - content_type="application/x-www-form-urlencoded") + send_data = { + 'deskripsi': 'penjelasan fasilitases', + 'rating': 3, + 'tag': 'KR', + } + response = self.client.put(self.update_fasilitas_url, data=send_data, + content_type='application/x-www-form-urlencoded') self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) diff --git a/informasi_fasilitas/test_views_komentar.py b/informasi_fasilitas/test_views_komentar.py index 6c50d89..8a90fd8 100644 --- a/informasi_fasilitas/test_views_komentar.py +++ b/informasi_fasilitas/test_views_komentar.py @@ -4,71 +4,51 @@ from django.urls import reverse from .test_base import InformasiFasilitasViewTest from .models import Komentar - -COMMENT = "halo saya komen" +from pplbackend.utils import response_decode +from .views import TIME_FORMAT class KomentarRelatedViewTest(InformasiFasilitasViewTest): + def setUp(self): + super().setUp() + self.kwargs_add_or_list_komentar = { + 'place_id': self.lokasi.place_id, + 'id': self.fasilitas.id, + } + self.add_komentar_url = \ + reverse('add-komentar', kwargs=self.kwargs_add_or_list_komentar) + + self.list_komentar_url = \ + reverse('list-komentar', kwargs=self.kwargs_add_or_list_komentar) def test_can_comment_facility(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls = reverse('add-komentar', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - }) - comment_data = {"deskripsi": COMMENT} - client.post(urls, comment_data) - count = Komentar.objects.filter(fasilitas=fasilitas).count() - self.assertEqual(count, 1) - - def test_can_comment_facility_status_code(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('add-komentar', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - }) - comment_data = {'deskripsi': COMMENT} - response = client.post(urls, comment_data) + response = \ + self.client.post(self.add_komentar_url, self.mock_komentar_test) self.assertEqual(response.status_code, HTTPStatus.CREATED) - def test_not_comment_facility_empty(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('add-komentar', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - }) - comment_data = {} - response = client.post(urls, comment_data) + count = Komentar.objects.filter(fasilitas=self.fasilitas).count() + self.assertEqual(count, 1) + + def test_cannot_comment_facility_if_deskripsi_empty(self): + response = self.client.post(self.add_komentar_url, {}) self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) - def test_not_comment_facility_empty_response(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('add-komentar', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - }) - comment_data = {} - response = client.post(urls, comment_data) - response_json = json.loads(response.content.decode('utf-8')) + response_json = response_decode(response) expected_json = {"response": "Bad Request. 'deskripsi' key is needed"} self.assertEqual(expected_json, response_json) def test_get_list_komentar(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('add-komentar', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - }) - comment_data = {"deskripsi": COMMENT} - client.post(urls, comment_data) - urls_get = reverse('list-komentar', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - }) - response = client.get(urls_get) + komentar = self.create_komentar_test(fasilitas=self.fasilitas) + response = self.client.get(self.list_komentar_url) self.assertEqual(response.status_code, HTTPStatus.OK) + + response_json = response_decode(response) + expected_json = { + '1': { + 'id': komentar.id, + 'deskripsi': komentar.deskripsi, + 'creator': komentar.user.last_name, + 'date_time': komentar.date_time.strftime(TIME_FORMAT), + } + } + self.assertEqual(response_json, expected_json) diff --git a/informasi_fasilitas/test_views_like_dislike.py b/informasi_fasilitas/test_views_like_dislike.py index adf8d90..305d8d0 100644 --- a/informasi_fasilitas/test_views_like_dislike.py +++ b/informasi_fasilitas/test_views_like_dislike.py @@ -5,168 +5,82 @@ from django.urls import reverse from .test_base import InformasiFasilitasViewTest from .models import Likes, Dislikes +from pplbackend.utils import response_decode class LikeDislikeRelatedViewTest(InformasiFasilitasViewTest): + def setUp(self): + super().setUp() + self.kwargs_like_or_dislike = { + 'place_id': self.lokasi.place_id, + 'id': self.fasilitas.id, + } + self.kwargs_like = { + 'operation': 'like', + } + self.kwargs_like.update(self.kwargs_like_or_dislike) + + self.kwargs_dislike = { + 'operation': 'dislike', + } + self.kwargs_dislike.update(self.kwargs_like_or_dislike) + + self.like_url = \ + reverse('update-like-fasilitas', kwargs=self.kwargs_like) + + self.dislike_url = \ + reverse('update-like-fasilitas', kwargs=self.kwargs_dislike) + def test_can_like_facility(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - client.put(urls) - count = Likes.objects.filter(fasilitas=fasilitas).count() - self.assertEqual(count, 1) + response = self.client.put(self.like_url) + count = Likes.objects.filter(fasilitas=self.fasilitas).count() - def test_can_like_facility_status_code(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - response = client.put(urls) + self.assertEqual(count, 1) self.assertEqual(response.status_code, HTTPStatus.CREATED) - def test_like_like_facility_status_code(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - response = client.put(urls) - response = client.put(urls) # like and like - self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) - def test_like_like_facility_response(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - response = client.put(urls) - response = client.put(urls) # like and like - json_response = json.loads(response.content.decode("utf-8")) + self.client.put(self.like_url) + response = self.client.put(self.like_url) # like and like + + json_response = response_decode(response) expected_json = {"response": "You have already liked this facility"} - self.assertEqual(json_response, expected_json) + count = Likes.objects.filter(fasilitas=self.fasilitas).count() - def test_like_like_facility_only_count_one(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - client.put(urls) - client.put(urls) # like and like - count = Likes.objects.filter(fasilitas=fasilitas).count() + self.assertEqual(json_response, expected_json) self.assertEqual(count, 1) def test_like_dislike_facility_count_like(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls_like = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - urls_dislike = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - client.put(urls_like) - - client.put(urls_dislike) # like and dislike - count = Likes.objects.filter(fasilitas=fasilitas).count() + self.client.put(self.like_url) + self.client.put(self.dislike_url) # like and dislike + count = Likes.objects.filter(fasilitas=self.fasilitas).count() self.assertEqual(count, 0) def test_can_dislike_facility(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - client.put(urls) - count = Dislikes.objects.filter(fasilitas=fasilitas).count() - self.assertEqual(count, 1) + response = self.client.put(self.dislike_url) + count = Dislikes.objects.filter(fasilitas=self.fasilitas).count() - def test_can_dislike_facility_status_code(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - response = client.put(urls) + self.assertEqual(count, 1) self.assertEqual(response.status_code, HTTPStatus.CREATED) def test_dislike_dislike_facility_status_code(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - response = client.put(urls) - response = client.put(urls) # Dislike and dislike - self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) + self.client.put(self.dislike_url) - def test_dislike_dislike_facility_response(self): - client = self.client_user_token() - self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - response = client.put(urls) - response = client.put(urls) # Dislike and dislike - json_response = json.loads(response.content.decode("utf-8")) + response = self.client.put(self.dislike_url) # Dislike and dislike + json_response = response_decode(response) expected_json = {"response": "You have already disliked this facility"} + + self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) self.assertEqual(json_response, expected_json) def test_dislike_dislike_facility_only_count_one(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - client.put(urls) - client.put(urls) # Dislike and dislike - count = Dislikes.objects.filter(fasilitas=fasilitas).count() + self.client.put(self.dislike_url) + self.client.put(self.dislike_url) # Dislike and dislike + count = Dislikes.objects.filter(fasilitas=self.fasilitas).count() self.assertEqual(count, 1) def test_dislike_like_facility_count_dislike(self): - client = self.client_user_token() - fasilitas = self.create_fasilitas_test() - urls_like = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "like" - }) - urls_dislike = reverse('update-like-fasilitas', - kwargs={'nama_lokasi': self.default_lokasi_name, - 'id': 1, - 'operation': "dislike" - }) - - client.put(urls_dislike) - client.put(urls_like) - - count = Dislikes.objects.filter(fasilitas=fasilitas).count() + self.client.put(self.dislike_url) + self.client.put(self.like_url) + + count = Dislikes.objects.filter(fasilitas=self.fasilitas).count() self.assertEqual(count, 0) diff --git a/informasi_fasilitas/test_views_lokasi.py b/informasi_fasilitas/test_views_lokasi.py index cf525dd..aa9b8bb 100644 --- a/informasi_fasilitas/test_views_lokasi.py +++ b/informasi_fasilitas/test_views_lokasi.py @@ -9,11 +9,23 @@ from .test_base import InformasiFasilitasViewTest from .models import Lokasi from .serializers import LokasiSerializer +from pplbackend.utils import response_decode + class LokasiRelatedViewTest(InformasiFasilitasViewTest): + def setUp(self): + super().setUp() + self.kwargs_detail_or_update = {'place_id': self.lokasi.place_id} + self.list_lokasi_url = reverse('lokasi-list') + self.detail_lokasi_url = reverse('lokasi-details', + kwargs=self.kwargs_detail_or_update) + self.add_lokasi_url = reverse('add-lokasi') + self.update_lokasi_url = reverse('update-lokasi', + kwargs=self.kwargs_detail_or_update) + def test_LokasiSerializer_valid(self): - serializer = LokasiSerializer(data=self.lokasi_test_1) + serializer = LokasiSerializer(data=self.mock_lokasi_test) self.assertTrue(serializer.is_valid) def test_amount_lokasi(self): @@ -21,90 +33,88 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): self.assertEqual(count, 1) def test_can_get_lokasi_list_url(self): - response = Client().get(reverse('lokasi-list')) + response = Client().get(self.list_lokasi_url) self.assertEqual(response.status_code, HTTPStatus.OK) def test_get_lokasi_list_json(self): - response = Client().get(reverse('lokasi-list')) - content = json.loads(response.content.decode('utf-8')) - expected_first_entry = copy.deepcopy(self.lokasi_test_1) + response = Client().get(self.list_lokasi_url) + content = response_decode(response) + + expected_first_entry = self.mock_lokasi_test.copy() expected_first_entry["image"] = None expected_json = [expected_first_entry] self.assertEqual(content, expected_json) def test_cannot_post_lokasi_list_url(self): - response = Client().post(reverse('lokasi-list')) + response = Client().post(self.list_lokasi_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_can_get_lokasi_details(self): - response = Client().get( - reverse('lokasi-details', kwargs={'nama_lokasi': self.lokasi_test_1["name"]})) - self.assertEqual(response.status_code, HTTPStatus.OK) + response = Client().get(self.detail_lokasi_url) + content = response_decode(response) - def test_can_get_lokasi_details_json(self): - response = Client().get( - reverse('lokasi-details', kwargs={'nama_lokasi': self.lokasi_test_1["name"]})) - content = json.loads(response.content.decode('utf-8')) - expected_json = copy.deepcopy(self.lokasi_test_1) + expected_json = self.mock_lokasi_test.copy() expected_json["image"] = None - # expected_json["id"] = 35 + + self.assertEqual(response.status_code, HTTPStatus.OK) self.assertEqual(content, expected_json) def test_cannot_post_lokasi_details(self): - response = Client().post( - reverse('lokasi-details', kwargs={'nama_lokasi': self.lokasi_test_1["name"]})) + response = Client().post(self.detail_lokasi_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_get_lokasi_details_not_found(self): - response = Client().get( - reverse('lokasi-details', kwargs={'nama_lokasi': 'Mall'})) + Lokasi.objects.all().delete() + response = Client().get(self.detail_lokasi_url) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) def test_cannot_get_add_lokasi(self): - client = self.client_user_token() - response = client.get(reverse('add-lokasi')) + response = self.client.get(self.add_lokasi_url) self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_can_post_add_lokasi(self): - client = self.client_user_token() Lokasi.objects.all().delete() - response = client.post(reverse('add-lokasi'), self.lokasi_test_1) - response_json = json.loads(response.content.decode("utf-8")) - expected_json = copy.deepcopy(self.lokasi_test_1) - expected_json['id'] = 2 + response = \ + self.client.post(self.add_lokasi_url, self.mock_lokasi_test) + response_json = response_decode(response) + expected_json = self.mock_lokasi_test.copy() expected_json['image'] = None + self.assertEqual(response_json, expected_json) self.assertEqual(response.status_code, HTTPStatus.CREATED) - def test_post_add_lokasi_missing_key(self): - test_lokasi = copy.deepcopy(self.lokasi_test_1) - del test_lokasi["name"] - client = self.client_user_token() - response = client.post(reverse('add-lokasi'), test_lokasi) + def test_cannot_post_lokasi_place_id_should_unique(self): + response = \ + self.client.post(self.add_lokasi_url, self.mock_lokasi_test) + response_json = response_decode(response) + expected_json = \ + {'place_id': ['lokasi with this place id already exists.']} + + self.assertEqual(response_json, expected_json) self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) - def test_post_add_lokasi_missing_key_return_message(self): - test_lokasi = copy.deepcopy(self.lokasi_test_1) + def test_post_add_lokasi_missing_key(self): + Lokasi.objects.all().delete() + test_lokasi = self.mock_lokasi_test.copy() del test_lokasi["name"] - client = self.client_user_token() - response = client.post(reverse('add-lokasi'), test_lokasi) - response_json = json.loads(response.content.decode('utf-8')) + response = self.client.post(self.add_lokasi_url, test_lokasi) + + response_json = response_decode(response) expected_json = {"name": ["This field is required."]} + + self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) self.assertEqual(response_json, expected_json) def test_put_update_detail_lokasi_success(self): - client = self.client_user_token() - urls = reverse('update-lokasi', - kwargs={'nama_lokasi': self.lokasi_test_1["name"]}) - response = client.put(urls, data=urlencode({'no_telp': '0000000121', }), - content_type="application/x-www-form-urlencoded") + response = self.client.put(self.update_lokasi_url, + data=urlencode({'no_telp': '0000000121'}), + content_type=self.put_content_type) self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) def test_put_update_detail_lokasi_missing_key(self): - client = self.client_user_token() - - urls = reverse('update-lokasi', - kwargs={'nama_lokasi': self.lokasi_test_1["name"]}) - response = client.put(urls, data={"latitude": 100}, - content_type="application/x-www-form-urlencoded") + response = self.client.put( + self.update_lokasi_url, + data={"latitude": 100}, + content_type=self.put_content_type + ) self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) -- GitLab From b4daf098e80adc44dde22e25eb549cbd5a721957 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 28 Mar 2021 18:39:20 +0700 Subject: [PATCH 18/29] [RED] utils for unittest, get client from user --- pplbackend/utils.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 pplbackend/utils.py diff --git a/pplbackend/utils.py b/pplbackend/utils.py new file mode 100644 index 0000000..dac4c0a --- /dev/null +++ b/pplbackend/utils.py @@ -0,0 +1,24 @@ +from rest_framework.test import APIClient +from rest_framework_simplejwt.tokens import RefreshToken +from rest_framework.authtoken.models import Token + +from django.urls import reverse +from django.test import Client + +import json + +def get_client_login_with_user(user): + token, _ = Token.objects.get_or_create(user=user) + client = Client(HTTP_AUTHORIZATION=f'token {token.key}') + + return client + + +def response_decode( + response, +): + try: + content = json.loads(response.content.decode('utf-8')) + except json.decoder.JSONDecodeError as json_error: + return None + return content -- GitLab From ce60339e59f6b1aadcd7fef0a43410a7bf3c6b8b Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 28 Mar 2021 18:46:57 +0700 Subject: [PATCH 19/29] [RED] remove allow failure keyword on UnitTest stage gitlab ci] --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a5172b2..f7cad09 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,6 @@ stages: UnitTest: image: python:3.7 stage: test - allow_failure: true coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+)%/' before_script: - pip install -r requirements.txt -- GitLab From cd6eccadac5c0f9b24215cc777ac7b77175e229e Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Sun, 28 Mar 2021 19:06:00 +0700 Subject: [PATCH 20/29] [GREEN] implemented models for PBI add lokasi - restructured models, added place_id and removed latitude and longitude attrs, default place_id is randomized with 40 chars - remove latitude and longitude from serializer and change id to place_id - change url dispatcher from string nama_lokasi to place_id and modified the views as well - added migration --- .../migrations/0009_auto_20210327_1436.py | 32 ++++++++++ informasi_fasilitas/models.py | 13 ++-- informasi_fasilitas/serializers.py | 2 +- informasi_fasilitas/urls.py | 44 ++++++------- informasi_fasilitas/views.py | 62 +++++++++---------- 5 files changed, 92 insertions(+), 61 deletions(-) create mode 100644 informasi_fasilitas/migrations/0009_auto_20210327_1436.py diff --git a/informasi_fasilitas/migrations/0009_auto_20210327_1436.py b/informasi_fasilitas/migrations/0009_auto_20210327_1436.py new file mode 100644 index 0000000..6fc27b9 --- /dev/null +++ b/informasi_fasilitas/migrations/0009_auto_20210327_1436.py @@ -0,0 +1,32 @@ +# Generated by Django 3.1.7 on 2021-03-27 14:36 + +from django.db import migrations, models +import informasi_fasilitas.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0008_lokasi_counter'), + ] + + operations = [ + migrations.RemoveField( + model_name='lokasi', + name='latitude', + ), + migrations.RemoveField( + model_name='lokasi', + name='longitude', + ), + migrations.AddField( + model_name='lokasi', + name='place_id', + field=models.TextField(default=informasi_fasilitas.models._default_lokasi_place_id, unique=True), + ), + migrations.AlterField( + model_name='lokasi', + name='image', + field=models.TextField(null=True), + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 8a6aa2e..439ece4 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -2,6 +2,9 @@ from django.db import models from django.contrib.auth.models import User from multiselectfield import MultiSelectField +import string +import random + # Create your models here. KURSI_RODA = 'KR' LIFT_DISABILITAS = 'LF' @@ -40,17 +43,17 @@ JENIS_DISABILITAS = ( ('DS', 'Disabilitas Sensorik'), ) +def _default_lokasi_place_id(): + return \ + ''.join([random.choice(string.ascii_letters) for _ in range(40)]) class Lokasi(models.Model): + place_id = models.TextField(unique=True, default=_default_lokasi_place_id) name = models.CharField(max_length=50) - latitude = models.FloatField() - longitude = models.FloatField() alamat = models.CharField(max_length=100) no_telp = models.CharField(max_length=16) - image = models.ImageField(upload_to='lokasi/') + image = models.TextField(null=True) counter = models.PositiveIntegerField(default=0) - objects = models.Manager() - POINT_FIELD = ""+str(latitude)+","+str(longitude) def __str__(self): return self.name diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index ecfa9c2..8ae0076 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -7,4 +7,4 @@ class LokasiSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Lokasi - fields = ('id', 'name', 'latitude', 'longitude', 'alamat', 'no_telp', 'image', 'counter') \ No newline at end of file + fields = ('place_id', 'name', 'alamat', 'no_telp', 'image', 'counter') diff --git a/informasi_fasilitas/urls.py b/informasi_fasilitas/urls.py index f7c755e..e2e2d57 100644 --- a/informasi_fasilitas/urls.py +++ b/informasi_fasilitas/urls.py @@ -3,34 +3,34 @@ from . import views from .views import * urlpatterns = [ - path('lokasi/list/', views.lokasi_list, name='lokasi-list'), + path('lokasi/list/', views.lokasi_list, name='lokasi-list'), - path('lokasi/detail//', - views.lokasi_details, name='lokasi-details'), + path('lokasi/detail//', + views.lokasi_details, name='lokasi-details'), - path('lokasi/update-detail//', - views.update_lokasi_details, name='update-lokasi'), + path('lokasi/update-detail//', + views.update_lokasi_details, name='update-lokasi'), - path('lokasi/add/', views.add_lokasi, name='add-lokasi'), + path('lokasi/add/', views.add_lokasi, name='add-lokasi'), - path('lokasi/add-fasilitas//', - views.add_fasilitas, name='add-fasilitas'), + path('lokasi/add-fasilitas//', + views.add_fasilitas, name='add-fasilitas'), - path('lokasi/list-fasilitas//', - views.list_fasilitas, name='list-fasilitas'), + path('lokasi/list-fasilitas//', + views.list_fasilitas, name='list-fasilitas'), - path('lokasi/detail-fasilitas///', - views.detail_fasilitas, name='detail-fasilitas'), + path('lokasi/detail-fasilitas///', + views.detail_fasilitas, name='detail-fasilitas'), - path('lokasi/add-komentar///', - views.add_komentar, name='add-komentar'), + path('lokasi/add-komentar///', + views.add_komentar, name='add-komentar'), - path('lokasi/list-komentar///', - views.list_komentar, name='list-komentar'), - - path('lokasi/update-fasilitas///', - views.update_fasilitas, name='update-fasilitas'), - - path('lokasi/like-fasilitas////', - views.update_like_fasilitas, name='update-like-fasilitas'), + path('lokasi/list-komentar///', + views.list_komentar, name='list-komentar'), + + path('lokasi/update-fasilitas///', + views.update_fasilitas, name='update-fasilitas'), + + path('lokasi/like-fasilitas////', + views.update_like_fasilitas, name='update-like-fasilitas'), ] diff --git a/informasi_fasilitas/views.py b/informasi_fasilitas/views.py index d799fce..d0e41c0 100644 --- a/informasi_fasilitas/views.py +++ b/informasi_fasilitas/views.py @@ -37,9 +37,9 @@ def lokasi_list(request): @api_view(['GET']) @authentication_classes([]) @permission_classes([]) -def lokasi_details(request, nama_lokasi): +def lokasi_details(request, place_id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) serializer = LokasiSerializer(lokasi) return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) except ObjectDoesNotExist as err: @@ -50,23 +50,21 @@ def lokasi_details(request, nama_lokasi): @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) def add_lokasi(request): - try: - data = request.data - serializer = LokasiSerializer(data=data) - if serializer.is_valid(): - serializer.save() - return JsonResponse(serializer.data, status=HTTPStatus.CREATED) - else: - return JsonResponse(serializer.errors, status=HTTPStatus.BAD_REQUEST) - except Exception as err: - return JsonResponse({'response': str(err)}, status=HTTPStatus.NOT_FOUND) + data = request.data + serializer = LokasiSerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, status=HTTPStatus.CREATED) + else: + return JsonResponse(serializer.errors, + status=HTTPStatus.BAD_REQUEST) @api_view(['PUT']) @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) -def update_lokasi_details(request, nama_lokasi): +def update_lokasi_details(request, place_id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) lokasi.no_telp = request.data['no_telp'] lokasi.counter = F('counter') + 1 lokasi.save() @@ -80,9 +78,9 @@ def update_lokasi_details(request, nama_lokasi): @api_view(['GET']) @authentication_classes([]) @permission_classes([]) -def list_fasilitas(request, nama_lokasi): +def list_fasilitas(request, place_id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) list_fasilitas = Fasilitas.objects.filter(lokasi=lokasi) return_json = {} for fasilitas in list_fasilitas: @@ -114,10 +112,9 @@ def list_fasilitas(request, nama_lokasi): @api_view(['POST']) @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) -def add_fasilitas(request, nama_lokasi): +def add_fasilitas(request, place_id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) - user = User.objects.get(email=str(request.user)) + lokasi = Lokasi.objects.get(place_id=place_id) deskripsi = request.POST['deskripsi'] rating = request.POST['rating'] tag = request.POST['tag'] @@ -133,7 +130,7 @@ def add_fasilitas(request, nama_lokasi): if 'image' in request.FILES.keys(): image = request.FILES['image'] fasilitas = Fasilitas.objects.create(lokasi=lokasi, - user=user, + user=request.user, deskripsi=deskripsi, rating=rating, tag=tag, @@ -152,16 +149,16 @@ def add_fasilitas(request, nama_lokasi): @api_view(['GET']) @authentication_classes([]) @permission_classes([]) -def detail_fasilitas(request, nama_lokasi, id): +def detail_fasilitas(request, place_id, id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) user = fasilitas.user fasilitas.like = Likes.objects.filter(fasilitas=fasilitas).count() fasilitas.dislike = Dislikes.objects.filter( fasilitas=fasilitas).count() return_json = {"id": fasilitas.id, - "nama_lokasi": lokasi.name, + "place_id": lokasi.name, "deskripsi": fasilitas.deskripsi, "creator": user.last_name, "creator_email": user.email, @@ -185,9 +182,9 @@ def detail_fasilitas(request, nama_lokasi, id): @api_view(['PUT']) @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) -def update_fasilitas(request, nama_lokasi, id): +def update_fasilitas(request, place_id, id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) user_creator = fasilitas.user desc = fasilitas.deskripsi @@ -226,9 +223,9 @@ def update_fasilitas(request, nama_lokasi, id): @api_view(['PUT']) @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) -def update_like_fasilitas(request, nama_lokasi, id, operation): +def update_like_fasilitas(request, place_id, id, operation): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) user = request.user @@ -267,14 +264,13 @@ def update_like_fasilitas(request, nama_lokasi, id, operation): @api_view(['POST']) @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) -def add_komentar(request, nama_lokasi, id): +def add_komentar(request, place_id, id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) - user = User.objects.get(email=str(request.user)) deskripsi = request.POST['deskripsi'] komentar = Komentar.objects.create(fasilitas=fasilitas, - user=user, + user=request.user, deskripsi=deskripsi) return JsonResponse({'response': 'komentar added', 'id': komentar.id, @@ -288,9 +284,9 @@ def add_komentar(request, nama_lokasi, id): @api_view(['GET']) @authentication_classes([]) @permission_classes([]) -def list_komentar(request, nama_lokasi, id): +def list_komentar(request, place_id, id): try: - lokasi = Lokasi.objects.get(name=nama_lokasi) + lokasi = Lokasi.objects.get(place_id=place_id) fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) list_komentar = Komentar.objects.filter(fasilitas=fasilitas) return_json = {} -- GitLab From 3fe8cef45515b950abfcdb77c25353a5cfe8da24 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 29 Mar 2021 15:09:57 +0700 Subject: [PATCH 21/29] [RED] tests for new Lokasi models - test remove alamat, name, no_telp - test timestamp with auto_now=True - test place_id is required on LokasiSerializer --- informasi_fasilitas/test_base.py | 5 ----- informasi_fasilitas/test_models.py | 22 ++++++++++----------- informasi_fasilitas/test_views_fasilitas.py | 2 +- informasi_fasilitas/test_views_lokasi.py | 4 ++-- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index 6ba0967..a759bf4 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -7,8 +7,6 @@ from django.urls import reverse, path, include from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.base import ContentFile -from base64 import b64decode - from .models import ( Lokasi, Fasilitas, @@ -33,9 +31,6 @@ class InformasiFasilitasTest(TestCase): mock_lokasi_test = { 'place_id': 'mock_place_id', - 'name': 'Ma Homie', - 'alamat': 'Jl. Raya Bogor no.2, Jakarta', - 'no_telp': '081212123132', 'counter': 0, } diff --git a/informasi_fasilitas/test_models.py b/informasi_fasilitas/test_models.py index c4798b3..9cbc5cf 100644 --- a/informasi_fasilitas/test_models.py +++ b/informasi_fasilitas/test_models.py @@ -6,16 +6,6 @@ from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes class InformasiFasilitasModelTest(InformasiFasilitasTest): - def test_models_lokasi_not_created_attrs_should_not_null(self): - with self.assertRaises(IntegrityError) as err_message: - Lokasi.objects.create( - name=None, - alamat=None, - no_telp=None, - ) - self.assertTrue(str(err_message.exception).startswith( - self.not_null_constraint_failed_message)) - def test_models_lokasi_not_created_place_id_should_unique(self): with self.assertRaises(IntegrityError) as err_message: Lokasi.objects.create( @@ -27,6 +17,15 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): self.assertTrue(str(err_message.exception).startswith( self.unique_constraint_failed_message)) + def test_models_timestamp_auto_now_is_working(self): + lokasi = self.create_lokasi_test() + timestamp_before_save = lokasi.timestamp + + lokasi.save() + timestamp_after_save = lokasi.timestamp + + self.assertGreater(timestamp_after_save, timestamp_before_save) + def test_models_create_new_lokasi(self): self.create_lokasi_test() count = Lokasi.objects.all().count() @@ -34,7 +33,8 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): def test_models_lokasi_string(self): lokasi = self.create_lokasi_test() - self.assertEqual(str(lokasi), lokasi.name) + self.assertEqual(str(lokasi), "%s | %s" % \ + (lokasi.timestamp.strftime("%d %b %Y %X"), lokasi.place_id)) def test_models_fasilitas_not_created(self): with self.assertRaises(IntegrityError) as err_message: diff --git a/informasi_fasilitas/test_views_fasilitas.py b/informasi_fasilitas/test_views_fasilitas.py index 7e9fc41..8328504 100644 --- a/informasi_fasilitas/test_views_fasilitas.py +++ b/informasi_fasilitas/test_views_fasilitas.py @@ -41,7 +41,7 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): expected_json = { '1': { 'id': self.fasilitas.id, - 'nama_lokasi': self.fasilitas.lokasi.name, + 'place_id': self.fasilitas.lokasi.place_id, 'deskripsi': self.fasilitas.deskripsi, 'creator': self.fasilitas.user.last_name, 'like': self.fasilitas.like, diff --git a/informasi_fasilitas/test_views_lokasi.py b/informasi_fasilitas/test_views_lokasi.py index aa9b8bb..91f5c5c 100644 --- a/informasi_fasilitas/test_views_lokasi.py +++ b/informasi_fasilitas/test_views_lokasi.py @@ -96,11 +96,11 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): def test_post_add_lokasi_missing_key(self): Lokasi.objects.all().delete() test_lokasi = self.mock_lokasi_test.copy() - del test_lokasi["name"] + del test_lokasi['place_id'] response = self.client.post(self.add_lokasi_url, test_lokasi) response_json = response_decode(response) - expected_json = {"name": ["This field is required."]} + expected_json = {"place_id": ["This field is required."]} self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) self.assertEqual(response_json, expected_json) -- GitLab From 3c9f835a671885316606a03b4f221a7f740b2b26 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 29 Mar 2021 15:15:43 +0700 Subject: [PATCH 22/29] [GREEN] implemented new Lokasi models - now image has param null and blank True - new attr timestamp with auto_now True - place_id is required on LokasiSerializer --- .../migrations/0010_auto_20210329_0714.py | 35 +++++++++++++++++++ informasi_fasilitas/models.py | 9 +++-- informasi_fasilitas/serializers.py | 3 +- informasi_fasilitas/views.py | 8 ++--- 4 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 informasi_fasilitas/migrations/0010_auto_20210329_0714.py diff --git a/informasi_fasilitas/migrations/0010_auto_20210329_0714.py b/informasi_fasilitas/migrations/0010_auto_20210329_0714.py new file mode 100644 index 0000000..918b686 --- /dev/null +++ b/informasi_fasilitas/migrations/0010_auto_20210329_0714.py @@ -0,0 +1,35 @@ +# Generated by Django 3.1.7 on 2021-03-29 07:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0009_auto_20210327_1436'), + ] + + operations = [ + migrations.RemoveField( + model_name='lokasi', + name='alamat', + ), + migrations.RemoveField( + model_name='lokasi', + name='name', + ), + migrations.RemoveField( + model_name='lokasi', + name='no_telp', + ), + migrations.AddField( + model_name='lokasi', + name='timestamp', + field=models.DateTimeField(auto_now=True), + ), + migrations.AlterField( + model_name='lokasi', + name='image', + field=models.ImageField(blank=True, null=True, upload_to='lokasi/'), + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 439ece4..8c84327 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -49,14 +49,13 @@ def _default_lokasi_place_id(): class Lokasi(models.Model): place_id = models.TextField(unique=True, default=_default_lokasi_place_id) - name = models.CharField(max_length=50) - alamat = models.CharField(max_length=100) - no_telp = models.CharField(max_length=16) - image = models.TextField(null=True) + image = models.ImageField(upload_to='lokasi/', null=True, blank=True) counter = models.PositiveIntegerField(default=0) + timestamp = models.DateTimeField(auto_now=True) def __str__(self): - return self.name + return "%s | %s" % \ + (self.timestamp.strftime("%d %b %Y %X"), self.place_id) class Fasilitas(models.Model): objects = models.Manager() diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index 8ae0076..5d37f54 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -7,4 +7,5 @@ class LokasiSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Lokasi - fields = ('place_id', 'name', 'alamat', 'no_telp', 'image', 'counter') + fields = ('place_id', 'image', 'counter') + extra_kwargs = {'place_id': {'required': True}} diff --git a/informasi_fasilitas/views.py b/informasi_fasilitas/views.py index d0e41c0..60d18cc 100644 --- a/informasi_fasilitas/views.py +++ b/informasi_fasilitas/views.py @@ -24,7 +24,6 @@ def missing_key_message(key): return "Bad Request. {} key is needed".format(key) -@csrf_exempt @api_view(['GET']) @authentication_classes([]) @permission_classes([]) @@ -91,7 +90,7 @@ def list_fasilitas(request, place_id): return_json[fasilitas.id] = {} fasilitas_details = return_json[fasilitas.id] fasilitas_details["id"] = fasilitas.id - fasilitas_details["nama_lokasi"] = fasilitas.lokasi.name + fasilitas_details["place_id"] = fasilitas.lokasi.place_id fasilitas_details["deskripsi"] = fasilitas.deskripsi fasilitas_details["creator"] = fasilitas.user.last_name fasilitas_details["date_time"] = fasilitas.date_time.strftime( @@ -151,14 +150,13 @@ def add_fasilitas(request, place_id): @permission_classes([]) def detail_fasilitas(request, place_id, id): try: - lokasi = Lokasi.objects.get(place_id=place_id) - fasilitas = Fasilitas.objects.get(lokasi=lokasi, id=id) + fasilitas = Fasilitas.objects.get(lokasi__place_id=place_id, id=id) user = fasilitas.user fasilitas.like = Likes.objects.filter(fasilitas=fasilitas).count() fasilitas.dislike = Dislikes.objects.filter( fasilitas=fasilitas).count() return_json = {"id": fasilitas.id, - "place_id": lokasi.name, + "place_id": fasilitas.lokasi.place_id, "deskripsi": fasilitas.deskripsi, "creator": user.last_name, "creator_email": user.email, -- GitLab From cbf2067abe7982fc55e769103b386e40e1423cb4 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 29 Mar 2021 16:49:12 +0700 Subject: [PATCH 23/29] [RED] test remove attribute image from Lokasi models --- informasi_fasilitas/test_views_lokasi.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/informasi_fasilitas/test_views_lokasi.py b/informasi_fasilitas/test_views_lokasi.py index 91f5c5c..e76981c 100644 --- a/informasi_fasilitas/test_views_lokasi.py +++ b/informasi_fasilitas/test_views_lokasi.py @@ -40,10 +40,7 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): response = Client().get(self.list_lokasi_url) content = response_decode(response) - expected_first_entry = self.mock_lokasi_test.copy() - expected_first_entry["image"] = None - expected_json = [expected_first_entry] - self.assertEqual(content, expected_json) + self.assertEqual(content, [self.mock_lokasi_test]) def test_cannot_post_lokasi_list_url(self): response = Client().post(self.list_lokasi_url) @@ -53,11 +50,8 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): response = Client().get(self.detail_lokasi_url) content = response_decode(response) - expected_json = self.mock_lokasi_test.copy() - expected_json["image"] = None - self.assertEqual(response.status_code, HTTPStatus.OK) - self.assertEqual(content, expected_json) + self.assertEqual(content, self.mock_lokasi_test) def test_cannot_post_lokasi_details(self): response = Client().post(self.detail_lokasi_url) @@ -77,10 +71,8 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): response = \ self.client.post(self.add_lokasi_url, self.mock_lokasi_test) response_json = response_decode(response) - expected_json = self.mock_lokasi_test.copy() - expected_json['image'] = None - self.assertEqual(response_json, expected_json) + self.assertEqual(response_json, self.mock_lokasi_test) self.assertEqual(response.status_code, HTTPStatus.CREATED) def test_cannot_post_lokasi_place_id_should_unique(self): -- GitLab From c04687bf44499a2e1c9e03259119c073ce0840f5 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Mon, 29 Mar 2021 16:54:31 +0700 Subject: [PATCH 24/29] [GREEN] removed image from Lokasi models now image can be retrieved from photo_reference --- .../migrations/0011_remove_lokasi_image.py | 17 +++++++++++++++++ informasi_fasilitas/models.py | 1 - informasi_fasilitas/serializers.py | 3 +-- 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 informasi_fasilitas/migrations/0011_remove_lokasi_image.py diff --git a/informasi_fasilitas/migrations/0011_remove_lokasi_image.py b/informasi_fasilitas/migrations/0011_remove_lokasi_image.py new file mode 100644 index 0000000..1d686d9 --- /dev/null +++ b/informasi_fasilitas/migrations/0011_remove_lokasi_image.py @@ -0,0 +1,17 @@ +# Generated by Django 3.1.7 on 2021-03-29 09:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0010_auto_20210329_0714'), + ] + + operations = [ + migrations.RemoveField( + model_name='lokasi', + name='image', + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 8c84327..9558415 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -49,7 +49,6 @@ def _default_lokasi_place_id(): class Lokasi(models.Model): place_id = models.TextField(unique=True, default=_default_lokasi_place_id) - image = models.ImageField(upload_to='lokasi/', null=True, blank=True) counter = models.PositiveIntegerField(default=0) timestamp = models.DateTimeField(auto_now=True) diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index 5d37f54..a51c0e6 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -3,9 +3,8 @@ from rest_framework import serializers from .models import Lokasi class LokasiSerializer(serializers.HyperlinkedModelSerializer): - image = serializers.ImageField(required=False) class Meta: model = Lokasi - fields = ('place_id', 'image', 'counter') + fields = ('place_id', 'counter') extra_kwargs = {'place_id': {'required': True}} -- GitLab From a83d609af1088af23956b77c67280ecbe520dc9d Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Tue, 30 Mar 2021 20:05:51 +0700 Subject: [PATCH 25/29] [RED] tests for counter and timestamp field for Lokasi models --- informasi_fasilitas/test_models.py | 4 +- informasi_fasilitas/test_views_lokasi.py | 88 +++++++++++++----------- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/informasi_fasilitas/test_models.py b/informasi_fasilitas/test_models.py index 9cbc5cf..d6b73aa 100644 --- a/informasi_fasilitas/test_models.py +++ b/informasi_fasilitas/test_models.py @@ -17,14 +17,14 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): self.assertTrue(str(err_message.exception).startswith( self.unique_constraint_failed_message)) - def test_models_timestamp_auto_now_is_working(self): + def test_models_timestamp_not_changing_after_save(self): lokasi = self.create_lokasi_test() timestamp_before_save = lokasi.timestamp lokasi.save() timestamp_after_save = lokasi.timestamp - self.assertGreater(timestamp_after_save, timestamp_before_save) + self.assertEqual(timestamp_after_save, timestamp_before_save) def test_models_create_new_lokasi(self): self.create_lokasi_test() diff --git a/informasi_fasilitas/test_views_lokasi.py b/informasi_fasilitas/test_views_lokasi.py index e76981c..ab304c6 100644 --- a/informasi_fasilitas/test_views_lokasi.py +++ b/informasi_fasilitas/test_views_lokasi.py @@ -16,13 +16,8 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): def setUp(self): super().setUp() - self.kwargs_detail_or_update = {'place_id': self.lokasi.place_id} - self.list_lokasi_url = reverse('lokasi-list') - self.detail_lokasi_url = reverse('lokasi-details', - kwargs=self.kwargs_detail_or_update) + self.list_lokasi_url = reverse('list-lokasi') self.add_lokasi_url = reverse('add-lokasi') - self.update_lokasi_url = reverse('update-lokasi', - kwargs=self.kwargs_detail_or_update) def test_LokasiSerializer_valid(self): serializer = LokasiSerializer(data=self.mock_lokasi_test) @@ -42,29 +37,52 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): self.assertEqual(content, [self.mock_lokasi_test]) - def test_cannot_post_lokasi_list_url(self): - response = Client().post(self.list_lokasi_url) - self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + def test_list_lokasi_should_return_sorted_descending_and_best_3(self): + data_lokasi1 = self.mock_lokasi_test.copy() + data_lokasi1['counter'] = 2 - def test_can_get_lokasi_details(self): - response = Client().get(self.detail_lokasi_url) + data_lokasi2 = {'counter': 7, 'place_id': 'a'} + data_lokasi3 = {'counter': 1, 'place_id': 'b'} + data_lokasi4 = {'counter': 0, 'place_id': 'c'} + + lokasi1 = self.lokasi + lokasi2 = self.create_lokasi_test(lokasi_dict=data_lokasi2) + lokasi3 = self.create_lokasi_test(lokasi_dict=data_lokasi3) + self.create_lokasi_test(lokasi_dict=data_lokasi4) + + lokasi1.counter = 2 + lokasi1.save() + + lokasi2.counter = 7 + lokasi2.save() + + lokasi3.counter = 1 + lokasi3.save() + + response = Client().get(self.list_lokasi_url) content = response_decode(response) - self.assertEqual(response.status_code, HTTPStatus.OK) - self.assertEqual(content, self.mock_lokasi_test) + expected = [ + data_lokasi2, + data_lokasi1, + data_lokasi3, + ] - def test_cannot_post_lokasi_details(self): - response = Client().post(self.detail_lokasi_url) - self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + self.assertEqual(len(content), 3) + self.assertEqual(content, expected) - def test_get_lokasi_details_not_found(self): - Lokasi.objects.all().delete() - response = Client().get(self.detail_lokasi_url) - self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + def test_attribute_counter_is_readonly_from_views(self): + new_lokasi = {'counter': 10, 'place_id': 'a'} + response = \ + self.client.post(self.add_lokasi_url, new_lokasi) - def test_cannot_get_add_lokasi(self): - response = self.client.get(self.add_lokasi_url) - self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) + lokasi = Lokasi.objects.get(place_id=new_lokasi['place_id']) + + self.assertEqual(lokasi.counter, 0) + + def test_cannot_delete_lokasi(self): + response = self.client.delete(self.list_lokasi_url) + self.assertEqual(response.status_code, HTTPStatus.FORBIDDEN) def test_can_post_add_lokasi(self): Lokasi.objects.all().delete() @@ -73,17 +91,17 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): response_json = response_decode(response) self.assertEqual(response_json, self.mock_lokasi_test) - self.assertEqual(response.status_code, HTTPStatus.CREATED) + self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) - def test_cannot_post_lokasi_place_id_should_unique(self): + def test_post_place_id_increase_counter(self): response = \ self.client.post(self.add_lokasi_url, self.mock_lokasi_test) response_json = response_decode(response) - expected_json = \ - {'place_id': ['lokasi with this place id already exists.']} + expected_json = self.mock_lokasi_test.copy() + expected_json['counter'] += 1 self.assertEqual(response_json, expected_json) - self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) + self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) def test_post_add_lokasi_missing_key(self): Lokasi.objects.all().delete() @@ -96,17 +114,3 @@ class LokasiRelatedViewTest(InformasiFasilitasViewTest): self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) self.assertEqual(response_json, expected_json) - - def test_put_update_detail_lokasi_success(self): - response = self.client.put(self.update_lokasi_url, - data=urlencode({'no_telp': '0000000121'}), - content_type=self.put_content_type) - self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) - - def test_put_update_detail_lokasi_missing_key(self): - response = self.client.put( - self.update_lokasi_url, - data={"latitude": 100}, - content_type=self.put_content_type - ) - self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST) -- GitLab From b72b72627ce9d11f632c8035dd32bc0b351bb542 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Tue, 30 Mar 2021 20:31:41 +0700 Subject: [PATCH 26/29] [GREEN] now Lokasi use default timezone.now instead of auto_now=True - also refactor some views for lokasi - added UserPermission in informasi_fasilitas.permissions --- .../migrations/0012_auto_20210329_1332.py | 19 +++++ informasi_fasilitas/models.py | 4 +- informasi_fasilitas/permissions.py | 9 ++ informasi_fasilitas/serializers.py | 11 ++- informasi_fasilitas/urls.py | 14 ++- informasi_fasilitas/views.py | 85 +++++++------------ 6 files changed, 78 insertions(+), 64 deletions(-) create mode 100644 informasi_fasilitas/migrations/0012_auto_20210329_1332.py create mode 100644 informasi_fasilitas/permissions.py diff --git a/informasi_fasilitas/migrations/0012_auto_20210329_1332.py b/informasi_fasilitas/migrations/0012_auto_20210329_1332.py new file mode 100644 index 0000000..ebbb285 --- /dev/null +++ b/informasi_fasilitas/migrations/0012_auto_20210329_1332.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.7 on 2021-03-29 13:32 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('informasi_fasilitas', '0011_remove_lokasi_image'), + ] + + operations = [ + migrations.AlterField( + model_name='lokasi', + name='timestamp', + field=models.DateTimeField(blank=True, default=django.utils.timezone.now), + ), + ] diff --git a/informasi_fasilitas/models.py b/informasi_fasilitas/models.py index 9558415..1e90a63 100644 --- a/informasi_fasilitas/models.py +++ b/informasi_fasilitas/models.py @@ -2,6 +2,8 @@ from django.db import models from django.contrib.auth.models import User from multiselectfield import MultiSelectField +from django.utils import timezone + import string import random @@ -50,7 +52,7 @@ def _default_lokasi_place_id(): class Lokasi(models.Model): place_id = models.TextField(unique=True, default=_default_lokasi_place_id) counter = models.PositiveIntegerField(default=0) - timestamp = models.DateTimeField(auto_now=True) + timestamp = models.DateTimeField(default=timezone.now, blank=True) def __str__(self): return "%s | %s" % \ diff --git a/informasi_fasilitas/permissions.py b/informasi_fasilitas/permissions.py new file mode 100644 index 0000000..20dedb0 --- /dev/null +++ b/informasi_fasilitas/permissions.py @@ -0,0 +1,9 @@ +from rest_framework import permissions + +class UserPermission(permissions.BasePermission): + def has_permission(self, request, view): + if view.action == 'list': + return True + elif view.action == 'create': + return request.user.is_authenticated + return False diff --git a/informasi_fasilitas/serializers.py b/informasi_fasilitas/serializers.py index a51c0e6..e191b99 100644 --- a/informasi_fasilitas/serializers.py +++ b/informasi_fasilitas/serializers.py @@ -2,9 +2,16 @@ from rest_framework import serializers from .models import Lokasi -class LokasiSerializer(serializers.HyperlinkedModelSerializer): +class LokasiSerializer(serializers.ModelSerializer): class Meta: model = Lokasi fields = ('place_id', 'counter') - extra_kwargs = {'place_id': {'required': True}} + extra_kwargs = { + 'place_id': { + 'required': True, + }, + 'counter': { + 'read_only': True, + }, + } diff --git a/informasi_fasilitas/urls.py b/informasi_fasilitas/urls.py index e2e2d57..eacb246 100644 --- a/informasi_fasilitas/urls.py +++ b/informasi_fasilitas/urls.py @@ -1,17 +1,13 @@ from django.urls import path from . import views -from .views import * -urlpatterns = [ - path('lokasi/list/', views.lokasi_list, name='lokasi-list'), - - path('lokasi/detail//', - views.lokasi_details, name='lokasi-details'), +list_lokasi_views = views.LokasiListCreateView.as_view({'get':'list'}) +add_lokasi_views = views.LokasiListCreateView.as_view({'post':'create'}) - path('lokasi/update-detail//', - views.update_lokasi_details, name='update-lokasi'), +urlpatterns = [ + path('lokasi/list/', list_lokasi_views, name='list-lokasi'), - path('lokasi/add/', views.add_lokasi, name='add-lokasi'), + path('lokasi/add/', add_lokasi_views, name='add-lokasi'), path('lokasi/add-fasilitas//', views.add_fasilitas, name='add-fasilitas'), diff --git a/informasi_fasilitas/views.py b/informasi_fasilitas/views.py index 60d18cc..02ab23d 100644 --- a/informasi_fasilitas/views.py +++ b/informasi_fasilitas/views.py @@ -8,14 +8,15 @@ from django.db.models import F from rest_framework.decorators import api_view, permission_classes, authentication_classes from rest_framework.authentication import TokenAuthentication from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.parsers import FileUploadParser +from rest_framework.views import APIView +from rest_framework.generics import ListCreateAPIView +from rest_framework import viewsets from .serializers import LokasiSerializer from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes - - - -from rest_framework.parsers import FileUploadParser -from rest_framework.views import APIView +from .permissions import UserPermission TIME_FORMAT = "%d-%m-%Y %H:%M:%S" @@ -23,56 +24,36 @@ TIME_FORMAT = "%d-%m-%Y %H:%M:%S" def missing_key_message(key): return "Bad Request. {} key is needed".format(key) +class LokasiListCreateView(viewsets.ModelViewSet): + queryset = Lokasi.objects.all() + serializer_class = LokasiSerializer + authentication_classes = [TokenAuthentication] + permission_classes = (UserPermission,) -@api_view(['GET']) -@authentication_classes([]) -@permission_classes([]) -def lokasi_list(request): - lokasi_list = Lokasi.objects.all() - serializer = LokasiSerializer(lokasi_list, many=True) - return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) - - -@api_view(['GET']) -@authentication_classes([]) -@permission_classes([]) -def lokasi_details(request, place_id): - try: - lokasi = Lokasi.objects.get(place_id=place_id) - serializer = LokasiSerializer(lokasi) - return JsonResponse(serializer.data, safe=False, status=HTTPStatus.OK) - except ObjectDoesNotExist as err: - return JsonResponse({'response': str(err)}, status=HTTPStatus.NOT_FOUND) - - -@api_view(['POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -def add_lokasi(request): - data = request.data - serializer = LokasiSerializer(data=data) - if serializer.is_valid(): - serializer.save() - return JsonResponse(serializer.data, status=HTTPStatus.CREATED) - else: - return JsonResponse(serializer.errors, - status=HTTPStatus.BAD_REQUEST) + def get_queryset(self): + return self.queryset -@api_view(['PUT']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -def update_lokasi_details(request, place_id): - try: - lokasi = Lokasi.objects.get(place_id=place_id) - lokasi.no_telp = request.data['no_telp'] - lokasi.counter = F('counter') + 1 - lokasi.save() - return JsonResponse({'response': "phone changed to {}".format(lokasi.no_telp)}, - status=HTTPStatus.ACCEPTED) - except KeyError as missing_key: - return JsonResponse({'response': missing_key_message(str(missing_key))}, - status=HTTPStatus.BAD_REQUEST) + def list(self, request): + queryset = self.get_queryset().order_by('-counter')[:3] + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data, status=HTTPStatus.OK) + def create(self, request): + data = request.data + serializer = self.get_serializer(data=data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=HTTPStatus.ACCEPTED) + try: + place_id = data.get("place_id") + lokasi = Lokasi.objects.get(place_id=place_id) + lokasi.counter += 1 + lokasi.save() + serializer = self.get_serializer(lokasi) + return Response(serializer.data, status=HTTPStatus.ACCEPTED) + except Lokasi.DoesNotExist as _: + pass + return Response(serializer.errors, status=HTTPStatus.BAD_REQUEST) @api_view(['GET']) @authentication_classes([]) -- GitLab From 48a56b537de6f64081c52a697e28fcdc8ac4b40d Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Wed, 31 Mar 2021 10:38:49 +0700 Subject: [PATCH 27/29] fix database issue with postgres's vector search from PBI-4 --- layanan_khusus/tests.py | 18 +++++++++++++----- pplbackend/settings.py | 29 ++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/layanan_khusus/tests.py b/layanan_khusus/tests.py index d45706c..835bd7c 100644 --- a/layanan_khusus/tests.py +++ b/layanan_khusus/tests.py @@ -4,6 +4,7 @@ from django.test import TestCase, Client from django.db.utils import IntegrityError from django.contrib.auth.models import User from django.urls import path, include, reverse +from dynamic_db_router import in_database from .models import Sekolah, Penyandang, Komunitas from .serializers import SekolahSerializer, KomunitasSerializer import django @@ -172,7 +173,10 @@ class LayananKhususModelTest(TestCase): self.assertNotEqual(count, 0) +@in_database('postgres') class LayananKhususViewsTest(TestCase): + databases = 'postgres' + urlpatterns = [ path('layanan-khusus/', include('layanan_khusus.urls')), ] @@ -294,7 +298,11 @@ class LayananKhususViewsTest(TestCase): reverse('detail-komunitas', kwargs={'id_komunitas': 1})) self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) + +@in_database('postgres') class LayananKhususSearchTest(TestCase): + databases = 'postgres' + urlpatterns = [ path('layanan-khusus/', include('layanan_khusus.urls')), ] @@ -312,7 +320,7 @@ class LayananKhususSearchTest(TestCase): def test_can_search_komunitas_json(self): komunitas_setup() response = Client().get("%s?search=Disabilitas" % reverse('pencarian')) - + content = json.loads(response.content.decode('utf-8')) expected_json = {'0': { 'name': MOCK_KOMUNITAS[NAME], @@ -327,7 +335,7 @@ class LayananKhususSearchTest(TestCase): def test_can_search_sekolah_json(self): sekolah_setup() response = Client().get("%s?search=Sekolah" % reverse('pencarian')) - + content = json.loads(response.content.decode('utf-8')) expected_json = {'0': { 'name': MOCK_SEKOLAH[NAME], @@ -345,13 +353,13 @@ class LayananKhususSearchTest(TestCase): def test_cannot_post_search_sekolah(self): sekolah_setup() response = Client().post("%s?search=Sekolah" % reverse('pencarian')) - + self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_cannot_post_search_komunitas(self): komunitas_setup() response = Client().post("%s?search=Disabilitas" % reverse('pencarian')) - + self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED) def test_search_komunitas_and_sekolah(self): @@ -364,7 +372,7 @@ class LayananKhususSearchTest(TestCase): sekolah_setup() komunitas_setup() response = Client().get("%s?search=Jakarta" % reverse('pencarian')) - + content = json.loads(response.content.decode('utf-8')) expected_json = {'0': { 'name': MOCK_SEKOLAH[NAME], diff --git a/pplbackend/settings.py b/pplbackend/settings.py index b8e8c2b..48109fe 100644 --- a/pplbackend/settings.py +++ b/pplbackend/settings.py @@ -95,30 +95,41 @@ WSGI_APPLICATION = 'pplbackend.wsgi.application' # Database # https://docs.djangoproject.com/en/3.0/ref/settings/#databases +DATABASE_ROUTERS = [ + 'dynamic_db_router.DynamicDbRouter', +] + DEFAULT_DATABASE = { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } +POSTGRES_DATABASE = { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': os.getenv('DB_NAME'), + 'USER': os.getenv('DB_USER'), + 'PASSWORD': os.getenv('DB_PASSWORD'), + 'HOST': os.getenv('DB_HOST'), + 'PORT': os.getenv('DB_PORT'), +} + DATABASES = { - 'default': DEFAULT_DATABASE + 'default': DEFAULT_DATABASE, } # custom database host, overriding the default if os.getenv('DB_HOST') is not None: - DATABASES['default'] = { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': os.getenv('DB_NAME'), - 'USER': os.getenv('DB_USER'), - 'PASSWORD': os.getenv('DB_PASSWORD'), - 'HOST': os.getenv('DB_HOST'), - 'PORT': os.getenv('DB_PORT'), - } + DATABASES['default'] = POSTGRES_DATABASE # use default database when running test if 'test' in sys.argv: DATABASES['default'] = DEFAULT_DATABASE + # database postgres context used in + # - layanan_khusus.tests.LayananKhususSearchTest + # - layanan_khusus.tests.LayananKhususViewsTest + DATABASES['postgres'] = POSTGRES_DATABASE + # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators -- GitLab From f1d80dcd38b6ffb11aab9d22515dd8766b185931 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Wed, 31 Mar 2021 17:44:26 +0700 Subject: [PATCH 28/29] [RED] revert database engine set back to postgresql - PBI 4 fungsi pencarian needs search vector which only support postgresql - use relative to object's id instead of hardcoded to prevent error because of postgresql's strange behavior - new assertion with expected IntegrityError when testing models - removed some variable like NOT_NULL_constraint_failed_message and UNIQUE_constraint_failed_message --- informasi_fasilitas/test_base.py | 2 -- informasi_fasilitas/test_models.py | 25 +++++++++------------ informasi_fasilitas/test_views_fasilitas.py | 8 ++++--- informasi_fasilitas/test_views_komentar.py | 2 +- layanan_khusus/tests.py | 21 +++++++---------- registrasi/tests.py | 4 ++-- 6 files changed, 26 insertions(+), 36 deletions(-) diff --git a/informasi_fasilitas/test_base.py b/informasi_fasilitas/test_base.py index a759bf4..5db110f 100644 --- a/informasi_fasilitas/test_base.py +++ b/informasi_fasilitas/test_base.py @@ -19,8 +19,6 @@ from pplbackend.utils import get_client_login_with_user class InformasiFasilitasTest(TestCase): - not_null_constraint_failed_message = 'NOT NULL constraint failed' - unique_constraint_failed_message = 'UNIQUE constraint failed' put_content_type = "application/x-www-form-urlencoded" mock_user_test = { diff --git a/informasi_fasilitas/test_models.py b/informasi_fasilitas/test_models.py index d6b73aa..b155d23 100644 --- a/informasi_fasilitas/test_models.py +++ b/informasi_fasilitas/test_models.py @@ -7,15 +7,14 @@ from .models import Lokasi, Fasilitas, Komentar, Likes, Dislikes class InformasiFasilitasModelTest(InformasiFasilitasTest): def test_models_lokasi_not_created_place_id_should_unique(self): - with self.assertRaises(IntegrityError) as err_message: + with self.assertRaises(IntegrityError) as ex: Lokasi.objects.create( place_id=self.mock_lokasi_test['place_id'], ) Lokasi.objects.create( place_id=self.mock_lokasi_test['place_id'], ) - self.assertTrue(str(err_message.exception).startswith( - self.unique_constraint_failed_message)) + self.assertEqual(ex.expected, IntegrityError) def test_models_timestamp_not_changing_after_save(self): lokasi = self.create_lokasi_test() @@ -37,11 +36,10 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): (lokasi.timestamp.strftime("%d %b %Y %X"), lokasi.place_id)) def test_models_fasilitas_not_created(self): - with self.assertRaises(IntegrityError) as err_message: + with self.assertRaises(IntegrityError) as ex: obj = Fasilitas(lokasi=None) obj.save() - self.assertTrue(str(err_message.exception).startswith( - self.not_null_constraint_failed_message)) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_fasilitas(self): self.create_fasilitas_test() @@ -49,11 +47,10 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): self.assertNotEqual(count, 0) def test_models_komentar_not_created(self): - with self.assertRaises(IntegrityError) as err_message: + with self.assertRaises(IntegrityError) as ex: obj = Komentar(fasilitas=None) obj.save() - self.assertTrue(str(err_message.exception).startswith( - self.not_null_constraint_failed_message)) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_komentar(self): self.create_komentar_test() @@ -61,11 +58,10 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): self.assertNotEqual(count, 0) def test_models_dislikes_not_created(self): - with self.assertRaises(IntegrityError) as err_message: + with self.assertRaises(IntegrityError) as ex: obj = Dislikes(fasilitas=None) obj.save() - self.assertTrue(str(err_message.exception).startswith( - self.not_null_constraint_failed_message)) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_dislikes(self): self.create_dislikes_test() @@ -73,11 +69,10 @@ class InformasiFasilitasModelTest(InformasiFasilitasTest): self.assertNotEqual(count, 0) def test_models_likes_not_created(self): - with self.assertRaises(IntegrityError) as err_message: + with self.assertRaises(IntegrityError) as ex: obj = Likes(fasilitas=None) obj.save() - self.assertTrue(str(err_message.exception).startswith( - self.not_null_constraint_failed_message)) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_likes(self): self.create_likes_test() diff --git a/informasi_fasilitas/test_views_fasilitas.py b/informasi_fasilitas/test_views_fasilitas.py index 8328504..9fa8193 100644 --- a/informasi_fasilitas/test_views_fasilitas.py +++ b/informasi_fasilitas/test_views_fasilitas.py @@ -31,6 +31,8 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): } ) + self.maxDiff = 1e5 + def test_can_get_list_fasilitas(self): response = Client().get(self.get_list_fasilitas_url) self.assertEqual(response.status_code, HTTPStatus.OK) @@ -39,7 +41,7 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): response = Client().get(self.get_list_fasilitas_url) content = json.loads(response.content.decode('utf-8')) expected_json = { - '1': { + str(self.fasilitas.id): { 'id': self.fasilitas.id, 'place_id': self.fasilitas.lokasi.place_id, 'deskripsi': self.fasilitas.deskripsi, @@ -56,7 +58,7 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): } time = self.fasilitas.date_time.strftime("%d-%m-%Y %H:%M:%S") - expected_json["1"]['date_time'] = time + expected_json[str(self.fasilitas.id)]['date_time'] = time self.assertEqual(content, expected_json) def test_cannot_post_list_fasilitas(self): @@ -148,5 +150,5 @@ class FasilitasRelatedViewTest(InformasiFasilitasViewTest): 'tag': 'KR', } response = self.client.put(self.update_fasilitas_url, data=send_data, - content_type='application/x-www-form-urlencoded') + content_type=self.put_content_type) self.assertEqual(response.status_code, HTTPStatus.ACCEPTED) diff --git a/informasi_fasilitas/test_views_komentar.py b/informasi_fasilitas/test_views_komentar.py index 8a90fd8..b81ba1f 100644 --- a/informasi_fasilitas/test_views_komentar.py +++ b/informasi_fasilitas/test_views_komentar.py @@ -44,7 +44,7 @@ class KomentarRelatedViewTest(InformasiFasilitasViewTest): response_json = response_decode(response) expected_json = { - '1': { + str(komentar.id): { 'id': komentar.id, 'deskripsi': komentar.deskripsi, 'creator': komentar.user.last_name, diff --git a/layanan_khusus/tests.py b/layanan_khusus/tests.py index 17424e7..2acae84 100644 --- a/layanan_khusus/tests.py +++ b/layanan_khusus/tests.py @@ -1,15 +1,12 @@ import json from http import HTTPStatus -from django.test import TestCase, Client +from django.test import TestCase, Client, override_settings +from django.conf import settings from django.db.utils import IntegrityError from django.contrib.auth.models import User from django.urls import path, include, reverse -from dynamic_db_router import in_database from .models import Sekolah, Penyandang, Komunitas from .serializers import SekolahSerializer, KomunitasSerializer -import django - -NOT_NULL_CONSTRAINT_FAILED_MESSAGE = 'NOT NULL constraint failed' ID = 'id' NAME = 'name' @@ -143,7 +140,7 @@ class LayananKhususModelTest(TestCase): with self.assertRaises(IntegrityError) as ex: obj = Sekolah(name=None) obj.save() - self.assertTrue(ex.expected == django.db.utils.IntegrityError) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_sekolah(self): sekolah_setup() @@ -154,7 +151,7 @@ class LayananKhususModelTest(TestCase): with self.assertRaises(IntegrityError) as ex: obj = Penyandang(name=None) obj.save() - self.assertTrue(ex.expected == django.db.utils.IntegrityError) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_penyandang(self): penyandang_setup() @@ -165,7 +162,7 @@ class LayananKhususModelTest(TestCase): with self.assertRaises(IntegrityError) as ex: obj = Komunitas(name=None) obj.save() - self.assertTrue(ex.expected == django.db.utils.IntegrityError) + self.assertEqual(ex.expected, IntegrityError) def test_models_create_new_komunitas(self): komunitas_setup() @@ -173,9 +170,7 @@ class LayananKhususModelTest(TestCase): self.assertNotEqual(count, 0) -@in_database('postgres') class LayananKhususViewsTest(TestCase): - databases = 'postgres' urlpatterns = [ path('layanan-khusus/', include('layanan_khusus.urls')), @@ -248,7 +243,9 @@ class LayananKhususViewsTest(TestCase): response = client.post(reverse('register-penyandang-disabilitas'), MOCK_PENYANDANG) content = json.loads(response.content.decode("utf-8")) - expected_json = MOCK_PENYANDANG + expected_json = MOCK_PENYANDANG.copy() + del content['id'] + del expected_json['id'] self.assertEqual(content, expected_json) def test_cannot_get_register_penyandang(self): @@ -298,9 +295,7 @@ class LayananKhususViewsTest(TestCase): self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND) -@in_database('postgres') class LayananKhususSearchTest(TestCase): - databases = 'postgres' urlpatterns = [ path('layanan-khusus/', include('layanan_khusus.urls')), diff --git a/registrasi/tests.py b/registrasi/tests.py index 0bef2cb..32f7ca8 100644 --- a/registrasi/tests.py +++ b/registrasi/tests.py @@ -14,7 +14,7 @@ class RegistrationTest(TestCase): self.assertNotEqual(count, 0) def test_user_model_not_created(self): - with self.assertRaises(IntegrityError) as error: + with self.assertRaises(IntegrityError) as ex: obj = BisaGoUser(user=None) obj.save() - self.assertTrue(str(error.exception).startswith('NOT NULL constraint failed')) + self.assertEqual(ex.expected, IntegrityError) -- GitLab From 03b471aba808bb05fe4edd3fe92e23d7c2742318 Mon Sep 17 00:00:00 2001 From: ariqbasyar Date: Wed, 31 Mar 2021 17:46:45 +0700 Subject: [PATCH 29/29] [GREEN] implemented postgresql config for test (see f1d80dc) - added new .gitignore from gitignore.io - bring back postgresql service to gitlab ci --- .gitignore | 283 +++++++++++++++++++++++++++++++++++++++-- .gitlab-ci.yml | 9 ++ pplbackend/settings.py | 22 ++-- 3 files changed, 296 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 3293b85..fe7c927 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,281 @@ -# Environments -env/ -#misc +# Created by https://www.toptal.com/developers/gitignore/api/django,vscode,python,virtualenv,dotenv,sonarqube,vim +# Edit at https://www.toptal.com/developers/gitignore?templates=django,vscode,python,virtualenv,dotenv,sonarqube,vim + +### Django ### +*.log +*.pot +*.pyc __pycache__/ +local_settings.py db.sqlite3 -.coverage +db.sqlite3-journal +media + +# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ +# in your Git repository. Update and uncomment the following line accordingly. +# /staticfiles/ + +### Django.Python Stack ### +# Byte-compiled / optimized / DLL files +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports htmlcov/ -static/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo + +# Django stuff: + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* +.env.export + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy .mypy_cache/ -#vscode +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +*.DS_Store #file properties cache/storage on macOS +Thumbs.db #thumbnail cache on Windows + +# profiling data +.prof + + +### dotenv ### +.env + +### Python ### +# Byte-compiled / optimized / DLL files + +# C extensions + +# Distribution / packaging + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. + +# Installer logs + +# Unit test / coverage reports + +# Translations + +# Django stuff: + +# Flask stuff: + +# Scrapy stuff: + +# Sphinx documentation + +# PyBuilder + +# Jupyter Notebook + +# IPython + +# pyenv + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. + +# poetry + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow + +# Celery stuff + +# SageMath parsed files + +# Environments +# .env + +# Spyder project settings + +# Rope project settings + +# mkdocs documentation + +# mypy + +# Pyre type checker + +# pytype static type analyzer + +# operating system-related files + +# profiling data + + +### SonarQube ### +# SonarQube ignore files. +# +# https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner +# Sonar Scanner working directories +.sonar/ +.scannerwork/ + +# http://www.sonarlint.org/commandline/ +# SonarLint working directories, configuration files (including credentials) +.sonarlint/ + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### VirtualEnv ### +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +.venv +pip-selfcheck.json + +### vscode ### .vscode/ -#intellij -.idea/ +*.code-workspace + +# End of https://www.toptal.com/developers/gitignore/api/django,vscode,python,virtualenv,dotenv,sonarqube,vim diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f7cad09..a43b2e1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,12 @@ +variables: + POSTGRES_DB: $DB_NAME + POSTGRES_USER: $DB_USER + POSTGRES_PASSWORD: $DB_PASSWORD + POSTGRES_HOST_AUTH_METHOD: trust + +services: + - postgres:12.2-alpine + stages: - test - linter diff --git a/pplbackend/settings.py b/pplbackend/settings.py index b8e8c2b..2f9ee04 100644 --- a/pplbackend/settings.py +++ b/pplbackend/settings.py @@ -100,24 +100,26 @@ DEFAULT_DATABASE = { 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } +POSTGRES_DATABASE = { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': os.getenv('DB_NAME'), + 'USER': os.getenv('DB_USER'), + 'PASSWORD': os.getenv('DB_PASSWORD'), + 'HOST': os.getenv('DB_HOST'), + 'PORT': os.getenv('DB_PORT'), +} + DATABASES = { 'default': DEFAULT_DATABASE } # custom database host, overriding the default if os.getenv('DB_HOST') is not None: - DATABASES['default'] = { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': os.getenv('DB_NAME'), - 'USER': os.getenv('DB_USER'), - 'PASSWORD': os.getenv('DB_PASSWORD'), - 'HOST': os.getenv('DB_HOST'), - 'PORT': os.getenv('DB_PORT'), - } + DATABASES['default'] = POSTGRES_DATABASE -# use default database when running test +# use postgresql database when running test if 'test' in sys.argv: - DATABASES['default'] = DEFAULT_DATABASE + DATABASES['default'] = POSTGRES_DATABASE # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators -- GitLab