Skip to content

Commit 06c2255

Browse files
committed
feat: Python API and SQL Validator framework
0 parents  commit 06c2255

16 files changed

Lines changed: 516 additions & 0 deletions

.github/workflows/ci.yml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
name: QA Validator CI Pipeline
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
jobs:
10+
python-api-sql-tests:
11+
runs-on: ubuntu-latest
12+
name: Python API & SQL Tests
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v3
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v4
19+
with:
20+
python-version: '3.11'
21+
22+
- name: Install Python dependencies
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install -r tests/python/requirements.txt
26+
27+
- name: Start API and Database (Docker Compose)
28+
run: docker compose up -d --build
29+
30+
- name: Wait for API to be ready
31+
run: |
32+
while ! curl -s http://localhost:8000/docs > /dev/null; do
33+
echo "Waiting for API..."
34+
sleep 2
35+
done
36+
37+
- name: Run Pytest
38+
run: pytest tests/python/ -v
39+
40+
- name: Tear down Docker Compose
41+
if: always()
42+
run: docker compose down -v
43+
44+
postman-newman-tests:
45+
runs-on: ubuntu-latest
46+
name: Postman Zero Code Tests
47+
steps:
48+
- name: Checkout code
49+
uses: actions/checkout@v3
50+
51+
- name: Start API and Database (Docker Compose)
52+
run: docker compose up -d --build
53+
54+
- name: Wait for API to be ready
55+
run: |
56+
while ! curl -s http://localhost:8000/docs > /dev/null; do
57+
echo "Waiting for API..."
58+
sleep 2
59+
done
60+
61+
- name: Install Node.js
62+
uses: actions/setup-node@v3
63+
with:
64+
node-version: '18'
65+
66+
- name: Install Newman
67+
run: npm install -g newman
68+
69+
- name: Run Newman Tests
70+
run: newman run tests/postman/postman_collection.json -e tests/postman/postman_environment.json --reporters cli
71+
72+
- name: Tear down Docker Compose
73+
if: always()
74+
run: docker compose down -v

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
.pytest_cache/
6+
7+
# Docker
8+
.docker/

Dockerfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM python:3.11-slim
2+
3+
WORKDIR /app
4+
5+
# Install system dependencies for psycopg2
6+
RUN apt-get update && apt-get install -y libpq-dev gcc
7+
8+
COPY app/requirements.txt .
9+
RUN pip install --no-cache-dir -r requirements.txt
10+
11+
COPY app/ ./app/
12+
13+
# Expose port and run uvicorn
14+
EXPOSE 8000
15+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# QA Framework: Python API & SQL Validator
2+
3+
Este projeto é o núcleo da **Fase 2 do Roadmap de Qualidade**, provando proficiência em testes automatizados integrando as camadas de Contrato (HTTP/REST) e Persistência de Dados (SQL).
4+
5+
O grande diferencial deste framework é unir ferramentas híbridas de QA:
6+
1. **Zero Code / CI:** Automação de API via Postman/Newman.
7+
2. **Code / DB:** Automação via Python (`pytest`, `requests`) integrada a validações SQL diretas (`psycopg2`) no banco PostgreSQL.
8+
9+
## 📁 Estrutura da Aplicação (SUT)
10+
A aplicação local que estamos testando consiste em uma API de Ingestão de Clientes construída em **FastAPI** conectada a um banco **PostgreSQL** provisionado via Docker.
11+
12+
Para subir a infraestrutura:
13+
```bash
14+
docker-compose up --build
15+
```

app/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import os
2+
3+
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://qa_admin:qa_password@localhost:5432/qa_validator_db")

app/database.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import psycopg2
2+
from app.config import DATABASE_URL
3+
4+
def get_db_connection():
5+
"""Establishes a connection to the PostgreSQL database."""
6+
conn = psycopg2.connect(DATABASE_URL)
7+
return conn

