qa #6

Merged
steve merged 45 commits from qa into master 2025-02-17 22:38:25 -05:00
10 changed files with 441 additions and 178 deletions

View File

@@ -9,8 +9,9 @@ clean:
rm -rf .coverage
rm -rf report.xml
rm -rf test.db
rm -rf htmlcov
coverage: clean
pytest --cov=papi test_papi.py --junitxml=report.xml --cov-report=html --cov-report=xml --cov-report=term --cov-fail-under 90
pytest --cov=papi --cov=posts --cov=users test_papi.py test_posts.py test_users.py --junitxml=report.xml --cov-report=html --cov-report=xml --cov-report=term --cov-fail-under 90
docker-img: coverage
docker build -t ${DOCK_IMG} .
docker-push: docker-img

0
README.md Normal file
View File

20
db.py Normal file
View File

@@ -0,0 +1,20 @@
from sqlalchemy import create_engine, Column, Integer,String, ForeignKey
from sqlalchemy.engine import URL
from sqlalchemy.orm import sessionmaker, Session, declarative_base
# Database setup
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

View File

@@ -1 +1,201 @@
{"v":1,"name":"test_assignment","folders":[{"v":1,"name":"users","folders":[],"requests":[{"v":"1","endpoint":"https://papi.kube-dev.fairfaxmedia.net/users","name":"list_users","params":[],"headers":[],"method":"GET","auth":{"authType":"none","authActive":true},"preRequestScript":"","testScript":"","body":{"contentType":null,"body":null}},{"v":"1","endpoint":"https://papi.kube-dev.fairfaxmedia.net/users","name":"create_user","params":[],"headers":[],"method":"POST","auth":{"authType":"none","authActive":true},"preRequestScript":"","testScript":"","body":{"contentType":"application/json","body":"{\n \"name\":\"test2\",\n \"email\":\"mynameisdeleted@gmail.com\",\n \"password\":\"pass2\"\n}"}},{"v":"1","endpoint":"https://papi.kube-dev.fairfaxmedia.net/users/1","name":"update_user","params":[],"headers":[],"method":"PUT","auth":{"authType":"none","authActive":true},"preRequestScript":"","testScript":"","body":{"contentType":"application/json","body":"{\n \"name\":\"new name\"\n}"}},{"v":"1","endpoint":"https://papi.kube-dev.fairfaxmedia.net/users/1","name":"delete_user","params":[],"headers":[],"method":"DELETE","auth":{"authType":"none","authActive":true},"preRequestScript":"","testScript":"","body":{"contentType":"application/json","body":"{\n \"name\":\"new name\"\n}"}},{"v":"1","endpoint":"https://papi.kube-dev.fairfaxmedia.net/users/1","name":"get_user","params":[],"headers":[],"method":"GET","auth":{"authType":"none","authActive":true},"preRequestScript":"","testScript":"","body":{"contentType":null,"body":null}}]},{"v":1,"name":"posts","folders":[],"requests":[{"v":"1","endpoint":"https://papi.kube-dev.fairfaxmedia.net/posts","name":"list_posts","params":[],"headers":[],"method":"GET","auth":{"authType":"none","authActive":true},"preRequestScript":"","testScript":"","body":{"contentType":"application/json","body":"{\n \"name\":\"new name\"\n}"}},{"v":"1","endpoint":"https://papi.kube-dev.fairfaxmedia.net/posts","name":"create_post","params":[],"headers":[],"method":"POST","auth":{"authType":"none","authActive":true},"preRequestScript":"","testScript":"","body":{"contentType":"application/json","body":"{\n \"title\":\"test title\",\n \"content\":\"testing123\",\n \"author_id\":\"1\"\n}"}},{"v":"1","endpoint":"https://papi.kube-dev.fairfaxmedia.net/posts/1","name":"get_post","params":[],"headers":[],"method":"GET","auth":{"authType":"none","authActive":true},"preRequestScript":"","testScript":"","body":{"contentType":"application/json","body":"{\n \"name\":\"new name\"\n}"}},{"v":"1","endpoint":"https://papi.kube-dev.fairfaxmedia.net/posts/1","name":"update_post","params":[],"headers":[],"method":"PUT","auth":{"authType":"none","authActive":true},"preRequestScript":"","testScript":"","body":{"contentType":"application/json","body":"{\n \"title\":\"new title\"\n}"}},{"v":"1","endpoint":"https://papi.kube-dev.fairfaxmedia.net/posts/1","name":"delete_post","params":[],"headers":[],"method":"DELETE","auth":{"authType":"none","authActive":true},"preRequestScript":"","testScript":"","body":{"contentType":"application/json","body":"{\n \"title\":\"new title\"\n}"}}]}],"requests":[]}
{
"v": 1,
"name": "test_assignment",
"folders": [
{
"v": 1,
"name": "users",
"folders": [],
"requests": [
{
"v": "1",
"endpoint": "https://papi.kube-dev.fairfaxmedia.net/users",
"name": "list_users",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "none",
"authActive": true
},
"preRequestScript": "",
"testScript": "",
"body": {
"contentType": null,
"body": null
}
},
{
"v": "1",
"endpoint": "https://papi.kube-dev.fairfaxmedia.net/users",
"name": "create_user",
"params": [],
"headers": [],
"method": "POST",
"auth": {
"authType": "none",
"authActive": true
},
"preRequestScript": "",
"testScript": "",
"body": {
"contentType": "application/json",
"body": "{\n \"name\":\"test2\",\n \"email\":\"mynameisdeleted@gmail.com\",\n \"password\":\"pass2\"\n}"
}
},
{
"v": "1",
"endpoint": "https://papi.kube-dev.fairfaxmedia.net/users/1",
"name": "update_user",
"params": [],
"headers": [],
"method": "PUT",
"auth": {
"authType": "none",
"authActive": true
},
"preRequestScript": "",
"testScript": "",
"body": {
"contentType": "application/json",
"body": "{\n \"name\":\"new name\"\n}"
}
},
{
"v": "1",
"endpoint": "https://papi.kube-dev.fairfaxmedia.net/users/1",
"name": "delete_user",
"params": [],
"headers": [],
"method": "DELETE",
"auth": {
"authType": "none",
"authActive": true
},
"preRequestScript": "",
"testScript": "",
"body": {
"contentType": "application/json",
"body": "{\n \"name\":\"new name\"\n}"
}
},
{
"v": "1",
"endpoint": "https://papi.kube-dev.fairfaxmedia.net/users/1",
"name": "get_user",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "none",
"authActive": true
},
"preRequestScript": "",
"testScript": "",
"body": {
"contentType": null,
"body": null
}
}
]
},
{
"v": 1,
"name": "posts",
"folders": [],
"requests": [
{
"v": "1",
"endpoint": "https://papi.kube-dev.fairfaxmedia.net/posts",
"name": "list_posts",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "none",
"authActive": true
},
"preRequestScript": "",
"testScript": "",
"body": {
"contentType": "application/json",
"body": "{\n \"name\":\"new name\"\n}"
}
},
{
"v": "1",
"endpoint": "https://papi.kube-dev.fairfaxmedia.net/posts",
"name": "create_post",
"params": [],
"headers": [],
"method": "POST",
"auth": {
"authType": "none",
"authActive": true
},
"preRequestScript": "",
"testScript": "",
"body": {
"contentType": "application/json",
"body": "{\n \"title\":\"test title\",\n \"content\":\"testing123\",\n \"author_id\":\"1\"\n}"
}
},
{
"v": "1",
"endpoint": "https://papi.kube-dev.fairfaxmedia.net/posts/1",
"name": "get_post",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "none",
"authActive": true
},
"preRequestScript": "",
"testScript": "",
"body": {
"contentType": "application/json",
"body": "{\n \"name\":\"new name\"\n}"
}
},
{
"v": "1",
"endpoint": "https://papi.kube-dev.fairfaxmedia.net/posts/1",
"name": "update_post",
"params": [],
"headers": [],
"method": "PUT",
"auth": {
"authType": "none",
"authActive": true
},
"preRequestScript": "",
"testScript": "",
"body": {
"contentType": "application/json",
"body": "{\n \"title\":\"new title\"\n}"
}
},
{
"v": "1",
"endpoint": "https://papi.kube-dev.fairfaxmedia.net/posts/1",
"name": "delete_post",
"params": [],
"headers": [],
"method": "DELETE",
"auth": {
"authType": "none",
"authActive": true
},
"preRequestScript": "",
"testScript": "",
"body": {
"contentType": "application/json",
"body": "{\n \"title\":\"new title\"\n}"
}
}
]
}
],
"requests": []
}

