Skip to content

Commit d395034

Browse files
committed
Merge branch 'ci'
2 parents b9677e4 + bd28465 commit d395034

File tree

11 files changed

+122
-34
lines changed

11 files changed

+122
-34
lines changed

.github/workflows/ci.yml

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,62 @@
1-
name: CI
2-
on: [push]
1+
name: CI Pipeline
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
pull_request:
7+
branches: ["main"]
8+
39
jobs:
4-
test:
10+
test-backend:
11+
name: Test Backend + Lint + Build
512
runs-on: ubuntu-latest
13+
14+
services:
15+
postgres:
16+
image: postgres:15
17+
env:
18+
POSTGRES_USER: postgres
19+
POSTGRES_PASSWORD: 123
20+
POSTGRES_DB: listacompras_test
21+
ports:
22+
- 5432:5432
23+
options: >-
24+
--health-cmd="pg_isready -U postgres"
25+
--health-interval=5s
26+
--health-timeout=5s
27+
--health-retries=5
28+
629
steps:
7-
- uses: actions/checkout@v4
8-
- uses: actions/setup-python@v5
30+
31+
- name: Checkout código
32+
uses: actions/checkout@v4
33+
34+
- name: Configurar Python
35+
uses: actions/setup-python@v5
936
with:
10-
python-version: '3.11'
11-
- run: pip install -r backend/requirements.txt
12-
- run: pytest backend/tests/
37+
python-version: '3.12'
38+
39+
- name: Instalar dependências do backend
40+
working-directory: backend
41+
run: |
42+
pip install --upgrade pip
43+
pip install -r requirements.txt
44+
pip install flake8
45+
46+
- name: Rodar flake8 (lint)
47+
working-directory: backend
48+
run: |
49+
flake8 . --max-line-length=120
50+
51+
- name: Rodar testes (pytest)
52+
working-directory: backend
53+
env:
54+
DATABASE_URL: postgresql://postgres:123@localhost:5432/listacompras_test
55+
TEST_DATABASE_URL: postgresql://postgres:123@localhost:5432/listacompras_test
56+
SECRET_KEY: "testingkey"
57+
run: |
58+
pytest -q --disable-warnings --maxfail=1
59+
60+
- name: Testar build do Docker (backend e frontend)
61+
run: |
62+
docker compose -f docker-compose.yml build

backend/database.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
1-
# backend/database.py
21
import os
32
from dotenv import load_dotenv
43
from sqlalchemy import create_engine
54
from sqlalchemy.orm import sessionmaker
65

7-
# 📌 Carrega .env da pasta backend APENAS se existir
8-
# (Docker sempre usa variáveis de ambiente externas)
96
env_path = os.path.join(os.path.dirname(__file__), '.env')
107
if os.path.exists(env_path):
118
load_dotenv(env_path)
129

13-
# 🔄 Docker ou o sistema operacional podem sobrescrever
1410
DATABASE_URL = os.getenv("DATABASE_URL")
1511

1612
if not DATABASE_URL:
1713
raise ValueError("❌ Erro: DATABASE_URL não encontrada no ambiente ou backend/.env")
1814

19-
# 🚀 Engine pronto para SQLAlchemy 2.x e Docker
2015
engine = create_engine(
2116
DATABASE_URL,
2217
pool_pre_ping=True, # evita conexões quebradas
@@ -29,6 +24,7 @@
2924
bind=engine
3025
)
3126

27+
3228
def get_db():
3329
db = SessionLocal()
3430
try:

backend/main.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
import sys
22
import os
3-
4-
backend_dir = os.path.dirname(os.path.abspath(__file__))
5-
if backend_dir not in sys.path:
6-
sys.path.insert(0, backend_dir)
7-
83
from fastapi import FastAPI
94
from fastapi.middleware.cors import CORSMiddleware
105
from models import Base
116
from database import engine
127
from routes.auth import router as auth_router
138
from routes.carts import router as cart_router
149

15-
# ✅ CRIA AS TABELAS NO BANCO (se não existirem)
10+
backend_dir = os.path.dirname(os.path.abspath(__file__))
11+
if backend_dir not in sys.path:
12+
sys.path.insert(0, backend_dir)
13+
1614
Base.metadata.create_all(bind=engine)
1715

1816
app = FastAPI(title="Lista de Compras API")
@@ -30,4 +28,4 @@
3028

3129
@app.get("/")
3230
def root():
33-
return {"message": "API ListaCompras está rodando 🚀"}
31+
return {"message": "API ListaCompras está rodando 🚀"}

backend/routes/auth.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
from fastapi import APIRouter, Depends, HTTPException
33
from sqlalchemy.orm import Session
44
from pydantic import BaseModel
5-
65
from models import User
76
from database import get_db
87
from security import create_access_token
98

9+
1010
router = APIRouter(tags=["Auth"])
1111

12+
1213
class UserCreate(BaseModel):
1314
username: str
1415
password: str

backend/routes/carts.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
from fastapi import APIRouter, Depends, HTTPException
22
from sqlalchemy.orm import Session
33
from pydantic import BaseModel
4-
54
from models import Cart, Item, User
65
from database import get_db
76
from security import get_current_user
87

