diff --git a/app/__init__.py b/app/__init__.py index 4ab3975b8..8f89d9ee1 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -8,6 +8,7 @@ migrate = Migrate() load_dotenv() + def create_app(test_config=None): app = Flask(__name__) app.url_map.strict_slashes = False @@ -21,7 +22,6 @@ def create_app(test_config=None): app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( "SQLALCHEMY_TEST_DATABASE_URI") - # import models for Alembic Setup from app.models.customer import Customer from app.models.video import Video @@ -31,6 +31,12 @@ def create_app(test_config=None): db.init_app(app) migrate.init_app(app, db) - #Register Blueprints Here + # Register Blueprints Here + from .video_routes import video_bp + app.register_blueprint(video_bp) + from .customer_routes import customers_bp + app.register_blueprint(customers_bp) + from .rental_routes import rentals_bp + app.register_blueprint(rentals_bp) - return app \ No newline at end of file + return app diff --git a/app/customer_routes.py b/app/customer_routes.py new file mode 100644 index 000000000..90caf77d4 --- /dev/null +++ b/app/customer_routes.py @@ -0,0 +1,95 @@ +from app import db +from app.models.customer import Customer +from flask import Blueprint, jsonify, request +from datetime import datetime + +customers_bp = Blueprint("customers_bp", __name__, url_prefix="/customers") + + +@customers_bp.route("", methods=["POST"]) +def customer_create(): + request_body = request.get_json() + + if "name" not in request_body or "phone" not in request_body or "postal_code" not in request_body: + response_body ={} + if "name" not in request_body: + response_body["details"] = "Request body must include name." + elif "phone" not in request_body: + response_body["details"] = "Request body must include phone." + elif "postal_code" not in request_body: + response_body["details"] = "Request body must include postal_code." + return jsonify(response_body), 400 + + new_customer = Customer( + name=request_body["name"], + phone=request_body["phone"], + postal_code=request_body["postal_code"], + + ) + db.session.add(new_customer) + db.session.commit() + + return jsonify({"id": new_customer.customer_id}), 201 + + +@customers_bp.route("", methods=["GET"]) +def handle_customers(): + customers = Customer.query.all() + response_body = [] + for customer in customers: + response_body.append(customer.customer_dict()) + + return jsonify(response_body), 200 + + +@customers_bp.route("/", methods=["GET"]) +def customer_get(customer_id): + try: + customer = Customer.query.get(customer_id) + except: + return jsonify({"message": f"Customer {customer_id} was not found"}), 400 + + if customer is None: + return jsonify({"message": f"Customer {customer_id} was not found"}), 404 + + return jsonify(customer.customer_dict()), 200 + + return jsonify(customer.customer_dict()), 200 + +# WORK IN PROGRESS +@customers_bp.route("/", methods=["PUT"]) +def customer_put(customer_id): + customer = Customer.query.get(customer_id) + if customer == None: + return jsonify({"message": f"Customer {customer_id} was not found"}), 404 + + request_body = request.get_json() + if "name" not in request_body or "phone" not in request_body or "postal_code" not in request_body: + return jsonify(), 400 + + customer.name = request_body["name"] + customer.phone = request_body["phone"] + customer.postal_code = request_body["postal_code"] + + if "name" in request_body or "phone" in request_body or "postal_code" in request_body: + response_body ={} + response_body["name"] = customer.name + response_body["phone"] = customer.phone + response_body["postal_code"] = customer.postal_code + + db.session.commit() + + return jsonify(response_body), 200 + + +@customers_bp.route("/", methods=["DELETE"]) +def customer_delete(customer_id): + customer = Customer.query.get(customer_id) + + if customer == None: + return jsonify({"message": f"Customer {customer_id} was not found"}), 404 + + db.session.delete(customer) + db.session.commit() + + return jsonify({"id": customer.customer_id}), 200 \ No newline at end of file diff --git a/app/models/customer.py b/app/models/customer.py index e3aece97b..e1c010e31 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -1,4 +1,31 @@ from app import db +from sqlalchemy.sql.functions import func class Customer(db.Model): - id = db.Column(db.Integer, primary_key=True) \ No newline at end of file + customer_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + name = db.Column(db.String) + phone = db.Column(db.String) + postal_code = db.Column(db.String) + # server_default tells sqlA to pass the default value as part of the CREATE TABLE + # func.now() or func.current_timestamp() - they are aliases of each other. This tells DB to calcaate the timestamp itself + registered_at = db.Column(db.DateTime(timezone=True), server_default=func.now()) + #I DON'T KNOW IF THIS IS CORRECT + #adding videos attribute to Customer Model + videos = db.relationship("Video", secondary="rental", backref="customers") + #videos = db.relationship("Rental", back_populates="customer") + + + def customer_dict(self): + dict = { + "id": self.customer_id, + "name": self.name, + "phone": self.phone, + "postal_code": self.postal_code, + # weekday|day of month (16)|month name|year|local version of time|, UTC offset +HHMM or -HHMM + "registered_at": self.registered_at.strftime("%a, %-d %b %Y %X %z") + } + # if self.registered_at is not None: + # dict["registered_at"] = self.registered_at.strfttime("%a, %-d %b %Y %X %z") + + return dict + diff --git a/app/models/rental.py b/app/models/rental.py index 11009e593..92fec9e77 100644 --- a/app/models/rental.py +++ b/app/models/rental.py @@ -1,4 +1,13 @@ from app import db +from datetime import datetime, timedelta class Rental(db.Model): - id = db.Column(db.Integer, primary_key=True) \ No newline at end of file + rental_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + customer_id = db.Column(db.Integer, db.ForeignKey('customer.customer_id'), primary_key=True,nullable=False) + video_id = db.Column(db.Integer, db.ForeignKey('video.video_id'), primary_key=True,nullable=False) + due_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow() + timedelta(days = 7)) + #bool is truthy and falsey + checked_out = db.Column(db.Boolean, default=False) + #need to update check_in and check_out routes. Change check_in to True and False for checkout + #video = db.relationship("Video", back_populates="customers") + #customer = db.releationship("Customer", back_populates="videos") diff --git a/app/models/video.py b/app/models/video.py index 9893a6ef9..d41f5cc98 100644 --- a/app/models/video.py +++ b/app/models/video.py @@ -1,4 +1,17 @@ from app import db class Video(db.Model): - id = db.Column(db.Integer, primary_key=True) \ No newline at end of file + video_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + title = db.Column(db.String) + release_date = db.Column(db.DateTime) + #how many I own + total_inventory = db.Column(db.Integer) + #customers = db.relationship("Rental", back_populates="video") + #checked_out column + def to_dict(self): + return { + "id": self.video_id, + "title": self.title, + "release_date": self.release_date, + "total_inventory": self.total_inventory + } \ No newline at end of file diff --git a/app/rental_routes.py b/app/rental_routes.py new file mode 100644 index 000000000..8a52a5fb5 --- /dev/null +++ b/app/rental_routes.py @@ -0,0 +1,174 @@ +from flask.wrappers import Response +from app import db +from app.customer_routes import customers_bp +from app.video_routes import video_bp +from app.models.customer import Customer +from app.models.video import Video +from app.models.rental import Rental +from flask import Blueprint, jsonify, request +from datetime import datetime + +rentals_bp = Blueprint("rentals_bp", __name__, url_prefix="/rentals") + + +@rentals_bp.route("/check-out", methods=["POST"]) +def rentals_checkout(): + #{"c_id": [1], "v_id": [4]} + request_body = request.get_json() + + # is this key string not in the request_body return 400 + if "customer_id" not in request_body or "video_id" not in request_body: + return jsonify(), 400 + + # get instance of a customer and an instance of a video and return 404 if it's None + customer = Customer.query.get(request_body["customer_id"]) + video = Video.query.get(request_body["video_id"]) + if video is None or customer is None: + return jsonify(), 404 + + # filtering the video ID & checked_out attributes and count the records + rentals = Rental.query.filter_by( + video_id=video.video_id, checked_out=True).count() + + # finding avaliable inventory + available_inventory = video.total_inventory - rentals + + if available_inventory == 0: + return jsonify({"message": "Could not perform checkout"}), 400 + + # Instantiate a new instance for rental + new_rental = Rental( + video_id=video.video_id, + customer_id=customer.customer_id + ) + + # staging rental instance to database + db.session.add(new_rental) + # set rental instance to True + new_rental.checked_out = True + # commit to database + db.session.commit() + + # count() - tells how many rentals are currently checked out + videos_checked_out = Rental.query.filter_by( + video_id=video.video_id, checked_out=True).count() + + available_inventory = video.total_inventory - videos_checked_out + + # return the response body and status code + return jsonify({ + "video_id": new_rental.video_id, + "customer_id": new_rental.customer_id, + "videos_checked_out_count": videos_checked_out, + "available_inventory": available_inventory + }), 200 + + +@rentals_bp.route("/check-in", methods=["POST"]) +def rentals_checkin(): + request_body = request.get_json() + + # checks that the required request body parameters are in request + if "customer_id" not in request_body or "video_id" not in request_body: + return jsonify(), 400 + + # store customer/video instance into variables. If customer/video does not exist, returns 404 + customer = Customer.query.get(request_body["customer_id"]) + video = Video.query.get(request_body["video_id"]) + if customer is None or video is None: + return jsonify(), 404 + + # query through Rental to find matching customer and video ids and return first on the list + rental = Rental.query.filter_by( + customer_id=customer.customer_id, video_id=video.video_id).first() + # (customer_id=customer.customer_id, video_id=video.video_id, checked_out=True).first() #this also passed the test + + # "is" (compare location in memory) is 50% faster than "==" (comparing values) + if rental is None: + return jsonify({"message": f"No outstanding rentals for customer {customer.customer_id} and video {video.video_id}"}), 400 + + # rental.checked_out = True + db.session.commit() + + # return response body + videos_checked_out = Rental.query.filter_by( + video_id=video.video_id, checked_out=False).count() + + # finding avaiable inventory + available_inventory = video.total_inventory - videos_checked_out + + return jsonify({ + "video_id": video.video_id, + "customer_id": customer.customer_id, + "videos_checked_out_count": videos_checked_out, + "available_inventory": available_inventory + }) + + +@customers_bp.route("//rentals", methods=["GET"]) +def customer_read(customer_id): + """ List the videos a customer currently has checked out """ + request_body = request.get_json() + + # checks to see if customer exists. If not, returns 404 + customer = Customer.query.get(customer_id) + if customer is None: + return jsonify({"message": f"Customer {customer_id} was not found"}), 404 + + # sets up empty list to store a customer's checked out videos & iterates through customer.videos to retreive all videos a customer currently has + checked_out = [] + for video in customer.videos: + checked_out.append(video) + + # gets rental instance for each video a customer has + rentals = Rental.query.all() + customer_rentals = [] + for video in checked_out: + for rental in rentals: + if video.video_id == rental.video_id and customer.customer_id == rental.customer_id: + customer_rentals.append(rental) + + # create response body + response_body = [] + for rental in customer_rentals: + response_body.append({ + "release_date": video.release_date, + "title": video.title, + "due_date": rental.due_date + }) + return jsonify(response_body) + + +@video_bp.route("//rentals", methods=["GET"]) +def video_read(video_id): + """ List the customers who currently have the video checked out """ + request_body = request.get_json() + + # checks to see if video exists. If not, returns 404 + video = Video.query.get(video_id) + if video is None: + return jsonify({"message": f"Video {video_id} was not found"}), 404 + + # sets up empty list to store the video's current customers & iterates through video.customers to retreive all customers that currently have the video + current_customers = [] + for customer in video.customers: + current_customers.append(customer) + + # gets rental instance for each customer a video has + rentals = Rental.query.all() + video_rentals = [] + for customer in current_customers: + for rental in rentals: + if video.video_id == rental.video_id and customer.customer_id == rental.customer_id: + video_rentals.append(rental) + + # create response body + response_body = [] + for rental in video_rentals: + response_body.append({ + "due_date": rental.due_date, + "name": customer.name, + "phone": customer.phone, + "postal_code": customer.postal_code + }) + return jsonify(response_body) diff --git a/app/routes.py b/app/routes.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/video_routes.py b/app/video_routes.py new file mode 100644 index 000000000..71124304f --- /dev/null +++ b/app/video_routes.py @@ -0,0 +1,90 @@ +from app.models.video import Video +from app import db +from flask import Blueprint, request, jsonify +from datetime import datetime +import os, requests, json + +video_bp = Blueprint("videos", __name__, url_prefix="/videos") + +# /videos routes +@video_bp.route("", methods=["GET"]) +def get_videos(): + videos = Video.query.all() + if videos is None: + return jsonify([]) + response_body = [] + for video in videos: + response_body.append(video.to_dict()) + return jsonify(response_body) + +@video_bp.route("", methods=["POST"]) +def create_video(): + request_body = request.get_json() + if "title" not in request_body or "release_date" not in request_body or "total_inventory" not in request_body: + response_body = {} + if "title" not in request_body: + response_body["details"] = "Request body must include title." + elif "release_date" not in request_body: + response_body["details"] = "Request body must include release_date." + elif "total_inventory" not in request_body: + response_body["details"] = "Request body must include total_inventory." + return jsonify(response_body), 400 + + new_video = Video( + title=request_body["title"], + release_date=request_body["release_date"], + total_inventory=request_body["total_inventory"] + ) + db.session.add(new_video) + db.session.commit() + + response_body=new_video.to_dict() + response_body["id"] = new_video.video_id + return jsonify(response_body), 201 + +# /videos/ routes +@video_bp.route("/", methods=["GET"]) +def get_video(video_id): + ## try + # if not isinstance(video_id, int): + ## or + # if not video_id.is_integer(): + # return jsonify(), 400 + # maybe try-except? for invalid id test + try: + video = Video.query.get(video_id) + except: + return jsonify(), 400 + if video is None: + return jsonify({"message": f"Video {video_id} was not found"}), 404 + response_body = video.to_dict() + return jsonify(response_body) + +@video_bp.route("/", methods=["PUT"]) +def update_video(video_id): + video = Video.query.get(video_id) + if video is None: + return jsonify({"message": f"Video {video_id} was not found"}), 404 + + request_body = request.get_json() + if "title" not in request_body or "release_date" not in request_body or "total_inventory" not in request_body: + return jsonify(), 400 + + video.title = request_body["title"] + video.release_date = request_body["release_date"] + video.total_inventory = request_body["total_inventory"] + db.session.commit() + + response_body = video.to_dict() + return jsonify(response_body) + +@video_bp.route("/", methods=["DELETE"]) +def delete_video(video_id): + video = Video.query.get(video_id) + if video is None: + return jsonify({"message": f"Video {video_id} was not found"}), 404 + db.session.delete(video) + db.session.commit() + + response_body = {"id": video.video_id} + return jsonify(response_body) \ No newline at end of file diff --git a/migrations/README b/migrations/README new file mode 100644 index 000000000..98e4f9c44 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 000000000..f8ed4801f --- /dev/null +++ b/migrations/alembic.ini @@ -0,0 +1,45 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/migrations/env.py b/migrations/env.py new file mode 100644 index 000000000..8b3fb3353 --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,96 @@ +from __future__ import with_statement + +import logging +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool +from flask import current_app + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +config.set_main_option( + 'sqlalchemy.url', + str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%')) +target_metadata = current_app.extensions['migrate'].db.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, target_metadata=target_metadata, literal_binds=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 000000000..2c0156303 --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/d4e5fb64e076_create_base_models.py b/migrations/versions/d4e5fb64e076_create_base_models.py new file mode 100644 index 000000000..d8bac7d5f --- /dev/null +++ b/migrations/versions/d4e5fb64e076_create_base_models.py @@ -0,0 +1,48 @@ +"""create base models + +Revision ID: d4e5fb64e076 +Revises: +Create Date: 2021-11-09 07:19:35.458780 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'd4e5fb64e076' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('customer', + sa.Column('customer_id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(), nullable=True), + sa.Column('phone', sa.String(), nullable=True), + sa.Column('postal_code', sa.Integer(), nullable=True), + sa.Column('registered_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('customer_id') + ) + op.create_table('rental', + sa.Column('id', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('video', + sa.Column('video_id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('title', sa.String(), nullable=True), + sa.Column('release_date', sa.DateTime(), nullable=True), + sa.Column('total_inventory', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('video_id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('video') + op.drop_table('rental') + op.drop_table('customer') + # ### end Alembic commands ### diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index 8d32038f2..39346455a 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -10,6 +10,7 @@ CUSTOMER_NAME = "A Brand New Customer" CUSTOMER_ID = 1 CUSTOMER_POSTAL_CODE = "12345" +# CUSTOMER_POSTAL_CODE = 12345 CUSTOMER_PHONE = "123-123-1234" # -------------------------------- @@ -17,6 +18,8 @@ # -------------------------------- # READ + + def test_get_videos_no_saved_videos(client): # Act response = client.get("/videos") @@ -26,6 +29,7 @@ def test_get_videos_no_saved_videos(client): assert response.status_code == 200 assert response_body == [] + def test_get_videos_one_saved_video(client, one_video): # Act response = client.get("/videos") @@ -38,6 +42,7 @@ def test_get_videos_one_saved_video(client, one_video): assert response_body[0]["id"] == VIDEO_ID assert response_body[0]["total_inventory"] == VIDEO_INVENTORY + def test_get_video(client, one_video): # Act response = client.get("/videos/1") @@ -49,6 +54,7 @@ def test_get_video(client, one_video): assert response_body["id"] == VIDEO_ID assert response_body["total_inventory"] == VIDEO_INVENTORY + def test_get_video_not_found(client): # Act response = client.get("/videos/1") @@ -58,6 +64,7 @@ def test_get_video_not_found(client): assert response.status_code == 404 assert response_body == {"message": "Video 1 was not found"} + def test_get_invalid_video_id(client, one_video): # Act response = client.get("/videos/hello") @@ -89,6 +96,7 @@ def test_create_video(client): assert new_video assert new_video.title == VIDEO_TITLE + def test_create_video_must_contain_title(client): # Act response = client.post("/videos", json={ @@ -103,6 +111,7 @@ def test_create_video_must_contain_title(client): assert response.status_code == 400 assert Video.query.all() == [] + def test_create_video_must_contain_release_date(client): # Act response = client.post("/videos", json={ @@ -117,6 +126,7 @@ def test_create_video_must_contain_release_date(client): assert response.status_code == 400 assert Video.query.all() == [] + def test_create_video_must_contain_inventory(client): # Act response = client.post("/videos", json={ @@ -131,6 +141,8 @@ def test_create_video_must_contain_inventory(client): assert Video.query.all() == [] # DELETE + + def test_delete_video(client, one_video): # Act response = client.delete("/videos/1") @@ -142,6 +154,7 @@ def test_delete_video(client, one_video): assert response.status_code == 200 assert Video.query.get(1) == None + def test_delete_video_not_found(client): # Act response = client.delete("/videos/1") @@ -152,6 +165,7 @@ def test_delete_video_not_found(client): assert response.status_code == 404 assert Video.query.all() == [] + def test_update_video(client, one_video): # Act response = client.put("/videos/1", json={ @@ -171,6 +185,7 @@ def test_update_video(client, one_video): assert video.title == "Updated Video Title" assert video.total_inventory == 2 + def test_update_video_not_found(client): # Act response = client.put("/videos/1", json={ @@ -184,6 +199,7 @@ def test_update_video_not_found(client): assert response.status_code == 404 assert response_body == {"message": "Video 1 was not found"} + def test_update_video_invalid_data(client, one_video): # Act response = client.put("/videos/1", json={ @@ -209,6 +225,7 @@ def test_get_customers_no_saved_customers(client): assert response.status_code == 200 assert response_body == [] + def test_get_customers_one_saved_customer(client, one_customer): # Act response = client.get("/customers") @@ -220,7 +237,8 @@ def test_get_customers_one_saved_customer(client, one_customer): assert response_body[0]["name"] == CUSTOMER_NAME assert response_body[0]["id"] == CUSTOMER_ID assert response_body[0]["phone"] == CUSTOMER_PHONE - assert response_body[0]["postal_code"] == CUSTOMER_POSTAL_CODE + assert response_body[0]["postal_code"] == CUSTOMER_POSTAL_CODE #another string + def test_get_customer(client, one_customer): # Act @@ -232,7 +250,8 @@ def test_get_customer(client, one_customer): assert response_body["name"] == CUSTOMER_NAME assert response_body["id"] == CUSTOMER_ID assert response_body["phone"] == CUSTOMER_PHONE - assert response_body["postal_code"] == CUSTOMER_POSTAL_CODE + assert response_body["postal_code"] == CUSTOMER_POSTAL_CODE #another string + def test_get_customer_not_found(client): # Act @@ -243,6 +262,7 @@ def test_get_customer_not_found(client): assert response.status_code == 404 assert response_body == {"message": "Customer 1 was not found"} + def test_get_invalid_customer_id(client, one_customer): # Act response = client.get("/customers/hello") @@ -251,6 +271,8 @@ def test_get_invalid_customer_id(client, one_customer): assert response.status_code == 400 # CREATE + + def test_create_customer(client): # Act response = client.post("/customers", json={ @@ -272,6 +294,7 @@ def test_create_customer(client): assert new_customer.postal_code == CUSTOMER_POSTAL_CODE assert new_customer.phone == CUSTOMER_PHONE + def test_create_customer_must_contain_postal(client): # Act response = client.post("/customers", json={ @@ -286,6 +309,7 @@ def test_create_customer_must_contain_postal(client): assert response.status_code == 400 assert Customer.query.all() == [] + def test_create_customer_must_contain_name(client): # Act response = client.post("/customers", json={ @@ -300,6 +324,7 @@ def test_create_customer_must_contain_name(client): assert response.status_code == 400 assert Customer.query.all() == [] + def test_create_customer_must_contain_phone(client): # Act response = client.post("/customers", json={ @@ -314,6 +339,8 @@ def test_create_customer_must_contain_phone(client): assert Customer.query.all() == [] # DELETE + + def test_delete_customer(client, one_customer): # Act response = client.delete("/customers/1") @@ -324,6 +351,7 @@ def test_delete_customer(client, one_customer): assert response.status_code == 200 assert Customer.query.get(1) == None + def test_delete_customer_not_found(client): # Act response = client.delete("/customers/1") @@ -334,12 +362,15 @@ def test_delete_customer_not_found(client): assert response.status_code == 404 assert Customer.query.all() == [] + def test_update_customer(client, one_customer): # Act response = client.put("/customers/1", json={ "name": f"Updated ${CUSTOMER_NAME}", "phone": f"Updated ${CUSTOMER_PHONE}", + # postal_code data type is an integer but the test case is passing in a string. "postal_code": f"Updated ${CUSTOMER_POSTAL_CODE}" + # "postal_code": CUSTOMER_POSTAL_CODE+5 }) response_body = response.get_json() @@ -348,12 +379,14 @@ def test_update_customer(client, one_customer): assert response_body["name"] == f"Updated ${CUSTOMER_NAME}" assert response_body["phone"] == f"Updated ${CUSTOMER_PHONE}" assert response_body["postal_code"] == f"Updated ${CUSTOMER_POSTAL_CODE}" + # assert response_body["postal_code"] == CUSTOMER_POSTAL_CODE+5 customer = Customer.query.get(1) assert customer.name == f"Updated ${CUSTOMER_NAME}" assert customer.phone == f"Updated ${CUSTOMER_PHONE}" assert customer.postal_code == f"Updated ${CUSTOMER_POSTAL_CODE}" - + # assert customer.postal_code == CUSTOMER_POSTAL_CODE+5 + def test_update_customer_not_found(client): # Act @@ -368,6 +401,7 @@ def test_update_customer_not_found(client): assert response.status_code == 404 assert response_body == {"message": "Customer 1 was not found"} + def test_update_customer_invalid_data(client, one_customer): # Act response = client.put("/customers/1", json={ @@ -377,9 +411,3 @@ def test_update_customer_invalid_data(client, one_customer): # Assert assert response.status_code == 400 - - - - - -