117
papi.py
View File

@@ -4,126 +4,21 @@ import uvicorn
from sqlalchemy import create_engine, Column, Integer,String, ForeignKey
from sqlalchemy.engine import URL
from sqlalchemy.orm import sessionmaker, Session, declarative_base
import db
import users
import posts
# Database setup
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
email = Column(String, unique=True, index=True)
password = Column(String)
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True, index=True)
title = Column(String)
content = Column(String)
author_id = Column(Integer, ForeignKey("users.id"))
Base.metadata.create_all(bind=engine)
db.Base.metadata.create_all(bind=db.engine)
app = FastAPI()
app.include_router(users.router)
app.include_router(posts.router)
@app.get("/")
async def root():
return {"message": "Hello World"}
@app.get("/users")
async def get_users(db: Session = Depends(get_db)):
return db.query(User).all()
@app.get("/users/{id}")
async def get_user(id: str,db: Session = Depends(get_db)):
db_user = db.query(User).filter(User.id == id).first()
return db_user
@app.post("/users")
async def create_user(user: dict,db: Session = Depends(get_db)):
#db_user = User(name=user["name"], email=user["email"], password=user["password"])
db_user = User(**user)
db.add(db_user)
db.commit()
db.refresh(db_user)
return {"message":"User created successfully","user":db_user}
@app.put("/users/{id}")
async def update_user(id: str, user: dict,db: Session = Depends(get_db)):
db_user = db.query(User).filter(User.id == id).first()
if "name" in user:
db_user.name = user["name"]
if "email" in user:
db_user.email = user["email"]
if "password" in user:
db_user.password = user["password"]
db.commit()
db.refresh(db_user)
return {"message":"User updated successfully","user":db_user}
@app.delete("/users/{id}")
async def delete_user(id: str, db: Session = Depends(get_db)):
db_user = db.query(User).filter(User.id == id).first()
db.delete(db_user)
db.commit()
return {"message":"User deleted successfully","user":db_user}
@app.get("/posts")
async def get_posts(db: Session = Depends(get_db)):
return db.query(Post).all()
@app.get("/posts/{id}")
async def get_post(id: str,db: Session = Depends(get_db)):
return db.query(Post).filter(Post.id == id).first()
@app.post("/posts")
async def create_post(post: dict,db: Session = Depends(get_db)):
db_post = Post(**post)
db.add(db_post)
db.commit()
db.refresh(db_post)
return {"message":"Post created successfully","post":db_post}
@app.put("/posts/{id}")
async def update_post(id: str, post: dict,db: Session = Depends(get_db)):
db_post = db.query(Post).filter(Post.id == id).first()
if "title" in post:
db_post.title = post["title"]
if "content" in post:
db_post.content = post["content"]
db.commit()
db.refresh(db_post)
return {"message":"Post updated successfully","post":db_post}
@app.delete("/posts/{id}")
async def delete_post(id: str, db: Session = Depends(get_db)):
db_post = db.query(Post).filter(Post.id == id).first()
db.delete(db_post)
db.commit()
return {"message":"Post deleted successfully","post":db_post}
if __name__ == "__main__":

