diff --git a/.env.example b/.env.example index a4ec410..aa080b4 100755 --- a/.env.example +++ b/.env.example @@ -1,7 +1,5 @@ -# Environment variables for PyNews Server - -# API Configuration PYTHONPATH=/server +BASE_URL=http://localhost:8000 # SQLite Database Configuration SQLITE_PATH=/app/data/pynewsdb.db diff --git a/.gitignore b/.gitignore index 3fb254e..13d2c5d 100755 --- a/.gitignore +++ b/.gitignore @@ -136,6 +136,7 @@ celerybeat.pid # Environments .env +.env.test .envrc .venv env/ diff --git a/Dockerfile b/Dockerfile index 05502f0..eec6591 100755 --- a/Dockerfile +++ b/Dockerfile @@ -62,3 +62,20 @@ COPY tests tests COPY scripts scripts CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--lifespan", "on"] + + +FROM builder-base AS scanapi-test + +WORKDIR $PYSETUP_PATH + +RUN poetry install --no-root --no-interaction + +WORKDIR $PROJECT_PATH + +COPY poetry.lock pyproject.toml ./ + +COPY app app +COPY scanapi scanapi +COPY scanapi.conf ./ + +CMD ["poetry", "run", "scanapi", "run"] diff --git a/Makefile b/Makefile index b02b1d5..3c081f1 100755 --- a/Makefile +++ b/Makefile @@ -11,6 +11,10 @@ help: ## Mostra esta mensagem de ajuda @echo "" @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-15s$(NC) %s\n", $$1, $$2}' +install: ## Instala dependências com Poetry + @echo "$(YELLOW)Instalando dependências...$(NC)" + poetry install + build: ## Constrói as imagens Docker @echo "$(YELLOW)Construindo imagens Docker...$(NC)" docker-compose build @@ -26,6 +30,19 @@ down: ## Para os serviços logs: ## Mostra os logs dos serviços docker-compose logs -f pynews-api +restart: ## Reinicia os serviços + @echo "$(YELLOW)Reiniciando serviços...$(NC)" + docker-compose restart + +dev: build up ## Ambiente de desenvolvimento completo + @echo "$(GREEN)Ambiente de desenvolvimento iniciado!$(NC)" + @echo "API: http://localhost:8000" + @echo "Docs: http://localhost:8000/docs" + +prod: ## Inicia em modo produção + @echo "$(YELLOW)Iniciando em modo produção...$(NC)" + docker-compose -f docker-compose.yaml up -d + test: ## Executa os testes @echo "$(YELLOW)Executando testes...$(NC)" poetry run pytest @@ -34,6 +51,12 @@ test-cov: ## Executa os testes com coverage @echo "$(YELLOW)Executando testes com coverage...$(NC)" poetry run pytest --cov=app --cov-report=html +docker-test: + docker exec -e PYTHONPATH=/app $(API_CONTAINER_NAME) pytest -s --cov-report=term-missing --cov-report html --cov-report=xml --cov=app tests/ + +scanapi-test: # Executa testes com scanapi e gera report acessado na porta 8080 no path {url}/scanapi-report.html + docker-compose run --rm scanapi-tests + lint: ## Verifica o código com ruff @echo "$(YELLOW)Verificando código...$(NC)" poetry run ruff check . @@ -42,24 +65,6 @@ format: ## Formata o código @echo "$(YELLOW)Formatando código...$(NC)" poetry run ruff format . -clean: ## Remove containers, volumes e imagens - @echo "$(YELLOW)Limpando containers e volumes...$(NC)" - docker-compose down -v --remove-orphans - docker system prune -f - -dev: build up ## Ambiente de desenvolvimento completo - @echo "$(GREEN)Ambiente de desenvolvimento iniciado!$(NC)" - @echo "API: http://localhost:8000" - @echo "Docs: http://localhost:8000/docs" - -prod: ## Inicia em modo produção - @echo "$(YELLOW)Iniciando em modo produção...$(NC)" - docker-compose -f docker-compose.yaml up -d - -restart: ## Reinicia os serviços - @echo "$(YELLOW)Reiniciando serviços...$(NC)" - docker-compose restart - health: ## Verifica o health check da API @echo "$(YELLOW)Verificando saúde da API...$(NC)" curl -f http://localhost:8000/api/healthcheck || echo "API não está respondendo" @@ -110,6 +115,11 @@ install: ## Instala dependências com Poetry shell: ## Entra no shell do container docker-compose exec pynews-api bash +clean: ## Remove containers, volumes e imagens + @echo "$(YELLOW)Limpando containers e volumes...$(NC)" + docker-compose down -v --remove-orphans + docker system prune -f + setup: install build up ## Setup completo do projeto @echo "$(GREEN)Setup completo realizado!$(NC)" @echo "$(GREEN)Acesse: http://localhost:8000/docs$(NC)" diff --git a/README.md b/README.md index 182619c..af3a346 100755 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # pynewsserver + Serviço de Noticas e Bibliotecas PyNews ## 💡 Visão Geral ## 💻 Tecnologias Utilizadas + - Python - FastAPI - Pydantic @@ -13,15 +15,19 @@ Serviço de Noticas e Bibliotecas PyNews - ruff (linter) ## 🚀 Recursos e Funcionalidades + Endpoints para CRUD de noticias selecionadas pela comunidade. ### Schema da API + [Documentação de referencia API Dog](https://apidog.com/apidoc/shared/70418cab-ddba-4c7d-97a4-8b70b43a7946/) +
--- ### Schema do Servidor + ``` fastapi_news_service/ │ @@ -84,24 +90,28 @@ sequenceDiagram ## ⚙️ Como Rodar ### 📋 Pré-requisitos + - Docker e Docker Compose instalados - Git (para clonar o repositório) ### 🚀 Início Rápido 1. **Clone o repositório:** + ```bash git clone cd PyNewsServer ``` 2. **Configure as variáveis de ambiente (opcional):** + ```bash cp .env.example .env # Edite o arquivo .env conforme necessário ``` 3. **Inicie o serviço:** + ```bash docker-compose up -d ``` @@ -126,6 +136,7 @@ Para mais detalhes sobre configuração do SQLite, consulte: [docs/sqlite-setup. ### ▶️ Guia de Execução para Desenvolvimento #### Usando Docker (Recomendado) + ```bash # Construir e iniciar em modo desenvolvimento docker-compose up --build @@ -138,6 +149,7 @@ docker-compose down ``` #### Usando Poetry (Local) + ```bash # Instalar dependências poetry install @@ -179,42 +191,68 @@ docker-compose up -d --force-recreate ### 🔧 Comandos Úteis #### Usando Makefile (Recomendado) + ```bash # Ver todos os comandos disponíveis make help -# Setup completo do projeto -make setup +# Instalar dependências com Poetry +make install -# Ambiente de desenvolvimento -make dev +# Setup completo do projeto (instala, constrói e sobe containers) +make setup -# Construir e iniciar +# Construir imagens Docker make build + +# Iniciar serviços make up -# Ver logs +# Parar serviços +make down + +# Reiniciar serviços +make restart + +# Ver logs da API make logs -# Executar testes +# Acessar shell dentro do container da API +make shell + +# Executar testes locais make test + +# Executar testes com coverage make test-cov -# Linting e formatação +# Executar testes dentro do container Docker +make docker-test + +# Executar testes com ScanAPI e gerar report que pode ser acessado na porta 8080 no path {url}/scanapi-report.html +make scanapi-test + +# Verificar código com Ruff make lint + +# Formatar código com Ruff make format # Verificar saúde da API make health -# Parar serviços -make down +# Ambiente de desenvolvimento completo +make dev -# Limpeza completa +# Iniciar em modo produção +make prod + +# Limpeza completa de containers, volumes e imagens make clean ``` #### Comandos Docker Diretos + ```bash # Entrar no container docker-compose exec pynews-api bash @@ -232,6 +270,7 @@ docker-compose down -v ### 🛠️ Desenvolvimento #### Estrutura de Testes + ```bash # Rodar todos os testes poetry run pytest @@ -243,7 +282,12 @@ poetry run pytest --cov=app poetry run pytest tests/test_auth.py ``` +##### Requisitos para utilizar [ScanAPI](https://scanapi.dev/) + +- Criar .env.test seguindo o exemplo em .env.example com as credenciais necessárias + #### Linting e Formatação + ```bash # Verificar código poetry run ruff check . @@ -255,6 +299,6 @@ poetry run ruff format . poetry run ruff check . --fix ``` - ## referencias + [Opinion based fastapi best practices](https://github.com/zhanymkanov/fastapi-best-practices) diff --git a/app/routers/admin/routes.py b/app/routers/admin/routes.py index 157b06b..c67e85f 100644 --- a/app/routers/admin/routes.py +++ b/app/routers/admin/routes.py @@ -16,9 +16,9 @@ async def create_admin(session: AsyncSession): - ADMIN_USER = os.getenv("ADMIN_USER") - ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD") - ADMIN_EMAIL = os.getenv("ADMIN_EMAIL") + ADMIN_USER = os.getenv("ADMIN_USER", "") + ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "") + ADMIN_EMAIL = os.getenv("ADMIN_EMAIL", "") password = ADMIN_PASSWORD hashed_password = auth.hash_password(password) community = DBCommunity( diff --git a/app/routers/libraries/routes.py b/app/routers/libraries/routes.py index f18cb3f..259a08a 100644 --- a/app/routers/libraries/routes.py +++ b/app/routers/libraries/routes.py @@ -30,6 +30,10 @@ class SubscribeLibraryResponse(BaseModel): status: str = "Subscribed in libraries successfully" +class LibraryRequestResponse(BaseModel): + status: str = "Library requested successfully" + + def setup(): router = APIRouter(prefix="/libraries", tags=["libraries"]) @@ -163,7 +167,7 @@ async def subscribe_libraries( @router.post( "/request", - response_model=LibraryResponse, + response_model=LibraryRequestResponse, status_code=status.HTTP_200_OK, summary="Request a library", description="Request a library to follow", @@ -189,7 +193,7 @@ async def request_library( library_request, request.app.db_session_factory ) - return LibraryResponse() + return LibraryRequestResponse() except HTTPException as e: raise e except Exception as e: diff --git a/docker-compose.yaml b/docker-compose.yaml index 48cc04d..e9dcf78 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -40,7 +40,35 @@ services: " restart: "no" + scanapi-tests: + build: + context: . + dockerfile: Dockerfile + target: scanapi-test + container_name: scanapi-tests + env_file: + - .env + environment: + - BASE_URL=http://pynews-server:8000 + volumes: + - report-data:/server/scanapi + depends_on: + pynews-api: + condition: service_healthy + command: poetry run scanapi run + + scanapi-report-viewer: + image: nginx:alpine + container_name: scanapi-report-viewer + ports: + - "8080:80" + volumes: + - report-data:/usr/share/nginx/html:ro + depends_on: + - scanapi-tests + volumes: + report-data: sqlite_data: driver: local driver_opts: diff --git a/poetry.lock b/poetry.lock old mode 100644 new mode 100755 index 2205579..f86a744 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "aiosqlite" @@ -51,6 +51,18 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] trio = ["trio (>=0.31.0)"] +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] + [[package]] name = "bcrypt" version = "4.3.0" @@ -483,19 +495,27 @@ files = [ [[package]] name = "cucumber-tag-expressions" -version = "6.2.0" +version = "8.0.0" description = "Provides a tag-expression parser and evaluation logic for cucumber/behave" optional = false -python-versions = ">=2.7" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "cucumber_tag_expressions-6.2.0-py2.py3-none-any.whl", hash = "sha256:f94404b656831c56a3815da5305ac097003884d2ae64fa51f5f4fad82d97e583"}, - {file = "cucumber_tag_expressions-6.2.0.tar.gz", hash = "sha256:b60aa2cdbf9ac43e28d9b0e4fd49edf9f09d5d941257d2912f5228f9d166c023"}, + {file = "cucumber_tag_expressions-8.0.0-py3-none-any.whl", hash = "sha256:bfe552226f62a4462ee91c9643582f524af84ac84952643fb09057580cbb110a"}, + {file = "cucumber_tag_expressions-8.0.0.tar.gz", hash = "sha256:4af80282ff0349918c332428176089094019af6e2a381a2fd8f1c62a7a6bb7e8"}, ] -[package.extras] -develop = ["backports.shutil_which ; python_version <= \"3.3\"", "build (>=0.5.1)", "coverage", "invoke (>=1.7.3)", "path (>=13.1.0) ; python_version >= \"3.5\"", "path.py (>=11.5.0) ; python_version < \"3.5\"", "pathlib ; python_version <= \"3.4\"", "pycmd", "pylint", "pytest (<5.0) ; python_version < \"3.0\"", "pytest (>=5.0) ; python_version >= \"3.0\"", "pytest-html (>=1.19.0)", "ruff", "setuptools", "setuptools-scm", "six (>=1.16.0)", "tox (>=4.26,<4.27)", "twine (>=1.13.0)", "wheel"] -testing = ["PyYAML (>=5.4.1)", "pathlib ; python_version <= \"3.4\"", "pytest (<5.0) ; python_version < \"3.0\"", "pytest (>=5.0) ; python_version >= \"3.0\"", "pytest-html (>=1.19.0)"] +[[package]] +name = "curlify2" +version = "1.0.3.1" +description = "Library to convert python requests and httpx object to curl command." +optional = false +python-versions = ">=3.7,<4.0" +groups = ["main"] +files = [ + {file = "curlify2-1.0.3.1-py3-none-any.whl", hash = "sha256:59018ef55c4458d1a79565bdcee44f53f9e565d920d84ec16daf6a61bddd6d37"}, + {file = "curlify2-1.0.3.1.tar.gz", hash = "sha256:cebec254a0e4fbe3945a4abe93639ebe622ae6957e70f683b18879db2ab48703"}, +] [[package]] name = "deprecated" @@ -699,14 +719,14 @@ trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" -version = "0.28.1" +version = "0.27.2" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, - {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, + {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, + {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, ] [package.dependencies] @@ -714,6 +734,7 @@ anyio = "*" certifi = "*" httpcore = "==1.*" idna = "*" +sniffio = "*" [package.extras] brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] @@ -764,6 +785,24 @@ files = [ {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + [[package]] name = "limits" version = "5.6.0" @@ -792,6 +831,102 @@ redis = ["redis (>3,!=4.5.2,!=4.5.3,<7.0.0)"] rediscluster = ["redis (>=4.2.0,!=4.5.2,!=4.5.3)"] valkey = ["valkey (>=6)"] +[[package]] +name = "markdown-it-py" +version = "4.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"}, + {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins (>=0.5.0)"] +profiling = ["gprof2dot"] +rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] + +[[package]] +name = "markupsafe" +version = "2.1.2" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "mslex" version = "1.3.0" @@ -912,14 +1047,14 @@ files = [ [[package]] name = "packaging" -version = "25.0" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -1191,7 +1326,7 @@ version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, @@ -1392,6 +1527,25 @@ files = [ {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, ] +[[package]] +name = "rich" +version = "14.0.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, + {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "ruff" version = "0.6.9" @@ -1420,6 +1574,29 @@ files = [ {file = "ruff-0.6.9.tar.gz", hash = "sha256:b076ef717a8e5bc819514ee1d602bbdca5b4420ae13a9cf61a0c0a4f53a2baa2"}, ] +[[package]] +name = "scanapi" +version = "2.12.0" +description = "Automated Testing and Documentation for your REST API" +optional = false +python-versions = "<4.0,>=3.10" +groups = ["main"] +files = [ + {file = "scanapi-2.12.0-py3-none-any.whl", hash = "sha256:5aa0e644c2037965c4c0c1db884bf4fc02078acb51c065700785de25557b5399"}, + {file = "scanapi-2.12.0.tar.gz", hash = "sha256:fd488a9f9eaed51c0999c7f2d54649507786172fbb053b347b4e4c465937f1d8"}, +] + +[package.dependencies] +appdirs = ">=1.4.4,<2.0.0" +click = ">=8.1.3" +curlify2 = ">=1.0.1,<2.0.0" +httpx = ">=0.27.0,<0.28.0" +Jinja2 = ">=3.1.0,<3.2.0" +MarkupSafe = "2.1.2" +packaging = ">=24.0,<25.0" +PyYAML = ">=6.0.2,<6.1.0" +rich = "14.0.0" + [[package]] name = "six" version = "1.17.0" @@ -1825,4 +2002,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = "^3.12" -content-hash = "112d46cef288dccf9f24dc2619f239731fb75f3873640335ce0dc78126921a25" +content-hash = "1e925f5e9e4e5f11abd63f69a63eb75da5fa567a86ef668c0824c4b88424aca6" diff --git a/pyproject.toml b/pyproject.toml index 9d92249..38e5b3a 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ python = "^3.12" fastapi = "^0.115.8" orjson = "^3.10.15" uvicorn = "^0.22.0" -httpx = "^0.28.1" +httpx = "^0.27.0" sqlmodel = "^0.0.24" aiosqlite = "^0.21.0" pre-commit = "^4.2.0" @@ -22,6 +22,7 @@ pyjwt = "^2.10.1" bcrypt = "^4.3.0" cryptography = "^45.0.7" slowapi = "^0.1.9" +scanapi = "^2.12.0" [tool.poetry.group.dev.dependencies] pytest = "^8.3.2" diff --git a/scanapi.conf b/scanapi.conf new file mode 100644 index 0000000..b037963 --- /dev/null +++ b/scanapi.conf @@ -0,0 +1,12 @@ +project_name: PyNewsServer +spec_path: scanapi/scanapi.yaml +output_path: scanapi/scanapi-report.html +report: + hide_request: + headers: + - Authorization + body: + - password + hide_response: + body: + - access_token \ No newline at end of file diff --git a/scanapi/libraries_authenticated.yaml b/scanapi/libraries_authenticated.yaml new file mode 100644 index 0000000..27804dc --- /dev/null +++ b/scanapi/libraries_authenticated.yaml @@ -0,0 +1,48 @@ +name: libraries_authenticated +path: libraries +headers: + Authorization: Bearer ${access_token} + user-email: teste@teste.com +requests: + - name: get_libraries + params: + language: Python + tests: + - !include tests/status_code_is_200.yaml + - !include tests/body_is_array.yaml + - name: create_library + method: POST + body: + library_name: "Flask" + news: + - tag: "updates" + description: "News about Python" + logo: "https://example.com/python-logo.png" + version: "1.0.0" + release_date: "2023-01-01" + releases_doc_url: "https://example.com/python-release-notes.txt" + fixed_release_url: "https://example.com/python-fixed-release.txt" + language: "Python" + tests: + - !include tests/status_code_is_200.yaml + - !include tests/libraries/body_status_created.yaml + - name: subscribe_to_library + path: subscribe + method: POST + body: + tags: + - updates + libraries_list: + - Flask + tests: + - !include tests/status_code_is_200.yaml + - !include tests/libraries/body_status_subscribed.yaml + - name: request_library_news + path: request + method: POST + body: + library_name: "Flask" + library_home_page: "https://flask.palletsprojects.com/en/2.3.x/" + tests: + - !include tests/status_code_is_200.yaml + - !include tests/libraries/body_status_requested.yaml diff --git a/scanapi/libraries_unauthenticated.yaml b/scanapi/libraries_unauthenticated.yaml new file mode 100644 index 0000000..a02f29f --- /dev/null +++ b/scanapi/libraries_unauthenticated.yaml @@ -0,0 +1,45 @@ +name: libraries_unauthenticated +path: libraries +requests: + - name: get_libraries + params: + language: Python + tests: + - !include tests/status_code_is_401.yaml + - !include tests/authentication/body_detail_not_authenticated.yaml + - name: create_library + method: POST + body: + library_name: "Python" + news: + - tag: "updates" + description: "News about Python" + logo: "https://example.com/python-logo.png" + version: "1.0.0" + release_date: "2023-01-01" + releases_doc_url: "https://example.com/python-release-notes.txt" + fixed_release_url: "https://example.com/python-fixed-release.txt" + language: "Python" + tests: + - !include tests/status_code_is_401.yaml + - !include tests/authentication/body_detail_not_authenticated.yaml + - name: subscribe_to_library + path: subscribe + method: POST + body: + tags: + - updates + libraries_list: + - Flask + tests: + - !include tests/status_code_is_401.yaml + - !include tests/authentication/body_detail_not_authenticated.yaml + - name: request_library_news + path: request + method: POST + body: + library_name: "Flask" + library_home_page: "https://flask.palletsprojects.com/en/2.3.x/" + tests: + - !include tests/status_code_is_401.yaml + - !include tests/authentication/body_detail_not_authenticated.yaml diff --git a/scanapi/news_authenticated.yaml b/scanapi/news_authenticated.yaml new file mode 100644 index 0000000..1250675 --- /dev/null +++ b/scanapi/news_authenticated.yaml @@ -0,0 +1,36 @@ +name: news_authenticated +path: news +headers: + Authorization: Bearer ${access_token} + user-email: teste@teste.com +requests: + - name: create_news + method: POST + body: + title: "Test News" + content: "This is a test news article." + category: "test_category" + tags: "test_tag" + source_url: "https://example.com/test-news" + social_media_url: "https://example.com/testnews" + tests: + - !include tests/status_code_is_200.yaml + - name: get_news + vars: + news_id: ${{ response.json()["news_list"][0]["id"] }} + tests: + - !include tests/status_code_is_200.yaml + - name: like_news + path: ${news_id}/like + method: POST + body: + total_likes: 1 + tests: + - !include tests/status_code_is_200.yaml + - name: dislike_news + path: ${news_id}/like + method: DELETE + body: + total_likes: 1 + tests: + - !include tests/status_code_is_200.yaml diff --git a/scanapi/news_unauthenticated.yaml b/scanapi/news_unauthenticated.yaml new file mode 100644 index 0000000..ae2bb4d --- /dev/null +++ b/scanapi/news_unauthenticated.yaml @@ -0,0 +1,37 @@ +name: news_unauthenticated +path: news +vars: + news_id: "1" +requests: + - name: get_news + tests: + - !include tests/status_code_is_401.yaml + - !include tests/authentication/body_detail_not_authenticated.yaml + - name: create_news + method: POST + body: + title: "Test News" + content: "This is a test news article." + category: "test_category" + tags: "test_tag" + source_url: "https://example.com/test-news" + social_media_url: "https://example.com/testnews" + tests: + - !include tests/status_code_is_401.yaml + - !include tests/authentication/body_detail_not_authenticated.yaml + - name: like_news + path: ${news_id}/like + method: POST + body: + total_likes: 1 + tests: + - !include tests/status_code_is_401.yaml + - !include tests/authentication/body_detail_not_authenticated.yaml + - name: dislike_news + path: ${news_id}/like + method: DELETE + body: + total_likes: 1 + tests: + - !include tests/status_code_is_401.yaml + - !include tests/authentication/body_detail_not_authenticated.yaml diff --git a/scanapi/scanapi-report.html b/scanapi/scanapi-report.html new file mode 100644 index 0000000..ec0fcd4 --- /dev/null +++ b/scanapi/scanapi-report.html @@ -0,0 +1,5302 @@ + + + + + + + + ScanAPI Report + + + + + + + + +
+ +
+ +
+
+ +

