Compare commits

..

No commits in common. "master" and "CICD" have entirely different histories.
master ... CICD

30 changed files with 237 additions and 392 deletions

13
.env
View File

@ -1,13 +0,0 @@
# Environment Variables used to configure WMGZON
# ENVIRON: Used to incidicate the type of environment the app is running in.
# DEFAULT = prod
ENVIRON=prod
# ENVIRON=test
# App secret used to encrpy client-side cookies
APPSECRET=test
# Filestore for product photos
FILESTORE=static/assets/img/products/

1
.gitignore vendored
View File

@ -17,6 +17,7 @@ cicd/runner-data
instance/* instance/*
!instance/.gitignore !instance/.gitignore
.webassets-cache .webassets-cache
.env
### Flask.Python Stack ### ### Flask.Python Stack ###
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files

View File

@ -1,4 +1,7 @@
variables: variables:
APPSECRET: "test"
ENVIRON: "test"
FILESTORE: "static/assets/img/products/"
DOCKER_HOST: tcp://docker:2375/ DOCKER_HOST: tcp://docker:2375/
DOCKER_DRIVER: overlay2 DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "" DOCKER_TLS_CERTDIR: ""
@ -16,13 +19,13 @@ deploy:
stage: deploy stage: deploy
tags: tags:
- docker - docker
image: docker:latest image: docker:20.10.16
services: services:
- name: docker:dind - name: docker:20.10.16-dind
alias: docker alias: docker
script: script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker buildx build --platform linux/amd64 -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA . - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA $CI_REGISTRY_IMAGE:latest - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest - docker push $CI_REGISTRY_IMAGE:latest

View File

@ -40,25 +40,4 @@ In order to run the web app, simply use the command
docker-compose up -d docker-compose up -d
``` ```
to run the container in a detatched mode. to run the container in a detatched mode.
### Container
Alternatively, to deploy the app from the lastest published container:
```yml
version: '3.8'
services:
wmgzon:
container_name: "wmgzon"
image: lukeelse/wmgzon:latest
environment:
- FILESTORE=static/assets/img/products/
tty: true
ports:
- "8080:8080"
volumes:
- ./files:/app/$FILESTORE
restart: unless-stopped
```

2
app.py
View File

@ -1,6 +1,5 @@
from flask import Flask from flask import Flask
from os import environ from os import environ
from dotenv import load_dotenv
from controllers.web.endpoints import blueprint from controllers.web.endpoints import blueprint
''' '''
@ -12,7 +11,6 @@ from controllers.web.endpoints import blueprint
app: Flask = Flask(__name__) app: Flask = Flask(__name__)
# Set app secret key to sign session cookies # Set app secret key to sign session cookies
load_dotenv()
secret_key = environ.get("APPSECRET") secret_key = environ.get("APPSECRET")
if secret_key is None: if secret_key is None:
# NO Secret Key set! # NO Secret Key set!

View File

@ -1,13 +0,0 @@
version: '3.8'
services:
wmgzon:
container_name: "wmgzon"
image: lukeelse/wmgzon:latest
tty: true
ports:
- "8080:8080"
volumes:
- ./files:/app/static/assets/img/products/
- ./data:/app/data/
restart: unless-stopped

View File

@ -1,15 +1,15 @@
version: '3.8' version: '3.8'
services: services:
gitlab-runner-python: gitlab-runner-python:
image: gitlab/gitlab-runner:latest image: gitlab/gitlab-runner:latest
volumes: volumes:
- ./runner-data-python:/etc/gitlab-runner - ./runner-data-python:/etc/gitlab-runner
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped restart: unless-stopped
gitlab-runner-docker: gitlab-runner-docker:
image: gitlab/gitlab-runner:latest image: gitlab/gitlab-runner:latest
volumes: volumes:
- ./runner-data-docker:/etc/gitlab-runner - ./runner-data-docker:/etc/gitlab-runner
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped restart: unless-stopped

View File

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

View File

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

View File

@ -5,12 +5,14 @@ services:
container_name: "wmgzon" container_name: "wmgzon"
build: . build: .
environment: environment:
- APPSECRET=test
- ENVIRON=test - ENVIRON=test
#- ENVIRON=prod
- FILESTORE=static/assets/img/products/
tty: true tty: true
ports: ports:
- "8080:8080" - "8080:8080"
# Dev container -> Copies complete root directory with latest
# code into the container
volumes: volumes:
- .:/app - .:/app
restart: unless-stopped restart: unless-stopped

View File

@ -4,5 +4,4 @@ WORKDIR /app
RUN pip install -r requirements.txt RUN pip install -r requirements.txt
COPY . /app COPY . /app
RUN chmod +x scripts/run.bash RUN chmod +x scripts/run.bash
EXPOSE 8080 CMD ["bash", "scripts/run.bash"]
ENTRYPOINT ["scripts/run.bash"]

View File

@ -1 +0,0 @@
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") print("Table creation complete")
# Populate with test data if we are in Test Mode # Populate with test data if we are in Test Mode
if os.environ.get("ENVIRON", "test") == "test": if os.environ.get("ENVIRON") == "test":
sql = open("scripts/test_data.sql", "r") sql = open("scripts/test_data.sql", "r")
conn.executescript(sql.read()) conn.executescript(sql.read())
@ -37,21 +37,15 @@ def create_connection(path: str, filename: str):
conn.close() conn.close()
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") == "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)
# Ensure a directory is created given a path to it # Ensure a directory is created given a path to it
if __name__ == "__main__": dir = r"./data/"
run() db_name = r"wmgzon.db"
# Check for test environ
if os.environ.get("ENVIRON") == "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)

View File

@ -1,26 +1,24 @@
#!/bin/bash #! /bin/bash
pytest --disable-warnings pytest --disable-warnings
python ./scripts/create_database.py python ./scripts/create_database.py
EXPECTED_VALUE="test" EXPECTED_VALUE="test"
if [ -z "$ENVIRON" ]; then if [ -z "$ENVIRON" ]; then
echo "ENVIRON is not set." echo "ENVIRON is not set."
echo "Using DEFAULT Environ Value: prod"
export ENVIRON=prod
fi
echo "ENVIRON is set to: $ENVIRON"
# Trim leading and trailing whitespaces
ACTUAL_VALUE=$(echo "$ENVIRON" | tr -d '[:space:]')
if [ "$ACTUAL_VALUE" = "$EXPECTED_VALUE" ]; then
echo "Launching DEV Server"
python ./app.py
else else
echo "Launching PROD Server" echo "ENVIRON is set to: $ENVIRON"
waitress-serve --host 0.0.0.0 app:app
# Trim leading and trailing whitespaces
ACTUAL_VALUE=$(echo "$ENVIRON" | tr -d '[:space:]')
if [ "$ACTUAL_VALUE" = "$EXPECTED_VALUE" ]; then
echo "Launching DEV Server"
python ./app.py
else
echo "Launching PROD Server"
waitress-serve --host 0.0.0.0 app:app
fi
fi fi

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 ("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 ("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 ("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", 129.99, 1, 4, 3, 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.", 129.99, 1, 4, 2, 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", 129.99, 1, 4, 1, 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 ("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 ("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, 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 ("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 ("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", 17.37, 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", 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", 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", 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", 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", 17.37, 1, 3, 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.", 17.37, 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, 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 ("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", 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, 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 ("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 ("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 ("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", 6.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", 6.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 ("Single Turbo", "turbo.bmp", "this is a product", 69.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 ("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 ("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 ("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", 6.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", 69.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.", 6.99, 1, 6, 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", 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, 6, datetime());
INSERT INTO Views (userID, productID, viewDate) VALUES (1, 1, 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"> <a href="{{ url_for('main.products.product', id=product.id) }}" class="product product-link">
<div class="product-title">{{product.name}}</div> <div class="product-title">{{product.name}}</div>
<div class="product-content-container"> <div class="product-content-container">
<img class="product-image-preview" src="{{ url_for('static', filename='assets/img/products/' + product.image) }}" alt="Brake Disks" loading="lazy"/> <img class="product-image-preview" src="{{ url_for('static', filename='assets/img/products/' + product.image) }}" alt="Brake Disks" />
<div class="product-details"> <div class="product-details">
<div class="product-price">£{{product.cost}}</div> <div class="product-price">£{{product.cost}}</div>

View File

@ -0,0 +1,8 @@
import os
# Setup test environment variables
# 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"

View File

@ -1,39 +0,0 @@
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

@ -1,67 +0,0 @@
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

@ -0,0 +1,41 @@
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

@ -1,41 +0,0 @@
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

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

View File

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

View File

@ -1,4 +1,5 @@
from tests.unit.database.database_test_class import TestDatabase import pytest
import sqlite3
from datetime import datetime from datetime import datetime
from controllers.database.product import ProductController from controllers.database.product import ProductController
from models.products.product import Product from models.products.product import Product
@ -14,52 +15,53 @@ product = Product(
1 1
) )
# Tests a new product can be created
class TestProduct(TestDatabase):
""" Class that controls the
testing of the WMGZON database
"""
def test_create_product(self): def test_create_product():
"""Tests a new product can be created""" db = ProductController()
db = ProductController() db.create(product)
db.create(product)
def test_duplicate_product(self): # Tests the database maintains integrity when we try
""" Tests the database maintains integrity when we try # and add a product with the same details
and add a product with the same detail
"""
self.test_create_product()
def test_search_category(self):
""" Tests that products can be refined by category """
db = ProductController()
# Check each category for correct amount of test products def test_duplicate_product():
assert len(db.read_all("Car Parts")) == 9 + \ test_create_product()
2 # Added in previous tests
assert len(db.read_all("Books")) == 9
assert db.read_all("Phones") is None
def test_search_term(self): # Tests that products can be refined by category
""" Tests that products can be refined by search term"""
db = ProductController()
# Check each search term for correct amount of test products
assert len(db.read_all(search_term="Alloy")) == 9
assert len(db.read_all("Car Parts", "tur")) == 2
assert len(db.read_all(search_term="fold")) == 8
assert db.read_all(search_term="Twin") is None
def test_read_product(self): def test_search_category():
""" Test we the same product details get returned from the database """ db = ProductController()
db = ProductController()
# Test the same product is returned # Check each category for correct amount of test products
new_product = db.read("product") assert len(db.read_all("Car Parts")) == 9 + 2 # Added in previous tests
assert isinstance(new_product, list) assert len(db.read_all("Books")) == 9
assert isinstance(new_product[0], Product) assert db.read_all("Phones") is None
# Update the ID on the item as database assigns new id
product.id = new_product[0].id # Tests that products can be refined by search term
assert new_product[0].__dict__ == product.__dict__ def test_search_term():
db = ProductController()
# Check each search term for correct amount of test products
assert len(db.read_all(search_term="Alloy")) == 9
assert len(db.read_all("Car Parts", "tur")) == 2
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():
db = ProductController()
# Test the same product is returned
new_product = db.read("product")
assert isinstance(new_product, list)
assert isinstance(new_product[0], Product)
# Update the ID on the item as database assigns new id
product.id = new_product[0].id
assert new_product[0].__dict__ == product.__dict__

View File

@ -1,6 +1,5 @@
import pytest import pytest
import sqlite3 import sqlite3
from tests.unit.database.database_test_class import TestDatabase
from controllers.database.user import UserController from controllers.database.user import UserController
from models.users.customer import Customer from models.users.customer import Customer
from models.users.seller import Seller from models.users.seller import Seller
@ -23,53 +22,53 @@ seller = Seller(
"987654321" "987654321"
) )
# Tests a new user can be created
class TestUsers(TestDatabase):
""" Class to encapsulate all of the user
datbase tests
"""
def test_create_user(self): def test_create_user():
""" Tests a new user can be created """ db = UserController()
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():
db = UserController()
with pytest.raises(sqlite3.IntegrityError):
db.create(customer) db.create(customer)
def test_duplicate_user(self): # Test we the same user details get returned from the database
""" 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)
def test_read_user(self):
""" Test we the same user details get returned from the database """
db = UserController()
# Test the same user is returned def test_read_user():
user = db.read(customer.username) db = UserController()
assert isinstance(user, Customer)
# Update the ID on the item as database assigns new id # Test the same user is returned
customer.id = user.id user = db.read(customer.username)
assert user.__dict__ == customer.__dict__ assert isinstance(user, Customer)
def test_create_seller(self): # Update the ID on the item as database assigns new id
""" Tests a new seller can be created """ customer.id = user.id
db = UserController() assert user.__dict__ == customer.__dict__
db.create(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 # Tests a new seller can be created
user = db.read(seller.username) def test_create_seller():
assert isinstance(user, Seller) db = UserController()
db.create(seller)
# Update the ID on the item as database assigns new id # Test that the same seller details get returned from the database
seller.id = user.id
user.store = ""
assert user.__dict__ == seller.__dict__ def test_read_seller():
db = UserController()
# Test the same user is returned
user = db.read(seller.username)
assert isinstance(user, Seller)
# Update the ID on the item as database assigns new id
seller.id = user.id
user.store = ""
assert user.__dict__ == seller.__dict__

View File

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

View File

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

1
venv.bat Normal file
View File

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