52
posts.py Normal file
View File

@@ -0,0 +1,52 @@
from fastapi import APIRouter, Depends
from sqlalchemy.orm import sessionmaker, Session, declarative_base
from sqlalchemy import create_engine, Column, Integer,String, ForeignKey
import db
class Post(db.Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True, index=True)
title = Column(String)
content = Column(String)
author_id = Column(Integer, ForeignKey("users.id"))
router = APIRouter(prefix="/posts")
@router.get("")
def get_users(db: Session = Depends(db.get_db)):
return db.query(Post).all()
@router.get("/{id}")
def get_user( id, db: Session = Depends(db.get_db)):
db_post = db.query(Post).filter(Post.id == id).first()
return db_post
@router.post("")
def create_post( post: dict, db: Session = Depends(db.get_db)):
db_post = Post(**post)
db.add(db_post)
db.commit()
db.refresh(db_post)
return {"message":"Post created successfully","post":db_post}
@router.put("/{id}")
def update_post( id, post: dict, db: Session = Depends(db.get_db)):
db_post = db.query(Post).filter(Post.id == id).first()
if "title" in post:
db_post.title = post["title"]
if "content" in post:
db_post.content = post["content"]
db.commit()
db.refresh(db_post)
return {"message":"Post updated successfully","post":db_post}
@router.delete("/{id}")
def delete_post(id, db: Session = Depends(db.get_db)):
db_post = db.query(Post).filter(Post.id == id).first()
db.delete(db_post)
db.commit()
return {"message":"Post deleted successfully","post":db_post}