Report generated for PyNewsServer

+ +

Generated at: 2025-10-08 22:02:51

+
+ + +
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/healthcheck

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ + + + +
+ cURL +
curl -X GET -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -d 'b''' http://localhost:8000/api/healthcheck --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 200 +
+ response time + + 0.012535 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:51 GMT +
+ server + + uvicorn +
+ content-length + + 38 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"status":"healthy","version":"2.0.0"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::healthcheck::status_code_is_200 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::healthcheck::body_status_healthy + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/authentication/create_community

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ content-length + + 26 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X POST -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "content-length: 26" -H "content-type: application/json" -d '{"name": "Test Community"}' http://localhost:8000/api/authentication/create_community --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 200 +
+ response time + + 0.277971 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:51 GMT +
+ server + + uvicorn +
+ content-length + + 18 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"msg":"succes? "} +

+ +
+ +
+ +
+

Tests

+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/authentication/token

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ content-type + + application/x-www-form-urlencoded +
+ content-length + + 42 +
+ + + + +
+ cURL +
curl -X POST -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "content-type: application/x-www-form-urlencoded" -H "content-length: 42" -d 'username=username&password=123Asd%21%40%23' http://localhost:8000/api/authentication/token --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 200 +
+ response time + + 0.299019 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:52 GMT +
+ server + + uvicorn +
+ content-length + + 187 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"access_token": "SENSITIVE_INFORMATION", "token_type": "Bearer", "expires_in": 1200} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::login_for_access_token::status_code_is_200 + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/libraries?language=Python

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ authorization + + SENSITIVE_INFORMATION +
+ user-email + + teste@teste.com +
+ + + + +
+ cURL +
curl -X GET -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "authorization: SENSITIVE_INFORMATION" -H "user-email: teste@teste.com" -d 'b''' http://localhost:8000/api/libraries?language=Python --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 200 +
+ response time + + 0.012075 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:52 GMT +
+ server + + uvicorn +
+ content-length + + 2 +
+ content-type + + application/json +
+
+ +
+ Content +

