-
Notifications
You must be signed in to change notification settings - Fork 17
Feat/test #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
AxelPCG
wants to merge
11
commits into
main
Choose a base branch
from
feat/test
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Feat/test #6
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
7084489
feat: implement initial checkout processing logic with simulated paym…
AxelPCG f4f13b3
refactor: refatoração checkour-chaos
AxelPCG 6f4ee33
feat: add static frontend e integração com FastAPI
AxelPCG fc99f57
Refactor code structure for improved readability and maintainability
AxelPCG 3c5490b
fix: set PYTHONPATH environment variable for test coverage
AxelPCG 38647ea
feat: add coverage configuration to pytest settings
AxelPCG 1e54269
feat: add unit tests for checkout process and cart functionality
AxelPCG dd3e97c
feat: update CI workflow to install development dependencies
AxelPCG c3dff17
feat: update CI workflow to install development dependencies correctly
AxelPCG e1323d7
feat: add step to create virtual environment in CI workflow
AxelPCG ca12b5b
feat: update backlog and add product requirements document for authen…
AxelPCG File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ "main" ] | ||
| pull_request: | ||
| branches: [ "main" ] | ||
|
|
||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.11' | ||
|
|
||
| - name: Install uv | ||
| run: | | ||
| pip install uv | ||
| uv --version | ||
| - name: Create Virtual Environment | ||
| run: uv venv | ||
| - name: Install dependencies | ||
| run: uv pip install -e ".[dev]" | ||
| - name: Run tests with coverage | ||
| run: uv run pytest --cov=src --cov-fail-under=80 | ||
| env: | ||
| PYTHONPATH: . | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,28 @@ | ||
| # Backlog da Sprint - Fluxo de Carrinho | ||
| # Backlog do Produto - TechShop | ||
|
|
||
| ## User Stories | ||
| Este documento rastreia todas as User Stories planejadas para o projeto TechShop. | ||
|
|
||
| 1. **US001 - Adicionar item ao carrinho:** | ||
| * **Como um** usuário, | ||
| * **Eu quero** adicionar um produto ao meu carrinho de compras a partir da página de detalhes do produto, | ||
| * **Para que** eu possa comprá-lo posteriormente. | ||
| ## MVP - Carrinho e Checkout | ||
|
|
||
| 2. **US002 - Visualizar total do carrinho:** | ||
| * **Como um** usuário, | ||
| * **Eu quero** ver o valor total dos itens no meu carrinho, | ||
| * **Para que** eu saiba o custo total da minha compra antes de ir para o checkout. | ||
| ### Gerenciamento de Carrinho | ||
| - **US1:** Como cliente, quero poder adicionar produtos ao meu carrinho de compras para que eu possa comprá-los mais tarde. ([Issue #11](https://github.com/AxelPCG/techshop/issues/11)) | ||
| - **US2:** Como cliente, quero poder remover produtos do meu carrinho de compras para ajustar meu pedido. ([Issue #12](https://github.com/AxelPCG/techshop/issues/12)) | ||
| - **US3:** Como cliente, quero poder visualizar os produtos no meu carrinho para revisar meu pedido antes de finalizar a compra. ([Issue #13](https://github.com/AxelPCG/techshop/issues/13)) | ||
|
|
||
| 3. **US003 - Remover item do carrinho:** | ||
| * **Como um** usuário, | ||
| * **Eu quero** remover um item do meu carrinho de compras, | ||
| * **Para que** eu possa gerenciar os produtos que desejo comprar. | ||
| ### Visualização de Produtos | ||
| - **US6:** Como cliente, quero poder navegar por uma lista de produtos para descobrir o que está disponível na loja. ([Issue #16](https://github.com/AxelPCG/techshop/issues/16)) | ||
| - **US7:** Como cliente, quero poder ver os detalhes de um produto específico para tomar uma decisão de compra informada. ([Issue #17](https://github.com/AxelPCG/techshop/issues/17)) | ||
|
|
||
| ### Checkout | ||
| - **US4:** Como cliente, quero poder iniciar o processo de checkout a partir do meu carrinho de compras para finalizar minha compra. ([Issue #14](https://github.com/AxelPCG/techshop/issues/14)) | ||
| - **US5:** Como cliente, quero poder inserir minhas informações de envio e pagamento durante o checkout para que o pedido possa ser processado e entregue. ([Issue #15](https://github.com/AxelPCG/techshop/issues/15)) | ||
|
|
||
| ## Próximos Passos - Autenticação e Perfis | ||
|
|
||
| ### Autenticação | ||
| - **US8:** Como um novo usuário, quero poder me cadastrar na plataforma usando e-mail e senha para ter uma conta pessoal. ([Issue #7](https://github.com/AxelPCG/techshop/issues/7)) | ||
| - **US9:** Como um usuário cadastrado, quero poder fazer login no sistema para acessar minha conta e histórico. ([Issue #8](https://github.com/AxelPCG/techshop/issues/8)) | ||
| - **US10:** Como um usuário logado, quero poder fazer logout para proteger minha conta em dispositivos compartilhados. ([Issue #9](https://github.com/AxelPCG/techshop/issues/9)) | ||
|
|
||
| ### Perfil de Usuário | ||
| - **US11:** Como um usuário logado, quero ter uma página de perfil onde posso ver e editar minhas informações básicas (como nome e endereço de entrega). ([Issue #10](https://github.com/AxelPCG/techshop/issues/10)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| ### Product Requirements Document: Autenticação e Perfis de Usuário | ||
|
|
||
| #### 1. Objetivo | ||
| Implementar um sistema de autenticação seguro que permita aos usuários criar contas, fazer login/logout e gerenciar um perfil básico. Isso é fundamental para personalizar a experiência de compra e permitir o rastreamento de pedidos. | ||
|
|
||
| #### 2. Requisitos Funcionais | ||
| * **US8:** Como um novo usuário, quero poder me cadastrar na plataforma usando e-mail and senha para ter uma conta pessoal. | ||
|
|
||
| * **US9:** Como um usuário cadastrado, quero poder fazer login no sistema para acessar minha conta e histórico. | ||
| * **US10:** Como um usuário logado, quero poder fazer logout para proteger minha conta em dispositivos compartilhados. | ||
| * **US11:** Como um usuário logado, quero ter uma página de perfil onde posso ver e editar minhas informações básicas (como nome e endereço de entrega). | ||
|
|
||
| #### 3. Requisitos Não-Funcionais | ||
| * **Segurança:** Senhas devem ser armazenadas de forma segura (hashed). O sistema deve ter proteção contra ataques comuns (ex: brute-force). | ||
| * **Usabilidade:** O processo de login e cadastro deve ser simples e intuitivo. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| # checkout.py - Refatorado | ||
|
|
||
| from typing import List, Dict, Any | ||
| from .models import CartItem, UserData | ||
|
|
||
| # --- Protocolos para Injeção de Dependência --- | ||
|
|
||
| class StockValidatorProtocol: | ||
| def validate(self, items: List[CartItem]) -> bool: ... | ||
|
|
||
| class ShippingServiceProtocol: | ||
| def calculate(self, items: List[CartItem]) -> float: ... | ||
|
|
||
| class DiscountServiceProtocol: | ||
| def calculate(self, total_value: float, user: UserData) -> float: ... | ||
|
|
||
| class PaymentServiceProtocol: | ||
|
Comment on lines
+3
to
+17
Comment on lines
+3
to
+17
|
||
| def process(self, user: UserData, final_amount: float) -> Dict[str, Any]: ... | ||
|
|
||
| # --- Implementações Concretas dos Serviços --- | ||
|
|
||
| class StockValidator: | ||
| """Valida o estoque dos itens (implementação mock).""" | ||
| def validate(self, items: List[CartItem]) -> bool: | ||
| print("--- Validando estoque... ---") | ||
|
Comment on lines
+22
to
+25
|
||
| for item in items: | ||
| # Lógica de estoque mockada | ||
| mock_stock = 10 | ||
|
Comment on lines
+24
to
+28
|
||
| if item.quantity > mock_stock: | ||
| print(f"ERRO: Estoque insuficiente para o produto {item.product.id}.") | ||
| raise ValueError(f"Estoque insuficiente para {item.product.name}") | ||
| print("--- Estoque OK. ---") | ||
| return True | ||
|
Comment on lines
+24
to
+33
|
||
|
|
||
| class ShippingService: | ||
| """Calcula o frete (valor fixo para este exemplo).""" | ||
| _SHIPPING_FEE = 15.50 | ||
|
|
||
| def calculate(self, items: List[CartItem]) -> float: | ||
| print(f"--- Calculando frete fixo de R$ {self._SHIPPING_FEE}... ---") | ||
| return self._SHIPPING_FEE | ||
|
|
||
| class DiscountService: | ||
| """Calcula descontos com base no valor total e no tipo de usuário.""" | ||
| def calculate(self, total_value: float, user: UserData) -> float: | ||
| print("--- Calculando descontos... ---") | ||
| if total_value > 1000: | ||
| print("--- Aplicando desconto de 20% (compras > R$ 1000) ---") | ||
| return total_value * 0.80 | ||
| if total_value > 500: | ||
| print("--- Aplicando desconto de 10% (compras > R$ 500) ---") | ||
| return total_value * 0.90 | ||
| if user.is_vip: | ||
| print("--- Aplicando desconto de 15% (usuário VIP) ---") | ||
| return total_value * 0.85 | ||
| return total_value | ||
|
|
||
| class FakePaymentAPI: | ||
| """Simula uma API de pagamento externa.""" | ||
| def charge(self, amount: float, user_id: int) -> Dict[str, Any]: | ||
| print(f"--- Processando pagamento de R$ {amount:.2f} para o usuário {user_id}... ---") | ||
| if 0 < amount < 9999: | ||
| print("--- Pagamento APROVADO (simulado) ---") | ||
| return {"status": "pagamento_aprovado", "transaction_id": "xyz123abc"} | ||
| else: | ||
| print("--- Pagamento RECUSADO (simulado) ---") | ||
| raise ConnectionError("Falha ao processar pagamento: valor inválido.") | ||
|
|
||
|
Comment on lines
+60
to
+68
|
||
| class PaymentService: | ||
| """Serviço que interage com a API de pagamento.""" | ||
| def __init__(self, payment_api: FakePaymentAPI): | ||
| self.payment_api = payment_api | ||
|
Comment on lines
+69
to
+72
|
||
|
|
||
| def process(self, user: UserData, final_amount: float) -> Dict[str, Any]: | ||
| return self.payment_api.charge(amount=final_amount, user_id=user.id) | ||
|
|
||
| # --- Orquestrador do Checkout --- | ||
|
|
||
| def process_checkout( | ||
| cart_items: List[CartItem], | ||
| user_data: UserData, | ||
| stock_validator: StockValidatorProtocol, | ||
| shipping_service: ShippingServiceProtocol, | ||
| discount_service: DiscountServiceProtocol, | ||
| payment_service: PaymentServiceProtocol, | ||
| ) -> Dict[str, Any]: | ||
| """ | ||
| Orquestra o processo de checkout utilizando injeção de dependência. | ||
| """ | ||
| print("--- Iniciando processo de checkout refatorado ---") | ||
|
|
||
| # 1. Validar estoque | ||
| stock_validator.validate(cart_items) | ||
|
|
||
| # 2. Calcular total inicial | ||
| subtotal = sum(item.product.price * item.quantity for item in cart_items) | ||
| print(f"Subtotal: R$ {subtotal:.2f}") | ||
|
|
||
| # 3. Calcular frete | ||
| shipping_cost = shipping_service.calculate(cart_items) | ||
| total_with_shipping = subtotal + shipping_cost | ||
| print(f"Total com frete: R$ {total_with_shipping:.2f}") | ||
|
|
||
| # 4. Aplicar descontos | ||
| final_amount = discount_service.calculate(total_with_shipping, user_data) | ||
| print(f"Valor final após descontos: R$ {final_amount:.2f}") | ||
|
|
||
| # 5. Processar pagamento | ||
| payment_result = payment_service.process(user_data, round(final_amount, 2)) | ||
|
|
||
| print(f"--- Checkout finalizado! Resultado: {payment_result} ---") | ||
| return {"success": True, "payment_details": payment_result} | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,102 @@ | ||
| from fastapi import FastAPI | ||
| from fastapi import FastAPI, Depends, HTTPException | ||
| from fastapi.staticfiles import StaticFiles | ||
| from typing import List, Dict, Any | ||
|
|
||
| app = FastAPI() | ||
| from .models import CartItem, UserData, Product | ||
| from .cart import ShoppingCart | ||
|
Comment on lines
+1
to
+6
Comment on lines
+3
to
+6
|
||
| from .checkout import ( | ||
| process_checkout, | ||
| StockValidator, | ||
| ShippingService, | ||
| DiscountService, | ||
| PaymentService, | ||
| FakePaymentAPI, | ||
| ) | ||
|
|
||
| app = FastAPI( | ||
| title="TechShop API", | ||
| description="API para um e-commerce de tecnologia, com gerenciamento de carrinho e checkout.", | ||
| version="1.0.0", | ||
| ) | ||
|
|
||
| # Monta a pasta 'static' para servir arquivos estáticos (nosso frontend) | ||
| app.mount("/app", StaticFiles(directory="static", html=True), name="static") | ||
|
|
||
|
Comment on lines
+22
to
+24
|
||
| # --- Simulação de um "banco de dados" em memória para o carrinho --- | ||
| # Em uma aplicação real, isso seria substituído por um banco de dados (Redis, PostgreSQL, etc.) | ||
| shopping_cart_db: Dict[int, ShoppingCart] = { | ||
| 1: ShoppingCart() # Carrinho para o usuário com ID 1 | ||
| } | ||
|
|
||
| # --- Injeção de Dependências (FastAPI Depends) --- | ||
|
|
||
| def get_cart(user_id: int = 1) -> ShoppingCart: | ||
| """Retorna a instância do carrinho de compras para o usuário.""" | ||
| cart = shopping_cart_db.get(user_id) | ||
| if not cart: | ||
| raise HTTPException(status_code=404, detail="Carrinho não encontrado para o usuário.") | ||
| return cart | ||
|
|
||
| # Instâncias dos serviços que serão injetados | ||
| stock_validator = StockValidator() | ||
| shipping_service = ShippingService() | ||
| discount_service = DiscountService() | ||
| payment_api = FakePaymentAPI() | ||
| payment_service = PaymentService(payment_api=payment_api) | ||
|
|
||
|
|
||
| # --- Rotas da API --- | ||
|
|
||
| @app.post("/cart/add", response_model=CartItem, tags=["Carrinho"]) | ||
| def add_to_cart(item: CartItem, cart: ShoppingCart = Depends(get_cart)): | ||
| """ | ||
| Adiciona um item ao carrinho de compras do usuário. | ||
|
|
||
| Se o produto já existir no carrinho, a quantidade será somada. | ||
| """ | ||
| cart.add_item(item.product, item.quantity) | ||
| return item | ||
|
|
||
|
Comment on lines
+57
to
+59
|
||
| @app.get("/cart", tags=["Carrinho"]) | ||
| def get_cart_details(cart: ShoppingCart = Depends(get_cart)): | ||
| """ | ||
| Retorna os detalhes do carrinho de compras do usuário, incluindo | ||
| os itens, o subtotal e o total com descontos aplicados. | ||
| """ | ||
| return { | ||
| "items": cart.items, | ||
| "subtotal": cart.calculate_total(), | ||
| "total_with_discount": cart.calculate_total_with_discount(), | ||
| } | ||
|
|
||
| @app.post("/checkout", tags=["Checkout"]) | ||
| def perform_checkout(user_data: UserData, cart: ShoppingCart = Depends(get_cart)): | ||
| """ | ||
|
Comment on lines
+50
to
+74
|
||
| Processa o checkout do carrinho de compras. | ||
|
|
||
| Realiza a validação de estoque, cálculo de frete, aplicação de descontos | ||
| e processamento do pagamento. | ||
| """ | ||
| if not cart.items: | ||
| raise HTTPException(status_code=400, detail="Carrinho está vazio.") | ||
|
|
||
| try: | ||
|
Comment on lines
+72
to
+83
|
||
| result = process_checkout( | ||
| cart_items=cart.items, | ||
| user_data=user_data, | ||
| stock_validator=stock_validator, | ||
| shipping_service=shipping_service, | ||
| discount_service=discount_service, | ||
| payment_service=payment_service, | ||
| ) | ||
| # Limpa o carrinho após checkout bem-sucedido | ||
| cart.items.clear() | ||
| return {"status": "Sucesso", "details": result} | ||
| except ValueError as e: | ||
| raise HTTPException(status_code=400, detail=str(e)) | ||
| except ConnectionError as e: | ||
| raise HTTPException(status_code=503, detail=str(e)) | ||
| except Exception as e: | ||
| # Captura genérica para outros erros inesperados | ||
| raise HTTPException(status_code=500, detail=f"Ocorreu um erro inesperado: {e}") | ||
|
Comment on lines
+99
to
+101
Comment on lines
+99
to
+101
|
||
|
|
||
| @app.get("/") | ||
| def read_root(): | ||
| return {"status": "ok"} | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.