diff --git a/.gitignore b/.gitignore index 391f3a2..06fbcc6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ __pycache__ -test.db +test*.db .coverage report.xml -coverage.xml \ No newline at end of file +coverage.xml diff --git a/Dockerfile b/Dockerfile index 1955847..a63b3e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,5 @@ +# +# FROM python:3.12 @@ -13,4 +15,4 @@ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt COPY ./*.py /code/ -CMD ["python3", "papi.py", "--port", "8912"] \ No newline at end of file +CMD ["uvicorn", "papi:app","--port", "8912","--host","0.0.0.0"] diff --git a/Makefile b/Makefile index d018127..bb76e3d 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,35 @@ ENV=dev -VER=4 +VER=0.0.4 TAG=${ENV}-${VER} REGISTRY=kube-reg.fairfaxmedia.net SVC=papi/svc1 DOCK_IMG=${REGISTRY}/${SVC}:${TAG} +LIVE_HOST:="http://localhost:8912" + clean: rm -rf .pytest_cache rm -rf .coverage rm -rf report.xml rm -rf test.db rm -rf htmlcov -coverage: clean + +install: + pip3 install -r requirements.txt +coverage: clean install 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 docker push ${DOCK_IMG} + deploy: docker-push - helm upgrade papi ./papi-helm/ -f ./papi-helm/v.${ENV}.yaml --set image.tag=${TAG} \ No newline at end of file + helm upgrade papi ./papi-helm/ -f ./papi-helm/v.${ENV}.yaml --set image.tag=${TAG} --set env.VERSION=${VER} +integration: install + pyresttest ${LIVE_HOST} integration.pyrest.yaml +run: install + uvicorn papi:app --reload --port 8912 --host 0.0.0.0 + + +all: deploy \ No newline at end of file diff --git a/README.md b/README.md index cb6747f..5d0b82a 100644 --- a/README.md +++ b/README.md @@ -72,4 +72,18 @@ ensure you are logged into kubernetes so that commands kubectl and helm both wor then run ```bash make deploy -``` \ No newline at end of file +``` + + + +##integration testing instructions +to test live on papi.kube-dev.fairfaxmedia.net +run the following: +```bash + make integration -e LIVE_HOST=http://papi.kube-dev.fairfaxmedia.net + ``` + + to integration-test on localhost on the default port run + ```bash + make integration + ``` \ No newline at end of file diff --git a/db.py b/db.py index 3ac403e..1d4d928 100644 --- a/db.py +++ b/db.py @@ -11,7 +11,7 @@ if os.getenv("DATABASE_URL"): engine = create_engine(DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() - +version = os.environ.get("VERSION", "0.0.0") diff --git a/integration.pyrest.yaml b/integration.pyrest.yaml new file mode 100644 index 0000000..bb77088 --- /dev/null +++ b/integration.pyrest.yaml @@ -0,0 +1,85 @@ +# Test using included Django test app +# First install python-django +# Then launch the app in another terminal by doing +# cd testapp +# python manage.py testserver test_data.json +# Once launched, tests can be executed via: +# python resttest.py http://localhost:8000 miniapp-test.yaml +--- +- config: + - testset: "Tests using test app" + - variable_binds: + - 'uid': 1 + - 'post_id': 1 + +- test: # create entity + - name: "Basic get" + - url: "/" +- test: # create entity + - name: "Get version" + - url: "/version" + + +- test: # create person by POST + - name: "Create person" + - url: "/users" + - method: "POST" + - body: '{"name": "Willim","email": "theadmiral@gmail.com"}' + - headers: {Content-Type: application/json} + - extract_binds: + - 'uid' : {'jsonpath_mini': "user.id"} + +- test: # get single person + - name: "Get single person" + - url: {'template':'/users/$uid'} # use binded value + - method: 'GET' + +- test: # update person by PUT + - name: "update person by PUT" + - url: {'template':'/users/$uid'} + - method: "PUT" + - body: '{"name": "Gaius"}' + - headers: {'Content-Type': 'application/json'} + + + + + + + +- test: # create post by POST + - name: "Create post" + - url: "/posts" + - method: "POST" + - body: '{"title": "Willims post","content": "william made a post","author_id": 1}' + - headers: {Content-Type: application/json} + - extract_binds: + - 'post_id' : {'jsonpath_mini': "post.id"} + +- test: # get single post + - name: "Get single post" + - url: {'template':'/posts/$post_id'} + - method: 'GET' + +- test: # update post by PUT + - name: "update post by PUT" + - url: {'template':'/posts/$post_id'} + - method: "PUT" + - body: '{"title": "Gaius made a post"}' + - headers: {'Content-Type': 'application/json'} +- test: # delete post + - name: "Delete post" + - url: {'template':'/posts/$post_id'} + - method: "DELETE" + + + + + +- test: # delete person + - name: "Delete person" + - url: {'template':'/users/$uid'} + - method: "DELETE" + + + diff --git a/papi.py b/papi.py index a35ac26..07358f5 100644 --- a/papi.py +++ b/papi.py @@ -1,4 +1,11 @@ #!/bin/env python3 +# +# author: Stephen Schuresko +# date: 2025-02-18 +# file: papi.py +# description: A simple FastAPI app (papi) with a few routes and models for users and posts +# + from fastapi import FastAPI, Depends import uvicorn from sqlalchemy import create_engine, Column, Integer,String, ForeignKey @@ -7,6 +14,7 @@ from sqlalchemy.orm import sessionmaker, Session, declarative_base import db import users import posts +import os @@ -19,9 +27,6 @@ app.include_router(posts.router) async def root(): return {"message": "Hello World"} - - -if __name__ == "__main__": - uvicorn.run("papi:app", host="0.0.0.0", port=8912, reload=True, log_level="debug", - workers=5, limit_concurrency=10, limit_max_requests=100) - +@app.get("/version") +async def ver(): + return {"version":db.version} diff --git a/requirements.txt b/requirements.txt index c1040e6..13519c5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ fastapi sqlalchemy uvicorn +pyresttest \ No newline at end of file diff --git a/test_papi.py b/test_papi.py index 33e70e6..e6b423a 100644 --- a/test_papi.py +++ b/test_papi.py @@ -12,3 +12,10 @@ def test_read_main(): assert response.json() == {"message": "Hello World"} + + +def test_read_version(): + response = client.get("/version") + assert response.status_code == 200 + +