+ [] +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_authenticated::get_libraries::status_code_is_200 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_authenticated::get_libraries::body_is_array + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/libraries

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ authorization + + SENSITIVE_INFORMATION +
+ user-email + + teste@teste.com +
+ content-length + + 347 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X POST -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "authorization: SENSITIVE_INFORMATION" -H "user-email: teste@teste.com" -H "content-length: 347" -H "content-type: application/json" -d '{"library_name": "Flask", "news": [{"tag": "updates", "description": "News about Python"}], "logo": "https://example.com/python-logo.png", "version": "1.0.0", "release_date": "2023-01-01", "releases_doc_url": "https://example.com/python-release-notes.txt", "fixed_release_url": "https://example.com/python-fixed-release.txt", "language": "Python"}' http://localhost:8000/api/libraries --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 200 +
+ response time + + 0.016772 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:53 GMT +
+ server + + uvicorn +
+ content-length + + 41 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"status":"Library created successfully"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_authenticated::create_library::status_code_is_200 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_authenticated::create_library::body_status_created + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/libraries/subscribe

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ authorization + + SENSITIVE_INFORMATION +
+ user-email + + teste@teste.com +
+ content-length + + 50 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X POST -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "authorization: SENSITIVE_INFORMATION" -H "user-email: teste@teste.com" -H "content-length: 50" -H "content-type: application/json" -d '{"tags": ["updates"], "libraries_list": ["Flask"]}' http://localhost:8000/api/libraries/subscribe --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 200 +
+ response time + + 0.019481 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:53 GMT +
+ server + + uvicorn +
+ content-length + + 49 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"status":"Subscribed in libraries successfully"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_authenticated::subscribe_to_library::status_code_is_200 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_authenticated::subscribe_to_library::body_status_subscribed + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/libraries/request

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ authorization + + SENSITIVE_INFORMATION +
+ user-email + + teste@teste.com +
+ content-length + + 93 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X POST -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "authorization: SENSITIVE_INFORMATION" -H "user-email: teste@teste.com" -H "content-length: 93" -H "content-type: application/json" -d '{"library_name": "Flask", "library_home_page": "https://flask.palletsprojects.com/en/2.3.x/"}' http://localhost:8000/api/libraries/request --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 200 +
+ response time + + 0.014056 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:53 GMT +
+ server + + uvicorn +
+ content-length + + 43 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"status":"Library requested successfully"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_authenticated::request_library_news::status_code_is_200 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_authenticated::request_library_news::body_status_requested + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/libraries?language=Python

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ + + + +
+ cURL +
curl -X GET -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -d 'b''' http://localhost:8000/api/libraries?language=Python --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 401 +
+ response time + + 0.005129 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:54 GMT +
+ server + + uvicorn +
+ www-authenticate + + Bearer +
+ content-length + + 30 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"detail":"Not authenticated"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_unauthenticated::get_libraries::status_code_is_401 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_unauthenticated::get_libraries::body_detail_not_authenticated + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/libraries

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ content-length + + 348 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X POST -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "content-length: 348" -H "content-type: application/json" -d '{"library_name": "Python", "news": [{"tag": "updates", "description": "News about Python"}], "logo": "https://example.com/python-logo.png", "version": "1.0.0", "release_date": "2023-01-01", "releases_doc_url": "https://example.com/python-release-notes.txt", "fixed_release_url": "https://example.com/python-fixed-release.txt", "language": "Python"}' http://localhost:8000/api/libraries --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 401 +
+ response time + + 0.005741 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:54 GMT +
+ server + + uvicorn +
+ www-authenticate + + Bearer +
+ content-length + + 30 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"detail":"Not authenticated"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_unauthenticated::create_library::status_code_is_401 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_unauthenticated::create_library::body_detail_not_authenticated + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/libraries/subscribe

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ content-length + + 50 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X POST -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "content-length: 50" -H "content-type: application/json" -d '{"tags": ["updates"], "libraries_list": ["Flask"]}' http://localhost:8000/api/libraries/subscribe --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 401 +
+ response time + + 0.005629 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:54 GMT +
+ server + + uvicorn +
+ www-authenticate + + Bearer +
+ content-length + + 30 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"detail":"Not authenticated"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_unauthenticated::subscribe_to_library::status_code_is_401 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_unauthenticated::subscribe_to_library::body_detail_not_authenticated + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/libraries/request

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ content-length + + 93 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X POST -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "content-length: 93" -H "content-type: application/json" -d '{"library_name": "Flask", "library_home_page": "https://flask.palletsprojects.com/en/2.3.x/"}' http://localhost:8000/api/libraries/request --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 401 +
+ response time + + 0.004986 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:56 GMT +
+ server + + uvicorn +
+ www-authenticate + + Bearer +
+ content-length + + 30 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"detail":"Not authenticated"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_unauthenticated::request_library_news::status_code_is_401 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::libraries_unauthenticated::request_library_news::body_detail_not_authenticated + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/news

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ authorization + + SENSITIVE_INFORMATION +
+ user-email + + teste@teste.com +
+ content-length + + 213 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X POST -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "authorization: SENSITIVE_INFORMATION" -H "user-email: teste@teste.com" -H "content-length: 213" -H "content-type: application/json" -d '{"title": "Test News", "content": "This is a test news article.", "category": "test_category", "tags": "test_tag", "source_url": "https://example.com/test-news", "social_media_url": "https://example.com/testnews"}' http://localhost:8000/api/news --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 200 +
+ response time + + 0.015065 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:56 GMT +
+ server + + uvicorn +
+ content-length + + 24 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"status":"News Criada"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::news_authenticated::create_news::status_code_is_200 + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/news

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ authorization + + SENSITIVE_INFORMATION +
+ user-email + + teste@teste.com +
+ + + + +
+ cURL +
curl -X GET -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "authorization: SENSITIVE_INFORMATION" -H "user-email: teste@teste.com" -d 'b''' http://localhost:8000/api/news --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 200 +
+ response time + + 0.009937 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:56 GMT +
+ server + + uvicorn +
+ content-length + + 425 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"status":"Lista de News Obtida","news_list":[{"content":"This is a test news article.","title":"Test News","category":"test_category","source_url":"https://example.com/test-news","user_email_list":"[]","likes":0,"created_at":"2025-10-09T01:02:56.548455","id":1,"user_email":"teste@teste.com","tags":"test_tag","social_media_url":"https://example.com/testnews","community_id":null,"updated_at":"2025-10-09T01:02:56.548593"}]} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::news_authenticated::get_news::status_code_is_200 + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/news/1/like

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ authorization + + SENSITIVE_INFORMATION +
+ user-email + + teste@teste.com +
+ content-length + + 18 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X POST -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "authorization: SENSITIVE_INFORMATION" -H "user-email: teste@teste.com" -H "content-length: 18" -H "content-type: application/json" -d '{"total_likes": 1}' http://localhost:8000/api/news/1/like --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 200 +
+ response time + + 0.014314 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:57 GMT +
+ server + + uvicorn +
+ content-length + + 17 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"total_likes":1} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::news_authenticated::like_news::status_code_is_200 + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/news/1/like

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ authorization + + SENSITIVE_INFORMATION +
+ user-email + + teste@teste.com +
+ content-length + + 18 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X DELETE -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "authorization: SENSITIVE_INFORMATION" -H "user-email: teste@teste.com" -H "content-length: 18" -H "content-type: application/json" -d '{"total_likes": 1}' http://localhost:8000/api/news/1/like --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 200 +
+ response time + + 0.012571 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:57 GMT +
+ server + + uvicorn +
+ content-length + + 17 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"total_likes":0} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::news_authenticated::dislike_news::status_code_is_200 + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/news

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ + + + +
+ cURL +
curl -X GET -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -d 'b''' http://localhost:8000/api/news --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 401 +
+ response time + + 0.005016 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:58 GMT +
+ server + + uvicorn +
+ www-authenticate + + Bearer +
+ content-length + + 30 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"detail":"Not authenticated"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::news_unauthenticated::get_news::status_code_is_401 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::news_unauthenticated::get_news::body_detail_not_authenticated + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/news

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ content-length + + 213 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X POST -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "content-length: 213" -H "content-type: application/json" -d '{"title": "Test News", "content": "This is a test news article.", "category": "test_category", "tags": "test_tag", "source_url": "https://example.com/test-news", "social_media_url": "https://example.com/testnews"}' http://localhost:8000/api/news --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 401 +
+ response time + + 0.0052 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:58 GMT +
+ server + + uvicorn +
+ www-authenticate + + Bearer +
+ content-length + + 30 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"detail":"Not authenticated"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::news_unauthenticated::create_news::status_code_is_401 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::news_unauthenticated::create_news::body_detail_not_authenticated + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/news/1/like

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ content-length + + 18 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X POST -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "content-length: 18" -H "content-type: application/json" -d '{"total_likes": 1}' http://localhost:8000/api/news/1/like --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 401 +
+ response time + + 0.004478 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:58 GMT +
+ server + + uvicorn +
+ www-authenticate + + Bearer +
+ content-length + + 30 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"detail":"Not authenticated"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::news_unauthenticated::like_news::status_code_is_401 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::news_unauthenticated::like_news::body_detail_not_authenticated + + + +
+
+ +
+
+
+ + + + + + + +
+ + + +
+
+