View File

@@ -11,69 +11,4 @@ def test_read_main():
assert response.status_code == 200
assert response.json() == {"message": "Hello World"}
def test_read_users():
response = client.get("/users")
assert response.status_code == 200
assert response.json() == []
def test_create_read_delete_user():
response = client.post("/users",json={"name":"test","email":"mynameisdeleted@gmail.com","password":"pass2"})
assert response.status_code == 200
user=response.json()['user']
user_id = str(user['id'])
response = client.get("/users/"+user_id)
assert response.status_code == 200
assert response.json() == user
update_response = client.put("/users/"+user_id,json={"name":"updated name"})
#assert update_respose.status_code == 200
put_user = update_response.json()['user']
assert put_user['name'] == "updated name"
update_response = client.put("/users/"+user_id,json={"email":"updated@gmail.com"})
put_user = update_response.json()['user']
assert put_user['email'] == "updated@gmail.com"
response = client.delete("/users/"+user_id)
assert response.status_code == 200
response = client.get("/users")
assert response.status_code == 200
assert response.json() == []
def test_create_read_delete_post():
response = client.post("/posts",json={"title":"test title","content":"testing123","author_id":"1"})
assert response.status_code == 200
post=response.json()['post']
post_id = str(post['id'])
response = client.get("/posts/"+post_id)
assert response.status_code == 200
assert response.json() == post
update_response = client.put("/posts/"+post_id,json={"title":"test title updated"})
#assert update_respose.status_code == 200
put_post = update_response.json()['post']
assert put_post['title'] == "test title updated"
update_response = client.put("/posts/"+post_id,json={"content":"updated content"})
#assert update_respose.status_code == 200
put_post = update_response.json()['post']
assert put_post['title'] == "test title updated"
assert put_post['content'] == "updated content"
response = client.delete("/posts/"+post_id)
assert response.status_code == 200
response = client.get("/posts")
assert response.status_code == 200
assert response.json() == []

51
test_posts.py Normal file
View File

