Merge branch 'e2eTesting' into 'master'

E2e testing

See merge request u5500327/wmgzon!2
This commit is contained in:
u5500327 2024-02-26 08:11:55 +00:00
commit 95f543ca1d
20 changed files with 304 additions and 197 deletions

View File

@ -12,7 +12,7 @@ class DatabaseController(ABC):
__db_name = "wmgzon.db"
# Use test file if necessary
if os.environ.get("ENVIRON") == "test":
if os.environ.get("ENVIRON", "test") == "test":
__db_name = "test_" + __db_name
__sqlitefile = __data_dir + __db_name

View File

@ -2,7 +2,6 @@
in the web app
"""
from flask import Blueprint
from flask import render_template, request, flash, session, redirect, url_for
from controllers.database.stats import StatsController
from controllers.database.product import ProductController
@ -48,8 +47,8 @@ def view_product_stats(id: int):
# Recent Views
product_view_frequency_data = dict(map(
lambda k, v: (k, random.randint(70, 100)),
# lambda k, v: (k, len(v)),
# lambda k, v: (k, random.randint(70, 100)),
lambda k, v: (k, len(v)),
data.keys(),
data.values()
))

1
pep8.bat Normal file
View File

@ -0,0 +1 @@
autopep8 --exclude '*/.*/*' --in-place --recursive .

Binary file not shown.

View File

@ -26,7 +26,7 @@ def create_connection(path: str, filename: str):
print("Table creation complete")
# Populate with test data if we are in Test Mode
if os.environ.get("ENVIRON") == "test":
if os.environ.get("ENVIRON", "test") == "test":
sql = open("scripts/test_data.sql", "r")
conn.executescript(sql.read())
@ -37,15 +37,21 @@ def create_connection(path: str, filename: str):
conn.close()
# Ensure a directory is created given a path to it
dir = r"./data/"
db_name = r"wmgzon.db"
def run():
""" Create the database for the application"""
dir = r"./data/"
db_name = r"wmgzon.db"
# Check for test environ
if os.environ.get("ENVIRON") == "test":
# Check for test environ
if os.environ.get("ENVIRON", "test") == "test":
# Remove the original test database
print("TEST ENVIRONMENT ACTIVE")
db_name = "test_" + db_name
remove_file(dir + db_name)
create_connection(dir, db_name)
create_connection(dir, db_name)
# Ensure a directory is created given a path to it
if __name__ == "__main__":
run()

View File

@ -8,34 +8,34 @@ INSERT INTO Products (name, image, description, cost, sellerID, categoryID, quan
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, quantityAvailable, postedDate) VALUES ("Exhaust Manifold", "manifold.bmp", "This is a super cool product that can be installed into your car to take the gasses from the inside all the way to the outside. Mad I know.", 20.99, 1, 2, 4, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, quantityAvailable, postedDate) VALUES ("12' Brake Disks", "brake-disks.bmp", "this is a product", 20.99, 1, 2, 3, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, quantityAvailable, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 20.99, 1, 3, 9, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, quantityAvailable, postedDate) VALUES ("Single Turbo", "turbo.bmp", "this is a product", 20.99, 1, 4, 3, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, quantityAvailable, postedDate) VALUES ("Exhaust Manifold", "manifold.bmp", "This is a super cool product that can be installed into your car to take the gasses from the inside all the way to the outside. Mad I know.", 20.99, 1, 4, 2, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, quantityAvailable, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 20.99, 1, 4, 1, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, quantityAvailable, postedDate) VALUES ("Single Turbo", "turbo.bmp", "this is a product", 129.99, 1, 4, 3, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, quantityAvailable, postedDate) VALUES ("Exhaust Manifold", "manifold.bmp", "This is a super cool product that can be installed into your car to take the gasses from the inside all the way to the outside. Mad I know.", 129.99, 1, 4, 2, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, quantityAvailable, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 129.99, 1, 4, 1, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, quantityAvailable, postedDate) VALUES ("Exhaust Manifold", "manifold.bmp", "This is a super cool product that can be installed into your car to take the gasses from the inside all the way to the outside. Mad I know.", 20.99, 1, 6, 7, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, quantityAvailable, postedDate) VALUES ("Single Turbo", "turbo.bmp", "this is a product", 20.99, 1, 6, 1232, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 20.99, 1, 1, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Exhaust Manifold", "manifold.bmp", "This is a super cool product that can be installed into your car to take the gasses from the inside all the way to the outside. Mad I know.", 20.99, 1, 1, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Single Turbo", "turbo.bmp", "this is a product", 20.99, 1, 1, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 20.99, 1, 2, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("12' Brake Disks", "brake-disks.bmp", "this is a product", 20.99, 1, 2, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 20.99, 1, 3, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Exhaust Manifold", "manifold.bmp", "This is a super cool product that can be installed into your car to take the gasses from the inside all the way to the outside. Mad I know.", 20.99, 1, 4, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Single Turbo", "turbo.bmp", "this is a product", 17.37, 1, 1, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 17.37, 1, 2, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("12' Brake Disks", "brake-disks.bmp", "this is a product", 17.37, 1, 2, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 17.37, 1, 3, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Exhaust Manifold", "manifold.bmp", "This is a super cool product that can be installed into your car to take the gasses from the inside all the way to the outside. Mad I know.", 17.37, 1, 4, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Single Turbo", "turbo.bmp", "this is a product", 20.99, 1, 4, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("12' Brake Disks", "brake-disks.bmp", "this is a product", 20.99, 1, 4, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("12' Brake Disks", "brake-disks.bmp", "this is a product", 6.99, 1, 4, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("12' Brake Disks", "brake-disks.bmp", "this is a product", 20.99, 1, 6, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("12' Brake Disks", "brake-disks.bmp", "this is a product", 20.99, 1, 6, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 20.99, 1, 1, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Exhaust Manifold", "manifold.bmp", "This is a super cool product that can be installed into your car to take the gasses from the inside all the way to the outside. Mad I know.", 20.99, 1, 1, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("12' Brake Disks", "brake-disks.bmp", "this is a product", 20.99, 1, 1, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Single Turbo", "turbo.bmp", "this is a product", 20.99, 1, 2, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Single Turbo", "turbo.bmp", "this is a product", 20.99, 1, 2, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("12' Brake Disks", "brake-disks.bmp", "this is a product", 6.99, 1, 1, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Single Turbo", "turbo.bmp", "this is a product", 6.99, 1, 2, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Single Turbo", "turbo.bmp", "this is a product", 69.99, 1, 2, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 20.99, 1, 3, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("12' Brake Disks", "brake-disks.bmp", "this is a product", 20.99, 1, 4, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 20.99, 1, 4, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Single Turbo", "turbo.bmp", "this is a product", 20.99, 1, 4, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Exhaust Manifold", "manifold.bmp", "This is a super cool product that can be installed into your car to take the gasses from the inside all the way to the outside. Mad I know.", 20.99, 1, 6, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 6.99, 1, 4, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Single Turbo", "turbo.bmp", "this is a product", 69.99, 1, 4, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("Exhaust Manifold", "manifold.bmp", "This is a super cool product that can be installed into your car to take the gasses from the inside all the way to the outside. Mad I know.", 6.99, 1, 6, datetime());
INSERT INTO Products (name, image, description, cost, sellerID, categoryID, postedDate) VALUES ("17' Alloy Wheels", "alloy.bmp", "These super stylish alloys offer a fresh trendy look for your car. Whether a brand new Mercedes or a niffty little banger, it will uplift the vehicle 10-fold", 20.99, 1, 6, datetime());
INSERT INTO Views (userID, productID, viewDate) VALUES (1, 1, datetime());

View File

@ -7,7 +7,7 @@
<a href="{{ url_for('main.products.product', id=product.id) }}" class="product product-link">
<div class="product-title">{{product.name}}</div>
<div class="product-content-container">
<img class="product-image-preview" src="{{ url_for('static', filename='assets/img/products/' + product.image) }}" alt="Brake Disks" />
<img class="product-image-preview" src="{{ url_for('static', filename='assets/img/products/' + product.image) }}" alt="Brake Disks" loading="lazy"/>
<div class="product-details">
<div class="product-price">£{{product.cost}}</div>

View File

@ -1,10 +0,0 @@
import os
from dotenv import load_dotenv
# Setup test environment variables
load_dotenv()
# Capture environment variables at start of testing so we can
# Monitor current setting during tests
old_env = os.environ.get("ENVIRON")
os.environ["ENVIRON"] = "test"

39
tests/base_test.py Normal file
View File

@ -0,0 +1,39 @@
import pytest
import os
from scripts.create_database import run
from app import app
from dotenv import load_dotenv
from flask.testing import FlaskClient
class TestBase:
""" Base test class that ensures environment variables
and test setup is complete before any other unit tests are run
"""
def setup_class(self):
""" Setup class level resources or configurations
Setup test environment variables
"""
load_dotenv()
# Capture environment variables at start of testing so we can
# Monitor current setting during tests
self.old_env = os.environ.get("ENVIRON")
os.environ["ENVIRON"] = "test"
run()
def teardown_class(self):
""" Teardown class level resources or configurations """
os.environ["ENVIRON"] = self.old_env
@pytest.fixture(scope="class")
def test_client(self) -> FlaskClient:
""" Enables tests to create requests to the web app
"""
os.environ['CONFIG_TYPE'] = 'config.TestingConfig'
with app.test_client() as testing_client:
with app.app_context():
yield testing_client

View File

View File

@ -0,0 +1,67 @@
from bs4 import BeautifulSoup
from flask.testing import FlaskClient
from tests.base_test import TestBase
class TestFunctionalRequirements(TestBase):
def test_product_filters(self, test_client: FlaskClient):
base_url = '/products/Car Part?filter={{FILTER}}'
# Make get request for all products in car parts
response = test_client.get(
base_url.replace("{{FILTER}}", "Price: High -> Low")
)
assert response.status_code == 200
# Extract first and last product
first, last = self.get_first_and_last_product(response.data)
# Get Prices of each
first_cost = float(self.get_tag_value(
str(first), "product-price").replace('£', '')
)
last_cost = float(self.get_tag_value(
str(last), "product-price").replace('£', '')
)
# Check filter is working
assert first_cost >= last_cost
# =============================================
# Test the reverse of the previous filter
# Get html data
response = test_client.get(
base_url.replace("{{FILTER}}", "Price: Low -> High")
)
assert response.status_code == 200
# Extract first and last product
first, last = self.get_first_and_last_product(response.data)
# Get Prices of each
first_cost = float(self.get_tag_value(
str(first), "product-price").replace('£', '')
)
last_cost = float(self.get_tag_value(
str(last), "product-price").replace('£', '')
)
# Check filter is working
assert first_cost <= last_cost
def get_tag_value(self, html: str, class_name: str) -> str:
""" Returns the value of a given tag in a html element """
soup = BeautifulSoup(html, features='html.parser')
return str(soup.find("div", {'class': class_name}).contents[0])
def get_first_and_last_product(self, html: str) -> (str, str):
""" Returns the first and last product on the page """
soup = BeautifulSoup(html, features='html.parser')
products = soup.findAll("a", {"class": "product"})
assert len(products) > 0
first = products[0]
last = products[-1]
return (first, last)

View File

@ -1,41 +0,0 @@
from app import app
from flask.testing import FlaskClient
import pytest
import os
@pytest.fixture(scope="module")
def test_client() -> FlaskClient:
""" Test that required environment variables are set
ahead of runtime
"""
os.environ['CONFIG_TYPE'] = 'config.TestingConfig'
with app.test_client() as testing_client:
with app.app_context():
yield testing_client
def test_homepage(test_client: FlaskClient):
""" Tests that the main homepage loads correctly
once the '/' endpoint is hit
"""
response = test_client.get('/')
assert response.status_code == 302
response = test_client.get('/products')
assert response.status_code == 308
def test_products(test_client: FlaskClient):
""" Tests that a product page is displayed when
hitting one of the product endpoints
"""
response = test_client.get('/products/2')
assert response.status_code == 200
response = test_client.get('/products/50')
assert response.status_code == 302
response = test_client.get('/products/Books')
assert response.status_code == 200

View File

@ -0,0 +1,41 @@
from flask.testing import FlaskClient
from tests.base_test import TestBase
class TestPages(TestBase):
""" Test class that encapsulates tests for
the main pages on the site
"""
def test_homepage(self, test_client: FlaskClient):
""" Tests that the main homepage loads correctly
once the '/' endpoint is hit
"""
response = test_client.get('/')
assert response.status_code == 302
response = test_client.get('/products')
assert response.status_code == 308
def test_products(self, test_client: FlaskClient):
""" Tests that a product page is displayed when
hitting one of the product endpoints
"""
response = test_client.get('/products/2')
assert response.status_code == 200
response = test_client.get('/products/50')
assert response.status_code == 302
response = test_client.get('/products/Books')
assert response.status_code == 200
def test_admin(self, test_client: FlaskClient):
""" Tests that the admin pages can be reached and redirect
upon reaching
"""
response = test_client.get('/admin/users/')
assert response.status_code == 302
response = test_client.get('/admin/products/')
assert response.status_code == 302

View File

@ -1,3 +0,0 @@
# Ensure test environment is set before using
# Runs the database creation scripts
import scripts.create_database

View File

@ -0,0 +1,8 @@
from tests.base_test import TestBase
class TestDatabase(TestBase):
""" Class that controls the
testing of the WMGZON database
"""
pass

View File

@ -1,5 +1,4 @@
import pytest
import sqlite3
from tests.unit.database.database_test_class import TestDatabase
from datetime import datetime
from controllers.database.product import ProductController
from models.products.product import Product
@ -15,34 +14,35 @@ product = Product(
1
)
# Tests a new product can be created
class TestProduct(TestDatabase):
""" Class that controls the
testing of the WMGZON database
"""
def test_create_product():
def test_create_product(self):
"""Tests a new product can be created"""
db = ProductController()
db.create(product)
# Tests the database maintains integrity when we try
# and add a product with the same details
def test_duplicate_product(self):
""" Tests the database maintains integrity when we try
and add a product with the same detail
"""
self.test_create_product()
def test_duplicate_product():
test_create_product()
# Tests that products can be refined by category
def test_search_category():
def test_search_category(self):
""" Tests that products can be refined by category """
db = ProductController()
# Check each category for correct amount of test products
assert len(db.read_all("Car Parts")) == 9 + 2 # Added in previous tests
assert len(db.read_all("Car Parts")) == 9 + \
2 # Added in previous tests
assert len(db.read_all("Books")) == 9
assert db.read_all("Phones") is None
# Tests that products can be refined by search term
def test_search_term():
def test_search_term(self):
""" Tests that products can be refined by search term"""
db = ProductController()
# Check each search term for correct amount of test products
@ -51,10 +51,8 @@ def test_search_term():
assert len(db.read_all(search_term="fold")) == 8
assert db.read_all(search_term="Twin") is None
# Test we the same product details get returned from the database
def test_read_product():
def test_read_product(self):
""" Test we the same product details get returned from the database """
db = ProductController()
# Test the same product is returned

View File

@ -1,5 +1,6 @@
import pytest
import sqlite3
from tests.unit.database.database_test_class import TestDatabase
from controllers.database.user import UserController
from models.users.customer import Customer
from models.users.seller import Seller
@ -22,26 +23,27 @@ seller = Seller(
"987654321"
)
# Tests a new user can be created
class TestUsers(TestDatabase):
""" Class to encapsulate all of the user
datbase tests
"""
def test_create_user():
def test_create_user(self):
""" Tests a new user can be created """
db = UserController()
db.create(customer)
# Tests the database maintains integrity when we try
# and add a user with the same details
def test_duplicate_user():
def test_duplicate_user(self):
""" Tests the database maintains integrity when we try
and add a user with the same details
"""
db = UserController()
with pytest.raises(sqlite3.IntegrityError):
db.create(customer)
# Test we the same user details get returned from the database
def test_read_user():
def test_read_user(self):
""" Test we the same user details get returned from the database """
db = UserController()
# Test the same user is returned
@ -52,16 +54,15 @@ def test_read_user():
customer.id = user.id
assert user.__dict__ == customer.__dict__
# Tests a new seller can be created
def test_create_seller():
def test_create_seller(self):
""" Tests a new seller can be created """
db = UserController()
db.create(seller)
# Test that the same seller details get returned from the database
def test_read_seller():
def test_read_seller(self):
""" Test that the same seller details get
returned from the database
"""
db = UserController()
# Test the same user is returned

View File

@ -1,31 +1,31 @@
from tests.base_test import TestBase
from os import environ
from warnings import warn
from tests import old_env
# Tests environment variables used within the projects domain are
# set in the correct environment
VARS = ['ENVIRON', 'APPSECRET', 'FILESTORE']
ENV_STATES = ['test', 'prod']
def test_env_vars():
class TestEnv(TestBase):
""" Tests environment variables used within the projects domain are
set in the correct environment
"""
VARS = ['ENVIRON', 'APPSECRET', 'FILESTORE']
ENV_STATES = ['test', 'prod']
def test_env_vars(self):
""" Test that required environment variables are set
ahead of runtime
"""
for var in VARS:
for var in self.VARS:
env = environ.get(var)
# Check to see what variable we are comparing
if env is None:
warn(f"Variable {var} is not set!")
def test_environment_var_state():
def test_environment_var_state(self):
""" Tests that the 'ENVIRON' Environment variable
is in a correct state
"""
var = old_env
var = self.old_env
assert var is not None
assert (var in ENV_STATES)
assert (var in self.ENV_STATES)

View File

@ -1,9 +1,11 @@
import pycodestyle
from tests.base_test import TestBase
# Tests files to ensure they conform to pep8 standards
def test_pep8_conformance():
class TestPep8(TestBase):
def test_pep8_conformance(self):
"""Test that we conform to PEP8."""
pep8style = pycodestyle.StyleGuide()
dirs = ["./controllers", "./models", "./scripts", "./tests", "./utils"]

View File

@ -1 +0,0 @@
./.venv/Scripts/activate