Request

+

Full URL: http://localhost:8000/api/news/1/like

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ host + + localhost:8000 +
+ accept + + */* +
+ accept-encoding + + gzip, deflate +
+ connection + + keep-alive +
+ user-agent + + python-httpx/0.27.2 +
+ content-length + + 18 +
+ content-type + + application/json +
+ + + + +
+ cURL +
curl -X DELETE -H "host: localhost:8000" -H "accept: */*" -H "accept-encoding: gzip, deflate" -H "connection: keep-alive" -H "user-agent: python-httpx/0.27.2" -H "content-length: 18" -H "content-type: application/json" -d '{"total_likes": 1}' http://localhost:8000/api/news/1/like --compressed
+ +
+
+ +
+

Response

+ + + + + + + + + + + + + + + +
+ status code + + 401 +
+ response time + + 0.017986 s +
+ redirect + + False +
+ + +
+ Headers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Header + + Value +
+ date + + Thu, 09 Oct 2025 01:02:59 GMT +
+ server + + uvicorn +
+ www-authenticate + + Bearer +
+ content-length + + 30 +
+ content-type + + application/json +
+
+ +
+ Content +

+ {"detail":"Not authenticated"} +

+ +
+ +
+ +
+

Tests

+
+
+ + [PASSED] + +
+
+ + py_news_server::news_unauthenticated::dislike_news::status_code_is_401 + + + +
+
+
+
+ + [PASSED] + +
+
+ + py_news_server::news_unauthenticated::dislike_news::body_detail_not_authenticated + + + +
+
+ +
+
+
+ +
+ + +
+