@@ -0,0 +1,51 @@
from fastapi.testclient import TestClient
from papi import app
client = TestClient(app)
def test_read_post():
response = client.get("/posts")
assert response.status_code == 200
assert response.json() != None
def test_create_post():
response = client.post("/posts",json={"title":"test title","content":"testing123","author_id":"1"})
assert response.status_code == 200
post=response.json()['post']
assert post['title'] == "test title"
assert post['content'] == "testing123"
assert post['author_id'] == 1
client.delete("/posts/"+str(post['id']))
def test_create_read_delete_post():
first_posts = client.get("/posts").json()
response = client.post("/posts",json={"title":"test title","content":"testing123","author_id":"1"})
assert response.status_code == 200
post=response.json()['post']
post_id = str(post['id'])
response = client.get("/posts/"+post_id)
assert response.status_code == 200
assert response.json() == post
update_response = client.put("/posts/"+post_id,json={"title":"test title updated"})
#assert update_respose.status_code == 200
put_post = update_response.json()['post']
assert put_post['title'] == "test title updated"
update_response = client.put("/posts/"+post_id,json={"content":"updated content"})
#assert update_respose.status_code == 200
put_post = update_response.json()['post']
assert put_post['title'] == "test title updated"
assert put_post['content'] == "updated content"
response = client.delete("/posts/"+post_id)
assert response.status_code == 200
response = client.get("/posts")
assert response.status_code == 200
assert response.json() == first_posts

57
test_users.py Normal file
View File

@@ -0,0 +1,57 @@
from fastapi.testclient import TestClient
from papi import app
client = TestClient(app)
def test_read_user():
response = client.get("/users")
assert response.status_code == 200
assert response.json() != None
def test_create_user():
response = client.post("/users",json={"name":"test","email":"mynameisdeleted@gmail.com","password":"pass2"})
assert response.status_code == 200
user=response.json()['user']
assert user['name'] == "test"
assert user['email'] == "mynameisdeleted@gmail.com"
assert user['password'] == "pass2"
client.delete("/users/"+str(user['id']))
def test_create_read_delete_user():
response = client.post("/users",json={"name":"test","email":"mynameisdeleted@gmail.com","password":"pass2"})
assert response.status_code == 200
user=response.json()['user']
user_id = str(user['id'])
response = client.get("/users/"+user_id)
assert response.status_code == 200
assert response.json() == user
update_response = client.put("/users/"+user_id,json={"name":"updated name"})
#assert update_respose.status_code == 200
put_user = update_response.json()['user']
assert put_user['name'] == "updated name"
update_response = client.put("/users/"+user_id,json={"email":"updated@gmail.com"})
put_user = update_response.json()['user']
assert put_user['email'] == "updated@gmail.com"
assert put_user['name'] == "updated name"
update_response = client.put("/users/"+user_id,json={"password":"pw_updated"})
put_user = update_response.json()['user']
assert put_user['password'] == "pw_updated"
assert put_user['email'] == "updated@gmail.com"
assert put_user['name'] == "updated name"
response = client.delete("/users/"+user_id)
assert response.status_code == 200
response = client.get("/users")
assert response.status_code == 200
assert response.json() == []

52
users.py Normal file
View File

@@ -0,0 +1,52 @@
from fastapi import APIRouter, Depends
from sqlalchemy.orm import sessionmaker, Session, declarative_base
from sqlalchemy import create_engine, Column, Integer,String, ForeignKey
import db
class User(db.Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
email = Column(String, unique=True, index=True)
password = Column(String)
router = APIRouter(prefix="/users")
@router.get("")
def get_users(db: Session = Depends(db.get_db)):
return db.query(User).all()
@router.get("/{id}")
def get_user( id, db: Session = Depends(db.get_db)):
db_user = db.query(User).filter(User.id == id).first()
return db_user
@router.post("")
def create_user( user: dict, db: Session = Depends(db.get_db)):
db_user = User(**user)
db.add(db_user)
db.commit()
db.refresh(db_user)
return {"message":"User created successfully","user":db_user}
@router.put("/{id}")
def update_user( id, user: dict, db: Session = Depends(db.get_db)):
db_user = db.query(User).filter(User.id == id).first()
if "name" in user:
db_user.name = user["name"]
if "email" in user:
db_user.email = user["email"]
if "password" in user:
db_user.password = user["password"]
db.commit()
db.refresh(db_user)
return {"message":"User updated successfully","user":db_user}
@router.delete("/{id}")
def delete_user(id, db: Session = Depends(db.get_db)):
db_user = db.query(User).filter(User.id == id).first()
db.delete(db_user)
db.commit()
return {"message":"User deleted successfully","user":db_user}