first
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Pipfile
|
||||||
|
codealike.json
|
||||||
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
72
app/crud.py
Normal file
72
app/crud.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
import app.models
|
||||||
|
from app.database import Base
|
||||||
|
from . import schemas
|
||||||
|
|
||||||
|
|
||||||
|
class DoesntExist(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ArgumentError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def create_thing(session: Session, model: Base, data: BaseModel):
|
||||||
|
instance = model(**data.dict())
|
||||||
|
session.add(instance)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(instance)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
def get_thing(session: Session, model: Base, thing_id: int = None, multi=False, **kwargs):
|
||||||
|
if thing_id and multi:
|
||||||
|
raise ArgumentError
|
||||||
|
|
||||||
|
if thing_id:
|
||||||
|
return session.query(model).filter_by(id=thing_id).first()
|
||||||
|
elif kwargs:
|
||||||
|
return session.query(model).filter_by(**kwargs).first()
|
||||||
|
else:
|
||||||
|
if multi:
|
||||||
|
return session.query(model).all()
|
||||||
|
else:
|
||||||
|
return session.query(model).first()
|
||||||
|
|
||||||
|
|
||||||
|
def update_thing(db: Session,
|
||||||
|
model: Base,
|
||||||
|
schema: BaseModel,
|
||||||
|
thing_id: int,
|
||||||
|
**kwargs
|
||||||
|
) -> BaseModel:
|
||||||
|
if not kwargs:
|
||||||
|
raise ArgumentError
|
||||||
|
|
||||||
|
if any(kwarg_key not in schema.__fields__.keys() for kwarg_key in kwargs):
|
||||||
|
raise ArgumentError
|
||||||
|
|
||||||
|
thing = get_thing(db, model, thing_id=thing_id)
|
||||||
|
if thing is None:
|
||||||
|
raise DoesntExist
|
||||||
|
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(thing, key, value)
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
db.refresh(thing)
|
||||||
|
return schema(**thing.to_dict())
|
||||||
|
|
||||||
|
|
||||||
|
def delete_thing(db: Session,
|
||||||
|
model: Base,
|
||||||
|
thing_id: int):
|
||||||
|
thing = get_thing(db, model=model, thing_id=thing_id)
|
||||||
|
if not thing:
|
||||||
|
raise DoesntExist
|
||||||
|
else:
|
||||||
|
db.delete(thing)
|
||||||
|
db.commit()
|
||||||
9
app/database.py
Normal file
9
app/database.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
from config import SQLALCHEMY_DATABASE_URL
|
||||||
|
|
||||||
|
engine = create_engine(SQLALCHEMY_DATABASE_URL)
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
Base = declarative_base()
|
||||||
153
app/main.py
Normal file
153
app/main.py
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import FastAPI, Depends, HTTPException
|
||||||
|
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_409_CONFLICT
|
||||||
|
|
||||||
|
from app import schemas, models
|
||||||
|
from config import PERSONAL_API_USERNAME, PERSONAL_API_PASS
|
||||||
|
from .crud import (
|
||||||
|
create_thing as create, get_thing as get, update_thing as update, DoesntExist,
|
||||||
|
delete_thing as delete,
|
||||||
|
)
|
||||||
|
from .database import SessionLocal, Base, engine
|
||||||
|
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
app = FastAPI()
|
||||||
|
security = HTTPBasic()
|
||||||
|
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
try:
|
||||||
|
db = SessionLocal()
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
# noinspection PyUnboundLocalVariable
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
|
||||||
|
if credentials.username != PERSONAL_API_USERNAME or credentials.password != PERSONAL_API_PASS:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Forbidden",
|
||||||
|
headers={"WWW-Authenticate": "Basic"},
|
||||||
|
)
|
||||||
|
return credentials.username
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def read_root(_: str = Depends(get_current_username)):
|
||||||
|
return {"Hello": "World"}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/availability/", response_model=schemas.Availability)
|
||||||
|
def get_availability(db: Session = Depends(get_db), _: str = Depends(get_current_username)):
|
||||||
|
return get(db, models.Availability)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/resume/", response_model=schemas.Resume)
|
||||||
|
def get_resume(db: Session = Depends(get_db), _: str = Depends(get_current_username)):
|
||||||
|
return get(db, models.Resume)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/projects/", response_model=List[schemas.Project])
|
||||||
|
def get_projects(db: Session = Depends(get_db), _: str = Depends(get_current_username)):
|
||||||
|
return get(db, models.Project, multi=True)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/projects/{project_id}", response_model=schemas.Project)
|
||||||
|
def get_projects(project_id: int, db: Session = Depends(get_db), _: str = Depends(get_current_username)):
|
||||||
|
return get(db, models.Project, thing_id=project_id)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/posts/", response_model=List[schemas.Post])
|
||||||
|
def get_posts(db: Session = Depends(get_db), _: str = Depends(get_current_username)):
|
||||||
|
return get(db, models.Post, multi=True)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/posts/{post_id}", response_model=schemas.Post)
|
||||||
|
def get_post(post_id: int, db: Session = Depends(get_db), _: str = Depends(get_current_username)):
|
||||||
|
return get(db, models.Post, post_id)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/posts/", response_model=schemas.Post)
|
||||||
|
def post_post(
|
||||||
|
post_: schemas.PostCreate,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
_: str = Depends(get_current_username)
|
||||||
|
):
|
||||||
|
if get(db, model=models.Post, title=post_.title):
|
||||||
|
raise HTTPException(status_code=409)
|
||||||
|
return create(db, model=models.Post, data=post_)
|
||||||
|
|
||||||
|
|
||||||
|
@app.patch("/posts/{post_id}")
|
||||||
|
async def patch_project(post_id: int,
|
||||||
|
request: Request,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
_: str = Depends(get_current_username),
|
||||||
|
):
|
||||||
|
return update(db, model=models.Post, schema=schemas.PostCreate, thing_id=post_id, **await request.json())
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/availability/", response_model=schemas.Availability)
|
||||||
|
def post_availability(
|
||||||
|
availability: schemas.AvailabilityCreate,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
_: str = Depends(get_current_username)
|
||||||
|
):
|
||||||
|
return create(db, model=models.Availability, data=availability)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/resume/", response_model=schemas.Resume)
|
||||||
|
def post_resume(
|
||||||
|
resume: schemas.ResumeCreate,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
_: str = Depends(get_current_username)
|
||||||
|
):
|
||||||
|
return create(db, model=models.Resume, data=resume)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/projects/", response_model=schemas.Project)
|
||||||
|
def post_project(
|
||||||
|
project: schemas.ProjectCreate,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
_: str = Depends(get_current_username)
|
||||||
|
):
|
||||||
|
if get(db, model=models.Project, title=project.title):
|
||||||
|
raise HTTPException(status_code=409)
|
||||||
|
return create(db, model=models.Project, data=project)
|
||||||
|
|
||||||
|
|
||||||
|
@app.patch("/projects/{project_id}")
|
||||||
|
async def patch_project(project_id: int,
|
||||||
|
request: Request,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
_: str = Depends(get_current_username),
|
||||||
|
):
|
||||||
|
return update(db, models.Project, schemas.ProjectCreate, project_id, **await request.json())
|
||||||
|
|
||||||
|
|
||||||
|
@app.delete("/projects/{project_id}")
|
||||||
|
def delete_project(project_id: int,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
_: str = Depends(get_current_username),
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
delete(db, models.Project, project_id)
|
||||||
|
except DoesntExist:
|
||||||
|
raise HTTPException(status_code=404)
|
||||||
|
|
||||||
|
|
||||||
|
@app.delete("/posts/{post_id}")
|
||||||
|
def delete_project(post_id: int,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
_: str = Depends(get_current_username),
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
delete(db, models.Post, post_id)
|
||||||
|
except DoesntExist:
|
||||||
|
raise HTTPException(status_code=404)
|
||||||
75
app/models.py
Normal file
75
app/models.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
"""
|
||||||
|
BaseMixIn include `id` as the primary key,
|
||||||
|
as well as adding a self.when value in the constructor
|
||||||
|
"""
|
||||||
|
import datetime as dt
|
||||||
|
from sqlalchemy import Column, DateTime, String, Boolean, Date
|
||||||
|
|
||||||
|
from app.database import Base, SessionLocal
|
||||||
|
from helpers import parse_date_string, BaseMixIn
|
||||||
|
|
||||||
|
|
||||||
|
def update(instance, **kwargs):
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(instance, key, value)
|
||||||
|
instance.when = dt.datetime.now()
|
||||||
|
db = SessionLocal()
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
|
||||||
|
class Resume(BaseMixIn, Base):
|
||||||
|
__tablename__ = "résumé"
|
||||||
|
|
||||||
|
when = Column(DateTime, nullable=False)
|
||||||
|
url = Column(String, nullable=False, unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Availability(BaseMixIn, Base):
|
||||||
|
__tablename__ = "availability"
|
||||||
|
|
||||||
|
when = Column(DateTime, nullable=False)
|
||||||
|
available = Column(Boolean, nullable=False)
|
||||||
|
next_available = Column(Date)
|
||||||
|
|
||||||
|
def __init__(self, next_available=None, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
if next_available:
|
||||||
|
self.next_available = parse_date_string(next_available)
|
||||||
|
else:
|
||||||
|
self.next_available = next_available
|
||||||
|
|
||||||
|
|
||||||
|
class Project(BaseMixIn, Base):
|
||||||
|
# TODO: add 'tags' table and make a many-to-many relationship between projects and tags
|
||||||
|
|
||||||
|
__tablename__ = 'projects'
|
||||||
|
|
||||||
|
when = Column(DateTime, nullable=False)
|
||||||
|
title = Column(String, nullable=False, unique=True)
|
||||||
|
description = Column(String, nullable=False)
|
||||||
|
link = Column(String, unique=True)
|
||||||
|
|
||||||
|
def update(self, **kwargs):
|
||||||
|
update(self, **kwargs)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {attr_name: getattr(self, attr_name) for attr_name in ['title', 'description', 'link']}
|
||||||
|
|
||||||
|
|
||||||
|
class Post(BaseMixIn, Base):
|
||||||
|
|
||||||
|
__tablename__ = "posts"
|
||||||
|
|
||||||
|
when = Column(DateTime, nullable=False)
|
||||||
|
title = Column(String, nullable=False, unique=True)
|
||||||
|
description = Column(String, nullable=False)
|
||||||
|
body = Column(String, nullable=False)
|
||||||
|
custom_url = Column(String)
|
||||||
|
syndicate = Column(Boolean)
|
||||||
|
|
||||||
|
def update(self, **kwargs):
|
||||||
|
update(self, **kwargs)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {attr_name: getattr(self, attr_name)
|
||||||
|
for attr_name in ['title', 'description', 'body', 'custom_url', 'syndicate']}
|
||||||
75
app/schemas.py
Normal file
75
app/schemas.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
from datetime import date, datetime
|
||||||
|
|
||||||
|
from pydantic import BaseModel, UrlStr
|
||||||
|
|
||||||
|
|
||||||
|
class AvailabilityBase(BaseModel):
|
||||||
|
available: bool
|
||||||
|
next_available: str = None
|
||||||
|
|
||||||
|
|
||||||
|
class AvailabilityCreate(AvailabilityBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Availability(AvailabilityBase):
|
||||||
|
id: int
|
||||||
|
when: datetime
|
||||||
|
next_available: date = None
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
|
||||||
|
class ResumeBase(BaseModel):
|
||||||
|
url: UrlStr
|
||||||
|
|
||||||
|
|
||||||
|
class ResumeCreate(ResumeBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Resume(ResumeBase):
|
||||||
|
id: int
|
||||||
|
when: datetime
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectBase(BaseModel):
|
||||||
|
title: str
|
||||||
|
description: str
|
||||||
|
link: UrlStr = None
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectCreate(ProjectBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Project(ProjectBase):
|
||||||
|
id: int
|
||||||
|
when: datetime
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
|
||||||
|
class PostBase(BaseModel):
|
||||||
|
title: str
|
||||||
|
description: str
|
||||||
|
body: str
|
||||||
|
custom_url: str = None
|
||||||
|
syndicate: bool = None
|
||||||
|
|
||||||
|
|
||||||
|
class PostCreate(PostBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Post(PostBase):
|
||||||
|
id: int
|
||||||
|
when: datetime
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
7
config.py
Normal file
7
config.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
PERSONAL_API_USERNAME = os.getenv("PERSONAL_API_USERNAME")
|
||||||
|
PERSONAL_API_PASS = os.getenv("PERSONAL_API_USERNAME")
|
||||||
|
|
||||||
|
# must be a postgres instance
|
||||||
|
SQLALCHEMY_DATABASE_URL = os.getenv("PERSONAL_API_SQLALCHEMY_DATABASE_URL")
|
||||||
34
helpers.py
Normal file
34
helpers.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import datetime as dt
|
||||||
|
|
||||||
|
import dateparser as dp
|
||||||
|
from sqlalchemy import Column, Integer, desc
|
||||||
|
|
||||||
|
from app.database import SessionLocal
|
||||||
|
|
||||||
|
|
||||||
|
class Invalid(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def parse_date_string(date_string) -> dt.date:
|
||||||
|
try:
|
||||||
|
return dp.parse(date_string).date()
|
||||||
|
except Exception:
|
||||||
|
raise Invalid
|
||||||
|
|
||||||
|
|
||||||
|
class BaseMixIn:
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
|
||||||
|
__mapper_args__ = {"order_by": desc("id")}
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super(BaseMixIn, self).__init__(**kwargs)
|
||||||
|
self.when = dt.datetime.now()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def delete_most_recent(cls):
|
||||||
|
db = SessionLocal()
|
||||||
|
db.delete(db.query(cls).first())
|
||||||
|
db.commit()
|
||||||
201
tests.py
Normal file
201
tests.py
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
"""
|
||||||
|
TODO: set up a test db or mocks instead of relying there being at least one item available in most of the 'get' tests.
|
||||||
|
"""
|
||||||
|
from pytest import skip, mark
|
||||||
|
from starlette.testclient import TestClient
|
||||||
|
|
||||||
|
from app.crud import create_thing
|
||||||
|
from app.models import Resume, Availability, Project, Post
|
||||||
|
from config import PERSONAL_API_USERNAME, PERSONAL_API_PASS
|
||||||
|
from app.main import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
VALID_AUTH = (PERSONAL_API_USERNAME, PERSONAL_API_PASS)
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_no_auth():
|
||||||
|
response = client.get('/')
|
||||||
|
assert response.status_code == 401
|
||||||
|
assert response.json() == {"detail": "Not authenticated"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_wrong_auth():
|
||||||
|
response = client.get('/', auth=('wronguser', 'wrongpass'))
|
||||||
|
assert response.status_code == 401
|
||||||
|
assert response.json() == {"detail": "Forbidden"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_correct_auth():
|
||||||
|
response = client.get('/', auth=VALID_AUTH)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {"Hello": "World"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_availability():
|
||||||
|
response = client.get('/availability/', auth=VALID_AUTH)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_availability():
|
||||||
|
response = client.post('/availability/',
|
||||||
|
auth=VALID_AUTH,
|
||||||
|
json={
|
||||||
|
"available": False,
|
||||||
|
"next_available": "in two weeks",
|
||||||
|
})
|
||||||
|
assert response.status_code == 200
|
||||||
|
Availability.delete_most_recent()
|
||||||
|
assert response.json()['available'] is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_resume():
|
||||||
|
response = client.get('/resume/', auth=VALID_AUTH)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_resume():
|
||||||
|
response = client.post('/resume/', auth=VALID_AUTH, json={"url": "http://hypo.thetical"})
|
||||||
|
assert response.status_code == 200
|
||||||
|
Resume.delete_most_recent()
|
||||||
|
assert response.json()['url'] == 'http://hypo.thetical'
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_projects():
|
||||||
|
response = client.get('/projects/', auth=VALID_AUTH)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_project():
|
||||||
|
response = client.get('/projects/1', auth=VALID_AUTH)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_project():
|
||||||
|
response = client.post('/projects/', auth=VALID_AUTH,
|
||||||
|
json={
|
||||||
|
"title": "Project Time",
|
||||||
|
"description": "This is a project I built one time.",
|
||||||
|
"link": "http://hypo.thetical",
|
||||||
|
})
|
||||||
|
assert response.status_code == 200
|
||||||
|
Project.delete_most_recent()
|
||||||
|
assert response.json()['link'] == 'http://hypo.thetical'
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_project_duplicate_title():
|
||||||
|
projects = client.get('/projects/', auth=VALID_AUTH).json()
|
||||||
|
existing_project = projects[0]
|
||||||
|
original_title = existing_project['title']
|
||||||
|
|
||||||
|
response = client.post('/projects/', auth=VALID_AUTH,
|
||||||
|
json={
|
||||||
|
"title": original_title,
|
||||||
|
"description": "yup"
|
||||||
|
})
|
||||||
|
assert response.status_code == 409
|
||||||
|
|
||||||
|
|
||||||
|
def test_patch_project():
|
||||||
|
projects = client.get('/projects/', auth=VALID_AUTH).json()
|
||||||
|
existing_project = projects[0]
|
||||||
|
original_title = existing_project['title']
|
||||||
|
id_ = existing_project['id']
|
||||||
|
|
||||||
|
response = client.patch(f'/projects/{id_}', auth=VALID_AUTH, json={"title": "Different Title"})
|
||||||
|
assert response.status_code == 200
|
||||||
|
new_title = response.json()['title']
|
||||||
|
# put it back to how it was
|
||||||
|
response = client.patch(f'/projects/{id_}', auth=VALID_AUTH, json={"title": original_title})
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert new_title == "Different Title"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_posts():
|
||||||
|
response = client.get('/posts/', auth=VALID_AUTH)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_post():
|
||||||
|
response = client.post('/posts/', auth=VALID_AUTH,
|
||||||
|
json={
|
||||||
|
"title": "Project Time",
|
||||||
|
"body": "This is a project I built one time.",
|
||||||
|
"description": "yup"
|
||||||
|
})
|
||||||
|
assert response.status_code == 200
|
||||||
|
Post.delete_most_recent()
|
||||||
|
assert response.json()['title'] == 'Project Time'
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_post_insufficient_data():
|
||||||
|
response = client.post('/posts/', auth=VALID_AUTH,
|
||||||
|
json={
|
||||||
|
"title": "Project Time",
|
||||||
|
"body": "This is a project I built one time.",
|
||||||
|
})
|
||||||
|
assert response.status_code == 422
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_post_duplicate_title():
|
||||||
|
posts = client.get('/posts/', auth=VALID_AUTH).json()
|
||||||
|
existing_post = posts[0]
|
||||||
|
original_title = existing_post['title']
|
||||||
|
|
||||||
|
response = client.post('/posts/', auth=VALID_AUTH,
|
||||||
|
json={
|
||||||
|
"title": original_title,
|
||||||
|
"body": "This is a project I built one time.",
|
||||||
|
"description": "yup"
|
||||||
|
})
|
||||||
|
assert response.status_code == 409
|
||||||
|
|
||||||
|
|
||||||
|
def test_patch_post():
|
||||||
|
posts = client.get('/posts/', auth=VALID_AUTH).json()
|
||||||
|
existing_post = posts[0]
|
||||||
|
original_title = existing_post['title']
|
||||||
|
id_ = existing_post['id']
|
||||||
|
|
||||||
|
response = client.patch(f'/posts/{id_}', auth=VALID_AUTH, json={"title": "Different Title"})
|
||||||
|
assert response.status_code == 200
|
||||||
|
new_title = response.json()['title']
|
||||||
|
# put it back to how it was
|
||||||
|
response = client.patch(f'/posts/{id_}', auth=VALID_AUTH, json={"title": original_title})
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert new_title == "Different Title"
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_post():
|
||||||
|
posts = client.get('/posts/', auth=VALID_AUTH).json()
|
||||||
|
existing_post = posts[0]
|
||||||
|
id_ = existing_post['id']
|
||||||
|
response = client.delete(f"/posts/{id_}", auth=VALID_AUTH)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# restore post
|
||||||
|
response = client.post('/posts/', auth=VALID_AUTH,
|
||||||
|
json={
|
||||||
|
"title": existing_post['title'],
|
||||||
|
"body": existing_post['body'],
|
||||||
|
"description": existing_post['description'],
|
||||||
|
})
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_project():
|
||||||
|
projects = client.get('/projects/', auth=VALID_AUTH).json()
|
||||||
|
existing_project = projects[0]
|
||||||
|
id_ = existing_project['id']
|
||||||
|
print(id_)
|
||||||
|
response = client.delete(f"/projects/{id_}", auth=VALID_AUTH)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# restore project
|
||||||
|
response = client.post('/projects/', auth=VALID_AUTH,
|
||||||
|
json={
|
||||||
|
"title": existing_project['title'],
|
||||||
|
"link": existing_project['link'],
|
||||||
|
"description": existing_project['description'],
|
||||||
|
})
|
||||||
|
assert response.status_code == 200
|
||||||
Reference in New Issue
Block a user