Tests Summary

+
+ PASSED: 31 + FAILURES: 0 + ERRORS: 0 + Total Time: 0:00:07.858519 +
+
+
+ + + +
+ Generated by ScanAPI 2.12.0 +
+ + + + + + + + \ No newline at end of file diff --git a/scanapi/scanapi.yaml b/scanapi/scanapi.yaml new file mode 100644 index 0000000..79e3994 --- /dev/null +++ b/scanapi/scanapi.yaml @@ -0,0 +1,29 @@ +endpoints: + - name: py_news_server + path: ${BASE_URL}/api + vars: + admin_user: ${ADMIN_USER} + admin_password: ${ADMIN_PASSWORD} + requests: + - name: healthcheck + path: healthcheck + tests: + - !include tests/status_code_is_200.yaml + - !include tests/healthcheck/body_status_healthy.yaml + - name: login_for_access_token + path: authentication/token + method: POST + headers: + Content-Type: application/x-www-form-urlencoded + body: + username: ${admin_user} + password: ${admin_password} + tests: + - !include tests/status_code_is_200.yaml + vars: + access_token: ${{ response.json()["access_token"] if response.status_code == 200 else "" }} + endpoints: + - !include libraries_authenticated.yaml + - !include libraries_unauthenticated.yaml + - !include news_authenticated.yaml + - !include news_unauthenticated.yaml diff --git a/scanapi/tests/authentication/body_detail_not_authenticated.yaml b/scanapi/tests/authentication/body_detail_not_authenticated.yaml new file mode 100644 index 0000000..c0b6a60 --- /dev/null +++ b/scanapi/tests/authentication/body_detail_not_authenticated.yaml @@ -0,0 +1,2 @@ +name: body_detail_not_authenticated +assert: ${{ response.json()["detail"] == "Not authenticated" }} diff --git a/scanapi/tests/body_is_array.yaml b/scanapi/tests/body_is_array.yaml new file mode 100644 index 0000000..1b7462a --- /dev/null +++ b/scanapi/tests/body_is_array.yaml @@ -0,0 +1,2 @@ +name: body_is_array +assert: ${{ len(response.json()) >= 0 }} diff --git a/scanapi/tests/healthcheck/body_status_healthy.yaml b/scanapi/tests/healthcheck/body_status_healthy.yaml new file mode 100644 index 0000000..cf3ccac --- /dev/null +++ b/scanapi/tests/healthcheck/body_status_healthy.yaml @@ -0,0 +1,2 @@ +name: body_status_healthy +assert: ${{ response.json()["status"] == "healthy" and response.json()["version"] == "2.0.0" }} diff --git a/scanapi/tests/libraries/body_status_created.yaml b/scanapi/tests/libraries/body_status_created.yaml new file mode 100644 index 0000000..48e3c33 --- /dev/null +++ b/scanapi/tests/libraries/body_status_created.yaml @@ -0,0 +1,2 @@ +name: body_status_created +assert: ${{ response.json()["status"] == "Library created successfully" }} diff --git a/scanapi/tests/libraries/body_status_requested.yaml b/scanapi/tests/libraries/body_status_requested.yaml new file mode 100644 index 0000000..1aeed88 --- /dev/null +++ b/scanapi/tests/libraries/body_status_requested.yaml @@ -0,0 +1,2 @@ +name: body_status_requested +assert: ${{ response.json()["status"] == "Library requested successfully" }} diff --git a/scanapi/tests/libraries/body_status_subscribed.yaml b/scanapi/tests/libraries/body_status_subscribed.yaml new file mode 100644 index 0000000..d87e542 --- /dev/null +++ b/scanapi/tests/libraries/body_status_subscribed.yaml @@ -0,0 +1,2 @@ +name: body_status_subscribed +assert: ${{ response.json()["status"] == "Subscribed in libraries successfully" }} diff --git a/scanapi/tests/status_code_is_200.yaml b/scanapi/tests/status_code_is_200.yaml new file mode 100644 index 0000000..d1dd721 --- /dev/null +++ b/scanapi/tests/status_code_is_200.yaml @@ -0,0 +1,2 @@ +name: status_code_is_200 +assert: ${{ response.status_code == 200 }} diff --git a/scanapi/tests/status_code_is_401.yaml b/scanapi/tests/status_code_is_401.yaml new file mode 100644 index 0000000..af2b05b --- /dev/null +++ b/scanapi/tests/status_code_is_401.yaml @@ -0,0 +1,2 @@ +name: status_code_is_401 +assert: ${{ response.status_code == 401 }}