8+
99
router = APIRouter(tags=["Carrinhos"])
1010

11+
1112
class CartCreate(BaseModel):
1213
name: str
1314

@@ -19,6 +20,7 @@ class ItemCreate(BaseModel):
1920
class BulkItems(BaseModel):
2021
items: list[str]
2122

23+
2224
@router.post("/carrinhos")
2325
def create_cart(
2426
cart: CartCreate,

backend/tests/conftest.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from sqlalchemy import create_engine
44
from sqlalchemy.orm import sessionmaker
55
from fastapi.testclient import TestClient
6-
76
from backend.models import Base
87
from backend.main import app
98
from backend.database import get_db
@@ -24,7 +23,6 @@
2423
)
2524

2625

27-
# 🔥 Cria e limpa o banco INTEIRO
2826
@pytest.fixture(scope="session", autouse=True)
2927
def setup_test_db():
3028
print("✅ BANCO DE TESTE EM USO:", TEST_DATABASE_URL)
@@ -34,9 +32,6 @@ def setup_test_db():
3432
Base.metadata.drop_all(bind=engine_test)
3533

3634

37-
# 🔁 Limpa tabelas a cada teste (isolamento real)
38-
from sqlalchemy import text
39-
4035
@pytest.fixture(autouse=True)
4136
def clean_tables():
4237
with engine_test.connect() as connection:
@@ -48,7 +43,6 @@ def clean_tables():
4843
trans.commit()
4944

5045

51-
# 🔄 Sessão isolada
5246
@pytest.fixture()
5347
def db_session():
5448
db = TestingSessionLocal()
@@ -58,7 +52,6 @@ def db_session():
5852
db.close()
5953

6054

61-
# 💡 Sobrescrita REAL do get_db
6255
@pytest.fixture(autouse=True)
6356
def override_get_db(db_session):
6457
def _get_test_db():

backend/tests/test_carts_api.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pytest
22

3+
34
def perform_auth_and_get_token(client):
45
# cria usuário
56
client.post("/api/register", json={"username": "cartuser", "password": "senha"})
@@ -11,6 +12,7 @@ def auth_headers(client):
1112
token = perform_auth_and_get_token(client)
1213
return {"Authorization": f"Bearer {token}"}
1314

15+
1416
def test_create_and_list_cart(client, auth_headers):
1517
# Cria carrinho
1618
resp = client.post("/api/carrinhos", json={"name": "Carrinho 1"}, headers=auth_headers)
@@ -24,6 +26,7 @@ def test_create_and_list_cart(client, auth_headers):
2426
carts = resp.json()
2527
assert any(c["name"] == "Carrinho 1" for c in carts)
2628

29+
2730
def test_add_item_and_list_items(client, auth_headers):
2831
# Cria carrinho
2932
resp = client.post("/api/carrinhos", json={"name": "Cart para itens"}, headers=auth_headers)

backend/tests/test_routes_protection.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import pytest
21

32
def test_protected_routes_without_token(client):
43
protected = [
5-
( "GET", "/api/carrinhos" ),
6-
( "POST", "/api/carrinhos", {"name": "Cart Test"} ),
4+
("GET", "/api/carrinhos"),
5+
("POST", "/api/carrinhos", {"name": "Cart Test"}),
76
]
87
for method, path, *rest in protected:
98
if method == "GET":

frontend/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
77
build-essential \
88
&& rm -rf /var/lib/apt/lists/*
99

10-
COPY requirements.txt /app/requirements.txt
10+
COPY frontend/requirements.txt /app/requirements.txt
1111

1212
RUN pip install --no-cache-dir --upgrade pip \
1313
&& pip install --no-cache-dir -r /app/requirements.txt

textos/ci.txt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
✅ 2. O que SEU CI está testando hoje
2+
3+
Seu ci.yml executa 3 coisas IMPORTANTES:
4+
5+
(1) Lint — flake8
6+
7+
Ele está verificando:
8+
9+
erros de sintaxe
10+
imports não usados
11+
variáveis não usadas
12+
quebra de estilo
13+
linhas maiores que 120 chars
14+
15+
Se isso quebrar → CI fica vermelho.
16+
----
17+
(2) Testes automáticos — pytest
18+
19+
Seu CI roda exatamente isso:
20+
21+
pytest -q --disable-warnings --maxfail=1
22+
23+
24+
Isso significa:
25+
26+
Executa TODOS os testes do backend
27+
Para na primeira falha
28+
Se não houver nenhum teste → NÃO QUEBRA
29+
Apenas marca como sucesso
30+
31+
Se você não tiver testes, o pytest roda vazio, mas passa.
32+
---
33+
(3) Build da sua aplicação Docker
34+
35+
O CI roda:
36+
docker compose -f docker-compose.yml build
37+
38+
39+
Ou seja:
40+
41+
testa build da imagem backend
42+
testa build da imagem frontend
43+
se o build quebrar → ❌ vermelho
44+
45+
Isso é essencial para evitar deploy que quebra.

0 commit comments

Comments
 (0)