diff --git a/backend/payment_database.json b/backend/payment_database.json new file mode 100644 index 00000000..3aef39c4 --- /dev/null +++ b/backend/payment_database.json @@ -0,0 +1,10 @@ +{ + "111.111.111-10": [ + { + "id": "2616058799231613355", + "tipo": "pix", + "nome_completo": "Breno gabriel de Melo Lima", + "cpf": "111.111.111-10" + } + ] +} \ No newline at end of file diff --git a/backend/src/api/items.py b/backend/src/api/items.py index 635e5f45..e50a350f 100644 --- a/backend/src/api/items.py +++ b/backend/src/api/items.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, status from src.schemas.response import HttpResponseModel -from src.service.impl.item_service import ItemService +# from src.service.impl.item_service import ItemService router = APIRouter() @@ -34,8 +34,8 @@ def get_item(item_id: str) -> HttpResponseModel: - HTTPException 404: If the item is not found. """ - item_get_response = ItemService.get_item(item_id) - return item_get_response + # item_get_response = ItemService.get_item(item_id) + # return item_get_response @router.get( @@ -60,9 +60,9 @@ def get_items() -> HttpResponseModel: """ - item_list_response = ItemService.get_items() + # item_list_response = ItemService.get_items() - return item_list_response + # return item_list_response # TODO: Add POST, PUT, DELETE endpoints \ No newline at end of file diff --git a/backend/src/api/payment_methods.py b/backend/src/api/payment_methods.py new file mode 100644 index 00000000..ee4490a7 --- /dev/null +++ b/backend/src/api/payment_methods.py @@ -0,0 +1,112 @@ +from fastapi import APIRouter, status, HTTPException +from src.service.impl.payment_method_service import PaymentService +from src.schemas.payment_schema import Cartao, Boleto, Pix, CartaoUpdate, PixUpdate, BoletoUpdate +from src.schemas.response import HTTPResponses, HttpResponseModel +from fastapi.responses import JSONResponse + + +router = APIRouter() + +@router.post( + '/inserting/cartao', + response_model=HttpResponseModel, + status_code=201, + description="Insert a new card", + ) +def insert_payment(cartao: Cartao) -> HttpResponseModel: + response = PaymentService.inserting_card(cartao) + return response + +@router.post( + '/inserting/pix', + response_model=HttpResponseModel, + status_code=201, + description="Insert a new pix acount", + ) +def insert_payment(pix: Pix) -> HttpResponseModel: + response = PaymentService.insertion_pix(pix) + return response + +@router.post( + '/inserting/boleto', + response_model=HttpResponseModel, + status_code=201, + description="Insert a new boleto acount", + ) +def insert_payment(boleto: Boleto) -> HttpResponseModel: + response = PaymentService.insertion_ticket(boleto) + return response + +# @router.get( +# "/get/payment_methods", +# description="Get the payments methods of a especific user" +# ) +# def get_payment_methods(username:str): + +# request = PaymentService.get_payment_methods(username) + +# return request + +@router.put( + "update/cartao/{id}", + response_model=HttpResponseModel, + status_code=200, + description="Update the card payment method" +) +def update_payment(id: str, cartao: CartaoUpdate): + + response = PaymentService.update_card(id, cartao) + + return response + +@router.put( + "/update/pix/{id}", + response_model=HttpResponseModel, + status_code=200, + description="Update the pix payment method" +) +def update_payment(id: str, pix: PixUpdate): + + response = PaymentService.update_pix(id, pix) + + return response + +@router.put( + "/update/boleto/{id}", + response_model=HttpResponseModel, + status_code=200, + description="Update the boleto payment method" +) +def update_payment(id: str, boleto: BoletoUpdate): + + response = PaymentService.update_ticket(id, boleto) + + return response + +@router.delete( + "/delete/{method_id}", + response_model=HttpResponseModel, + status_code=200, + description="Delete a payment method" +) +def delete_method(method_id: str): + + response = PaymentService.delete_method(method_id) + + return response + +# @router.post( +# "/inserting/{method_name}", +# response_model=HttpResponseModel, +# summary="Create a new payment method" +# ) +# def insert_payment_method(method_name: str, cartao: Cartao, pix: Pix, boleto: Boleto) -> HttpResponseModel: +# if method_name == "boleto": +# response = PaymentService.inserting_cartao(boleto) +# return response +# elif method_name == "pix": +# response = PaymentService.insertion_pix(pix) +# return response +# elif method_name == "cartao": +# response = PaymentService.inserting_cartao(cartao) +# return response \ No newline at end of file diff --git a/backend/src/api/router.py b/backend/src/api/router.py index a0cc6026..b5905153 100644 --- a/backend/src/api/router.py +++ b/backend/src/api/router.py @@ -1,7 +1,8 @@ from fastapi import APIRouter -from src.api import users +from src.api import users, payment_methods api_router = APIRouter() -# api_router.include_router(items.router, prefix="/items", tags=["items"]) -api_router.include_router(users.router, prefix="/auth/user", tags = ["user"]) \ No newline at end of file + +api_router.include_router(users.router, prefix="/auth/user", tags = ["user"]) +api_router.include_router(payment_methods.router, prefix="/payment", tags=["Payment"]) \ No newline at end of file diff --git a/backend/src/db/__init__.py b/backend/src/db/__init__.py index 6881c32d..5a4d6a20 100644 --- a/backend/src/db/__init__.py +++ b/backend/src/db/__init__.py @@ -2,9 +2,6 @@ from .user_database import UserDatabase from .config.create_collections import create_collections -database = Database() user_database = UserDatabase() user_database_example = UserDatabase("Usuários teste.json") user_database_example.clear_database() - -# create_collections(database) \ No newline at end of file diff --git a/backend/src/db/database.py b/backend/src/db/database.py index b1488324..d4945195 100644 --- a/backend/src/db/database.py +++ b/backend/src/db/database.py @@ -22,7 +22,7 @@ def connect(self): logger.setLevel(INFO) - self.db = mongo_connection[env.DB_NAME] + self.db = mongo_connection[str(env.DB_NAME)] print("--------------------") logger.info("MongoDB connected!") @@ -208,4 +208,27 @@ def insert_item(self, collection_name: str, item: dict) -> dict: - list: A list of all items in the collection. - """ \ No newline at end of file + """ + + def add(self, collection_name: Collection, item: dict) -> dict: + + #Insert a item in a collection. + + #Args: + #collection_name (Collection) --> name of the collection. + #item (dict) --> the item in form of a dict. + + #Process: + #acessing the collection of the item. + #storing the item in the collection. + + #Outuput: + #A document containing: + #A boolean acknowledged as true if the operation ran with write concern or false if write concern was disabled. + #A field insertedId with the _id value of the inserted document. + + collection: Collection = self.db[collection_name] + + result = collection.insert_one(dict(item)) + + return result diff --git a/backend/src/db/payment_database.py b/backend/src/db/payment_database.py new file mode 100644 index 00000000..99ab7bb5 --- /dev/null +++ b/backend/src/db/payment_database.py @@ -0,0 +1,303 @@ +import os +from typing import List, Dict +from uuid import uuid4 +import jsonpickle +from pymongo import MongoClient, errors +from pymongo.collection import Collection, IndexModel +#from src.config.config import env +from logging import INFO, WARNING, getLogger +import datetime +import hmac +import hashlib +import re +import json +import datetime +import uuid + + +logger = getLogger('uvicorn') + +#Regex for CPF and card number. +cpf_pattern = re.compile(r"^[0-9]{3}\.[0-9]{3}\.[0-9]{3}\-[0-9]{2}$") +cartao_pattern = re.compile(r"^(4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9]{2})[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])?[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$") + +def write_file(database): + + """Write the content of a dict in a JSON. + + Input: database: dict. + + Process: Open the JSON file and overwrite it's content. + + Output: JSON file overwrited. """ + + with open("payment_database.json", "w") as f: + json.dump(database, f, default=str, indent=4) + +def read_file(): + with open("payment_database.json", "r") as f: + return json.load(f) + +database = {} +database = read_file() + +def validate_CPF(cpf: str) -> bool: + + """Validate a CPF number. + + Input: a CPF number. + + Process: verify the validity of a CPF number using a regex. + + Outuput: A boolean indicating the validity or not of the CPF number. """ + + if not cpf_pattern.match(cpf): + return False + + return True + +def validate_date(validade: datetime.date) -> bool: + + """Validate a validity date. + + Input: a date. + + Process: compare the input date with the today date. + + Outuput: A boolean indicating the validity or not of the input date. """ + + + if not validade >= datetime.date.today(): + return False + + return True + +def validate_card_number(numero_cartao: str) -> bool: + + """Validate a card number. + + Input: a card number. + + Process: verify the validity of the card number using a regex. + + Outuput: A boolean indicating the validity or not of the card number. """ + + if not cartao_pattern.match(numero_cartao): + return False + + return True + +def insert_card(nome_cartao: str, numero_cartao: str, cvv: str, cpf: str, validade: datetime.date): + + """Validate and insert or not a card. + + Input: the card information pass by the service layer. + + Process: use the "validate_cpf", "validate_date" and "validate_card_number" for verify the validity of the card informations. + if the informations is valid, the card is insert in a dict and writted in the JSON file. + + Output: A tuple contain the sucess (bool) of the insertion and the problems (List) in case of validation problem. """ + + + problems = [] + + if not validate_CPF(cpf): + problems.append("CPF") + + if not validate_date(validade): + problems.append("VALIDADE") + + if not validate_card_number(numero_cartao): + problems.append("CARD_NUMBER") + + if len(problems) > 0: + return (False, problems) + + if cpf not in database: + database[cpf] = [] + + id_value = str(abs(hash((datetime.date.today(), cpf)))) + + cartao = { + "id" : id_value, + "tipo": "cartao", + "nome_cartao": nome_cartao, + "numero_cartao": numero_cartao, + "cvv": cvv, + "validade": validade.strftime("%Y-%m-%d"), + "cpf": cpf + } + + database[cpf].append(cartao) + write_file(database) + + return (True, id_value) + +def insert_pix(nome_completo: str, cpf: str) : + + """Validate and insert or not a pix. + + Input: the pix information pass by the service layer. + + Process: use the "validate_cpf" for verify the validity of the pix informations. + if the informations is valid, the pix is insert in a dict and writted in the JSON file. + + Output: A tuple contain the sucess (bool) of the insertion. """ + + result = validate_CPF(cpf) + + if not result: + return ("CPF", None) + + if cpf not in database: + database[cpf] = [] + + for val in database[cpf]: + if val["tipo"] == "pix": + return ("ALREADY_EXIST", None) + + id_value = str(abs(hash((datetime.date.today(), cpf)))) + + pix = { + "id" : id_value, + "tipo": "pix", + "nome_completo": nome_completo, + "cpf": cpf + } + + database[cpf].append(pix) + write_file(database) + + print(database) + + return ("OK", id_value) + +def insert_ticket(nome_completo: str, cpf: str) -> str: + + + """Validate and insert or not a ticket. + + Input: the ticket information pass by the service layer. + + Process: use the "validate_cpf" for verify the validity of the ticket informations. + if the informations is valid, the ticket is insert in a dict and writted in the JSON file. + + Output: A tuple contain the sucess (bool) of the insertion. """ + + result = validate_CPF(cpf) + + if not result: + return ("INVALID_CPF", None) + + if cpf not in database: + database[cpf] = [] + + for val in database[cpf]: + if val["tipo"] == "boleto": + return ("ALREADY_EXIST", None) + + id_value = str(abs(hash((datetime.date.today(), cpf)))) + + boleto = { + "id" : id_value, + "tipo": "boleto", + "nome_completo": nome_completo, + "cpf": cpf + } + + database[cpf].append(boleto) + write_file(database) + + return ("OK", id_value) + +def update_card(id: str, nome_cartao: str, numero_cartao: str, cvv: str, validade: datetime.date): + + database = read_file() + + """Update the nome_cartao, numero_cartao, cvv and validade fields. + + #Input: the card informations (id, nome_cartao, numero_cartao, cvv and validade). + + #Process: Verify if the card is in the database (using the ID). If the card is in database, + make the validation of card number and validity date. If the validation is sucessfull, + update the card information in the database and write in the JSON file + + Output: a tuple with the result and a array with the problems""" + + problems = [] + + + for key in database: + for val in database[key]: + if val["id"] == id: + + if not validade >= datetime.date.today(): + problems.append("VALIDADE") + + if not cartao_pattern.match(numero_cartao): + problems.append("CARD_NUMBER") + + if len(problems) > 0: + return (False, problems) + + val["nome_cartao"] = nome_cartao + val["numero_cartao"] = numero_cartao + val["cvv"] = cvv + val["validade"] = validade + + write_file(database) + + print(database) + + return (True, ["SUCESS"]) + + problems.append("ID_NOT_FOUND") + return (False, problems) + + +def update_pix_or_ticket(id: str, nome_completo: str) -> bool: + + + + for key in database: + for val in database[key]: + if val["id"] == id: + val["nome_completo"] = nome_completo + write_file(database) + return True + + return False + +def delete_method(id: str) -> bool: + + + + for key in database: + for val in database[key]: + + if val["id"] == id: + + index = database[key].index(val) + + del database[key][index] + + if len(database[key]) == 0: + del database[key] + + write_file(database) + + return True + + return False + +def get_by_number(numero_cartao: str): + + + for key in database: + for val in database[key]: + if val["tipo"] == "cartao": + if val["numero_cartao"] == numero_cartao: + + return val + + return None \ No newline at end of file diff --git a/backend/src/schemas/payment_response.py b/backend/src/schemas/payment_response.py new file mode 100644 index 00000000..c63ac2dc --- /dev/null +++ b/backend/src/schemas/payment_response.py @@ -0,0 +1,110 @@ +from typing import Optional +from pydantic import BaseModel +from .response import HttpResponseModel + +class HTTPPaymentResponse: + + @staticmethod + def INSERTION_SUCESSFULLY(id: str) -> HttpResponseModel: + return HttpResponseModel ( + message="metodo de pagamento cadastrado com sucesso", + status_code=201, + data = { + "ID": id + } + ) + + # @staticmethod + # def INVALID_CPF() -> HttpResponseModel: + # return HttpResponseModel ( + # message="Invalid CPF", + # status_code=400 + # ) + + # @staticmethod + # def EXPIRED_DATA() -> HttpResponseModel: + # return HttpResponseModel ( + # message="The card is expired", + # status_code=400 + # ) + + # @staticmethod + # def INVALID_NUMBER() -> HttpResponseModel: + # return HttpResponseModel ( + # message="The card number is invalid", + # status_code=400 + # ) + + @staticmethod + def BAD_REQUEST(problemas) -> HttpResponseModel: + + return HttpResponseModel ( + message=f" informações inválidas", + status_code=400, + data= problemas + ) + + @staticmethod + def PIX_INSERTED_SUCESSFULLY() -> HttpResponseModel: + + return HttpResponseModel ( + message="pix inserido com sucesso", + status_code=201 + ) + + @staticmethod + def BOLETO_INSERTED_SUCESSFULLY() -> HttpResponseModel: + + return HttpResponseModel ( + message="boleto inserido com sucesso", + status_code=201 + ) + + @staticmethod + def INEXISTENT_USER() -> HttpResponseModel: + + return HttpResponseModel ( + message="O usuário não está registrado na base de dados", + status_code=400 + ) + + @staticmethod + def INEXISTENT_ID() -> HttpResponseModel: + + return HttpResponseModel ( + message="ID não encontrado na base de dados", + status_code=400 + ) + + @staticmethod + def UPDATE_SUCESSFULLY() -> HttpResponseModel: + + return HttpResponseModel ( + message="Dados atualizados com sucesso", + status_code=200 + ) + + @staticmethod + def DELETE_SUCESSFULLY() -> HttpResponseModel: + + return HttpResponseModel ( + message="Deleção realizada com sucesso", + status_code=200 + ) + + @staticmethod + def PIX_ALREADY_EXIST() -> HttpResponseModel: + + return HttpResponseModel ( + message="Já existe um pix cadastrado no sistema", + status_code=400 + ) + + @staticmethod + def BOLETO_ALREADY_EXIST() -> HttpResponseModel: + + return HttpResponseModel ( + message="Já existe um boleto cadastrado no sistema", + status_code=400 + ) + \ No newline at end of file diff --git a/backend/src/schemas/payment_schema.py b/backend/src/schemas/payment_schema.py new file mode 100644 index 00000000..ad72e28b --- /dev/null +++ b/backend/src/schemas/payment_schema.py @@ -0,0 +1,34 @@ +from pydantic import BaseModel +import datetime + +class Cartao(BaseModel): + + nome_cartao: str + numero_cartao: str + cvv: str + cpf: str + validade: datetime.date + +class Pix(BaseModel): + + nome_completo: str + cpf: str + +class Boleto(BaseModel): + + nome_completo: str + cpf: str + +class CartaoUpdate(BaseModel): + + nome_cartao: str + numero_cartao: str + cvv: str + validade: datetime.date + +class PixUpdate(BaseModel): + nome_completo: str + +class BoletoUpdate(PixUpdate): + pass + diff --git a/backend/src/service/impl/item_service.py b/backend/src/service/impl/item_service.py index 762ea8bf..5f571947 100644 --- a/backend/src/service/impl/item_service.py +++ b/backend/src/service/impl/item_service.py @@ -1,38 +1,38 @@ -from src.schemas.response import HTTPResponses, HttpResponseModel -from src.service.meta.item_service_meta import ItemServiceMeta -from src.db.__init__ import database as db +# # from src.schemas.response import HTTPResponses, HttpResponseModel +# # from src.service.meta.item_service_meta import ItemServiceMeta +# # from src.db.__init__ import database as db -class ItemService(ItemServiceMeta): +# class ItemService(ItemServiceMeta): - @staticmethod - def get_item(item_id: str) -> HttpResponseModel: - """Get item by id method implementation""" - item = db.get_item_by_id('items', item_id) - if not item: - return HttpResponseModel( - message=HTTPResponses.ITEM_NOT_FOUND().message, - status_code=HTTPResponses.ITEM_NOT_FOUND().status_code, - ) - return HttpResponseModel( - message=HTTPResponses.ITEM_FOUND().message, - status_code=HTTPResponses.ITEM_FOUND().status_code, - data=item, - ) +# @staticmethod +# def get_item(item_id: str) -> HttpResponseModel: +# """Get item by id method implementation""" +# item = db.get_item_by_id('items', item_id) +# if not item: +# return HttpResponseModel( +# message=HTTPResponses.ITEM_NOT_FOUND().message, +# status_code=HTTPResponses.ITEM_NOT_FOUND().status_code, +# ) +# return HttpResponseModel( +# message=HTTPResponses.ITEM_FOUND().message, +# status_code=HTTPResponses.ITEM_FOUND().status_code, +# data=item, +# ) - @staticmethod - def get_items(): - """Get items method implementation""" - items = db.get_all_items('items') - if not items: - return HttpResponseModel( - message=HTTPResponses.ITEM_NOT_FOUND().message, - status_code=HTTPResponses.ITEM_NOT_FOUND().status_code, - ) +# @staticmethod +# def get_items(): +# """Get items method implementation""" +# items = db.get_all_items('items') +# if not items: +# return HttpResponseModel( +# message=HTTPResponses.ITEM_NOT_FOUND().message, +# status_code=HTTPResponses.ITEM_NOT_FOUND().status_code, +# ) - return HttpResponseModel( - message=HTTPResponses.ITEM_FOUND().message, - status_code=HTTPResponses.ITEM_FOUND().status_code, - data=items, - ) +# return HttpResponseModel( +# message=HTTPResponses.ITEM_FOUND().message, +# status_code=HTTPResponses.ITEM_FOUND().status_code, +# data=items, +# ) - # TODO: implement other methods (create, update, delete) \ No newline at end of file +# # TODO: implement other methods (create, update, delete) \ No newline at end of file diff --git a/backend/src/service/impl/payment_method_service.py b/backend/src/service/impl/payment_method_service.py new file mode 100644 index 00000000..cf002c41 --- /dev/null +++ b/backend/src/service/impl/payment_method_service.py @@ -0,0 +1,98 @@ +from src.db.payment_database import * +from src.schemas.payment_schema import Cartao, Pix, Boleto, CartaoUpdate, PixUpdate, BoletoUpdate +from src.schemas.response import HTTPResponses, HttpResponseModel +from src.schemas.payment_response import HTTPPaymentResponse + +class PaymentService: + + @staticmethod + def inserting_card(cartao: Cartao) -> HttpResponseModel: + + sucess, problems = insert_card(*cartao.model_dump().values()) + + if sucess: + return HTTPPaymentResponse.INSERTION_SUCESSFULLY(problems) + else: + return HTTPPaymentResponse.BAD_REQUEST(problems) + + @staticmethod + def insertion_pix(pix: Pix) -> HttpResponseModel: + + result, id = insert_pix(*pix.model_dump().values()) + + if result == "CPF": + return HTTPPaymentResponse.BAD_REQUEST(["CPF"]) + elif result == "ALREADY_EXIST": + return HTTPPaymentResponse.PIX_ALREADY_EXIST() + + return HTTPPaymentResponse.INSERTION_SUCESSFULLY(id) + + @staticmethod + def insertion_ticket(boleto: Boleto) -> HttpResponseModel: + + result, id = insert_ticket(*boleto.model_dump().values()) + + if result == "CPF": + return HTTPPaymentResponse.BAD_REQUEST(["CPF"]) + elif result == "ALREADY_EXIST": + return HTTPPaymentResponse.BOLETO_ALREADY_EXIST() + + return HTTPPaymentResponse.INSERTION_SUCESSFULLY(id) + + # @staticmethod + # def get_payment_methods(cpf: str): + + # resultado = obter_lista_de_metodos_pagamento(cpf) + + # if resultado is None: + # return HTTPPaymentResponse.INEXISTENT_USER() + # else: + # return resultado + + @staticmethod + def update_card(id: int, cartao: CartaoUpdate): + + sucess, problems = update_card(id, *cartao.model_dump().values()) + + if not sucess: + + if "VALIDADE" in problems: + return HTTPPaymentResponse.BAD_REQUEST() + + if "ID_NOT_FOUND" in problems: + return HTTPPaymentResponse.INEXISTENT_ID() + + return HTTPPaymentResponse.UPDATE_SUCESSFULLY() + + @staticmethod + def update_pix(id:int, pix: PixUpdate): + + sucess = update_pix_or_ticket(id, *pix.model_dump().values()) + + if not sucess: + + return HTTPPaymentResponse.INEXISTENT_ID() + + return HTTPPaymentResponse.UPDATE_SUCESSFULLY() + + @staticmethod + def update_ticket(id:int, boleto: BoletoUpdate): + + sucess = update_pix_or_ticket(id, *boleto.model_dump().values()) + + if not sucess: + + return HTTPPaymentResponse.INEXISTENT_ID() + + return HTTPPaymentResponse.UPDATE_SUCESSFULLY() + + @staticmethod + def delete_method(id: int): + + sucess = delete_method(id) + + if not sucess: + + return HTTPPaymentResponse.INEXISTENT_ID() + + return HTTPPaymentResponse.DELETE_SUCESSFULLY() \ No newline at end of file diff --git a/backend/src/tests/api/step_definitions/no_test_items.py b/backend/src/tests/api/step_definitions/no_test_items.py index d10d720a..c345d13e 100644 --- a/backend/src/tests/api/step_definitions/no_test_items.py +++ b/backend/src/tests/api/step_definitions/no_test_items.py @@ -1,110 +1,110 @@ -from src.schemas.response import HTTPResponses, HttpResponseModel -from pytest_bdd import parsers, given, when, then, scenario -from src.service.impl.item_service import ItemService -from src.tests.api.utils.utils import get_response_items_list, req_type_to_function - -""" Scenario: Obter item por ID """ -# This method is used to define the scenario name and feature file path -@scenario(scenario_name="Obter item por ID", feature_name="../features/items.feature") -def test_get_item(): - """ Get item by id """ - -# Step definitions for the "Obter item por ID" scenario -@given(parsers.cfparse('o ItemService retorna um item com id "{item_id}" e nome "{item_name}"')) -def mock_item_service_response(item_id: str, item_name: str): - """ - Mock the ItemService.get_item() method to return an item with the given id and name - """ - - ItemService.get_item = lambda id : HttpResponseModel( - message=HTTPResponses.ITEM_FOUND().message, - status_code=HTTPResponses.ITEM_FOUND().status_code, - data={"id": item_id, "name": item_name} - ) - -@when( - parsers.cfparse('uma requisição "{req_type}" for enviada para "{req_url}"'), - target_fixture="context" -) -def send_get_item_request(client, context, req_type: str, req_url: str): - """ - Send a request to the given URL using the given request type - """ - - response = req_type_to_function(client, req_type)(req_url) - context["response"] = response - return context - -@then(parsers.cfparse('o status da resposta deve ser "{status_code}"'), target_fixture="context") -def check_response_status_code(context, status_code: str): - """ - Check if the response status code is the expected - """ - - assert context["response"].status_code == int(status_code) - return context - -@then( - parsers.cfparse('o JSON da resposta deve conter id "{item_id}" e nome "{item_name}"'), - target_fixture="context" -) -def check_response_json_contains_item_data(context, item_id: str, item_name: str): - """ - Check if the response JSON contains the item id and name - """ - - expected_data = { "id": item_id, "name": item_name } - assert context["response"].json()["data"] == expected_data - return context - - -""" Scenario: Obter todos os itens """ - -@scenario(scenario_name="Obter todos os itens", feature_name="../features/items.feature") -def test_get_items(): - """ Get all items """ - -# Step definitions for the "Obter todos os itens" scenario -@given(parsers.cfparse('o ItemService retorna uma lista de itens')) -def mock_item_service_response_list(): - """ - Mock the ItemService.get_items() method to return a list of items - """ - ItemService.get_items = lambda: HttpResponseModel( - message=HTTPResponses.ITEM_FOUND().message, - status_code=HTTPResponses.ITEM_FOUND().status_code, - data={ - 'items': [ - {"id": "123", "name": "Exemplo de Item"}, - {"id": "456", "name": "Outro Item"} - ] - } - ) - -@then(parsers.cfparse('o item com id "{item_id}" e nome "{item_name}" está na lista'), target_fixture="context") -@given(parsers.cfparse('o item com id "{item_id}" e nome "{item_name}" está na lista'), target_fixture="context") -def check_item_is_in_list(context, item_id: str, item_name: str): - """ - Check if the item with the given id and name is in the response list - """ - items = get_response_items_list(context["response"]) - - assert {"id": item_id, "name": item_name} in items - - return context - -@then(parsers.cfparse('o JSON da resposta deve ser uma lista de itens'), target_fixture="context") -def check_response_json_is_an_item_list(context): - """ - Check if the response JSON is a list of items - """ - - items = get_response_items_list(context["response"]) - - assert isinstance(items, list) - for item in items: - assert isinstance(item, dict) - assert "name" in item and isinstance(item["name"], str) - assert "id" in item and isinstance(item["id"], str) - - return context \ No newline at end of file +# from src.schemas.response import HTTPResponses, HttpResponseModel +# from pytest_bdd import parsers, given, when, then, scenario +# from src.service.impl.item_service import ItemService +# from src.tests.api.utils.utils import get_response_items_list, req_type_to_function + +# """ Scenario: Obter item por ID """ +# # This method is used to define the scenario name and feature file path +# @scenario(scenario_name="Obter item por ID", feature_name="../features/items.feature") +# def test_get_item(): +# """ Get item by id """ + +# # Step definitions for the "Obter item por ID" scenario +# @given(parsers.cfparse('o ItemService retorna um item com id "{item_id}" e nome "{item_name}"')) +# def mock_item_service_response(item_id: str, item_name: str): +# """ +# Mock the ItemService.get_item() method to return an item with the given id and name +# """ + +# ItemService.get_item = lambda id : HttpResponseModel( +# message=HTTPResponses.ITEM_FOUND().message, +# status_code=HTTPResponses.ITEM_FOUND().status_code, +# data={"id": item_id, "name": item_name} +# ) + +# @when( +# parsers.cfparse('uma requisição "{req_type}" for enviada para "{req_url}"'), +# target_fixture="context" +# ) +# def send_get_item_request(client, context, req_type: str, req_url: str): +# """ +# Send a request to the given URL using the given request type +# """ + +# response = req_type_to_function(client, req_type)(req_url) +# context["response"] = response +# return context + +# @then(parsers.cfparse('o status da resposta deve ser "{status_code}"'), target_fixture="context") +# def check_response_status_code(context, status_code: str): +# """ +# Check if the response status code is the expected +# """ + +# assert context["response"].status_code == int(status_code) +# return context + +# @then( +# parsers.cfparse('o JSON da resposta deve conter id "{item_id}" e nome "{item_name}"'), +# target_fixture="context" +# ) +# def check_response_json_contains_item_data(context, item_id: str, item_name: str): +# """ +# Check if the response JSON contains the item id and name +# """ + +# expected_data = { "id": item_id, "name": item_name } +# assert context["response"].json()["data"] == expected_data +# return context + + +# """ Scenario: Obter todos os itens """ + +# @scenario(scenario_name="Obter todos os itens", feature_name="../features/items.feature") +# def test_get_items(): +# """ Get all items """ + +# # Step definitions for the "Obter todos os itens" scenario +# @given(parsers.cfparse('o ItemService retorna uma lista de itens')) +# def mock_item_service_response_list(): +# """ +# Mock the ItemService.get_items() method to return a list of items +# """ +# ItemService.get_items = lambda: HttpResponseModel( +# message=HTTPResponses.ITEM_FOUND().message, +# status_code=HTTPResponses.ITEM_FOUND().status_code, +# data={ +# 'items': [ +# {"id": "123", "name": "Exemplo de Item"}, +# {"id": "456", "name": "Outro Item"} +# ] +# } +# ) + +# @then(parsers.cfparse('o item com id "{item_id}" e nome "{item_name}" está na lista'), target_fixture="context") +# @given(parsers.cfparse('o item com id "{item_id}" e nome "{item_name}" está na lista'), target_fixture="context") +# def check_item_is_in_list(context, item_id: str, item_name: str): +# """ +# Check if the item with the given id and name is in the response list +# """ +# items = get_response_items_list(context["response"]) + +# assert {"id": item_id, "name": item_name} in items + +# return context + +# @then(parsers.cfparse('o JSON da resposta deve ser uma lista de itens'), target_fixture="context") +# def check_response_json_is_an_item_list(context): +# """ +# Check if the response JSON is a list of items +# """ + +# items = get_response_items_list(context["response"]) + +# assert isinstance(items, list) +# for item in items: +# assert isinstance(item, dict) +# assert "name" in item and isinstance(item["name"], str) +# assert "id" in item and isinstance(item["id"], str) + +# return context \ No newline at end of file diff --git a/backend/src/tests/service/features/items-service.feature b/backend/src/tests/service/features/items-service.feature index 50735a2f..aaaa9236 100644 --- a/backend/src/tests/service/features/items-service.feature +++ b/backend/src/tests/service/features/items-service.feature @@ -1,12 +1,12 @@ -Feature: Items Service +# Feature: Items Service -# Service -Scenario: Obter todos os itens - Given o método getItems do ItemService retorna um array com o item de nome "item" e id "123" - When o método getItems do ItemService for chamado - Then o array retornado deve conter o item de nome "item" e id "123" +# # Service +# Scenario: Obter todos os itens +# Given o método getItems do ItemService retorna um array com o item de nome "item" e id "123" +# When o método getItems do ItemService for chamado +# Then o array retornado deve conter o item de nome "item" e id "123" -Scenario: Obter item por ID - Given o método getItem do ItemService retorna um item de nome "item" e id "123" - When o método getItem do ItemService for chamado com o id "123" - Then o item retornado deve ter o nome "item" e id "123" \ No newline at end of file +# Scenario: Obter item por ID +# Given o método getItem do ItemService retorna um item de nome "item" e id "123" +# When o método getItem do ItemService for chamado com o id "123" +# Then o item retornado deve ter o nome "item" e id "123" \ No newline at end of file diff --git a/backend/src/tests/service/features/payment_methods-service.feature b/backend/src/tests/service/features/payment_methods-service.feature new file mode 100644 index 00000000..68a20b08 --- /dev/null +++ b/backend/src/tests/service/features/payment_methods-service.feature @@ -0,0 +1,26 @@ +Feature: Payment Methods Service + +#Service +Scenario: Inserir cartão + # Given o cartao de nome "masterCard", número "4916123456789012", cvv "847", cpf "111.111.11-11" e validade "2024-02-17" não existe no banco de dados + Given o método inserting_card do PaymentMethodService retorna um json com message "método cadastrado com sucesso" e status_code = "201" + And o cartao "cartao" nao existe no banco de dados + When o método inserting_card do PaymentMethodService for chamado com cartao "cartao" + Then o cartao "cartao" for criado com sucesso + + +Scenario: Inserir cartão com cpf inválido + Given o cartao de nome "masterCard", número "4916123456789012", cvv "847", cpf "111.111.11" e validade "2024-02-17" não existe no banco de dados + When o método insert_card do PaymentDatabase for chamado com nome "masterCard", número "4916123456789012", cvv "847", cpf "111.111.111" e validade "2024-02-17" + Then o método insert_card do PaymentDatabase retorna sucess "True" e problemas = "[CPF]" + +Scenario: Inserir pix + Given o pix de nome "Breno Gabriel de Melo Lima" e cpf "222.222.222-22" não existe no banco de dados + When o método insert_pix do PaymentDatabase for chamado com nome "Breno Gabriel de Melo Lima" e cpf "222.222.222-22" + Then o pix de nome "Breno Gabriel de Melo Lima" e cpf "222.222.222-22" foi criado com sucesso + And o método insert_pix do PaymentDatabase retorna sucess "True" + +Scenario: Inserir pix com cpf incorreto + Given o pix de nome "Breno Gabriel de Melo Lima" e cpf "222.222.222" não existe no banco de dados + When o método insert_pix do PaymentDatabase for chamado com nome "Breno Gabriel de Melo Lima" e cpf "222.222.222" + Then o método insert_pix do PaymentDatabase retorna sucess "False" diff --git a/backend/src/tests/service/step_definitions/no_test_items_service.py b/backend/src/tests/service/step_definitions/no_test_items_service.py index 262c7723..71466055 100644 --- a/backend/src/tests/service/step_definitions/no_test_items_service.py +++ b/backend/src/tests/service/step_definitions/no_test_items_service.py @@ -1,81 +1,81 @@ -from src.schemas.response import HTTPResponses, HttpResponseModel -from pytest_bdd import parsers, given, when, then, scenario -from src.service.impl.item_service import ItemService +# from src.schemas.response import HTTPResponses, HttpResponseModel +# from pytest_bdd import parsers, given, when, then, scenario +# from src.service.impl.item_service import ItemService -""" Scenario: Obter item por ID """ -# This method is used to define the scenario name and feature file path -@scenario(scenario_name="Obter item por ID", feature_name="../features/items-service.feature") -def test_service_get_item(): - """ Get item by id """ +# """ Scenario: Obter item por ID """ +# # This method is used to define the scenario name and feature file path +# @scenario(scenario_name="Obter item por ID", feature_name="../features/items-service.feature") +# def test_service_get_item(): +# """ Get item by id """ -# Step definitions for the "Obter item por ID" scenario -@given(parsers.cfparse( - 'o método getItem do ItemService retorna um item de nome "{item_name}" e id "{item_id}"')) -def mock_item_service(item_id: str, item_name: str): - """ - Mock the ItemService.get_item() method to return an item with the given id and name - """ +# # Step definitions for the "Obter item por ID" scenario +# @given(parsers.cfparse( +# 'o método getItem do ItemService retorna um item de nome "{item_name}" e id "{item_id}"')) +# def mock_item_service(item_id: str, item_name: str): +# """ +# Mock the ItemService.get_item() method to return an item with the given id and name +# """ - ItemService.get_item = lambda id : HttpResponseModel( - message=HTTPResponses.ITEM_FOUND().message, - status_code=HTTPResponses.ITEM_FOUND().status_code, - data={"id": item_id, "name": item_name} - ) +# ItemService.get_item = lambda id : HttpResponseModel( +# message=HTTPResponses.ITEM_FOUND().message, +# status_code=HTTPResponses.ITEM_FOUND().status_code, +# data={"id": item_id, "name": item_name} +# ) -@when( - parsers.cfparse('o método getItem do ItemService for chamado com o id "{item_id}"'), - target_fixture="context" -) -def get_item(context, item_id: str): - context['item'] = ItemService.get_item(item_id) - return context +# @when( +# parsers.cfparse('o método getItem do ItemService for chamado com o id "{item_id}"'), +# target_fixture="context" +# ) +# def get_item(context, item_id: str): +# context['item'] = ItemService.get_item(item_id) +# return context -@then( - parsers.cfparse('o item retornado deve ter o nome "{item_name}" e id "{item_id}"'), - target_fixture="context" -) -def check_received_item(context, item_name: str, item_id: str): - assert context['item'].data.name == item_name - assert context['item'].data.id == item_id +# @then( +# parsers.cfparse('o item retornado deve ter o nome "{item_name}" e id "{item_id}"'), +# target_fixture="context" +# ) +# def check_received_item(context, item_name: str, item_id: str): +# assert context['item'].data.name == item_name +# assert context['item'].data.id == item_id - return context +# return context -""" Scenario: Obter todos os itens """ +# """ Scenario: Obter todos os itens """ -@scenario(scenario_name="Obter todos os itens", feature_name="../features/items-service.feature") -def test_service_get_items(): - """ Get all items """ +# @scenario(scenario_name="Obter todos os itens", feature_name="../features/items-service.feature") +# def test_service_get_items(): +# """ Get all items """ -# Step definitions for the "Obter todos os itens" scenario -@given(parsers.cfparse( - 'o método getItems do ItemService retorna um array com o item de nome "{item_name}" e id "{item_id}"')) -def mock_item_service_list(item_name: str, item_id: str): +# # Step definitions for the "Obter todos os itens" scenario +# @given(parsers.cfparse( +# 'o método getItems do ItemService retorna um array com o item de nome "{item_name}" e id "{item_id}"')) +# def mock_item_service_list(item_name: str, item_id: str): - ItemService.get_items = lambda: HttpResponseModel( - message=HTTPResponses.ITEM_FOUND().message, - status_code=HTTPResponses.ITEM_FOUND().status_code, - data={ - 'items': [{"id": item_id, "name": item_name}] - } - ) +# ItemService.get_items = lambda: HttpResponseModel( +# message=HTTPResponses.ITEM_FOUND().message, +# status_code=HTTPResponses.ITEM_FOUND().status_code, +# data={ +# 'items': [{"id": item_id, "name": item_name}] +# } +# ) -@when( - parsers.cfparse('o método getItems do ItemService for chamado'), - target_fixture="context" -) -def get_service_items(context): - context['items'] = ItemService.get_items() - return context +# @when( +# parsers.cfparse('o método getItems do ItemService for chamado'), +# target_fixture="context" +# ) +# def get_service_items(context): +# context['items'] = ItemService.get_items() +# return context -@then( - parsers.cfparse('o item retornado deve ter o nome "{item_name}" e id "{item_id}"'), - target_fixture="context" -) -def check_service_list(context, item_id: str, item_name: str): - """ - Check if the item with the given id and name is in the response list - """ +# @then( +# parsers.cfparse('o item retornado deve ter o nome "{item_name}" e id "{item_id}"'), +# target_fixture="context" +# ) +# def check_service_list(context, item_id: str, item_name: str): +# """ +# Check if the item with the given id and name is in the response list +# """ - assert [{"id": item_id, "name": item_name}] == context['items'].data['items'] +# assert [{"id": item_id, "name": item_name}] == context['items'].data['items'] - return context \ No newline at end of file +# return context \ No newline at end of file diff --git a/backend/src/tests/service/step_definitions/test_payment_methods_service.py b/backend/src/tests/service/step_definitions/test_payment_methods_service.py new file mode 100644 index 00000000..60248a63 --- /dev/null +++ b/backend/src/tests/service/step_definitions/test_payment_methods_service.py @@ -0,0 +1,96 @@ + +# from pytest_bdd import parsers, given, when, then, scenario +# from src.db.payment_database import * +# from src.service.impl.payment_method_service import PaymentService +# from src.schemas.response import HttpResponseModel, HTTPResponses +# from src.schemas.payment_schema import * +# import typing +# import datetime + + +# @scenario(feature_name="../features/payment_methods-service.feature", +# scenario_name="Inserir cartão") + +# def test_payment_method_service(): +# pass + +# # @given(parsers.cfparse( +# # 'o cartao de nome {nome}, número {numero}, cvv {cvv}, cpf {cpf} e validade {validade} não existe no banco de dados' +# # )) + +# # def checar_nao_existencia(numero): + + + +# @given(parsers.cfparse( +# 'o método inserting_card do PaymentMethodService retorna um json com message {mensagem} e status_code = {status}' +# )) + +# def mock_insert_service(mensagem: str, status: int ): + +# PaymentService.inserting_card = lambda id: HttpResponseModel( +# message=mensagem, +# status_code=status +# ) + +# @when(parsers.cfparse( +# 'o método inserting_card do PaymentMethodService for chamado com cartao {cartao}' +# ), target_fixture = "context") + +# def insertion_card(context, cartao: Cartao): +# context["result"] = PaymentService.inserting_card(cartao) +# print(context) +# return context + +# @then(parsers.cfparse( +# 'o método inserting_card do PaymentMethodService retorna um json com message "método cadastrado com sucesso" e status_code = "201"', +# ), target_fixture = "context") +# def check_response(context, sucesso_esperado: bool, problemas_esperados: List[str]): + +# sucesso_retornado, problemas_retornados = context["result"] + +# sucesso_retornado = str(sucesso_retornado) +# problemas_esperados = str(problemas_esperados) + +# assert bool(sucesso_retornado) == bool(sucesso_esperado) +# assert problemas_retornados == json.loads(problemas_esperados.replace("'", '"')) + +# return context + +from src.main import app +from unittest.mock import MagicMock +from unittest.mock import patch +from fastapi.testclient import TestClient +from src.db import database as db +from datetime import datetime, timezone +from unittest.mock import patch, MagicMock +from src.service.impl.payment_method_service import PaymentService +from src.schemas.payment_schema import * + +def test_add_card(client: TestClient): + + mock_json = { + "message": "metodo de pagamento cadastrado com sucesso", + "status_code": 201, + "data": None + } + + body = { + "nome_cartao": "MasterCard", + "numero_cartao": "6011234567890123", + "cvv": "456", + "cpf": "111.111.111-11", + "validade": "2024-02-18" + } + + PaymentService.inserting_card = MagicMock(return_value = mock_json) + + response = client.post("/backend/api/payment/inserting/cartao", json=body) + + assert response.status_code == 201 + assert response.json() == { + "message": "metodo de pagamento cadastrado com sucesso", + "status_code": 201, + "data": None + } + diff --git a/features/payment_method.feature b/features/payment_method.feature new file mode 100644 index 00000000..051ba485 --- /dev/null +++ b/features/payment_method.feature @@ -0,0 +1,108 @@ +Feature: Cadastro e manutenção de métodos de pagamento (inserir, remover, atualizar) +As a usuário cadastrado +I want inserir, remover ou atualizar os métodos de pagamento +So that eu posso realizar o pagamento de minhas compras + +Scenario: Inserção bem-sucedida de boleto como método de pagamento +Given o usuário de email "usuário@gmail.com" está na página "Métodos de pagamento" +When usuário clicar na opção "inserir método de pagamento" +And seleciona o a opção "Boleto" +And preencher o campo "Nome completo" com "Breno Gabriel de Melo Lima" +And preencher o campo "CPF" com "925.830.910-34" +And o usuário clica em "Confirmar" +Then o método de pagamento será cadastrado no sistema + +Scenario: Inserção bem-sucedida de Pix como método de pagamento +Given o usuário de email "usuário@gmail.com" está na página "Métodos de pagamento" +When usuário clicar na opção "inserir método de pagamento" +And seleciona o a opção "Pix" +And preencher o campo "Nome completo" com "Breno Gabriel de Melo Lima" +And preencher o campo "CPF" com "925.830.910-34" +And o usuário clica em "Confirmar" +Then o método de pagamento será cadastrado no sistema + +Scenario: Inserção bem-sucedida de cartão como método de pagamento +Given o usuário de email "usuário@gmail.com" está na página "Métodos de pagamento" +When usuário clicar na opção "inserir método de pagamento" +And seleciona o a opção "Cartão" +And preencher o campo "Nome do cartão" com "Nubank" +And preencher o campo "CPF" com "925.830.910-34" +And preencher o campo "CVV" com "1234" +And preencher o campo "Número do cartão" com "12345678" +And preencher o campo "Validade" com "06/2028" +And o usuário clica em "Confirmar" +Then o método de pagamento será cadastrado no sistema + +Scenario: Inserção mal-sucedida de boleto como método de pagamento +Given o usuário de email "usuário@gmail.com" está na página "Métodos de pagamento" +When usuário clicar na opção "inserir método de pagamento" +And seleciona o a opção "Boleto" +And preencher o campo "Nome completo" com "Breno Gabriel de Melo Lima" +And preencher o campo "CPF" com "925.830.910" +And o usuário clica em "Confirmar" +Then o usuário visualiza a mensagem "Informações inválidas" + +Scenario: Inserção mal-sucedida de Pix como método de pagamento +Given o usuário de email "usuário@gmail.com" está na página "Métodos de pagamento" +When usuário clicar na opção "inserir método de pagamento" +And seleciona o a opção "Boleto" +And preencher o campo "Nome completo" com "Breno Gabriel de Melo Lima" +And preencher o campo "CPF" com "925.830.910" +And o usuário clica em "Confirmar" +Then o usuário visualiza a mensagem "Informações inválidas" + +Scenario: Inserção mal-sucedida de cartão como método de pagamento devido a CPF inválido +Given o usuário de email "usuário@gmail.com" está na página "Métodos de pagamento" +When usuário clicar na opção "inserir método de pagamento" +And seleciona o a opção "Cartão" +And preencher o campo "Nome do cartão" com "Nubank" +And preencher o campo "CPF" com "925.830.910" +And preencher o campo "CVV" com "1234" +And preencher o campo "Número do cartão" com "12345678" +And preencher o campo "Validade" com "06/2028" +And o usuário clica em "Confirmar" +Then o usuário visualiza a mensagem "Informações inválidas" + +Scenario: Inserção mal-sucedida de cartão como método de pagamento devido a CVV inválido +Given o usuário de email "usuário@gmail.com" está na página "Métodos de pagamento" +When usuário clicar na opção "inserir método de pagamento" +And seleciona o a opção "Cartão" +And preencher o campo "Nome do cartão" com "Nubank" +And preencher o campo "CPF" com "925.830.910-34" +And preencher o campo "CVV" com "123" +And preencher o campo "Número do cartão" com "12345678" +And preencher o campo "Validade" com "06/2028" +And o usuário clica em "Confirmar" +Then o usuário visualiza a mensagem "Informações inválidas" + +Scenario: Inserção mal-sucedida de cartão como método de pagamento devido a número de cartão inválido +Given o usuário de email "usuário@gmail.com" está na página "Métodos de pagamento" +When usuário clicar na opção "inserir método de pagamento" +And seleciona o a opção "Cartão" +And preencher o campo "Nome do cartão" com "Nubank" +And preencher o campo "CPF" com "925.830.910" +And preencher o campo "CVV" com "1234" +And preencher o campo "Número do cartão" com "123456" +And preencher o campo "Validade" com "06/2028" +And o usuário clica em "Confirmar" +Then o usuário visualiza a mensagem "Informações inválidas" + +Scenario: Inserção mal-sucedida de cartão como método de pagamento devido a data de validade inválida +Given o usuário de email "usuário@gmail.com" está na página "Métodos de pagamento" +When usuário clicar na opção "inserir método de pagamento" +And seleciona o a opção "Cartão" +And preencher o campo "Nome do cartão" com "Nubank" +And preencher o campo "CPF" com "925.830.910" +And preencher o campo "CVV" com "1234" +And preencher o campo "Número do cartão" com "12345678" +And preencher o campo "Validade" com "06/2020" +And o usuário clica em "Confirmar" +Then o usuário visualiza a mensagem "Informações inválidas" + +Scenario: Atualização bem-sucedida de informações do cartão +Given o usuário de email "usuário@gmail.com" está na página "Métodos de pagamento" +When usuário clicar na opção "atualizar método de pagamento" +And selecionar a opção "nubank" +And atualizar o campo "Nome completo" com "Maria Alvez da Cunha" +And o usuário clica em "Confirmar" +Then o usuário visualiza a mensagem "Informações atualizadas"