app/main.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from fastapi import FastAPI, HTTPException, status
2+
from pydantic import BaseModel, EmailStr
3+
from app.database import get_db_connection
4+
import psycopg2
5+
6+
app = FastAPI(title="QA Validator API")
7+
8+
class CustomerCreate(BaseModel):
9+
name: str
10+
email: EmailStr
11+
12+
class CustomerResponse(CustomerCreate):
13+
id: int
14+
status: str
15+
16+
@app.post("/customers", response_model=CustomerResponse, status_code=status.HTTP_201_CREATED)
17+
def create_customer(customer: CustomerCreate):
18+
"""Creates a new customer in the database."""
19+
conn = get_db_connection()
20+
cur = conn.cursor()
21+
22+
try:
23+
cur.execute(
24+
"INSERT INTO customers (name, email) VALUES (%s, %s) RETURNING id, name, email, status;",
25+
(customer.name, customer.email)
26+
)
27+
new_customer = cur.fetchone()
28+
conn.commit()
29+
30+
return CustomerResponse(
31+
id=new_customer[0],
32+
name=new_customer[1],
33+
email=new_customer[2],
34+
status=new_customer[3]
35+
)
36+
except psycopg2.IntegrityError:
37+
conn.rollback()
38+
raise HTTPException(status_code=400, detail="Email already exists")
39+
finally:
40+
cur.close()
41+
conn.close()
42+
43+
@app.get("/customers/{customer_id}", response_model=CustomerResponse)
44+
def get_customer(customer_id: int):
45+
"""Retrieves a customer by ID."""
46+
conn = get_db_connection()
47+
cur = conn.cursor()
48+
49+
cur.execute("SELECT id, name, email, status FROM customers WHERE id = %s;", (customer_id,))
50+
customer = cur.fetchone()
51+
cur.close()
52+
conn.close()
53+
54+
if not customer:
55+
raise HTTPException(status_code=404, detail="Customer not found")
56+
57+
return CustomerResponse(id=customer[0], name=customer[1], email=customer[2], status=customer[3])

app/requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fastapi==0.109.2
2+
uvicorn==0.27.1
3+
psycopg2-binary==2.9.9
4+
pydantic==2.6.1

db/init.sql

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
-- Schema definition
2+
CREATE TABLE IF NOT EXISTS customers (
3+
id SERIAL PRIMARY KEY,
4+
name VARCHAR(100) NOT NULL,
5+
email VARCHAR(100) UNIQUE NOT NULL,
6+
status VARCHAR(20) DEFAULT 'ACTIVE',
7+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
8+
);
9+
10+
CREATE TABLE IF NOT EXISTS orders (
11+
id SERIAL PRIMARY KEY,
12+
customer_id INT REFERENCES customers(id),
13+
amount DECIMAL(10, 2) NOT NULL,
14+
order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
15+
);
16+
17+
-- Seed data for SQL Dossier tests
18+
INSERT INTO customers (name, email, status) VALUES
19+
('Alice Smith', 'alice@example.com', 'ACTIVE'),
20+
('Bob Jones', 'bob@example.com', 'INACTIVE'),
21+
('Charlie Brown', 'charlie@example.com', 'ACTIVE');
22+
23+
-- Alice has an order, Bob does not (useful for JOIN tests)
24+
INSERT INTO orders (customer_id, amount) VALUES
25+
(1, 150.00),
26+
(1, 50.00);

docker-compose.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
version: '3.8'
2+
3+
services:
4+
db:
5+
image: postgres:15-alpine
6+
environment:
7+
POSTGRES_USER: qa_admin
8+
POSTGRES_PASSWORD: qa_password
9+
POSTGRES_DB: qa_validator_db
10+
ports:
11+
- "5432:5432"
12+
volumes:
13+
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
14+
healthcheck:
15+
test: ["CMD-SHELL", "pg_isready -U qa_admin -d qa_validator_db"]
16+
interval: 5s
17+
timeout: 5s
18+
retries: 5
19+
20+
api:
21+
build: .
22+
ports:
23+
- "8000:8000"
24+
environment:
25+
DATABASE_URL: postgresql://qa_admin:qa_password@db:5432/qa_validator_db
26+
depends_on:
27+
db:
28+
condition: service_healthy

0 commit comments

Comments
 (0)