diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f13db82 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,21 @@ +language: python +python: + - "3.6.4" +install: + - pip install pytest + - pip install -r requirements.txt + - pip install coveralls + - pip install pytest-cov +services: + - postgresql +# env: +# global: +# - app.config["TESTING"] = True +before_script: + - psql -c 'create database manager;' -U postgres + - psql -c 'create database test_db;' -U postgres +script: + - python -m pytest + - pytest --cov +after_success: + - coveralls \ No newline at end of file diff --git a/README.md b/README.md index af6e23a..f63c386 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ - +[![Build Status](https://travis-ci.org/oma0256/store-manager-api.svg?branch=ft-delete-category)](https://travis-ci.org/oma0256/store-manager-api) +[![Coverage Status](https://coveralls.io/repos/github/oma0256/store-manager-api/badge.svg?branch=ft-delete-category)](https://coveralls.io/github/oma0256/store-manager-api) +[![Maintainability](https://api.codeclimate.com/v1/badges/3303ae1c369c0bd693df/maintainability)](https://codeclimate.com/github/oma0256/store-manager-api/maintainability) # Store Manager Api Store Manager is a web application that helps store owners manage sales and product inventory records. This application is meant for use in a single store. diff --git a/api/__init__.py b/api/__init__.py index 428b362..e94ebde 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -6,11 +6,11 @@ from flask_jwt_extended import JWTManager app = Flask(__name__) -app.secret_key = 'super secret key' # Setup flask-jwt-extended app.config['JWT_SECRET_KEY'] = 'sajsvhca' +app.config.from_object("config.DevelopmentConfig") jwt = JWTManager(app) from api import views diff --git a/api/models.py b/api/models.py index 65ebf47..aeb45d7 100644 --- a/api/models.py +++ b/api/models.py @@ -37,3 +37,11 @@ def __init__(self, sale_id, cart_items, self.cart_items = cart_items self.attendant = attendannt self.total = total + +class Category: + """ + Define Category structure + """ + def __init__(self, name, description=None): + self.name = name + self.description = description diff --git a/api/views.py b/api/views.py index 90ae50f..ce37f88 100644 --- a/api/views.py +++ b/api/views.py @@ -7,7 +7,7 @@ from flask_jwt_extended import (create_access_token, jwt_required, get_jwt_identity) -from api.models import Product, Sale, User +from api.models import Product, Sale, User, Category from api.__init__ import app from api.utils.validators import (validate_product, validate_login_data, @@ -34,7 +34,6 @@ def post(self): Function to perform user login """ # Get data sent - db_conn = DB() data = request.get_json() # Get attributes of the data sent email = data.get("email") @@ -76,8 +75,6 @@ def post(self): """ Function to add a store attendant """ - db_conn = DB() - # Get logged in user current_user = get_jwt_identity() loggedin_user = db_conn.get_user(current_user) @@ -130,8 +127,6 @@ def post(self): """ Handles creating of a product """ - db_conn = DB() - # Get logged in user current_user = get_jwt_identity() loggedin_user = db_conn.get_user(current_user) @@ -170,7 +165,6 @@ def get(self, product_id=None): """ Get all products """ - db_conn = DB() # Check if an id has been passed if product_id: product = db_conn.get_product_by_id(int(product_id)) @@ -180,12 +174,14 @@ def get(self, product_id=None): "error": "This product does not exist" }), 404 return jsonify({ - "message": "Product returned successfully" + "message": "Product returned successfully", + "product": product }) # Get all products - db_conn.get_products() + products = db_conn.get_products() return jsonify({ - "message": "Products returned successfully" + "message": "Products returned successfully", + "products": products }) @jwt_required @@ -193,8 +189,6 @@ def put(self, product_id): """ Funtion to modify a product """ - db_conn = DB() - # Get logged in user current_user = get_jwt_identity() loggedin_user = db_conn.get_user(current_user) @@ -232,8 +226,6 @@ def delete(self, product_id): """ Funtion to delete a product """ - db_conn = DB() - # Get logged in user current_user = get_jwt_identity() loggedin_user = db_conn.get_user(current_user) @@ -267,8 +259,6 @@ def post(self): """ Method to create a sale record """ - db_conn = DB() - # Get logged in user current_user = get_jwt_identity() loggedin_user = db_conn.get_user(current_user) @@ -284,7 +274,7 @@ def post(self): total = 0 product_names = "" for cart_item in cart_items: - product_id = cart_item.get("product") + product_id = cart_item.get("product_id") quantity = cart_item.get("quantity") # validate each product res = validate_cart_item(product_id, quantity) @@ -311,7 +301,6 @@ def get(self, sale_id=None): """ Perform GET on sale records """ - db_conn = DB() # Get current user current_user = get_jwt_identity() user = db_conn.get_user(current_user) @@ -327,12 +316,14 @@ def get(self, sale_id=None): # run if it's a store owner if user["is_admin"]: return jsonify({ - "message": "Sale record returned successfully" + "message": "Sale record returned successfully", + "sale_record": sale_record }) # run if it's a store attendant if sale_record["attendant"] == db_conn.get_user(current_user)["id"]: return jsonify({ - "message": "Sale record returned successfully" + "message": "Sale record returned successfully", + "sale_record": sale_record }) return jsonify({"error": "You didn't make this sale"}), 403 # run if request is for all sale records and if it's a store @@ -340,9 +331,108 @@ def get(self, sale_id=None): if user["is_admin"]: sale_records = db_conn.get_sale_records() return jsonify({ - "message": "Sale records returned successfully" + "message": "Sale records returned successfully", + "sale_records": sale_records }) - return jsonify({"error": "Please login as a store owner"}), 403 + # run if request is for all sale records and if it's a store + # attendant + if not user["is_admin"]: + sale_records = db_conn.get_sale_records_user(user["id"]) + return jsonify({ + "message": "Sale records returned successfully", + "sale_records": sale_records + }) + + +class CategoryView(MethodView): + + @jwt_required + def post(self): + current_user = get_jwt_identity() + user = db_conn.get_user(current_user) + if not user["is_admin"]: + return jsonify({ + "error": "Please login as a store owner" + }), 403 + data = request.get_json() + name = data.get("name") + description = data.get("description") + if not name: + return jsonify({ + "error": "The category name is required" + }), 400 + category = db_conn.get_category_by_name(name) + if category: + return jsonify({ + "error": "Category with this name exists" + }), 400 + new_category = Category(name, description=description) + db_conn.add_category(new_category) + return jsonify({ + "message": "Successfully created product category" + }), 201 + + @jwt_required + def put(self, category_id): + """ + Function to modify a category + """ + # Get logged in user + current_user = get_jwt_identity() + loggedin_user = db_conn.get_user(current_user) + # # Check if it's not store owner + if not loggedin_user["is_admin"]: + return jsonify({ + "error": "Please login as a store owner" + }), 403 + + # Check if category exists + category = db_conn.get_category_by_id(int(category_id)) + if not category: + return jsonify({ + "error": "The category you're trying to modify doesn't exist" + }), 404 + + data = request.get_json() + # Get the fields which were sent + name = data.get("name") + description = data.get("description") + if not name: + return jsonify({ + "error": "The category name is required" + }), 400 + + # Modify category + db_conn.update_category(name, description, category_id) + return jsonify({ + "message": "Category updated successfully" + }) + + @jwt_required + def delete(self, category_id): + """ + Funtion to category a product + """ + # Get logged in user + current_user = get_jwt_identity() + loggedin_user = db_conn.get_user(current_user) + # Check if it's not store owner + if loggedin_user["is_admin"]: + # Check if category exists + category = db_conn.get_category_by_id(int(category_id)) + if not category: + return jsonify({ + "error": "Category you're trying to delete doesn't exist" + }), 404 + + # Delete category + db_conn.delete_category(int(category_id)) + return jsonify({ + "message": "Category has been deleted successfully" + }) + return jsonify({ + "error": "Please login as a store owner" + }), 403 # Map urls to view classes @@ -359,5 +449,10 @@ def get(self, sale_id=None): app.add_url_rule('/api/v2/sales', view_func=SaleView.as_view('sale_view'), methods=["GET","POST"]) -app.add_url_rule('/api/v1/sales/', +app.add_url_rule('/api/v2/sales/', view_func=SaleView.as_view('sale_view1'), methods=["GET"]) +app.add_url_rule('/api/v2/categories', + view_func=CategoryView.as_view('category_view'), + methods=["POST"]) +app.add_url_rule('/api/v2/categories/', + view_func=CategoryView.as_view('category_view1'), methods=["PUT", "DELETE"]) diff --git a/config.py b/config.py new file mode 100644 index 0000000..85f6a84 --- /dev/null +++ b/config.py @@ -0,0 +1,5 @@ +class TestConfig: + TESTING = True + +class DevelopmentConfig: + TESTING = False \ No newline at end of file diff --git a/db.py b/db.py index fe4f23a..463ff94 100644 --- a/db.py +++ b/db.py @@ -1,37 +1,48 @@ """ File to handle my database operations """ -""" -CREATE TABLE public.users( + +commands = ( + """ + CREATE TABLE IF NOT EXISTS public.users( id SERIAL PRIMARY KEY, first_name VARCHAR NOT NULL, last_name VARCHAR NOT NULL, email VARCHAR UNIQUE NOT NULL, password VARCHAR NOT NULL, is_admin BOOLEAN DEFAULT FALSE NOT NULL -); -""" -""" -CREATE TABLE public.products( - id SERIAL PRIMARY KEY, - name VARCHAR UNIQUE NOT NULL, - unit_cost INTEGER NOT NULL, - quantity INTEGER NOT NULL -); -""" -""" -CREATE TABLE public.sales( - id SERIAL PRIMARY KEY, - attendant INTEGER REFERENCES public.users(id) ON DELETE CASCADE NOT NULL, - cart_items VARCHAR NOT NULL, - total VARCHAR NOT NULL -); -""" -""" -INSERT INTO public.users(first_name, last_name, email, password, is_admin) VALUES () -""" + ) + """, + """ + CREATE TABLE IF NOT EXISTS public.products( + id SERIAL PRIMARY KEY, + name VARCHAR UNIQUE NOT NULL, + unit_cost INTEGER NOT NULL, + quantity INTEGER NOT NULL + ) + """, + """ + CREATE TABLE IF NOT EXISTS public.sales( + id SERIAL PRIMARY KEY, + attendant INTEGER REFERENCES public.users(id) ON DELETE CASCADE NOT NULL, + cart_items VARCHAR NOT NULL, + total VARCHAR NOT NULL + ) + """, + """ + CREATE TABLE IF NOT EXISTS public.categories( + id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + description VARCHAR NULL + ) + """, + """ + INSERT INTO public.users(first_name, last_name, email, password, is_admin) SELECT 'admin', 'owner', 'admin@email.com', 'pbkdf2:sha256:50000$q5STunEW$09107a77f6c6a7d7042aa1d1e5755736ea128a2eeac0219724bfeddf91bfd88b', True WHERE NOT EXISTS (SELECT * FROM public.users WHERE email='admin@email.com') + """ + ) + import psycopg2 -from psycopg2.extras import DictCursor +from psycopg2.extras import RealDictCursor from werkzeug.security import generate_password_hash from api.__init__ import app @@ -46,67 +57,99 @@ def __init__(self): password="pass1234") else: self.conn = psycopg2.connect(host="localhost", - database="manager", + database="test_db", user="postgres", password="pass1234") - self.cur = self.conn.cursor(cursor_factory=DictCursor) + self.cur = self.conn.cursor(cursor_factory=RealDictCursor) self.conn.autocommit = True except: print("Failed to connect") + for command in commands: + self.cur.execute(command) def create_admin(self): """Function to create an admin""" - self.cur.execute("INSERT INTO public.users(first_name, last_name, email, password, is_admin) VALUES (%s, %s, %s, %s, %s)", + self.cur.execute("INSERT INTO users(first_name, last_name, email, password, is_admin) VALUES (%s, %s, %s, %s, %s)", ("admin", "owner", "admin@email.com", generate_password_hash("pass1234"), True)) def get_user(self, email): - self.cur.execute("SELECT * FROM public.users WHERE email=%s", (email,)) + self.cur.execute("SELECT * FROM users WHERE email=%s", (email,)) return self.cur.fetchone() def create_user(self, user): - self.cur.execute("INSERT INTO public.users(first_name, last_name, email, password) VALUES (%s, %s, %s, %s)", + self.cur.execute("INSERT INTO users(first_name, last_name, email, password) VALUES (%s, %s, %s, %s)", (user.first_name, user.last_name, user.email, user.password)) def delete_attendants(self): - self.cur.execute("DELETE FROM public.users WHERE is_admin=%s", (False,)) + self.cur.execute("DELETE FROM users WHERE is_admin=%s", (False,)) def delete_products(self): - self.cur.execute("TRUNCATE public.products") + self.cur.execute("TRUNCATE products") def add_product(self, product): - self.cur.execute("INSERT INTO public.products(name, unit_cost, quantity) VALUES (%s, %s, %s)", + self.cur.execute("INSERT INTO products(name, unit_cost, quantity) VALUES (%s, %s, %s)", (product.name, product.unit_cost, product.quantity)) def get_product_by_name(self, name): - self.cur.execute("SELECT * FROM public.products WHERE name=%s", (name,)) + self.cur.execute("SELECT * FROM products WHERE name=%s", (name,)) return self.cur.fetchone() def get_products(self): - self.cur.execute("SELECT * FROM public.products") + self.cur.execute("SELECT * FROM products") return self.cur.fetchall() def get_product_by_id(self, product_id): - self.cur.execute("SELECT * FROM public.products WHERE id=%s", (product_id,)) + self.cur.execute("SELECT * FROM products WHERE id=%s", (product_id,)) return self.cur.fetchone() def update_product(self, name, unit_cost, quantity, product_id): - self.cur.execute("UPDATE public.products SET name=%s, unit_cost=%s, quantity=%s WHERE id=%s", + self.cur.execute("UPDATE products SET name=%s, unit_cost=%s, quantity=%s WHERE id=%s", (name, unit_cost, quantity, product_id)) def delete_product(self, product_id): - self.cur.execute("DELETE FROM public.products WHERE id=%s", (product_id,)) + self.cur.execute("DELETE FROM products WHERE id=%s", (product_id,)) def delete_sales(self): - self.cur.execute("TRUNCATE public.sales") + self.cur.execute("TRUNCATE sales") def add_sale(self, attendant, products, total): - self.cur.execute("INSERT INTO public.sales(attendant, cart_items, total) VALUES (%s, %s, %s)", + self.cur.execute("INSERT INTO sales(attendant, cart_items, total) VALUES (%s, %s, %s)", (attendant, products, total)) def get_sale_records(self): - self.cur.execute("SELECT * FROM public.sales") + self.cur.execute("SELECT * FROM sales") return self.cur.fetchall() def get_single_sale(self, sale_id): - self.cur.execute("SELECT * FROM public.sales WHERE id=%s", (sale_id,)) + self.cur.execute("SELECT * FROM sales WHERE id=%s", (sale_id,)) return self.cur.fetchone() + + def get_sale_records_user(self, user_id): + self.cur.execute("SELECT * FROM sales WHERE attendant=%s", (user_id,)) + return self.cur.fetchall() + + def get_category_by_name(self, name): + self.cur.execute("SELECT * FROM categories WHERE name=%s", (name,)) + return self.cur.fetchone() + + def add_category(self, category): + self.cur.execute("INSERT INTO categories(name, description) VALUES (%s, %s)", + (category.name, category.description)) + + def delete_categories(self): + self.cur.execute("TRUNCATE categories") + + def get_category_by_id(self, category_id): + self.cur.execute("SELECT * FROM categories WHERE id=%s", (category_id,)) + return self.cur.fetchone() + + def update_category(self, name, description, category_id): + self.cur.execute("UPDATE categories SET name=%s, description=%s WHERE id=%s", + (name, description, category_id)) + + def get_categories(self): + self.cur.execute("SELECT * FROM categories") + return self.cur.fetchall() + + def delete_category(self, category_id): + self.cur.execute("DELETE FROM categories WHERE id=%s", (category_id,)) diff --git a/tests/test_auth.py b/tests/test_auth.py index d13c2e2..5c6043c 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -7,11 +7,14 @@ from db import DB -app.config['TESTING'] = True class TestStoreOwnerAuth(unittest.TestCase): """ Test store owner authentication """ + def create_app(self): + app.config.from_object('config.TestConfig') + return app + def setUp(self): self.app = app.test_client() self.login_data = { @@ -80,6 +83,10 @@ class TestSoreAttendantauth(unittest.TestCase): """ Test store attendant authentication """ + def create_app(self): + app.config.from_object('config.TestConfig') + return app + def setUp(self): self.app = app.test_client() self.reg_data = { @@ -261,4 +268,4 @@ def test_login_valid_data(self): data=json.dumps(self.login_data)) res_data = json.loads(res.data) self.assertEqual(res.status_code, 200) - self.assertIsNotNone(res_data["token"]) + self.assertIsNotNone(res_data["token"]) \ No newline at end of file diff --git a/tests/test_category.py b/tests/test_category.py new file mode 100644 index 0000000..455c7a7 --- /dev/null +++ b/tests/test_category.py @@ -0,0 +1,256 @@ +import unittest +import json +from api import views +from api.__init__ import app +from db import DB + + +class TestProductView(unittest.TestCase): + """ + Class to test product view + """ + def create_app(self): + app.config.from_object('config.TestConfig') + return app + + def setUp(self): + self.app = app.test_client() + self.db_conn = DB() + self.admin_login = { + "email": "admin@email.com", + "password": "pass1234" + } + self.reg_data = { + "first_name": "joe", + "last_name": "doe", + "email": "joe@email.com", + "password": "pass1234", + "confirm_password": "pass1234" + } + self.login_data = { + "email": "joe@email.com", + "password": "pass1234" + } + self.headers = {"Content-Type": "application/json"} + response = self.app.post("/api/v2/auth/login", + headers=self.headers, + data=json.dumps(self.admin_login)) + self.access_token = json.loads(response.data)["token"] + self.category = { + "name": "Tech", + "description": "This is tech" + } + + def test_create_category_with_valid_data(self): + self.headers["Authorization"] = "Bearer " + self.access_token + res = self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + res_data = json.loads(res.data) + expected_output = { + "message": "Successfully created product category" + } + self.assertEqual(res.status_code, 201) + self.assertEqual(res_data, expected_output) + + def test_create_category_with_missing_fields(self): + self.headers["Authorization"] = "Bearer " + self.access_token + self.category["name"] = "" + res = self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + res_data = json.loads(res.data) + expected_output = { + "error": "The category name is required" + } + self.assertEqual(res.status_code, 400) + self.assertEqual(res_data, expected_output) + + def test_create_duplicate_category(self): + self.headers["Authorization"] = "Bearer " + self.access_token + self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + res = self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + res_data = json.loads(res.data) + expected_output = { + "error": "Category with this name exists" + } + self.assertEqual(res.status_code, 400) + self.assertEqual(res_data, expected_output) + + def test_create_category_authenticated_as_store_attendant(self): + self.headers["Authorization"] = "Bearer " + self.access_token + self.app.post("/api/v2/auth/signup", + headers=self.headers, + data=json.dumps(self.reg_data)) + res = self.app.post("/api/v2/auth/login", + headers=self.headers, + data=json.dumps(self.login_data)) + self.headers["Authorization"] = "Bearer " + json.loads(res.data)["token"] + res = self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + res_data = json.loads(res.data) + expected_output = { + "error": "Please login as a store owner" + } + self.assertEqual(res.status_code, 403) + self.assertEqual(res_data, expected_output) + + def test_create_category_unauthenticated(self): + res = self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + res_data = json.loads(res.data) + self.assertEqual(res.status_code, 401) + + def test_modify_category_as_store_owner(self): + """ + Test modify a category with valid data + """ + self.headers["Authorization"] = "Bearer " + self.access_token + self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + category_id = self.db_conn.get_categories()[0]["id"] + self.category["name"] = "svdkjsd" + res = self.app.put("/api/v2/categories/" + str(category_id), + headers=self.headers, + data=json.dumps(self.category)) + res_data = json.loads(res.data) + exepected_output = { + "message": "Category updated successfully" + } + self.assertEqual(res.status_code, 200) + self.assertEqual(res_data, exepected_output) + + def test_modify_category_as_store_attendant(self): + """ + Test modify a category as store attendant + """ + self.headers["Authorization"] = "Bearer " + self.access_token + self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + category_id = self.db_conn.get_categories()[0]["id"] + self.category["name"] = "svdkjsd" + self.app.post("/api/v2/auth/signup", + headers=self.headers, + data=json.dumps(self.reg_data)) + res = self.app.post("/api/v2/auth/login", + headers=self.headers, + data=json.dumps(self.login_data)) + self.headers["Authorization"] = "Bearer " + json.loads(res.data)["token"] + res = self.app.put("/api/v2/categories/" + str(category_id), + headers=self.headers, + data=json.dumps(self.category)) + res_data = json.loads(res.data) + exepected_output = { + "error": "Please login as a store owner" + } + self.assertEqual(res.status_code, 403) + self.assertEqual(res_data, exepected_output) + + def test_modify_category_non_existant(self): + """ + Test modify a category which doesn't exist + """ + self.headers["Authorization"] = "Bearer " + self.access_token + self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + self.category["name"] = "svdkjsd" + res = self.app.put("/api/v2/categories/553445354665789", + headers=self.headers, + data=json.dumps(self.category)) + res_data = json.loads(res.data) + exepected_output = { + "error": "The category you're trying to modify doesn't exist" + } + self.assertEqual(res.status_code, 404) + self.assertEqual(res_data, exepected_output) + + def test_modify_category_with_empty_value(self): + """ + Test modify a category with empty value + """ + self.headers["Authorization"] = "Bearer " + self.access_token + self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + category_id = self.db_conn.get_categories()[0]["id"] + self.category["name"] = "" + res = self.app.put("/api/v2/categories/" + str(category_id), + headers=self.headers, + data=json.dumps(self.category)) + res_data = json.loads(res.data) + exepected_output = { + "error": "The category name is required" + } + self.assertEqual(res.status_code, 400) + self.assertEqual(res_data, exepected_output) + + def test_delete_category_store_owner(self): + """ + Test delete a category as store owner + """ + self.headers["Authorization"] = "Bearer " + self.access_token + self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + category_id = self.db_conn.get_categories()[0]["id"] + res = self.app.delete("/api/v2/categories/" + str(category_id), + headers=self.headers, + data=json.dumps(self.category)) + res_data = json.loads(res.data) + exepected_output = { + "message": "Category has been deleted successfully" + } + self.assertEqual(res.status_code, 200) + self.assertEqual(res_data, exepected_output) + + def test_delete_category_store_attendant(self): + """ + Test delete a product as store attendant + """ + self.headers["Authorization"] = "Bearer " + self.access_token + self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + category_id = self.db_conn.get_categories()[0]["id"] + self.app.post("/api/v2/auth/signup", + headers=self.headers, + data=json.dumps(self.reg_data)) + res = self.app.post("/api/v2/auth/login", + headers=self.headers, + data=json.dumps(self.login_data)) + self.headers["Authorization"] = "Bearer " + json.loads(res.data)["token"] + res = self.app.delete("/api/v2/categories/" + str(category_id), + headers=self.headers) + res_data = json.loads(res.data) + exepected_output = { + "error": "Please login as a store owner" + } + self.assertEqual(res.status_code, 403) + self.assertEqual(res_data, exepected_output) + + def test_delete_category_non_existance(self): + """ + Test delete a category as store owner + """ + self.headers["Authorization"] = "Bearer " + self.access_token + self.app.post("/api/v2/categories", + headers=self.headers, + data=json.dumps(self.category)) + res = self.app.delete("/api/v2/categories/142556789068970", + headers=self.headers, + data=json.dumps(self.category)) + res_data = json.loads(res.data) + exepected_output = { + "error": "Category you're trying to delete doesn't exist" + } + self.assertEqual(res.status_code, 404) + self.assertEqual(res_data, exepected_output) diff --git a/tests/test_products.py b/tests/test_products.py index fc70565..1b5426f 100644 --- a/tests/test_products.py +++ b/tests/test_products.py @@ -9,11 +9,14 @@ from db import DB -app.config['TESTING'] = True class TestProductView(unittest.TestCase): """ Class to test product view """ + def create_app(self): + app.config.from_object('config.TestConfig') + return app + def setUp(self): self.db_conn = DB() self.app = app.test_client() @@ -137,9 +140,12 @@ def test_get_all_products_authenticated_user(self): data=json.dumps(self.product)) res = self.app.get("/api/v2/products", headers=self.headers) + product_id = self.db_conn.get_products()[0]["id"] + self.product["id"] = product_id res_data = json.loads(res.data) exepected_output = { - "message": "Products returned successfully" + "message": "Products returned successfully", + "products": [self.product] } self.assertEqual(res.status_code, 200) self.assertEqual(res_data, exepected_output) @@ -163,9 +169,12 @@ def test_get_single_product_authenticated(self): product_id = self.db_conn.get_products()[0]["id"] res = self.app.get("/api/v2/products/" + str(product_id), headers=self.headers) + product_id = self.db_conn.get_products()[0]["id"] + self.product["id"] = product_id res_data = json.loads(res.data) exepected_output = { - "message": "Product returned successfully" + "message": "Product returned successfully", + "product": self.product } self.assertEqual(res.status_code, 200) self.assertEqual(res_data, exepected_output) diff --git a/tests/test_sales.py b/tests/test_sales.py index 84efa72..5924b54 100644 --- a/tests/test_sales.py +++ b/tests/test_sales.py @@ -8,11 +8,14 @@ from db import DB -app.config['TESTING'] = True class TestSaleView(unittest.TestCase): """ Class to test sale view """ + def create_app(self): + app.config.from_object('config.TestConfig') + return app + def setUp(self): self.app = app.test_client() self.db_conn = DB() @@ -28,8 +31,8 @@ def setUp(self): "password": "pass1234" } self.admin_login = { - "email": "admin@email.com", - "password": "pass1234" + "email": "admin@email.com", + "password": "pass1234" } self.product = { "name": "Belt", @@ -37,7 +40,7 @@ def setUp(self): "quantity": 3 } self.cart_item = { - "product": 1, + "product_id": 1, "quantity": 1 } self.sale = { @@ -51,9 +54,8 @@ def setUp(self): def tearDown(self): db_conn = DB() - db_conn.delete_products() db_conn.delete_attendants() - db_conn.delete_sales() + db_conn.delete_categories() def test_create_sale_record_as_unauthenticated(self): """ @@ -74,14 +76,14 @@ def test_create_sale_with_missing_fields(self): headers=self.headers, data=json.dumps(self.product)) product_id = self.db_conn.get_products()[0]["id"] - self.cart_item["product"] = "" + self.cart_item["product_id"] = "" self.cart_items = [self.cart_item] self.app.post("/api/v2/auth/signup", headers=self.headers, data=json.dumps(self.reg_data)) res = self.app.post("/api/v2/auth/login", - headers=self.headers, - data=json.dumps(self.login_data)) + headers=self.headers, + data=json.dumps(self.login_data)) self.headers["Authorization"] = "Bearer " + json.loads(res.data)["token"] res = self.app.post("/api/v2/sales", headers=self.headers, @@ -102,14 +104,14 @@ def test_create_sale_with_valid_data(self): headers=self.headers, data=json.dumps(self.product)) product_id = self.db_conn.get_products()[0]["id"] - self.cart_item["product"] = product_id + self.cart_item["product_id"] = product_id self.cart_items = [self.cart_item] self.app.post("/api/v2/auth/signup", headers=self.headers, data=json.dumps(self.reg_data)) res = self.app.post("/api/v2/auth/login", - headers=self.headers, - data=json.dumps(self.login_data)) + headers=self.headers, + data=json.dumps(self.login_data)) self.headers["Authorization"] = "Bearer " + json.loads(res.data)["token"] res = self.app.post("/api/v2/sales", headers=self.headers, @@ -130,7 +132,7 @@ def test_create_sale_as_store_owner(self): headers=self.headers, data=json.dumps(self.product)) product_id = self.db_conn.get_products()[0]["id"] - self.cart_item["product"] = product_id + self.cart_item["product_id"] = product_id self.cart_items = [self.cart_item] res = self.app.post("/api/v2/sales", headers=self.headers, @@ -154,8 +156,8 @@ def test_create_sale_non_existant_product(self): headers=self.headers, data=json.dumps(self.reg_data)) res = self.app.post("/api/v2/auth/login", - headers=self.headers, - data=json.dumps(self.login_data)) + headers=self.headers, + data=json.dumps(self.login_data)) self.headers["Authorization"] = "Bearer " + json.loads(res.data)["token"] res = self.app.post("/api/v2/sales", headers=self.headers, @@ -194,37 +196,38 @@ def test_get_all_sale_records_authenticated_as_store_owner(self): headers=self.headers) res_data = json.loads(res.data) exepected_output = { - "message": "Sale records returned successfully" + "message": "Sale records returned successfully", + "sale_records": self.db_conn.get_sale_records() } self.assertEqual(res.status_code, 200) self.assertEqual(res_data, exepected_output) - def test_get_all_sale_records_authenticated_as_store_owner(self): - """ - Test getting all sale records logged in as store owner - """ - self.headers["Authorization"] = "Bearer " + self.access_token - self.app.post("/api/v2/products", - headers=self.headers, - data=json.dumps(self.product)) - self.app.post("/api/v2/auth/signup", - headers=self.headers, - data=json.dumps(self.reg_data)) - res = self.app.post("/api/v2/auth/login", - headers=self.headers, - data=json.dumps(self.login_data)) - self.headers["Authorization"] = "Bearer " + json.loads(res.data)["token"] - self.app.post("/api/v2/sales", - headers=self.headers, - data=json.dumps(self.sale)) - res = self.app.get("/api/v2/sales", - headers=self.headers) - res_data = json.loads(res.data) - exepected_output = { - "error": "Please login as a store owner" - } - self.assertEqual(res.status_code, 403) - self.assertEqual(res_data, exepected_output) + # def test_get_all_sale_records_authenticated_as_store_attendant(self): + # """ + # Test getting all sale records logged in as store owner + # """ + # self.headers["Authorization"] = "Bearer " + self.access_token + # self.app.post("/api/v2/products", + # headers=self.headers, + # data=json.dumps(self.product)) + # self.app.post("/api/v2/auth/signup", + # headers=self.headers, + # data=json.dumps(self.reg_data)) + # res = self.app.post("/api/v2/auth/login", + # headers=self.headers, + # data=json.dumps(self.login_data)) + # self.headers["Authorization"] = "Bearer " + json.loads(res.data)["token"] + # self.app.post("/api/v2/sales", + # headers=self.headers, + # data=json.dumps(self.sale)) + # res = self.app.get("/api/v2/sales", + # headers=self.headers) + # res_data = json.loads(res.data) + # exepected_output = { + # "error": "Please login as a store owner" + # } + # self.assertEqual(res.status_code, 403) + # self.assertEqual(res_data, exepected_output) def test_get_all_sale_records_unauthenticated_user(self): """ @@ -243,7 +246,7 @@ def test_get_sale_record_as_store_owner(self): headers=self.headers, data=json.dumps(self.product)) product_id = self.db_conn.get_products()[0]["id"] - self.cart_item["product"] = product_id + self.cart_item["product_id"] = product_id self.cart_items = [self.cart_item] self.app.post("/api/v2/auth/signup", headers=self.headers, @@ -255,17 +258,19 @@ def test_get_sale_record_as_store_owner(self): res = self.app.post("/api/v2/sales", headers=self.headers, data=json.dumps(self.sale)) - sale_id = self.db_conn.get_sale_records()[0]["id"] + sale_record = self.db_conn.get_sale_records()[0] + sale_id = sale_record["id"] response = self.app.post("/api/v2/auth/login", headers=self.headers, data=json.dumps(self.admin_login)) self.access_token = json.loads(response.data)["token"] self.headers["Authorization"] = "Bearer " + self.access_token - res = self.app.get("/api/v1/sales/" + str(sale_id), + res = self.app.get("/api/v2/sales/" + str(sale_id), headers=self.headers) res_data = json.loads(res.data) expected_output = { - "message": "Sale record returned successfully" + "message": "Sale record returned successfully", + "sale_record": sale_record } self.assertEqual(res.status_code, 200) self.assertEqual(res_data, expected_output) @@ -279,7 +284,7 @@ def test_get_sale_record_as_store_attendant(self): headers=self.headers, data=json.dumps(self.product)) product_id = self.db_conn.get_products()[0]["id"] - self.cart_item["product"] = product_id + self.cart_item["product_id"] = product_id self.cart_items = [self.cart_item] self.app.post("/api/v2/auth/signup", headers=self.headers, @@ -291,12 +296,14 @@ def test_get_sale_record_as_store_attendant(self): res = self.app.post("/api/v2/sales", headers=self.headers, data=json.dumps(self.sale)) - sale_id = self.db_conn.get_sale_records()[0]["id"] - res = self.app.get("/api/v1/sales/" + str(sale_id), + sale_record = self.db_conn.get_sale_records()[0] + sale_id = sale_record["id"] + res = self.app.get("/api/v2/sales/" + str(sale_id), headers=self.headers) res_data = json.loads(res.data) expected_output = { - "message": "Sale record returned successfully" + "message": "Sale record returned successfully", + "sale_record": sale_record } self.assertEqual(res.status_code, 200) self.assertEqual(res_data, expected_output) @@ -306,11 +313,11 @@ def test_get_sale_record_that_does_not_exist(self): Test getting a sale record that doesn't exist """ self.headers["Authorization"] = "Bearer " + self.access_token - res = self.app.get("/api/v1/sales/1", + res = self.app.get("/api/v2/sales/1", headers=self.headers) res_data = json.loads(res.data) expected_output = { "error": "Sale record with this id doesn't exist" } self.assertEqual(res.status_code, 404) - self.assertEqual(res_data, expected_output) + self.assertEqual(res_data, expected_output) \ No newline at end of file