committed by
GitHub
144 changed files with 1661 additions and 17103 deletions
@ -0,0 +1,52 @@ |
|||||
|
name: FastAPI People Sponsors |
||||
|
|
||||
|
on: |
||||
|
schedule: |
||||
|
- cron: "0 6 1 * *" |
||||
|
workflow_dispatch: |
||||
|
inputs: |
||||
|
debug_enabled: |
||||
|
description: "Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)" |
||||
|
required: false |
||||
|
default: "false" |
||||
|
|
||||
|
env: |
||||
|
UV_SYSTEM_PYTHON: 1 |
||||
|
|
||||
|
jobs: |
||||
|
job: |
||||
|
if: github.repository_owner == 'fastapi' |
||||
|
runs-on: ubuntu-latest |
||||
|
permissions: |
||||
|
contents: write |
||||
|
steps: |
||||
|
- name: Dump GitHub context |
||||
|
env: |
||||
|
GITHUB_CONTEXT: ${{ toJson(github) }} |
||||
|
run: echo "$GITHUB_CONTEXT" |
||||
|
- uses: actions/checkout@v4 |
||||
|
- name: Set up Python |
||||
|
uses: actions/setup-python@v5 |
||||
|
with: |
||||
|
python-version: "3.11" |
||||
|
- name: Setup uv |
||||
|
uses: astral-sh/setup-uv@v5 |
||||
|
with: |
||||
|
version: "0.4.15" |
||||
|
enable-cache: true |
||||
|
cache-dependency-glob: | |
||||
|
requirements**.txt |
||||
|
pyproject.toml |
||||
|
- name: Install Dependencies |
||||
|
run: uv pip install -r requirements-github-actions.txt |
||||
|
# Allow debugging with tmate |
||||
|
- name: Setup tmate session |
||||
|
uses: mxschmitt/action-tmate@v3 |
||||
|
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }} |
||||
|
with: |
||||
|
limit-access-to-actor: true |
||||
|
- name: FastAPI People Sponsors |
||||
|
run: python ./scripts/sponsors.py |
||||
|
env: |
||||
|
SPONSORS_TOKEN: ${{ secrets.SPONSORS_TOKEN }} |
||||
|
PR_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }} |
@ -8,7 +8,7 @@ Por isso é comum prover essas configurações como variáveis de ambiente que s |
|||||
|
|
||||
## Variáveis de Ambiente |
## Variáveis de Ambiente |
||||
|
|
||||
/// dica |
/// tip | Dica |
||||
|
|
||||
Se você já sabe o que são variáveis de ambiente e como utilizá-las, sinta-se livre para avançar para o próximo tópico. |
Se você já sabe o que são variáveis de ambiente e como utilizá-las, sinta-se livre para avançar para o próximo tópico. |
||||
|
|
||||
@ -67,7 +67,7 @@ name = os.getenv("MY_NAME", "World") |
|||||
print(f"Hello {name} from Python") |
print(f"Hello {name} from Python") |
||||
``` |
``` |
||||
|
|
||||
/// dica |
/// tip | Dica |
||||
|
|
||||
O segundo parâmetro em <a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> é o valor padrão para o retorno. |
O segundo parâmetro em <a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> é o valor padrão para o retorno. |
||||
|
|
||||
@ -124,7 +124,7 @@ Hello World from Python |
|||||
|
|
||||
</div> |
</div> |
||||
|
|
||||
/// dica |
/// tip | Dica |
||||
|
|
||||
Você pode ler mais sobre isso em: <a href="https://12factor.net/pt_br/config" class="external-link" target="_blank">The Twelve-Factor App: Configurações</a>. |
Você pode ler mais sobre isso em: <a href="https://12factor.net/pt_br/config" class="external-link" target="_blank">The Twelve-Factor App: Configurações</a>. |
||||
|
|
||||
@ -196,7 +196,7 @@ Na versão 1 do Pydantic você importaria `BaseSettings` diretamente do módulo |
|||||
|
|
||||
//// |
//// |
||||
|
|
||||
/// dica |
/// tip | Dica |
||||
|
|
||||
Se você quiser algo pronto para copiar e colar na sua aplicação, não use esse exemplo, mas sim o exemplo abaixo. |
Se você quiser algo pronto para copiar e colar na sua aplicação, não use esse exemplo, mas sim o exemplo abaixo. |
||||
|
|
||||
@ -226,7 +226,7 @@ $ ADMIN_EMAIL="[email protected]" APP_NAME="ChimichangApp" fastapi run main.p |
|||||
|
|
||||
</div> |
</div> |
||||
|
|
||||
/// dica |
/// tip | Dica |
||||
|
|
||||
Para definir múltiplas variáveis de ambiente para um único comando basta separá-las utilizando espaços, e incluir todas elas antes do comando. |
Para definir múltiplas variáveis de ambiente para um único comando basta separá-las utilizando espaços, e incluir todas elas antes do comando. |
||||
|
|
||||
@ -250,7 +250,7 @@ E utilizar essa configuração em `main.py`: |
|||||
|
|
||||
{* ../../docs_src/settings/app01/main.py hl[3,11:13] *} |
{* ../../docs_src/settings/app01/main.py hl[3,11:13] *} |
||||
|
|
||||
/// dica |
/// tip | Dica |
||||
|
|
||||
Você também precisa incluir um arquivo `__init__.py` como visto em [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=\_blank}. |
Você também precisa incluir um arquivo `__init__.py` como visto em [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md){.internal-link target=\_blank}. |
||||
|
|
||||
@ -276,7 +276,7 @@ Agora criamos a dependência que retorna um novo objeto `config.Settings()`. |
|||||
|
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *} |
{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *} |
||||
|
|
||||
/// dica |
/// tip | Dica |
||||
|
|
||||
Vamos discutir sobre `@lru_cache` logo mais. |
Vamos discutir sobre `@lru_cache` logo mais. |
||||
|
|
||||
@ -304,7 +304,7 @@ Se você tiver muitas configurações que variem bastante, talvez em ambientes d |
|||||
|
|
||||
Essa prática é tão comum que possui um nome, essas variáveis de ambiente normalmente são colocadas em um arquivo `.env`, e esse arquivo é chamado de "dotenv". |
Essa prática é tão comum que possui um nome, essas variáveis de ambiente normalmente são colocadas em um arquivo `.env`, e esse arquivo é chamado de "dotenv". |
||||
|
|
||||
/// dica |
/// tip | Dica |
||||
|
|
||||
Um arquivo iniciando com um ponto final (`.`) é um arquivo oculto em sistemas baseados em Unix, como Linux e MacOS. |
Um arquivo iniciando com um ponto final (`.`) é um arquivo oculto em sistemas baseados em Unix, como Linux e MacOS. |
||||
|
|
||||
@ -314,7 +314,7 @@ Mas um arquivo dotenv não precisa ter esse nome exato. |
|||||
|
|
||||
Pydantic suporta a leitura desses tipos de arquivos utilizando uma biblioteca externa. Você pode ler mais em <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support" class="external-link" target="_blank">Pydantic Settings: Dotenv (.env) support</a>. |
Pydantic suporta a leitura desses tipos de arquivos utilizando uma biblioteca externa. Você pode ler mais em <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support" class="external-link" target="_blank">Pydantic Settings: Dotenv (.env) support</a>. |
||||
|
|
||||
/// dica |
/// tip | Dica |
||||
|
|
||||
Para que isso funcione você precisa executar `pip install python-dotenv`. |
Para que isso funcione você precisa executar `pip install python-dotenv`. |
||||
|
|
||||
@ -337,7 +337,7 @@ E então adicionar o seguinte código em `config.py`: |
|||||
|
|
||||
{* ../../docs_src/settings/app03_an/config.py hl[9] *} |
{* ../../docs_src/settings/app03_an/config.py hl[9] *} |
||||
|
|
||||
/// dica |
/// tip | Dica |
||||
|
|
||||
O atributo `model_config` é usado apenas para configuração do Pydantic. Você pode ler mais em <a href="https://docs.pydantic.dev/latest/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>. |
O atributo `model_config` é usado apenas para configuração do Pydantic. Você pode ler mais em <a href="https://docs.pydantic.dev/latest/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>. |
||||
|
|
||||
@ -349,7 +349,7 @@ O atributo `model_config` é usado apenas para configuração do Pydantic. Você |
|||||
|
|
||||
{* ../../docs_src/settings/app03_an/config_pv1.py hl[9:10] *} |
{* ../../docs_src/settings/app03_an/config_pv1.py hl[9:10] *} |
||||
|
|
||||
/// dica |
/// tip | Dica |
||||
|
|
||||
A classe `Config` é usada apenas para configuração do Pydantic. Você pode ler mais em <a href="https://docs.pydantic.dev/1.10/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>. |
A classe `Config` é usada apenas para configuração do Pydantic. Você pode ler mais em <a href="https://docs.pydantic.dev/1.10/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>. |
||||
|
|
||||
|
@ -0,0 +1,274 @@ |
|||||
|
# OAuth2 com Senha (e hashing), Bearer com tokens JWT |
||||
|
|
||||
|
Agora que temos todo o fluxo de segurança, vamos tornar a aplicação realmente segura, usando tokens <abbr title="JSON Web Tokens">JWT</abbr> e hashing de senhas seguras. |
||||
|
|
||||
|
Este código é algo que você pode realmente usar na sua aplicação, salvar os hashes das senhas no seu banco de dados, etc. |
||||
|
|
||||
|
Vamos começar de onde paramos no capítulo anterior e incrementá-lo. |
||||
|
|
||||
|
## Sobre o JWT |
||||
|
|
||||
|
JWT significa "JSON Web Tokens". |
||||
|
|
||||
|
É um padrão para codificar um objeto JSON em uma string longa e densa sem espaços. Ele se parece com isso: |
||||
|
|
||||
|
``` |
||||
|
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c |
||||
|
``` |
||||
|
|
||||
|
Ele não é criptografado, então qualquer pessoa pode recuperar as informações do seu conteúdo. |
||||
|
|
||||
|
Mas ele é assinado. Assim, quando você recebe um token que você emitiu, você pode verificar que foi realmente você quem o emitiu. |
||||
|
|
||||
|
Dessa forma, você pode criar um token com um prazo de expiração, digamos, de 1 semana. E então, quando o usuário voltar no dia seguinte com o token, você sabe que ele ainda está logado no seu sistema. |
||||
|
|
||||
|
Depois de uma semana, o token expirará e o usuário não estará autorizado, precisando fazer login novamente para obter um novo token. E se o usuário (ou uma terceira parte) tentar modificar o token para alterar a expiração, você seria capaz de descobrir isso, pois as assinaturas não iriam corresponder. |
||||
|
|
||||
|
Se você quiser brincar com tokens JWT e ver como eles funcionam, visite <a href="https://jwt.io/" class="external-link" target="_blank">https://jwt.io</a>. |
||||
|
|
||||
|
## Instalar `PyJWT` |
||||
|
|
||||
|
Nós precisamos instalar o `PyJWT` para criar e verificar os tokens JWT em Python. |
||||
|
|
||||
|
Certifique-se de criar um [ambiente virtual](../../virtual-environments.md){.internal-link target=_blank}, ativá-lo e então instalar o `pyjwt`: |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
$ pip install pyjwt |
||||
|
|
||||
|
---> 100% |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
/// info | Informação |
||||
|
|
||||
|
Se você pretente utilizar algoritmos de assinatura digital como o RSA ou o ECDSA, você deve instalar a dependência da biblioteca de criptografia `pyjwt[crypto]`. |
||||
|
|
||||
|
Você pode ler mais sobre isso na <a href="https://pyjwt.readthedocs.io/en/latest/installation.html" class="external-link" target="_blank">documentação de instalação do PyJWT</a>. |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
## Hashing de senhas |
||||
|
|
||||
|
"Hashing" significa converter algum conteúdo (uma senha neste caso) em uma sequência de bytes (apenas uma string) que parece um monte de caracteres sem sentido. |
||||
|
|
||||
|
Sempre que você passar exatamente o mesmo conteúdo (exatamente a mesma senha), você obterá exatamente o mesmo resultado. |
||||
|
|
||||
|
Mas não é possível converter os caracteres sem sentido de volta para a senha original. |
||||
|
|
||||
|
### Por que usar hashing de senhas |
||||
|
|
||||
|
Se o seu banco de dados for roubado, o invasor não terá as senhas em texto puro dos seus usuários, apenas os hashes. |
||||
|
|
||||
|
Então, o invasor não poderá tentar usar essas senhas em outro sistema (como muitos usuários utilizam a mesma senha em vários lugares, isso seria perigoso). |
||||
|
|
||||
|
## Instalar o `passlib` |
||||
|
|
||||
|
O PassLib é uma excelente biblioteca Python para lidar com hashes de senhas. |
||||
|
|
||||
|
Ele suporta muitos algoritmos de hashing seguros e utilitários para trabalhar com eles. |
||||
|
|
||||
|
O algoritmo recomendado é o "Bcrypt". |
||||
|
|
||||
|
Certifique-se de criar um [ambiente virtual](../../virtual-environments.md){.internal-link target=_blank}, ativá-lo e então instalar o PassLib com Bcrypt: |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
$ pip install "passlib[bcrypt]" |
||||
|
|
||||
|
---> 100% |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
/// tip | Dica |
||||
|
|
||||
|
Com o `passlib`, você poderia até configurá-lo para ser capaz de ler senhas criadas pelo **Django**, um plug-in de segurança do **Flask** ou muitos outros. |
||||
|
|
||||
|
Assim, você poderia, por exemplo, compartilhar os mesmos dados de um aplicativo Django em um banco de dados com um aplicativo FastAPI. Ou migrar gradualmente uma aplicação Django usando o mesmo banco de dados. |
||||
|
|
||||
|
E seus usuários poderiam fazer login tanto pela sua aplicação Django quanto pela sua aplicação **FastAPI**, ao mesmo tempo. |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
## Criar o hash e verificar as senhas |
||||
|
|
||||
|
Importe as ferramentas que nós precisamos de `passlib`. |
||||
|
|
||||
|
Crie um "contexto" do PassLib. Este será usado para criar o hash e verificar as senhas. |
||||
|
|
||||
|
/// tip | Dica |
||||
|
|
||||
|
O contexto do PassLib também possui funcionalidades para usar diferentes algoritmos de hashing, incluindo algoritmos antigos que estão obsoletos, apenas para permitir verificá-los, etc. |
||||
|
|
||||
|
Por exemplo, você poderia usá-lo para ler e verificar senhas geradas por outro sistema (como Django), mas criar o hash de novas senhas com um algoritmo diferente, como o Bcrypt. |
||||
|
|
||||
|
E ser compatível com todos eles ao mesmo tempo. |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
Crie uma função utilitária para criar o hash de uma senha fornecida pelo usuário. |
||||
|
|
||||
|
E outra função utilitária para verificar se uma senha recebida corresponde ao hash armazenado. |
||||
|
|
||||
|
E outra para autenticar e retornar um usuário. |
||||
|
|
||||
|
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,56:57,60:61,70:76] *} |
||||
|
|
||||
|
/// note | Nota |
||||
|
|
||||
|
Se você verificar o novo banco de dados (falso) `fake_users_db`, você verá como o hash da senha se parece agora: `"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`. |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
## Manipular tokens JWT |
||||
|
|
||||
|
Importe os módulos instalados. |
||||
|
|
||||
|
Crie uma chave secreta aleatória que será usada para assinar os tokens JWT. |
||||
|
|
||||
|
Para gerar uma chave secreta aleatória e segura, use o comando: |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
$ openssl rand -hex 32 |
||||
|
|
||||
|
09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7 |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
E copie a saída para a variável `SECRET_KEY` (não use a do exemplo). |
||||
|
|
||||
|
Crie uma variável `ALGORITHM` com o algoritmo usado para assinar o token JWT e defina como `"HS256"`. |
||||
|
|
||||
|
Crie uma variável para a expiração do token. |
||||
|
|
||||
|
Defina um modelo Pydantic que será usado no endpoint de token para a resposta. |
||||
|
|
||||
|
Crie uma função utilitária para gerar um novo token de acesso. |
||||
|
|
||||
|
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *} |
||||
|
|
||||
|
## Atualize as dependências |
||||
|
|
||||
|
Atualize `get_current_user` para receber o mesmo token de antes, mas desta vez, usando tokens JWT. |
||||
|
|
||||
|
Decodifique o token recebido, verifique-o e retorne o usuário atual. |
||||
|
|
||||
|
Se o token for inválido, retorne um erro HTTP imediatamente. |
||||
|
|
||||
|
{* ../../docs_src/security/tutorial004_an_py310.py hl[90:107] *} |
||||
|
|
||||
|
## Atualize a *operação de rota* `/token` |
||||
|
|
||||
|
Crie um `timedelta` com o tempo de expiração do token. |
||||
|
|
||||
|
Crie um token de acesso JWT real e o retorne. |
||||
|
|
||||
|
{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *} |
||||
|
|
||||
|
### Detalhes técnicos sobre o "sujeito" `sub` do JWT |
||||
|
|
||||
|
A especificação JWT diz que existe uma chave `sub`, com o sujeito do token. |
||||
|
|
||||
|
É opcional usá-la, mas é onde você colocaria a identificação do usuário, então nós estamos usando aqui. |
||||
|
|
||||
|
O JWT pode ser usado para outras coisas além de identificar um usuário e permitir que ele execute operações diretamente na sua API. |
||||
|
|
||||
|
Por exemplo, você poderia identificar um "carro" ou uma "postagem de blog". |
||||
|
|
||||
|
Depois, você poderia adicionar permissões sobre essa entidade, como "dirigir" (para o carro) ou "editar" (para o blog). |
||||
|
|
||||
|
E então, poderia dar esse token JWT para um usuário (ou bot), e ele poderia usá-lo para realizar essas ações (dirigir o carro ou editar o blog) sem sequer precisar ter uma conta, apenas com o token JWT que sua API gerou para isso. |
||||
|
|
||||
|
Usando essas ideias, o JWT pode ser usado para cenários muito mais sofisticados. |
||||
|
|
||||
|
Nesses casos, várias dessas entidades poderiam ter o mesmo ID, digamos `foo` (um usuário `foo`, um carro `foo` e uma postagem de blog `foo`). |
||||
|
|
||||
|
Então, para evitar colisões de ID, ao criar o token JWT para o usuário, você poderia prefixar o valor da chave `sub`, por exemplo, com `username:`. Assim, neste exemplo, o valor de `sub` poderia ser: `username:johndoe`. |
||||
|
|
||||
|
O importante a se lembrar é que a chave `sub` deve ter um identificador único em toda a aplicação e deve ser uma string. |
||||
|
|
||||
|
## Testando |
||||
|
|
||||
|
Execute o servidor e vá para a documentação: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>. |
||||
|
|
||||
|
Você verá a interface de usuário assim: |
||||
|
|
||||
|
<img src="/img/tutorial/security/image07.png"> |
||||
|
|
||||
|
Autorize a aplicação da mesma maneira que antes. |
||||
|
|
||||
|
Usando as credenciais: |
||||
|
|
||||
|
Username: `johndoe` |
||||
|
Password: `secret` |
||||
|
|
||||
|
/// check | Verifique |
||||
|
|
||||
|
Observe que em nenhuma parte do código está a senha em texto puro "`secret`", nós temos apenas o hash. |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
<img src="/img/tutorial/security/image08.png"> |
||||
|
|
||||
|
Chame o endpoint `/users/me/`, você receberá o retorno como: |
||||
|
|
||||
|
```JSON |
||||
|
{ |
||||
|
"username": "johndoe", |
||||
|
"email": "[email protected]", |
||||
|
"full_name": "John Doe", |
||||
|
"disabled": false |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
<img src="/img/tutorial/security/image09.png"> |
||||
|
|
||||
|
Se você abrir as ferramentas de desenvolvedor, poderá ver que os dados enviados incluem apenas o token. A senha é enviada apenas na primeira requisição para autenticar o usuário e obter o token de acesso, mas não é enviada nas próximas requisições: |
||||
|
|
||||
|
<img src="/img/tutorial/security/image10.png"> |
||||
|
|
||||
|
/// note | Nota |
||||
|
|
||||
|
Perceba que o cabeçalho `Authorization`, com o valor que começa com `Bearer `. |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
## Uso avançado com `scopes` |
||||
|
|
||||
|
O OAuth2 tem a noção de "scopes" (escopos). |
||||
|
|
||||
|
Você pode usá-los para adicionar um conjunto específico de permissões a um token JWT. |
||||
|
|
||||
|
Então, você pode dar este token diretamente a um usuário ou a uma terceira parte para interagir com sua API com um conjunto de restrições. |
||||
|
|
||||
|
Você pode aprender como usá-los e como eles são integrados ao **FastAPI** mais adiante no **Guia Avançado do Usuário**. |
||||
|
|
||||
|
|
||||
|
## Recapitulação |
||||
|
|
||||
|
Com o que você viu até agora, você pode configurar uma aplicação **FastAPI** segura usando padrões como OAuth2 e JWT. |
||||
|
|
||||
|
Em quase qualquer framework, lidar com a segurança se torna rapidamente um assunto bastante complexo. |
||||
|
|
||||
|
Muitos pacotes que simplificam bastante isso precisam fazer muitas concessões com o modelo de dados, o banco de dados e os recursos disponíveis. E alguns desses pacotes que simplificam demais na verdade têm falhas de segurança subjacentes. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
O **FastAPI** não faz nenhuma concessão com nenhum banco de dados, modelo de dados ou ferramenta. |
||||
|
|
||||
|
Ele oferece toda a flexibilidade para você escolher as opções que melhor se ajustam ao seu projeto. |
||||
|
|
||||
|
E você pode usar diretamente muitos pacotes bem mantidos e amplamente utilizados, como `passlib` e `PyJWT`, porque o **FastAPI** não exige mecanismos complexos para integrar pacotes externos. |
||||
|
|
||||
|
Mas ele fornece as ferramentas para simplificar o processo o máximo possível, sem comprometer a flexibilidade, robustez ou segurança. |
||||
|
|
||||
|
E você pode usar e implementar protocolos padrão seguros, como o OAuth2, de uma maneira relativamente simples. |
||||
|
|
||||
|
Você pode aprender mais no **Guia Avançado do Usuário** sobre como usar os "scopes" do OAuth2 para um sistema de permissões mais refinado, seguindo esses mesmos padrões. O OAuth2 com scopes é o mecanismo usado por muitos provedores grandes de autenticação, como o Facebook, Google, GitHub, Microsoft, Twitter, etc. para autorizar aplicativos de terceiros a interagir com suas APIs em nome de seus usuários. |
@ -0,0 +1,221 @@ |
|||||
|
import logging |
||||
|
import secrets |
||||
|
import subprocess |
||||
|
from collections import defaultdict |
||||
|
from pathlib import Path |
||||
|
from typing import Any |
||||
|
|
||||
|
import httpx |
||||
|
import yaml |
||||
|
from github import Github |
||||
|
from pydantic import BaseModel, SecretStr |
||||
|
from pydantic_settings import BaseSettings |
||||
|
|
||||
|
github_graphql_url = "https://api.github.com/graphql" |
||||
|
|
||||
|
|
||||
|
sponsors_query = """ |
||||
|
query Q($after: String) { |
||||
|
user(login: "tiangolo") { |
||||
|
sponsorshipsAsMaintainer(first: 100, after: $after) { |
||||
|
edges { |
||||
|
cursor |
||||
|
node { |
||||
|
sponsorEntity { |
||||
|
... on Organization { |
||||
|
login |
||||
|
avatarUrl |
||||
|
url |
||||
|
} |
||||
|
... on User { |
||||
|
login |
||||
|
avatarUrl |
||||
|
url |
||||
|
} |
||||
|
} |
||||
|
tier { |
||||
|
name |
||||
|
monthlyPriceInDollars |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
""" |
||||
|
|
||||
|
|
||||
|
class SponsorEntity(BaseModel): |
||||
|
login: str |
||||
|
avatarUrl: str |
||||
|
url: str |
||||
|
|
||||
|
|
||||
|
class Tier(BaseModel): |
||||
|
name: str |
||||
|
monthlyPriceInDollars: float |
||||
|
|
||||
|
|
||||
|
class SponsorshipAsMaintainerNode(BaseModel): |
||||
|
sponsorEntity: SponsorEntity |
||||
|
tier: Tier |
||||
|
|
||||
|
|
||||
|
class SponsorshipAsMaintainerEdge(BaseModel): |
||||
|
cursor: str |
||||
|
node: SponsorshipAsMaintainerNode |
||||
|
|
||||
|
|
||||
|
class SponsorshipAsMaintainer(BaseModel): |
||||
|
edges: list[SponsorshipAsMaintainerEdge] |
||||
|
|
||||
|
|
||||
|
class SponsorsUser(BaseModel): |
||||
|
sponsorshipsAsMaintainer: SponsorshipAsMaintainer |
||||
|
|
||||
|
|
||||
|
class SponsorsResponseData(BaseModel): |
||||
|
user: SponsorsUser |
||||
|
|
||||
|
|
||||
|
class SponsorsResponse(BaseModel): |
||||
|
data: SponsorsResponseData |
||||
|
|
||||
|
|
||||
|
class Settings(BaseSettings): |
||||
|
sponsors_token: SecretStr |
||||
|
pr_token: SecretStr |
||||
|
github_repository: str |
||||
|
httpx_timeout: int = 30 |
||||
|
|
||||
|
|
||||
|
def get_graphql_response( |
||||
|
*, |
||||
|
settings: Settings, |
||||
|
query: str, |
||||
|
after: str | None = None, |
||||
|
) -> dict[str, Any]: |
||||
|
headers = {"Authorization": f"token {settings.sponsors_token.get_secret_value()}"} |
||||
|
variables = {"after": after} |
||||
|
response = httpx.post( |
||||
|
github_graphql_url, |
||||
|
headers=headers, |
||||
|
timeout=settings.httpx_timeout, |
||||
|
json={"query": query, "variables": variables, "operationName": "Q"}, |
||||
|
) |
||||
|
if response.status_code != 200: |
||||
|
logging.error(f"Response was not 200, after: {after}") |
||||
|
logging.error(response.text) |
||||
|
raise RuntimeError(response.text) |
||||
|
data = response.json() |
||||
|
if "errors" in data: |
||||
|
logging.error(f"Errors in response, after: {after}") |
||||
|
logging.error(data["errors"]) |
||||
|
logging.error(response.text) |
||||
|
raise RuntimeError(response.text) |
||||
|
return data |
||||
|
|
||||
|
|
||||
|
def get_graphql_sponsor_edges( |
||||
|
*, settings: Settings, after: str | None = None |
||||
|
) -> list[SponsorshipAsMaintainerEdge]: |
||||
|
data = get_graphql_response(settings=settings, query=sponsors_query, after=after) |
||||
|
graphql_response = SponsorsResponse.model_validate(data) |
||||
|
return graphql_response.data.user.sponsorshipsAsMaintainer.edges |
||||
|
|
||||
|
|
||||
|
def get_individual_sponsors( |
||||
|
settings: Settings, |
||||
|
) -> defaultdict[float, dict[str, SponsorEntity]]: |
||||
|
nodes: list[SponsorshipAsMaintainerNode] = [] |
||||
|
edges = get_graphql_sponsor_edges(settings=settings) |
||||
|
|
||||
|
while edges: |
||||
|
for edge in edges: |
||||
|
nodes.append(edge.node) |
||||
|
last_edge = edges[-1] |
||||
|
edges = get_graphql_sponsor_edges(settings=settings, after=last_edge.cursor) |
||||
|
|
||||
|
tiers: defaultdict[float, dict[str, SponsorEntity]] = defaultdict(dict) |
||||
|
for node in nodes: |
||||
|
tiers[node.tier.monthlyPriceInDollars][node.sponsorEntity.login] = ( |
||||
|
node.sponsorEntity |
||||
|
) |
||||
|
return tiers |
||||
|
|
||||
|
|
||||
|
def update_content(*, content_path: Path, new_content: Any) -> bool: |
||||
|
old_content = content_path.read_text(encoding="utf-8") |
||||
|
|
||||
|
new_content = yaml.dump(new_content, sort_keys=False, width=200, allow_unicode=True) |
||||
|
if old_content == new_content: |
||||
|
logging.info(f"The content hasn't changed for {content_path}") |
||||
|
return False |
||||
|
content_path.write_text(new_content, encoding="utf-8") |
||||
|
logging.info(f"Updated {content_path}") |
||||
|
return True |
||||
|
|
||||
|
|
||||
|
def main() -> None: |
||||
|
logging.basicConfig(level=logging.INFO) |
||||
|
settings = Settings() |
||||
|
logging.info(f"Using config: {settings.model_dump_json()}") |
||||
|
g = Github(settings.pr_token.get_secret_value()) |
||||
|
repo = g.get_repo(settings.github_repository) |
||||
|
|
||||
|
tiers = get_individual_sponsors(settings=settings) |
||||
|
keys = list(tiers.keys()) |
||||
|
keys.sort(reverse=True) |
||||
|
sponsors = [] |
||||
|
for key in keys: |
||||
|
sponsor_group = [] |
||||
|
for login, sponsor in tiers[key].items(): |
||||
|
sponsor_group.append( |
||||
|
{"login": login, "avatarUrl": sponsor.avatarUrl, "url": sponsor.url} |
||||
|
) |
||||
|
sponsors.append(sponsor_group) |
||||
|
github_sponsors = { |
||||
|
"sponsors": sponsors, |
||||
|
} |
||||
|
|
||||
|
# For local development |
||||
|
# github_sponsors_path = Path("../docs/en/data/github_sponsors.yml") |
||||
|
github_sponsors_path = Path("./docs/en/data/github_sponsors.yml") |
||||
|
updated = update_content( |
||||
|
content_path=github_sponsors_path, new_content=github_sponsors |
||||
|
) |
||||
|
|
||||
|
if not updated: |
||||
|
logging.info("The data hasn't changed, finishing.") |
||||
|
return |
||||
|
|
||||
|
logging.info("Setting up GitHub Actions git user") |
||||
|
subprocess.run(["git", "config", "user.name", "github-actions"], check=True) |
||||
|
subprocess.run( |
||||
|
["git", "config", "user.email", "[email protected]"], check=True |
||||
|
) |
||||
|
branch_name = f"fastapi-people-sponsors-{secrets.token_hex(4)}" |
||||
|
logging.info(f"Creating a new branch {branch_name}") |
||||
|
subprocess.run(["git", "checkout", "-b", branch_name], check=True) |
||||
|
logging.info("Adding updated file") |
||||
|
subprocess.run( |
||||
|
[ |
||||
|
"git", |
||||
|
"add", |
||||
|
str(github_sponsors_path), |
||||
|
], |
||||
|
check=True, |
||||
|
) |
||||
|
logging.info("Committing updated file") |
||||
|
message = "👥 Update FastAPI People - Sponsors" |
||||
|
subprocess.run(["git", "commit", "-m", message], check=True) |
||||
|
logging.info("Pushing branch") |
||||
|
subprocess.run(["git", "push", "origin", branch_name], check=True) |
||||
|
logging.info("Creating PR") |
||||
|
pr = repo.create_pull(title=message, body=message, base="master", head=branch_name) |
||||
|
logging.info(f"Created PR: {pr.number}") |
||||
|
logging.info("Finished") |
||||
|
|
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
main() |
@ -1,206 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.body_multiple_params.tutorial001_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
def test_post_body_q_bar_content(client: TestClient): |
|
||||
response = client.put("/items/5?q=bar", json={"name": "Foo", "price": 50.5}) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"item_id": 5, |
|
||||
"item": { |
|
||||
"name": "Foo", |
|
||||
"price": 50.5, |
|
||||
"description": None, |
|
||||
"tax": None, |
|
||||
}, |
|
||||
"q": "bar", |
|
||||
} |
|
||||
|
|
||||
|
|
||||
def test_post_no_body_q_bar(client: TestClient): |
|
||||
response = client.put("/items/5?q=bar", json=None) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"item_id": 5, "q": "bar"} |
|
||||
|
|
||||
|
|
||||
def test_post_no_body(client: TestClient): |
|
||||
response = client.put("/items/5", json=None) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"item_id": 5} |
|
||||
|
|
||||
|
|
||||
def test_post_id_foo(client: TestClient): |
|
||||
response = client.put("/items/foo", json=None) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "int_parsing", |
|
||||
"loc": ["path", "item_id"], |
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer", |
|
||||
"input": "foo", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["path", "item_id"], |
|
||||
"msg": "value is not a valid integer", |
|
||||
"type": "type_error.integer", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"put": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Update Item", |
|
||||
"operationId": "update_item_items__item_id__put", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": { |
|
||||
"title": "The ID of the item to get", |
|
||||
"maximum": 1000.0, |
|
||||
"minimum": 0.0, |
|
||||
"type": "integer", |
|
||||
}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [ |
|
||||
{"$ref": "#/components/schemas/Item"}, |
|
||||
{"type": "null"}, |
|
||||
], |
|
||||
"title": "Item", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"$ref": "#/components/schemas/Item"} |
|
||||
) |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"required": ["name", "price"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"description": IsDict( |
|
||||
{ |
|
||||
"title": "Description", |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Description", "type": "string"} |
|
||||
), |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"tax": IsDict( |
|
||||
{ |
|
||||
"title": "Tax", |
|
||||
"anyOf": [{"type": "number"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Tax", "type": "number"} |
|
||||
), |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,213 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.body_multiple_params.tutorial001_an_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_post_body_q_bar_content(client: TestClient): |
|
||||
response = client.put("/items/5?q=bar", json={"name": "Foo", "price": 50.5}) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"item_id": 5, |
|
||||
"item": { |
|
||||
"name": "Foo", |
|
||||
"price": 50.5, |
|
||||
"description": None, |
|
||||
"tax": None, |
|
||||
}, |
|
||||
"q": "bar", |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_post_no_body_q_bar(client: TestClient): |
|
||||
response = client.put("/items/5?q=bar", json=None) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"item_id": 5, "q": "bar"} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_post_no_body(client: TestClient): |
|
||||
response = client.put("/items/5", json=None) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"item_id": 5} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_post_id_foo(client: TestClient): |
|
||||
response = client.put("/items/foo", json=None) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "int_parsing", |
|
||||
"loc": ["path", "item_id"], |
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer", |
|
||||
"input": "foo", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["path", "item_id"], |
|
||||
"msg": "value is not a valid integer", |
|
||||
"type": "type_error.integer", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"put": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Update Item", |
|
||||
"operationId": "update_item_items__item_id__put", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": { |
|
||||
"title": "The ID of the item to get", |
|
||||
"maximum": 1000.0, |
|
||||
"minimum": 0.0, |
|
||||
"type": "integer", |
|
||||
}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [ |
|
||||
{"$ref": "#/components/schemas/Item"}, |
|
||||
{"type": "null"}, |
|
||||
], |
|
||||
"title": "Item", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"$ref": "#/components/schemas/Item"} |
|
||||
) |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"required": ["name", "price"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"description": IsDict( |
|
||||
{ |
|
||||
"title": "Description", |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Description", "type": "string"} |
|
||||
), |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"tax": IsDict( |
|
||||
{ |
|
||||
"title": "Tax", |
|
||||
"anyOf": [{"type": "number"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Tax", "type": "number"} |
|
||||
), |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,213 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.body_multiple_params.tutorial001_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body_q_bar_content(client: TestClient): |
|
||||
response = client.put("/items/5?q=bar", json={"name": "Foo", "price": 50.5}) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"item_id": 5, |
|
||||
"item": { |
|
||||
"name": "Foo", |
|
||||
"price": 50.5, |
|
||||
"description": None, |
|
||||
"tax": None, |
|
||||
}, |
|
||||
"q": "bar", |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_no_body_q_bar(client: TestClient): |
|
||||
response = client.put("/items/5?q=bar", json=None) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"item_id": 5, "q": "bar"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_no_body(client: TestClient): |
|
||||
response = client.put("/items/5", json=None) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"item_id": 5} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_id_foo(client: TestClient): |
|
||||
response = client.put("/items/foo", json=None) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "int_parsing", |
|
||||
"loc": ["path", "item_id"], |
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer", |
|
||||
"input": "foo", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["path", "item_id"], |
|
||||
"msg": "value is not a valid integer", |
|
||||
"type": "type_error.integer", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"put": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Update Item", |
|
||||
"operationId": "update_item_items__item_id__put", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": { |
|
||||
"title": "The ID of the item to get", |
|
||||
"maximum": 1000.0, |
|
||||
"minimum": 0.0, |
|
||||
"type": "integer", |
|
||||
}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [ |
|
||||
{"$ref": "#/components/schemas/Item"}, |
|
||||
{"type": "null"}, |
|
||||
], |
|
||||
"title": "Item", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"$ref": "#/components/schemas/Item"} |
|
||||
) |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"required": ["name", "price"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"description": IsDict( |
|
||||
{ |
|
||||
"title": "Description", |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Description", "type": "string"} |
|
||||
), |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"tax": IsDict( |
|
||||
{ |
|
||||
"title": "Tax", |
|
||||
"anyOf": [{"type": "number"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Tax", "type": "number"} |
|
||||
), |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,213 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.body_multiple_params.tutorial001_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_post_body_q_bar_content(client: TestClient): |
|
||||
response = client.put("/items/5?q=bar", json={"name": "Foo", "price": 50.5}) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"item_id": 5, |
|
||||
"item": { |
|
||||
"name": "Foo", |
|
||||
"price": 50.5, |
|
||||
"description": None, |
|
||||
"tax": None, |
|
||||
}, |
|
||||
"q": "bar", |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_post_no_body_q_bar(client: TestClient): |
|
||||
response = client.put("/items/5?q=bar", json=None) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"item_id": 5, "q": "bar"} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_post_no_body(client: TestClient): |
|
||||
response = client.put("/items/5", json=None) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"item_id": 5} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_post_id_foo(client: TestClient): |
|
||||
response = client.put("/items/foo", json=None) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "int_parsing", |
|
||||
"loc": ["path", "item_id"], |
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer", |
|
||||
"input": "foo", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["path", "item_id"], |
|
||||
"msg": "value is not a valid integer", |
|
||||
"type": "type_error.integer", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"put": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Update Item", |
|
||||
"operationId": "update_item_items__item_id__put", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": { |
|
||||
"title": "The ID of the item to get", |
|
||||
"maximum": 1000.0, |
|
||||
"minimum": 0.0, |
|
||||
"type": "integer", |
|
||||
}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [ |
|
||||
{"$ref": "#/components/schemas/Item"}, |
|
||||
{"type": "null"}, |
|
||||
], |
|
||||
"title": "Item", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"$ref": "#/components/schemas/Item"} |
|
||||
) |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"required": ["name", "price"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"description": IsDict( |
|
||||
{ |
|
||||
"title": "Description", |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Description", "type": "string"} |
|
||||
), |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"tax": IsDict( |
|
||||
{ |
|
||||
"title": "Tax", |
|
||||
"anyOf": [{"type": "number"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Tax", "type": "number"} |
|
||||
), |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,128 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.body_nested_models.tutorial009_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body(client: TestClient): |
|
||||
data = {"2": 2.2, "3": 3.3} |
|
||||
response = client.post("/index-weights/", json=data) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == data |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_invalid_body(client: TestClient): |
|
||||
data = {"foo": 2.2, "3": 3.3} |
|
||||
response = client.post("/index-weights/", json=data) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "int_parsing", |
|
||||
"loc": ["body", "foo", "[key]"], |
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer", |
|
||||
"input": "foo", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "__key__"], |
|
||||
"msg": "value is not a valid integer", |
|
||||
"type": "type_error.integer", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/index-weights/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Create Index Weights", |
|
||||
"operationId": "create_index_weights_index_weights__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"title": "Weights", |
|
||||
"type": "object", |
|
||||
"additionalProperties": {"type": "number"}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,317 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310, needs_pydanticv1, needs_pydanticv2 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.body_updates.tutorial001_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_get(client: TestClient): |
|
||||
response = client.get("/items/baz") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"name": "Baz", |
|
||||
"description": None, |
|
||||
"price": 50.2, |
|
||||
"tax": 10.5, |
|
||||
"tags": [], |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_put(client: TestClient): |
|
||||
response = client.put( |
|
||||
"/items/bar", json={"name": "Barz", "price": 3, "description": None} |
|
||||
) |
|
||||
assert response.json() == { |
|
||||
"name": "Barz", |
|
||||
"description": None, |
|
||||
"price": 3, |
|
||||
"tax": 10.5, |
|
||||
"tags": [], |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@needs_pydanticv2 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Item", |
|
||||
"operationId": "read_item_items__item_id__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
}, |
|
||||
"put": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Update Item", |
|
||||
"operationId": "update_item_items__item_id__put", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"type": "object", |
|
||||
"title": "Item", |
|
||||
"properties": { |
|
||||
"name": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Name", |
|
||||
}, |
|
||||
"description": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Description", |
|
||||
}, |
|
||||
"price": { |
|
||||
"anyOf": [{"type": "number"}, {"type": "null"}], |
|
||||
"title": "Price", |
|
||||
}, |
|
||||
"tax": {"title": "Tax", "type": "number", "default": 10.5}, |
|
||||
"tags": { |
|
||||
"title": "Tags", |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
"default": [], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_py310 |
|
||||
@needs_pydanticv1 |
|
||||
def test_openapi_schema_pv1(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Item", |
|
||||
"operationId": "read_item_items__item_id__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
}, |
|
||||
"put": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Update Item", |
|
||||
"operationId": "update_item_items__item_id__put", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"description": {"title": "Description", "type": "string"}, |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"tax": {"title": "Tax", "type": "number", "default": 10.5}, |
|
||||
"tags": { |
|
||||
"title": "Tags", |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
"default": [], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,317 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39, needs_pydanticv1, needs_pydanticv2 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.body_updates.tutorial001_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get(client: TestClient): |
|
||||
response = client.get("/items/baz") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"name": "Baz", |
|
||||
"description": None, |
|
||||
"price": 50.2, |
|
||||
"tax": 10.5, |
|
||||
"tags": [], |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_put(client: TestClient): |
|
||||
response = client.put( |
|
||||
"/items/bar", json={"name": "Barz", "price": 3, "description": None} |
|
||||
) |
|
||||
assert response.json() == { |
|
||||
"name": "Barz", |
|
||||
"description": None, |
|
||||
"price": 3, |
|
||||
"tax": 10.5, |
|
||||
"tags": [], |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
@needs_pydanticv2 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Item", |
|
||||
"operationId": "read_item_items__item_id__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
}, |
|
||||
"put": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Update Item", |
|
||||
"operationId": "update_item_items__item_id__put", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"type": "object", |
|
||||
"title": "Item", |
|
||||
"properties": { |
|
||||
"name": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Name", |
|
||||
}, |
|
||||
"description": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Description", |
|
||||
}, |
|
||||
"price": { |
|
||||
"anyOf": [{"type": "number"}, {"type": "null"}], |
|
||||
"title": "Price", |
|
||||
}, |
|
||||
"tax": {"title": "Tax", "type": "number", "default": 10.5}, |
|
||||
"tags": { |
|
||||
"title": "Tags", |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
"default": [], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_py39 |
|
||||
@needs_pydanticv1 |
|
||||
def test_openapi_schema_pv1(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Item", |
|
||||
"operationId": "read_item_items__item_id__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
}, |
|
||||
"put": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Update Item", |
|
||||
"operationId": "update_item_items__item_id__put", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"description": {"title": "Description", "type": "string"}, |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"tax": {"title": "Tax", "type": "number", "default": 10.5}, |
|
||||
"tags": { |
|
||||
"title": "Tags", |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
"default": [], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,108 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from docs_src.cookie_params.tutorial001_an import app |
|
||||
|
|
||||
|
|
||||
@pytest.mark.parametrize( |
|
||||
"path,cookies,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"ads_id": None}), |
|
||||
("/items", {"ads_id": "ads_track"}, 200, {"ads_id": "ads_track"}), |
|
||||
( |
|
||||
"/items", |
|
||||
{"ads_id": "ads_track", "session": "cookiesession"}, |
|
||||
200, |
|
||||
{"ads_id": "ads_track"}, |
|
||||
), |
|
||||
("/items", {"session": "cookiesession"}, 200, {"ads_id": None}), |
|
||||
], |
|
||||
) |
|
||||
def test(path, cookies, expected_status, expected_response): |
|
||||
client = TestClient(app, cookies=cookies) |
|
||||
response = client.get(path) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(): |
|
||||
client = TestClient(app) |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Ads Id", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Ads Id", "type": "string"} |
|
||||
), |
|
||||
"name": "ads_id", |
|
||||
"in": "cookie", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,114 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,cookies,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"ads_id": None}), |
|
||||
("/items", {"ads_id": "ads_track"}, 200, {"ads_id": "ads_track"}), |
|
||||
( |
|
||||
"/items", |
|
||||
{"ads_id": "ads_track", "session": "cookiesession"}, |
|
||||
200, |
|
||||
{"ads_id": "ads_track"}, |
|
||||
), |
|
||||
("/items", {"session": "cookiesession"}, 200, {"ads_id": None}), |
|
||||
], |
|
||||
) |
|
||||
def test(path, cookies, expected_status, expected_response): |
|
||||
from docs_src.cookie_params.tutorial001_an_py310 import app |
|
||||
|
|
||||
client = TestClient(app, cookies=cookies) |
|
||||
response = client.get(path) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(): |
|
||||
from docs_src.cookie_params.tutorial001_an_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Ads Id", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Ads Id", "type": "string"} |
|
||||
), |
|
||||
"name": "ads_id", |
|
||||
"in": "cookie", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,114 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,cookies,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"ads_id": None}), |
|
||||
("/items", {"ads_id": "ads_track"}, 200, {"ads_id": "ads_track"}), |
|
||||
( |
|
||||
"/items", |
|
||||
{"ads_id": "ads_track", "session": "cookiesession"}, |
|
||||
200, |
|
||||
{"ads_id": "ads_track"}, |
|
||||
), |
|
||||
("/items", {"session": "cookiesession"}, 200, {"ads_id": None}), |
|
||||
], |
|
||||
) |
|
||||
def test(path, cookies, expected_status, expected_response): |
|
||||
from docs_src.cookie_params.tutorial001_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app, cookies=cookies) |
|
||||
response = client.get(path) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(): |
|
||||
from docs_src.cookie_params.tutorial001_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Ads Id", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Ads Id", "type": "string"} |
|
||||
), |
|
||||
"name": "ads_id", |
|
||||
"in": "cookie", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,114 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,cookies,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"ads_id": None}), |
|
||||
("/items", {"ads_id": "ads_track"}, 200, {"ads_id": "ads_track"}), |
|
||||
( |
|
||||
"/items", |
|
||||
{"ads_id": "ads_track", "session": "cookiesession"}, |
|
||||
200, |
|
||||
{"ads_id": "ads_track"}, |
|
||||
), |
|
||||
("/items", {"session": "cookiesession"}, 200, {"ads_id": None}), |
|
||||
], |
|
||||
) |
|
||||
def test(path, cookies, expected_status, expected_response): |
|
||||
from docs_src.cookie_params.tutorial001_py310 import app |
|
||||
|
|
||||
client = TestClient(app, cookies=cookies) |
|
||||
response = client.get(path) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(): |
|
||||
from docs_src.cookie_params.tutorial001_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Ads Id", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Ads Id", "type": "string"} |
|
||||
), |
|
||||
"name": "ads_id", |
|
||||
"in": "cookie", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,183 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from docs_src.dependencies.tutorial001_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
|
|
||||
|
|
||||
@pytest.mark.parametrize( |
|
||||
"path,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", 200, {"q": None, "skip": 0, "limit": 100}), |
|
||||
("/items?q=foo", 200, {"q": "foo", "skip": 0, "limit": 100}), |
|
||||
("/items?q=foo&skip=5", 200, {"q": "foo", "skip": 5, "limit": 100}), |
|
||||
("/items?q=foo&skip=5&limit=30", 200, {"q": "foo", "skip": 5, "limit": 30}), |
|
||||
("/users", 200, {"q": None, "skip": 0, "limit": 100}), |
|
||||
], |
|
||||
) |
|
||||
def test_get(path, expected_status, expected_response): |
|
||||
response = client.get(path) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Limit", |
|
||||
"type": "integer", |
|
||||
"default": 100, |
|
||||
}, |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
}, |
|
||||
"/users/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Users", |
|
||||
"operationId": "read_users_users__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Limit", |
|
||||
"type": "integer", |
|
||||
"default": 100, |
|
||||
}, |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,191 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial001_an_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", 200, {"q": None, "skip": 0, "limit": 100}), |
|
||||
("/items?q=foo", 200, {"q": "foo", "skip": 0, "limit": 100}), |
|
||||
("/items?q=foo&skip=5", 200, {"q": "foo", "skip": 5, "limit": 100}), |
|
||||
("/items?q=foo&skip=5&limit=30", 200, {"q": "foo", "skip": 5, "limit": 30}), |
|
||||
("/users", 200, {"q": None, "skip": 0, "limit": 100}), |
|
||||
], |
|
||||
) |
|
||||
def test_get(path, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Limit", |
|
||||
"type": "integer", |
|
||||
"default": 100, |
|
||||
}, |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
}, |
|
||||
"/users/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Users", |
|
||||
"operationId": "read_users_users__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Limit", |
|
||||
"type": "integer", |
|
||||
"default": 100, |
|
||||
}, |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,191 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial001_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", 200, {"q": None, "skip": 0, "limit": 100}), |
|
||||
("/items?q=foo", 200, {"q": "foo", "skip": 0, "limit": 100}), |
|
||||
("/items?q=foo&skip=5", 200, {"q": "foo", "skip": 5, "limit": 100}), |
|
||||
("/items?q=foo&skip=5&limit=30", 200, {"q": "foo", "skip": 5, "limit": 30}), |
|
||||
("/users", 200, {"q": None, "skip": 0, "limit": 100}), |
|
||||
], |
|
||||
) |
|
||||
def test_get(path, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Limit", |
|
||||
"type": "integer", |
|
||||
"default": 100, |
|
||||
}, |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
}, |
|
||||
"/users/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Users", |
|
||||
"operationId": "read_users_users__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Limit", |
|
||||
"type": "integer", |
|
||||
"default": 100, |
|
||||
}, |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,191 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial001_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", 200, {"q": None, "skip": 0, "limit": 100}), |
|
||||
("/items?q=foo", 200, {"q": "foo", "skip": 0, "limit": 100}), |
|
||||
("/items?q=foo&skip=5", 200, {"q": "foo", "skip": 5, "limit": 100}), |
|
||||
("/items?q=foo&skip=5&limit=30", 200, {"q": "foo", "skip": 5, "limit": 30}), |
|
||||
("/users", 200, {"q": None, "skip": 0, "limit": 100}), |
|
||||
], |
|
||||
) |
|
||||
def test_get(path, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Limit", |
|
||||
"type": "integer", |
|
||||
"default": 100, |
|
||||
}, |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
}, |
|
||||
"/users/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Users", |
|
||||
"operationId": "read_users_users__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Limit", |
|
||||
"type": "integer", |
|
||||
"default": 100, |
|
||||
}, |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,162 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from docs_src.dependencies.tutorial004_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
|
|
||||
|
|
||||
@pytest.mark.parametrize( |
|
||||
"path,expected_status,expected_response", |
|
||||
[ |
|
||||
( |
|
||||
"/items", |
|
||||
200, |
|
||||
{ |
|
||||
"items": [ |
|
||||
{"item_name": "Foo"}, |
|
||||
{"item_name": "Bar"}, |
|
||||
{"item_name": "Baz"}, |
|
||||
] |
|
||||
}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=foo", |
|
||||
200, |
|
||||
{ |
|
||||
"items": [ |
|
||||
{"item_name": "Foo"}, |
|
||||
{"item_name": "Bar"}, |
|
||||
{"item_name": "Baz"}, |
|
||||
], |
|
||||
"q": "foo", |
|
||||
}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=foo&skip=1", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Bar"}, {"item_name": "Baz"}], "q": "foo"}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=bar&limit=2", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Foo"}, {"item_name": "Bar"}], "q": "bar"}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=bar&skip=1&limit=1", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Bar"}], "q": "bar"}, |
|
||||
), |
|
||||
( |
|
||||
"/items?limit=1&q=bar&skip=1", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Bar"}], "q": "bar"}, |
|
||||
), |
|
||||
], |
|
||||
) |
|
||||
def test_get(path, expected_status, expected_response): |
|
||||
response = client.get(path) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Limit", |
|
||||
"type": "integer", |
|
||||
"default": 100, |
|
||||
}, |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,170 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial004_an_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,expected_status,expected_response", |
|
||||
[ |
|
||||
( |
|
||||
"/items", |
|
||||
200, |
|
||||
{ |
|
||||
"items": [ |
|
||||
{"item_name": "Foo"}, |
|
||||
{"item_name": "Bar"}, |
|
||||
{"item_name": "Baz"}, |
|
||||
] |
|
||||
}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=foo", |
|
||||
200, |
|
||||
{ |
|
||||
"items": [ |
|
||||
{"item_name": "Foo"}, |
|
||||
{"item_name": "Bar"}, |
|
||||
{"item_name": "Baz"}, |
|
||||
], |
|
||||
"q": "foo", |
|
||||
}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=foo&skip=1", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Bar"}, {"item_name": "Baz"}], "q": "foo"}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=bar&limit=2", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Foo"}, {"item_name": "Bar"}], "q": "bar"}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=bar&skip=1&limit=1", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Bar"}], "q": "bar"}, |
|
||||
), |
|
||||
( |
|
||||
"/items?limit=1&q=bar&skip=1", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Bar"}], "q": "bar"}, |
|
||||
), |
|
||||
], |
|
||||
) |
|
||||
def test_get(path, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Limit", |
|
||||
"type": "integer", |
|
||||
"default": 100, |
|
||||
}, |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,170 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial004_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,expected_status,expected_response", |
|
||||
[ |
|
||||
( |
|
||||
"/items", |
|
||||
200, |
|
||||
{ |
|
||||
"items": [ |
|
||||
{"item_name": "Foo"}, |
|
||||
{"item_name": "Bar"}, |
|
||||
{"item_name": "Baz"}, |
|
||||
] |
|
||||
}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=foo", |
|
||||
200, |
|
||||
{ |
|
||||
"items": [ |
|
||||
{"item_name": "Foo"}, |
|
||||
{"item_name": "Bar"}, |
|
||||
{"item_name": "Baz"}, |
|
||||
], |
|
||||
"q": "foo", |
|
||||
}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=foo&skip=1", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Bar"}, {"item_name": "Baz"}], "q": "foo"}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=bar&limit=2", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Foo"}, {"item_name": "Bar"}], "q": "bar"}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=bar&skip=1&limit=1", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Bar"}], "q": "bar"}, |
|
||||
), |
|
||||
( |
|
||||
"/items?limit=1&q=bar&skip=1", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Bar"}], "q": "bar"}, |
|
||||
), |
|
||||
], |
|
||||
) |
|
||||
def test_get(path, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Limit", |
|
||||
"type": "integer", |
|
||||
"default": 100, |
|
||||
}, |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,170 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial004_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,expected_status,expected_response", |
|
||||
[ |
|
||||
( |
|
||||
"/items", |
|
||||
200, |
|
||||
{ |
|
||||
"items": [ |
|
||||
{"item_name": "Foo"}, |
|
||||
{"item_name": "Bar"}, |
|
||||
{"item_name": "Baz"}, |
|
||||
] |
|
||||
}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=foo", |
|
||||
200, |
|
||||
{ |
|
||||
"items": [ |
|
||||
{"item_name": "Foo"}, |
|
||||
{"item_name": "Bar"}, |
|
||||
{"item_name": "Baz"}, |
|
||||
], |
|
||||
"q": "foo", |
|
||||
}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=foo&skip=1", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Bar"}, {"item_name": "Baz"}], "q": "foo"}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=bar&limit=2", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Foo"}, {"item_name": "Bar"}], "q": "bar"}, |
|
||||
), |
|
||||
( |
|
||||
"/items?q=bar&skip=1&limit=1", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Bar"}], "q": "bar"}, |
|
||||
), |
|
||||
( |
|
||||
"/items?limit=1&q=bar&skip=1", |
|
||||
200, |
|
||||
{"items": [{"item_name": "Bar"}], "q": "bar"}, |
|
||||
), |
|
||||
], |
|
||||
) |
|
||||
def test_get(path, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Q", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Q", "type": "string"} |
|
||||
), |
|
||||
"name": "q", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Limit", |
|
||||
"type": "integer", |
|
||||
"default": 100, |
|
||||
}, |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,149 +0,0 @@ |
|||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from docs_src.dependencies.tutorial006_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
|
|
||||
|
|
||||
def test_get_no_headers(): |
|
||||
response = client.get("/items/") |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["header", "x-token"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["header", "x-key"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["header", "x-token"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["header", "x-key"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_get_invalid_one_header(): |
|
||||
response = client.get("/items/", headers={"X-Token": "invalid"}) |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "X-Token header invalid"} |
|
||||
|
|
||||
|
|
||||
def test_get_invalid_second_header(): |
|
||||
response = client.get( |
|
||||
"/items/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"} |
|
||||
) |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "X-Key header invalid"} |
|
||||
|
|
||||
|
|
||||
def test_get_valid_headers(): |
|
||||
response = client.get( |
|
||||
"/items/", |
|
||||
headers={ |
|
||||
"X-Token": "fake-super-secret-token", |
|
||||
"X-Key": "fake-super-secret-key", |
|
||||
}, |
|
||||
) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == [{"item": "Foo"}, {"item": "Bar"}] |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "X-Token", "type": "string"}, |
|
||||
"name": "x-token", |
|
||||
"in": "header", |
|
||||
}, |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "X-Key", "type": "string"}, |
|
||||
"name": "x-key", |
|
||||
"in": "header", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,161 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial006_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_no_headers(client: TestClient): |
|
||||
response = client.get("/items/") |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["header", "x-token"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["header", "x-key"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["header", "x-token"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["header", "x-key"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_invalid_one_header(client: TestClient): |
|
||||
response = client.get("/items/", headers={"X-Token": "invalid"}) |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "X-Token header invalid"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_invalid_second_header(client: TestClient): |
|
||||
response = client.get( |
|
||||
"/items/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"} |
|
||||
) |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "X-Key header invalid"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_valid_headers(client: TestClient): |
|
||||
response = client.get( |
|
||||
"/items/", |
|
||||
headers={ |
|
||||
"X-Token": "fake-super-secret-token", |
|
||||
"X-Key": "fake-super-secret-key", |
|
||||
}, |
|
||||
) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == [{"item": "Foo"}, {"item": "Bar"}] |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "X-Token", "type": "string"}, |
|
||||
"name": "x-token", |
|
||||
"in": "header", |
|
||||
}, |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "X-Key", "type": "string"}, |
|
||||
"name": "x-key", |
|
||||
"in": "header", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,23 +1,39 @@ |
|||||
|
import importlib |
||||
|
|
||||
|
import pytest |
||||
from fastapi.testclient import TestClient |
from fastapi.testclient import TestClient |
||||
|
|
||||
from docs_src.dependencies.tutorial008b import app |
from ...utils import needs_py39 |
||||
|
|
||||
|
|
||||
|
@pytest.fixture( |
||||
|
name="client", |
||||
|
params=[ |
||||
|
"tutorial008b", |
||||
|
"tutorial008b_an", |
||||
|
pytest.param("tutorial008b_an_py39", marks=needs_py39), |
||||
|
], |
||||
|
) |
||||
|
def get_client(request: pytest.FixtureRequest): |
||||
|
mod = importlib.import_module(f"docs_src.dependencies.{request.param}") |
||||
|
|
||||
client = TestClient(app) |
client = TestClient(mod.app) |
||||
|
return client |
||||
|
|
||||
|
|
||||
def test_get_no_item(): |
def test_get_no_item(client: TestClient): |
||||
response = client.get("/items/foo") |
response = client.get("/items/foo") |
||||
assert response.status_code == 404, response.text |
assert response.status_code == 404, response.text |
||||
assert response.json() == {"detail": "Item not found"} |
assert response.json() == {"detail": "Item not found"} |
||||
|
|
||||
|
|
||||
def test_owner_error(): |
def test_owner_error(client: TestClient): |
||||
response = client.get("/items/plumbus") |
response = client.get("/items/plumbus") |
||||
assert response.status_code == 400, response.text |
assert response.status_code == 400, response.text |
||||
assert response.json() == {"detail": "Owner error: Rick"} |
assert response.json() == {"detail": "Owner error: Rick"} |
||||
|
|
||||
|
|
||||
def test_get_item(): |
def test_get_item(client: TestClient): |
||||
response = client.get("/items/portal-gun") |
response = client.get("/items/portal-gun") |
||||
assert response.status_code == 200, response.text |
assert response.status_code == 200, response.text |
||||
assert response.json() == {"description": "Gun to create portals", "owner": "Rick"} |
assert response.json() == {"description": "Gun to create portals", "owner": "Rick"} |
||||
|
@ -1,23 +0,0 @@ |
|||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from docs_src.dependencies.tutorial008b_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
|
|
||||
|
|
||||
def test_get_no_item(): |
|
||||
response = client.get("/items/foo") |
|
||||
assert response.status_code == 404, response.text |
|
||||
assert response.json() == {"detail": "Item not found"} |
|
||||
|
|
||||
|
|
||||
def test_owner_error(): |
|
||||
response = client.get("/items/plumbus") |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "Owner error: Rick"} |
|
||||
|
|
||||
|
|
||||
def test_get_item(): |
|
||||
response = client.get("/items/portal-gun") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == {"description": "Gun to create portals", "owner": "Rick"} |
|
@ -1,33 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial008b_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_no_item(client: TestClient): |
|
||||
response = client.get("/items/foo") |
|
||||
assert response.status_code == 404, response.text |
|
||||
assert response.json() == {"detail": "Item not found"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_owner_error(client: TestClient): |
|
||||
response = client.get("/items/plumbus") |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "Owner error: Rick"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_item(client: TestClient): |
|
||||
response = client.get("/items/portal-gun") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == {"description": "Gun to create portals", "owner": "Rick"} |
|
@ -1,38 +1,50 @@ |
|||||
|
import importlib |
||||
|
from types import ModuleType |
||||
|
|
||||
import pytest |
import pytest |
||||
from fastapi.exceptions import FastAPIError |
from fastapi.exceptions import FastAPIError |
||||
from fastapi.testclient import TestClient |
from fastapi.testclient import TestClient |
||||
|
|
||||
|
from ...utils import needs_py39 |
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
@pytest.fixture( |
||||
def get_client(): |
name="mod", |
||||
from docs_src.dependencies.tutorial008c import app |
params=[ |
||||
|
"tutorial008c", |
||||
|
"tutorial008c_an", |
||||
|
pytest.param("tutorial008c_an_py39", marks=needs_py39), |
||||
|
], |
||||
|
) |
||||
|
def get_mod(request: pytest.FixtureRequest): |
||||
|
mod = importlib.import_module(f"docs_src.dependencies.{request.param}") |
||||
|
|
||||
client = TestClient(app) |
return mod |
||||
return client |
|
||||
|
|
||||
|
|
||||
def test_get_no_item(client: TestClient): |
def test_get_no_item(mod: ModuleType): |
||||
|
client = TestClient(mod.app) |
||||
response = client.get("/items/foo") |
response = client.get("/items/foo") |
||||
assert response.status_code == 404, response.text |
assert response.status_code == 404, response.text |
||||
assert response.json() == {"detail": "Item not found, there's only a plumbus here"} |
assert response.json() == {"detail": "Item not found, there's only a plumbus here"} |
||||
|
|
||||
|
|
||||
def test_get(client: TestClient): |
def test_get(mod: ModuleType): |
||||
|
client = TestClient(mod.app) |
||||
response = client.get("/items/plumbus") |
response = client.get("/items/plumbus") |
||||
assert response.status_code == 200, response.text |
assert response.status_code == 200, response.text |
||||
assert response.json() == "plumbus" |
assert response.json() == "plumbus" |
||||
|
|
||||
|
|
||||
def test_fastapi_error(client: TestClient): |
def test_fastapi_error(mod: ModuleType): |
||||
|
client = TestClient(mod.app) |
||||
with pytest.raises(FastAPIError) as exc_info: |
with pytest.raises(FastAPIError) as exc_info: |
||||
client.get("/items/portal-gun") |
client.get("/items/portal-gun") |
||||
assert "No response object was returned" in exc_info.value.args[0] |
assert "No response object was returned" in exc_info.value.args[0] |
||||
|
|
||||
|
|
||||
def test_internal_server_error(): |
def test_internal_server_error(mod: ModuleType): |
||||
from docs_src.dependencies.tutorial008c import app |
client = TestClient(mod.app, raise_server_exceptions=False) |
||||
|
|
||||
client = TestClient(app, raise_server_exceptions=False) |
|
||||
response = client.get("/items/portal-gun") |
response = client.get("/items/portal-gun") |
||||
assert response.status_code == 500, response.text |
assert response.status_code == 500, response.text |
||||
assert response.text == "Internal Server Error" |
assert response.text == "Internal Server Error" |
||||
|
@ -1,38 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.exceptions import FastAPIError |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial008c_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
def test_get_no_item(client: TestClient): |
|
||||
response = client.get("/items/foo") |
|
||||
assert response.status_code == 404, response.text |
|
||||
assert response.json() == {"detail": "Item not found, there's only a plumbus here"} |
|
||||
|
|
||||
|
|
||||
def test_get(client: TestClient): |
|
||||
response = client.get("/items/plumbus") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == "plumbus" |
|
||||
|
|
||||
|
|
||||
def test_fastapi_error(client: TestClient): |
|
||||
with pytest.raises(FastAPIError) as exc_info: |
|
||||
client.get("/items/portal-gun") |
|
||||
assert "No response object was returned" in exc_info.value.args[0] |
|
||||
|
|
||||
|
|
||||
def test_internal_server_error(): |
|
||||
from docs_src.dependencies.tutorial008c_an import app |
|
||||
|
|
||||
client = TestClient(app, raise_server_exceptions=False) |
|
||||
response = client.get("/items/portal-gun") |
|
||||
assert response.status_code == 500, response.text |
|
||||
assert response.text == "Internal Server Error" |
|
@ -1,44 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.exceptions import FastAPIError |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial008c_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_no_item(client: TestClient): |
|
||||
response = client.get("/items/foo") |
|
||||
assert response.status_code == 404, response.text |
|
||||
assert response.json() == {"detail": "Item not found, there's only a plumbus here"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get(client: TestClient): |
|
||||
response = client.get("/items/plumbus") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == "plumbus" |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_fastapi_error(client: TestClient): |
|
||||
with pytest.raises(FastAPIError) as exc_info: |
|
||||
client.get("/items/portal-gun") |
|
||||
assert "No response object was returned" in exc_info.value.args[0] |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_internal_server_error(): |
|
||||
from docs_src.dependencies.tutorial008c_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app, raise_server_exceptions=False) |
|
||||
response = client.get("/items/portal-gun") |
|
||||
assert response.status_code == 500, response.text |
|
||||
assert response.text == "Internal Server Error" |
|
@ -1,41 +1,51 @@ |
|||||
|
import importlib |
||||
|
from types import ModuleType |
||||
|
|
||||
import pytest |
import pytest |
||||
from fastapi.testclient import TestClient |
from fastapi.testclient import TestClient |
||||
|
|
||||
|
from ...utils import needs_py39 |
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
@pytest.fixture( |
||||
def get_client(): |
name="mod", |
||||
from docs_src.dependencies.tutorial008d import app |
params=[ |
||||
|
"tutorial008d", |
||||
|
"tutorial008d_an", |
||||
|
pytest.param("tutorial008d_an_py39", marks=needs_py39), |
||||
|
], |
||||
|
) |
||||
|
def get_mod(request: pytest.FixtureRequest): |
||||
|
mod = importlib.import_module(f"docs_src.dependencies.{request.param}") |
||||
|
|
||||
client = TestClient(app) |
return mod |
||||
return client |
|
||||
|
|
||||
|
|
||||
def test_get_no_item(client: TestClient): |
def test_get_no_item(mod: ModuleType): |
||||
|
client = TestClient(mod.app) |
||||
response = client.get("/items/foo") |
response = client.get("/items/foo") |
||||
assert response.status_code == 404, response.text |
assert response.status_code == 404, response.text |
||||
assert response.json() == {"detail": "Item not found, there's only a plumbus here"} |
assert response.json() == {"detail": "Item not found, there's only a plumbus here"} |
||||
|
|
||||
|
|
||||
def test_get(client: TestClient): |
def test_get(mod: ModuleType): |
||||
|
client = TestClient(mod.app) |
||||
response = client.get("/items/plumbus") |
response = client.get("/items/plumbus") |
||||
assert response.status_code == 200, response.text |
assert response.status_code == 200, response.text |
||||
assert response.json() == "plumbus" |
assert response.json() == "plumbus" |
||||
|
|
||||
|
|
||||
def test_internal_error(client: TestClient): |
def test_internal_error(mod: ModuleType): |
||||
from docs_src.dependencies.tutorial008d import InternalError |
client = TestClient(mod.app) |
||||
|
with pytest.raises(mod.InternalError) as exc_info: |
||||
with pytest.raises(InternalError) as exc_info: |
|
||||
client.get("/items/portal-gun") |
client.get("/items/portal-gun") |
||||
assert ( |
assert ( |
||||
exc_info.value.args[0] == "The portal gun is too dangerous to be owned by Rick" |
exc_info.value.args[0] == "The portal gun is too dangerous to be owned by Rick" |
||||
) |
) |
||||
|
|
||||
|
|
||||
def test_internal_server_error(): |
def test_internal_server_error(mod: ModuleType): |
||||
from docs_src.dependencies.tutorial008d import app |
client = TestClient(mod.app, raise_server_exceptions=False) |
||||
|
|
||||
client = TestClient(app, raise_server_exceptions=False) |
|
||||
response = client.get("/items/portal-gun") |
response = client.get("/items/portal-gun") |
||||
assert response.status_code == 500, response.text |
assert response.status_code == 500, response.text |
||||
assert response.text == "Internal Server Error" |
assert response.text == "Internal Server Error" |
||||
|
@ -1,41 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial008d_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
def test_get_no_item(client: TestClient): |
|
||||
response = client.get("/items/foo") |
|
||||
assert response.status_code == 404, response.text |
|
||||
assert response.json() == {"detail": "Item not found, there's only a plumbus here"} |
|
||||
|
|
||||
|
|
||||
def test_get(client: TestClient): |
|
||||
response = client.get("/items/plumbus") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == "plumbus" |
|
||||
|
|
||||
|
|
||||
def test_internal_error(client: TestClient): |
|
||||
from docs_src.dependencies.tutorial008d_an import InternalError |
|
||||
|
|
||||
with pytest.raises(InternalError) as exc_info: |
|
||||
client.get("/items/portal-gun") |
|
||||
assert ( |
|
||||
exc_info.value.args[0] == "The portal gun is too dangerous to be owned by Rick" |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_internal_server_error(): |
|
||||
from docs_src.dependencies.tutorial008d_an import app |
|
||||
|
|
||||
client = TestClient(app, raise_server_exceptions=False) |
|
||||
response = client.get("/items/portal-gun") |
|
||||
assert response.status_code == 500, response.text |
|
||||
assert response.text == "Internal Server Error" |
|
@ -1,47 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial008d_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_no_item(client: TestClient): |
|
||||
response = client.get("/items/foo") |
|
||||
assert response.status_code == 404, response.text |
|
||||
assert response.json() == {"detail": "Item not found, there's only a plumbus here"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get(client: TestClient): |
|
||||
response = client.get("/items/plumbus") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == "plumbus" |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_internal_error(client: TestClient): |
|
||||
from docs_src.dependencies.tutorial008d_an_py39 import InternalError |
|
||||
|
|
||||
with pytest.raises(InternalError) as exc_info: |
|
||||
client.get("/items/portal-gun") |
|
||||
assert ( |
|
||||
exc_info.value.args[0] == "The portal gun is too dangerous to be owned by Rick" |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_internal_server_error(): |
|
||||
from docs_src.dependencies.tutorial008d_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app, raise_server_exceptions=False) |
|
||||
response = client.get("/items/portal-gun") |
|
||||
assert response.status_code == 500, response.text |
|
||||
assert response.text == "Internal Server Error" |
|
@ -1,250 +0,0 @@ |
|||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from docs_src.dependencies.tutorial012_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
|
|
||||
|
|
||||
def test_get_no_headers_items(): |
|
||||
response = client.get("/items/") |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["header", "x-token"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["header", "x-key"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["header", "x-token"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["header", "x-key"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_get_no_headers_users(): |
|
||||
response = client.get("/users/") |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["header", "x-token"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["header", "x-key"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["header", "x-token"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["header", "x-key"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_get_invalid_one_header_items(): |
|
||||
response = client.get("/items/", headers={"X-Token": "invalid"}) |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "X-Token header invalid"} |
|
||||
|
|
||||
|
|
||||
def test_get_invalid_one_users(): |
|
||||
response = client.get("/users/", headers={"X-Token": "invalid"}) |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "X-Token header invalid"} |
|
||||
|
|
||||
|
|
||||
def test_get_invalid_second_header_items(): |
|
||||
response = client.get( |
|
||||
"/items/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"} |
|
||||
) |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "X-Key header invalid"} |
|
||||
|
|
||||
|
|
||||
def test_get_invalid_second_header_users(): |
|
||||
response = client.get( |
|
||||
"/users/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"} |
|
||||
) |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "X-Key header invalid"} |
|
||||
|
|
||||
|
|
||||
def test_get_valid_headers_items(): |
|
||||
response = client.get( |
|
||||
"/items/", |
|
||||
headers={ |
|
||||
"X-Token": "fake-super-secret-token", |
|
||||
"X-Key": "fake-super-secret-key", |
|
||||
}, |
|
||||
) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == [{"item": "Portal Gun"}, {"item": "Plumbus"}] |
|
||||
|
|
||||
|
|
||||
def test_get_valid_headers_users(): |
|
||||
response = client.get( |
|
||||
"/users/", |
|
||||
headers={ |
|
||||
"X-Token": "fake-super-secret-token", |
|
||||
"X-Key": "fake-super-secret-key", |
|
||||
}, |
|
||||
) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == [{"username": "Rick"}, {"username": "Morty"}] |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "X-Token", "type": "string"}, |
|
||||
"name": "x-token", |
|
||||
"in": "header", |
|
||||
}, |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "X-Key", "type": "string"}, |
|
||||
"name": "x-key", |
|
||||
"in": "header", |
|
||||
}, |
|
||||
], |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
"/users/": { |
|
||||
"get": { |
|
||||
"summary": "Read Users", |
|
||||
"operationId": "read_users_users__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "X-Token", "type": "string"}, |
|
||||
"name": "x-token", |
|
||||
"in": "header", |
|
||||
}, |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "X-Key", "type": "string"}, |
|
||||
"name": "x-key", |
|
||||
"in": "header", |
|
||||
}, |
|
||||
], |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,266 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.dependencies.tutorial012_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_no_headers_items(client: TestClient): |
|
||||
response = client.get("/items/") |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["header", "x-token"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["header", "x-key"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["header", "x-token"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["header", "x-key"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_no_headers_users(client: TestClient): |
|
||||
response = client.get("/users/") |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["header", "x-token"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["header", "x-key"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["header", "x-token"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["header", "x-key"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_invalid_one_header_items(client: TestClient): |
|
||||
response = client.get("/items/", headers={"X-Token": "invalid"}) |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "X-Token header invalid"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_invalid_one_users(client: TestClient): |
|
||||
response = client.get("/users/", headers={"X-Token": "invalid"}) |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "X-Token header invalid"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_invalid_second_header_items(client: TestClient): |
|
||||
response = client.get( |
|
||||
"/items/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"} |
|
||||
) |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "X-Key header invalid"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_invalid_second_header_users(client: TestClient): |
|
||||
response = client.get( |
|
||||
"/users/", headers={"X-Token": "fake-super-secret-token", "X-Key": "invalid"} |
|
||||
) |
|
||||
assert response.status_code == 400, response.text |
|
||||
assert response.json() == {"detail": "X-Key header invalid"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_valid_headers_items(client: TestClient): |
|
||||
response = client.get( |
|
||||
"/items/", |
|
||||
headers={ |
|
||||
"X-Token": "fake-super-secret-token", |
|
||||
"X-Key": "fake-super-secret-key", |
|
||||
}, |
|
||||
) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == [{"item": "Portal Gun"}, {"item": "Plumbus"}] |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_valid_headers_users(client: TestClient): |
|
||||
response = client.get( |
|
||||
"/users/", |
|
||||
headers={ |
|
||||
"X-Token": "fake-super-secret-token", |
|
||||
"X-Key": "fake-super-secret-key", |
|
||||
}, |
|
||||
) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == [{"username": "Rick"}, {"username": "Morty"}] |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "X-Token", "type": "string"}, |
|
||||
"name": "x-token", |
|
||||
"in": "header", |
|
||||
}, |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "X-Key", "type": "string"}, |
|
||||
"name": "x-key", |
|
||||
"in": "header", |
|
||||
}, |
|
||||
], |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
"/users/": { |
|
||||
"get": { |
|
||||
"summary": "Read Users", |
|
||||
"operationId": "read_users_users__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "X-Token", "type": "string"}, |
|
||||
"name": "x-token", |
|
||||
"in": "header", |
|
||||
}, |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "X-Key", "type": "string"}, |
|
||||
"name": "x-key", |
|
||||
"in": "header", |
|
||||
}, |
|
||||
], |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,175 +0,0 @@ |
|||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from docs_src.extra_data_types.tutorial001_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
|
|
||||
|
|
||||
def test_extra_types(): |
|
||||
item_id = "ff97dd87-a4a5-4a12-b412-cde99f33e00e" |
|
||||
data = { |
|
||||
"start_datetime": "2018-12-22T14:00:00+00:00", |
|
||||
"end_datetime": "2018-12-24T15:00:00+00:00", |
|
||||
"repeat_at": "15:30:00", |
|
||||
"process_after": 300, |
|
||||
} |
|
||||
expected_response = data.copy() |
|
||||
expected_response.update( |
|
||||
{ |
|
||||
"start_process": "2018-12-22T14:05:00+00:00", |
|
||||
"duration": 176_100, |
|
||||
"item_id": item_id, |
|
||||
} |
|
||||
) |
|
||||
response = client.put(f"/items/{item_id}", json=data) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"put": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__item_id__put", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": { |
|
||||
"title": "Item Id", |
|
||||
"type": "string", |
|
||||
"format": "uuid", |
|
||||
}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
"requestBody": { |
|
||||
"required": True, |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"allOf": [ |
|
||||
{ |
|
||||
"$ref": "#/components/schemas/Body_read_items_items__item_id__put" |
|
||||
} |
|
||||
], |
|
||||
"title": "Body", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"$ref": "#/components/schemas/Body_read_items_items__item_id__put" |
|
||||
} |
|
||||
) |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Body_read_items_items__item_id__put": { |
|
||||
"title": "Body_read_items_items__item_id__put", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"start_datetime": { |
|
||||
"title": "Start Datetime", |
|
||||
"type": "string", |
|
||||
"format": "date-time", |
|
||||
}, |
|
||||
"end_datetime": { |
|
||||
"title": "End Datetime", |
|
||||
"type": "string", |
|
||||
"format": "date-time", |
|
||||
}, |
|
||||
"repeat_at": IsDict( |
|
||||
{ |
|
||||
"title": "Repeat At", |
|
||||
"anyOf": [ |
|
||||
{"type": "string", "format": "time"}, |
|
||||
{"type": "null"}, |
|
||||
], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"title": "Repeat At", |
|
||||
"type": "string", |
|
||||
"format": "time", |
|
||||
} |
|
||||
), |
|
||||
"process_after": IsDict( |
|
||||
{ |
|
||||
"title": "Process After", |
|
||||
"type": "string", |
|
||||
"format": "duration", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"title": "Process After", |
|
||||
"type": "number", |
|
||||
"format": "time-delta", |
|
||||
} |
|
||||
), |
|
||||
}, |
|
||||
"required": ["start_datetime", "end_datetime", "process_after"], |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,184 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.extra_data_types.tutorial001_an_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_extra_types(client: TestClient): |
|
||||
item_id = "ff97dd87-a4a5-4a12-b412-cde99f33e00e" |
|
||||
data = { |
|
||||
"start_datetime": "2018-12-22T14:00:00+00:00", |
|
||||
"end_datetime": "2018-12-24T15:00:00+00:00", |
|
||||
"repeat_at": "15:30:00", |
|
||||
"process_after": 300, |
|
||||
} |
|
||||
expected_response = data.copy() |
|
||||
expected_response.update( |
|
||||
{ |
|
||||
"start_process": "2018-12-22T14:05:00+00:00", |
|
||||
"duration": 176_100, |
|
||||
"item_id": item_id, |
|
||||
} |
|
||||
) |
|
||||
response = client.put(f"/items/{item_id}", json=data) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"put": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__item_id__put", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": { |
|
||||
"title": "Item Id", |
|
||||
"type": "string", |
|
||||
"format": "uuid", |
|
||||
}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
"requestBody": { |
|
||||
"required": True, |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"allOf": [ |
|
||||
{ |
|
||||
"$ref": "#/components/schemas/Body_read_items_items__item_id__put" |
|
||||
} |
|
||||
], |
|
||||
"title": "Body", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"$ref": "#/components/schemas/Body_read_items_items__item_id__put" |
|
||||
} |
|
||||
) |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Body_read_items_items__item_id__put": { |
|
||||
"title": "Body_read_items_items__item_id__put", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"start_datetime": { |
|
||||
"title": "Start Datetime", |
|
||||
"type": "string", |
|
||||
"format": "date-time", |
|
||||
}, |
|
||||
"end_datetime": { |
|
||||
"title": "End Datetime", |
|
||||
"type": "string", |
|
||||
"format": "date-time", |
|
||||
}, |
|
||||
"repeat_at": IsDict( |
|
||||
{ |
|
||||
"title": "Repeat At", |
|
||||
"anyOf": [ |
|
||||
{"type": "string", "format": "time"}, |
|
||||
{"type": "null"}, |
|
||||
], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"title": "Repeat At", |
|
||||
"type": "string", |
|
||||
"format": "time", |
|
||||
} |
|
||||
), |
|
||||
"process_after": IsDict( |
|
||||
{ |
|
||||
"title": "Process After", |
|
||||
"type": "string", |
|
||||
"format": "duration", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"title": "Process After", |
|
||||
"type": "number", |
|
||||
"format": "time-delta", |
|
||||
} |
|
||||
), |
|
||||
}, |
|
||||
"required": ["start_datetime", "end_datetime", "process_after"], |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,184 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.extra_data_types.tutorial001_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_extra_types(client: TestClient): |
|
||||
item_id = "ff97dd87-a4a5-4a12-b412-cde99f33e00e" |
|
||||
data = { |
|
||||
"start_datetime": "2018-12-22T14:00:00+00:00", |
|
||||
"end_datetime": "2018-12-24T15:00:00+00:00", |
|
||||
"repeat_at": "15:30:00", |
|
||||
"process_after": 300, |
|
||||
} |
|
||||
expected_response = data.copy() |
|
||||
expected_response.update( |
|
||||
{ |
|
||||
"start_process": "2018-12-22T14:05:00+00:00", |
|
||||
"duration": 176_100, |
|
||||
"item_id": item_id, |
|
||||
} |
|
||||
) |
|
||||
response = client.put(f"/items/{item_id}", json=data) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"put": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__item_id__put", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": { |
|
||||
"title": "Item Id", |
|
||||
"type": "string", |
|
||||
"format": "uuid", |
|
||||
}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
"requestBody": { |
|
||||
"required": True, |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"allOf": [ |
|
||||
{ |
|
||||
"$ref": "#/components/schemas/Body_read_items_items__item_id__put" |
|
||||
} |
|
||||
], |
|
||||
"title": "Body", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"$ref": "#/components/schemas/Body_read_items_items__item_id__put" |
|
||||
} |
|
||||
) |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Body_read_items_items__item_id__put": { |
|
||||
"title": "Body_read_items_items__item_id__put", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"start_datetime": { |
|
||||
"title": "Start Datetime", |
|
||||
"type": "string", |
|
||||
"format": "date-time", |
|
||||
}, |
|
||||
"end_datetime": { |
|
||||
"title": "End Datetime", |
|
||||
"type": "string", |
|
||||
"format": "date-time", |
|
||||
}, |
|
||||
"repeat_at": IsDict( |
|
||||
{ |
|
||||
"title": "Repeat At", |
|
||||
"anyOf": [ |
|
||||
{"type": "string", "format": "time"}, |
|
||||
{"type": "null"}, |
|
||||
], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"title": "Repeat At", |
|
||||
"type": "string", |
|
||||
"format": "time", |
|
||||
} |
|
||||
), |
|
||||
"process_after": IsDict( |
|
||||
{ |
|
||||
"title": "Process After", |
|
||||
"type": "string", |
|
||||
"format": "duration", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"title": "Process After", |
|
||||
"type": "number", |
|
||||
"format": "time-delta", |
|
||||
} |
|
||||
), |
|
||||
}, |
|
||||
"required": ["start_datetime", "end_datetime", "process_after"], |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,184 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.extra_data_types.tutorial001_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_extra_types(client: TestClient): |
|
||||
item_id = "ff97dd87-a4a5-4a12-b412-cde99f33e00e" |
|
||||
data = { |
|
||||
"start_datetime": "2018-12-22T14:00:00+00:00", |
|
||||
"end_datetime": "2018-12-24T15:00:00+00:00", |
|
||||
"repeat_at": "15:30:00", |
|
||||
"process_after": 300, |
|
||||
} |
|
||||
expected_response = data.copy() |
|
||||
expected_response.update( |
|
||||
{ |
|
||||
"start_process": "2018-12-22T14:05:00+00:00", |
|
||||
"duration": 176_100, |
|
||||
"item_id": item_id, |
|
||||
} |
|
||||
) |
|
||||
response = client.put(f"/items/{item_id}", json=data) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"put": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__item_id__put", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": { |
|
||||
"title": "Item Id", |
|
||||
"type": "string", |
|
||||
"format": "uuid", |
|
||||
}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
"requestBody": { |
|
||||
"required": True, |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"allOf": [ |
|
||||
{ |
|
||||
"$ref": "#/components/schemas/Body_read_items_items__item_id__put" |
|
||||
} |
|
||||
], |
|
||||
"title": "Body", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"$ref": "#/components/schemas/Body_read_items_items__item_id__put" |
|
||||
} |
|
||||
) |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Body_read_items_items__item_id__put": { |
|
||||
"title": "Body_read_items_items__item_id__put", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"start_datetime": { |
|
||||
"title": "Start Datetime", |
|
||||
"type": "string", |
|
||||
"format": "date-time", |
|
||||
}, |
|
||||
"end_datetime": { |
|
||||
"title": "End Datetime", |
|
||||
"type": "string", |
|
||||
"format": "date-time", |
|
||||
}, |
|
||||
"repeat_at": IsDict( |
|
||||
{ |
|
||||
"title": "Repeat At", |
|
||||
"anyOf": [ |
|
||||
{"type": "string", "format": "time"}, |
|
||||
{"type": "null"}, |
|
||||
], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"title": "Repeat At", |
|
||||
"type": "string", |
|
||||
"format": "time", |
|
||||
} |
|
||||
), |
|
||||
"process_after": IsDict( |
|
||||
{ |
|
||||
"title": "Process After", |
|
||||
"type": "string", |
|
||||
"format": "duration", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"title": "Process After", |
|
||||
"type": "number", |
|
||||
"format": "time-delta", |
|
||||
} |
|
||||
), |
|
||||
}, |
|
||||
"required": ["start_datetime", "end_datetime", "process_after"], |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,144 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsOneOf |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.extra_models.tutorial003_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_get_car(client: TestClient): |
|
||||
response = client.get("/items/item1") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"description": "All my friends drive a low rider", |
|
||||
"type": "car", |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_get_plane(client: TestClient): |
|
||||
response = client.get("/items/item2") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"description": "Music is my aeroplane, it's my aeroplane", |
|
||||
"type": "plane", |
|
||||
"size": 5, |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"title": "Response Read Item Items Item Id Get", |
|
||||
"anyOf": [ |
|
||||
{"$ref": "#/components/schemas/PlaneItem"}, |
|
||||
{"$ref": "#/components/schemas/CarItem"}, |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Item", |
|
||||
"operationId": "read_item_items__item_id__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"PlaneItem": { |
|
||||
"title": "PlaneItem", |
|
||||
"required": IsOneOf( |
|
||||
["description", "type", "size"], |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
["description", "size"], |
|
||||
), |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"description": {"title": "Description", "type": "string"}, |
|
||||
"type": {"title": "Type", "type": "string", "default": "plane"}, |
|
||||
"size": {"title": "Size", "type": "integer"}, |
|
||||
}, |
|
||||
}, |
|
||||
"CarItem": { |
|
||||
"title": "CarItem", |
|
||||
"required": IsOneOf( |
|
||||
["description", "type"], |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
["description"], |
|
||||
), |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"description": {"title": "Description", "type": "string"}, |
|
||||
"type": {"title": "Type", "type": "string", "default": "car"}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,67 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.extra_models.tutorial004_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_items(client: TestClient): |
|
||||
response = client.get("/items/") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == [ |
|
||||
{"name": "Foo", "description": "There comes my hero"}, |
|
||||
{"name": "Red", "description": "It's my aeroplane"}, |
|
||||
] |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"title": "Response Read Items Items Get", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/Item"}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"required": ["name", "description"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"description": {"title": "Description", "type": "string"}, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,51 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.extra_models.tutorial005_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_get_items(client: TestClient): |
|
||||
response = client.get("/keyword-weights/") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == {"foo": 2.3, "bar": 3.4} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/keyword-weights/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"title": "Response Read Keyword Weights Keyword Weights Get", |
|
||||
"type": "object", |
|
||||
"additionalProperties": {"type": "number"}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
"summary": "Read Keyword Weights", |
|
||||
"operationId": "read_keyword_weights_keyword_weights__get", |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,102 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from docs_src.header_params.tutorial001_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
|
|
||||
|
|
||||
@pytest.mark.parametrize( |
|
||||
"path,headers,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"User-Agent": "testclient"}), |
|
||||
("/items", {"X-Header": "notvalid"}, 200, {"User-Agent": "testclient"}), |
|
||||
("/items", {"User-Agent": "FastAPI test"}, 200, {"User-Agent": "FastAPI test"}), |
|
||||
], |
|
||||
) |
|
||||
def test(path, headers, expected_status, expected_response): |
|
||||
response = client.get(path, headers=headers) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "User-Agent", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "User-Agent", "type": "string"} |
|
||||
), |
|
||||
"name": "user-agent", |
|
||||
"in": "header", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,110 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.header_params.tutorial001_an_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,headers,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"User-Agent": "testclient"}), |
|
||||
("/items", {"X-Header": "notvalid"}, 200, {"User-Agent": "testclient"}), |
|
||||
("/items", {"User-Agent": "FastAPI test"}, 200, {"User-Agent": "FastAPI test"}), |
|
||||
], |
|
||||
) |
|
||||
def test(path, headers, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path, headers=headers) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "User-Agent", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "User-Agent", "type": "string"} |
|
||||
), |
|
||||
"name": "user-agent", |
|
||||
"in": "header", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,110 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.header_params.tutorial001_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,headers,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"User-Agent": "testclient"}), |
|
||||
("/items", {"X-Header": "notvalid"}, 200, {"User-Agent": "testclient"}), |
|
||||
("/items", {"User-Agent": "FastAPI test"}, 200, {"User-Agent": "FastAPI test"}), |
|
||||
], |
|
||||
) |
|
||||
def test(path, headers, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path, headers=headers) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "User-Agent", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "User-Agent", "type": "string"} |
|
||||
), |
|
||||
"name": "user-agent", |
|
||||
"in": "header", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,113 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from docs_src.header_params.tutorial002_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
|
|
||||
|
|
||||
@pytest.mark.parametrize( |
|
||||
"path,headers,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"strange_header": None}), |
|
||||
("/items", {"X-Header": "notvalid"}, 200, {"strange_header": None}), |
|
||||
( |
|
||||
"/items", |
|
||||
{"strange_header": "FastAPI test"}, |
|
||||
200, |
|
||||
{"strange_header": "FastAPI test"}, |
|
||||
), |
|
||||
( |
|
||||
"/items", |
|
||||
{"strange-header": "Not really underscore"}, |
|
||||
200, |
|
||||
{"strange_header": None}, |
|
||||
), |
|
||||
], |
|
||||
) |
|
||||
def test(path, headers, expected_status, expected_response): |
|
||||
response = client.get(path, headers=headers) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Strange Header", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Strange Header", "type": "string"} |
|
||||
), |
|
||||
"name": "strange_header", |
|
||||
"in": "header", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,121 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.header_params.tutorial002_an_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,headers,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"strange_header": None}), |
|
||||
("/items", {"X-Header": "notvalid"}, 200, {"strange_header": None}), |
|
||||
( |
|
||||
"/items", |
|
||||
{"strange_header": "FastAPI test"}, |
|
||||
200, |
|
||||
{"strange_header": "FastAPI test"}, |
|
||||
), |
|
||||
( |
|
||||
"/items", |
|
||||
{"strange-header": "Not really underscore"}, |
|
||||
200, |
|
||||
{"strange_header": None}, |
|
||||
), |
|
||||
], |
|
||||
) |
|
||||
def test(path, headers, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path, headers=headers) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Strange Header", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Strange Header", "type": "string"} |
|
||||
), |
|
||||
"name": "strange_header", |
|
||||
"in": "header", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,124 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.header_params.tutorial002_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,headers,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"strange_header": None}), |
|
||||
("/items", {"X-Header": "notvalid"}, 200, {"strange_header": None}), |
|
||||
( |
|
||||
"/items", |
|
||||
{"strange_header": "FastAPI test"}, |
|
||||
200, |
|
||||
{"strange_header": "FastAPI test"}, |
|
||||
), |
|
||||
( |
|
||||
"/items", |
|
||||
{"strange-header": "Not really underscore"}, |
|
||||
200, |
|
||||
{"strange_header": None}, |
|
||||
), |
|
||||
], |
|
||||
) |
|
||||
def test(path, headers, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path, headers=headers) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(): |
|
||||
from docs_src.header_params.tutorial002_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Strange Header", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Strange Header", "type": "string"} |
|
||||
), |
|
||||
"name": "strange_header", |
|
||||
"in": "header", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,124 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.header_params.tutorial002_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,headers,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"strange_header": None}), |
|
||||
("/items", {"X-Header": "notvalid"}, 200, {"strange_header": None}), |
|
||||
( |
|
||||
"/items", |
|
||||
{"strange_header": "FastAPI test"}, |
|
||||
200, |
|
||||
{"strange_header": "FastAPI test"}, |
|
||||
), |
|
||||
( |
|
||||
"/items", |
|
||||
{"strange-header": "Not really underscore"}, |
|
||||
200, |
|
||||
{"strange_header": None}, |
|
||||
), |
|
||||
], |
|
||||
) |
|
||||
def test(path, headers, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path, headers=headers) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(): |
|
||||
from docs_src.header_params.tutorial002_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
"title": "Strange Header", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Strange Header", "type": "string"} |
|
||||
), |
|
||||
"name": "strange_header", |
|
||||
"in": "header", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,110 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from docs_src.header_params.tutorial003_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
|
|
||||
|
|
||||
@pytest.mark.parametrize( |
|
||||
"path,headers,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"X-Token values": None}), |
|
||||
("/items", {"x-token": "foo"}, 200, {"X-Token values": ["foo"]}), |
|
||||
# TODO: fix this, is it a bug? |
|
||||
# ("/items", [("x-token", "foo"), ("x-token", "bar")], 200, {"X-Token values": ["foo", "bar"]}), |
|
||||
], |
|
||||
) |
|
||||
def test(path, headers, expected_status, expected_response): |
|
||||
response = client.get(path, headers=headers) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"title": "X-Token", |
|
||||
"anyOf": [ |
|
||||
{"type": "array", "items": {"type": "string"}}, |
|
||||
{"type": "null"}, |
|
||||
], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"title": "X-Token", |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
} |
|
||||
), |
|
||||
"name": "x-token", |
|
||||
"in": "header", |
|
||||
} |
|
||||
], |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,118 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.header_params.tutorial003_an_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,headers,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"X-Token values": None}), |
|
||||
("/items", {"x-token": "foo"}, 200, {"X-Token values": ["foo"]}), |
|
||||
# TODO: fix this, is it a bug? |
|
||||
# ("/items", [("x-token", "foo"), ("x-token", "bar")], 200, {"X-Token values": ["foo", "bar"]}), |
|
||||
], |
|
||||
) |
|
||||
def test(path, headers, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path, headers=headers) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"title": "X-Token", |
|
||||
"anyOf": [ |
|
||||
{"type": "array", "items": {"type": "string"}}, |
|
||||
{"type": "null"}, |
|
||||
], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"title": "X-Token", |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
} |
|
||||
), |
|
||||
"name": "x-token", |
|
||||
"in": "header", |
|
||||
} |
|
||||
], |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,118 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.header_params.tutorial003_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,headers,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"X-Token values": None}), |
|
||||
("/items", {"x-token": "foo"}, 200, {"X-Token values": ["foo"]}), |
|
||||
# TODO: fix this, is it a bug? |
|
||||
# ("/items", [("x-token", "foo"), ("x-token", "bar")], 200, {"X-Token values": ["foo", "bar"]}), |
|
||||
], |
|
||||
) |
|
||||
def test(path, headers, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path, headers=headers) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"title": "X-Token", |
|
||||
"anyOf": [ |
|
||||
{"type": "array", "items": {"type": "string"}}, |
|
||||
{"type": "null"}, |
|
||||
], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"title": "X-Token", |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
} |
|
||||
), |
|
||||
"name": "x-token", |
|
||||
"in": "header", |
|
||||
} |
|
||||
], |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,118 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.header_params.tutorial003_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"path,headers,expected_status,expected_response", |
|
||||
[ |
|
||||
("/items", None, 200, {"X-Token values": None}), |
|
||||
("/items", {"x-token": "foo"}, 200, {"X-Token values": ["foo"]}), |
|
||||
# TODO: fix this, is it a bug? |
|
||||
# ("/items", [("x-token", "foo"), ("x-token", "bar")], 200, {"X-Token values": ["foo", "bar"]}), |
|
||||
], |
|
||||
) |
|
||||
def test(path, headers, expected_status, expected_response, client: TestClient): |
|
||||
response = client.get(path, headers=headers) |
|
||||
assert response.status_code == expected_status |
|
||||
assert response.json() == expected_response |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"get": { |
|
||||
"summary": "Read Items", |
|
||||
"operationId": "read_items_items__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"title": "X-Token", |
|
||||
"anyOf": [ |
|
||||
{"type": "array", "items": {"type": "string"}}, |
|
||||
{"type": "null"}, |
|
||||
], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"title": "X-Token", |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
} |
|
||||
), |
|
||||
"name": "x-token", |
|
||||
"in": "header", |
|
||||
} |
|
||||
], |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,226 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310, needs_pydanticv1, needs_pydanticv2 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.path_operation_configuration.tutorial005_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_query_params_str_validations(client: TestClient): |
|
||||
response = client.post("/items/", json={"name": "Foo", "price": 42}) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"name": "Foo", |
|
||||
"price": 42, |
|
||||
"description": None, |
|
||||
"tax": None, |
|
||||
"tags": [], |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@needs_pydanticv2 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "The created item", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Create an item", |
|
||||
"description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item", |
|
||||
"operationId": "create_item_items__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"required": ["name", "price"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"description": { |
|
||||
"title": "Description", |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
}, |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"tax": { |
|
||||
"title": "Tax", |
|
||||
"anyOf": [{"type": "number"}, {"type": "null"}], |
|
||||
}, |
|
||||
"tags": { |
|
||||
"title": "Tags", |
|
||||
"uniqueItems": True, |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
"default": [], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_py310 |
|
||||
@needs_pydanticv1 |
|
||||
def test_openapi_schema_pv1(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "The created item", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Create an item", |
|
||||
"description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item", |
|
||||
"operationId": "create_item_items__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"required": ["name", "price"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"description": {"title": "Description", "type": "string"}, |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"tax": {"title": "Tax", "type": "number"}, |
|
||||
"tags": { |
|
||||
"title": "Tags", |
|
||||
"uniqueItems": True, |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
"default": [], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,226 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39, needs_pydanticv1, needs_pydanticv2 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.path_operation_configuration.tutorial005_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_query_params_str_validations(client: TestClient): |
|
||||
response = client.post("/items/", json={"name": "Foo", "price": 42}) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"name": "Foo", |
|
||||
"price": 42, |
|
||||
"description": None, |
|
||||
"tax": None, |
|
||||
"tags": [], |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
@needs_pydanticv2 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "The created item", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Create an item", |
|
||||
"description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item", |
|
||||
"operationId": "create_item_items__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"required": ["name", "price"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"description": { |
|
||||
"title": "Description", |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
}, |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"tax": { |
|
||||
"title": "Tax", |
|
||||
"anyOf": [{"type": "number"}, {"type": "null"}], |
|
||||
}, |
|
||||
"tags": { |
|
||||
"title": "Tags", |
|
||||
"uniqueItems": True, |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
"default": [], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_py39 |
|
||||
@needs_pydanticv1 |
|
||||
def test_openapi_schema_pv1(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "The created item", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Create an item", |
|
||||
"description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item", |
|
||||
"operationId": "create_item_items__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"required": ["name", "price"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"description": {"title": "Description", "type": "string"}, |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"tax": {"title": "Tax", "type": "number"}, |
|
||||
"tags": { |
|
||||
"title": "Tags", |
|
||||
"uniqueItems": True, |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
"default": [], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,180 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.query_params.tutorial006_py310 import app |
|
||||
|
|
||||
c = TestClient(app) |
|
||||
return c |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_foo_needy_very(client: TestClient): |
|
||||
response = client.get("/items/foo?needy=very") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"item_id": "foo", |
|
||||
"needy": "very", |
|
||||
"skip": 0, |
|
||||
"limit": None, |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_foo_no_needy(client: TestClient): |
|
||||
response = client.get("/items/foo?skip=a&limit=b") |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["query", "needy"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "int_parsing", |
|
||||
"loc": ["query", "skip"], |
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer", |
|
||||
"input": "a", |
|
||||
}, |
|
||||
{ |
|
||||
"type": "int_parsing", |
|
||||
"loc": ["query", "limit"], |
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer", |
|
||||
"input": "b", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["query", "needy"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["query", "skip"], |
|
||||
"msg": "value is not a valid integer", |
|
||||
"type": "type_error.integer", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["query", "limit"], |
|
||||
"msg": "value is not a valid integer", |
|
||||
"type": "type_error.integer", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read User Item", |
|
||||
"operationId": "read_user_item_items__item_id__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
}, |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Needy", "type": "string"}, |
|
||||
"name": "needy", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Skip", |
|
||||
"type": "integer", |
|
||||
"default": 0, |
|
||||
}, |
|
||||
"name": "skip", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": IsDict( |
|
||||
{ |
|
||||
"anyOf": [{"type": "integer"}, {"type": "null"}], |
|
||||
"title": "Limit", |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Limit", "type": "integer"} |
|
||||
), |
|
||||
"name": "limit", |
|
||||
"in": "query", |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,232 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.request_form_models.tutorial001_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
def test_post_body_form(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"username": "Foo", "password": "secret"} |
|
||||
|
|
||||
|
|
||||
def test_post_body_form_no_password(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": {"username": "Foo"}, |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_post_body_form_no_username(client: TestClient): |
|
||||
response = client.post("/login/", data={"password": "secret"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": {"password": "secret"}, |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_post_body_form_no_data(client: TestClient): |
|
||||
response = client.post("/login/") |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_post_body_json(client: TestClient): |
|
||||
response = client.post("/login/", json={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/login/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Login", |
|
||||
"operationId": "login_login__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/x-www-form-urlencoded": { |
|
||||
"schema": {"$ref": "#/components/schemas/FormData"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"FormData": { |
|
||||
"properties": { |
|
||||
"username": {"type": "string", "title": "Username"}, |
|
||||
"password": {"type": "string", "title": "Password"}, |
|
||||
}, |
|
||||
"type": "object", |
|
||||
"required": ["username", "password"], |
|
||||
"title": "FormData", |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,240 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from tests.utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.request_form_models.tutorial001_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body_form(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"username": "Foo", "password": "secret"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body_form_no_password(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": {"username": "Foo"}, |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body_form_no_username(client: TestClient): |
|
||||
response = client.post("/login/", data={"password": "secret"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": {"password": "secret"}, |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body_form_no_data(client: TestClient): |
|
||||
response = client.post("/login/") |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body_json(client: TestClient): |
|
||||
response = client.post("/login/", json={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/login/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Login", |
|
||||
"operationId": "login_login__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/x-www-form-urlencoded": { |
|
||||
"schema": {"$ref": "#/components/schemas/FormData"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"FormData": { |
|
||||
"properties": { |
|
||||
"username": {"type": "string", "title": "Username"}, |
|
||||
"password": {"type": "string", "title": "Password"}, |
|
||||
}, |
|
||||
"type": "object", |
|
||||
"required": ["username", "password"], |
|
||||
"title": "FormData", |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,196 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from tests.utils import needs_pydanticv2 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.request_form_models.tutorial002_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
def test_post_body_form(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"username": "Foo", "password": "secret"} |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
def test_post_body_extra_form(client: TestClient): |
|
||||
response = client.post( |
|
||||
"/login/", data={"username": "Foo", "password": "secret", "extra": "extra"} |
|
||||
) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "extra_forbidden", |
|
||||
"loc": ["body", "extra"], |
|
||||
"msg": "Extra inputs are not permitted", |
|
||||
"input": "extra", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
def test_post_body_form_no_password(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": {"username": "Foo"}, |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
def test_post_body_form_no_username(client: TestClient): |
|
||||
response = client.post("/login/", data={"password": "secret"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": {"password": "secret"}, |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
def test_post_body_form_no_data(client: TestClient): |
|
||||
response = client.post("/login/") |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
def test_post_body_json(client: TestClient): |
|
||||
response = client.post("/login/", json={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/login/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Login", |
|
||||
"operationId": "login_login__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/x-www-form-urlencoded": { |
|
||||
"schema": {"$ref": "#/components/schemas/FormData"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"FormData": { |
|
||||
"properties": { |
|
||||
"username": {"type": "string", "title": "Username"}, |
|
||||
"password": {"type": "string", "title": "Password"}, |
|
||||
}, |
|
||||
"additionalProperties": False, |
|
||||
"type": "object", |
|
||||
"required": ["username", "password"], |
|
||||
"title": "FormData", |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,203 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from tests.utils import needs_py39, needs_pydanticv2 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.request_form_models.tutorial002_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
@needs_py39 |
|
||||
def test_post_body_form(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"username": "Foo", "password": "secret"} |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
@needs_py39 |
|
||||
def test_post_body_extra_form(client: TestClient): |
|
||||
response = client.post( |
|
||||
"/login/", data={"username": "Foo", "password": "secret", "extra": "extra"} |
|
||||
) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "extra_forbidden", |
|
||||
"loc": ["body", "extra"], |
|
||||
"msg": "Extra inputs are not permitted", |
|
||||
"input": "extra", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
@needs_py39 |
|
||||
def test_post_body_form_no_password(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": {"username": "Foo"}, |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
@needs_py39 |
|
||||
def test_post_body_form_no_username(client: TestClient): |
|
||||
response = client.post("/login/", data={"password": "secret"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": {"password": "secret"}, |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
@needs_py39 |
|
||||
def test_post_body_form_no_data(client: TestClient): |
|
||||
response = client.post("/login/") |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
@needs_py39 |
|
||||
def test_post_body_json(client: TestClient): |
|
||||
response = client.post("/login/", json={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": {}, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_pydanticv2 |
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/login/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Login", |
|
||||
"operationId": "login_login__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/x-www-form-urlencoded": { |
|
||||
"schema": {"$ref": "#/components/schemas/FormData"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"FormData": { |
|
||||
"properties": { |
|
||||
"username": {"type": "string", "title": "Username"}, |
|
||||
"password": {"type": "string", "title": "Password"}, |
|
||||
}, |
|
||||
"additionalProperties": False, |
|
||||
"type": "object", |
|
||||
"required": ["username", "password"], |
|
||||
"title": "FormData", |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,196 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from tests.utils import needs_pydanticv1 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.request_form_models.tutorial002_pv1_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
def test_post_body_form(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"username": "Foo", "password": "secret"} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
def test_post_body_extra_form(client: TestClient): |
|
||||
response = client.post( |
|
||||
"/login/", data={"username": "Foo", "password": "secret", "extra": "extra"} |
|
||||
) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "value_error.extra", |
|
||||
"loc": ["body", "extra"], |
|
||||
"msg": "extra fields not permitted", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
def test_post_body_form_no_password(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "value_error.missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
def test_post_body_form_no_username(client: TestClient): |
|
||||
response = client.post("/login/", data={"password": "secret"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "value_error.missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
def test_post_body_form_no_data(client: TestClient): |
|
||||
response = client.post("/login/") |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "value_error.missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
}, |
|
||||
{ |
|
||||
"type": "value_error.missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
def test_post_body_json(client: TestClient): |
|
||||
response = client.post("/login/", json={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "value_error.missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
}, |
|
||||
{ |
|
||||
"type": "value_error.missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/login/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Login", |
|
||||
"operationId": "login_login__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/x-www-form-urlencoded": { |
|
||||
"schema": {"$ref": "#/components/schemas/FormData"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"FormData": { |
|
||||
"properties": { |
|
||||
"username": {"type": "string", "title": "Username"}, |
|
||||
"password": {"type": "string", "title": "Password"}, |
|
||||
}, |
|
||||
"additionalProperties": False, |
|
||||
"type": "object", |
|
||||
"required": ["username", "password"], |
|
||||
"title": "FormData", |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,203 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from tests.utils import needs_py39, needs_pydanticv1 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.request_form_models.tutorial002_pv1_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
@needs_py39 |
|
||||
def test_post_body_form(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"username": "Foo", "password": "secret"} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
@needs_py39 |
|
||||
def test_post_body_extra_form(client: TestClient): |
|
||||
response = client.post( |
|
||||
"/login/", data={"username": "Foo", "password": "secret", "extra": "extra"} |
|
||||
) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "value_error.extra", |
|
||||
"loc": ["body", "extra"], |
|
||||
"msg": "extra fields not permitted", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
@needs_py39 |
|
||||
def test_post_body_form_no_password(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "value_error.missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
@needs_py39 |
|
||||
def test_post_body_form_no_username(client: TestClient): |
|
||||
response = client.post("/login/", data={"password": "secret"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "value_error.missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
@needs_py39 |
|
||||
def test_post_body_form_no_data(client: TestClient): |
|
||||
response = client.post("/login/") |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "value_error.missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
}, |
|
||||
{ |
|
||||
"type": "value_error.missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
@needs_py39 |
|
||||
def test_post_body_json(client: TestClient): |
|
||||
response = client.post("/login/", json={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == { |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "value_error.missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
}, |
|
||||
{ |
|
||||
"type": "value_error.missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
|
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
@needs_pydanticv1 |
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/login/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Login", |
|
||||
"operationId": "login_login__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/x-www-form-urlencoded": { |
|
||||
"schema": {"$ref": "#/components/schemas/FormData"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"FormData": { |
|
||||
"properties": { |
|
||||
"username": {"type": "string", "title": "Username"}, |
|
||||
"password": {"type": "string", "title": "Password"}, |
|
||||
}, |
|
||||
"additionalProperties": False, |
|
||||
"type": "object", |
|
||||
"required": ["username", "password"], |
|
||||
"title": "FormData", |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,234 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.request_forms.tutorial001_an import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
def test_post_body_form(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"username": "Foo"} |
|
||||
|
|
||||
|
|
||||
def test_post_body_form_no_password(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_post_body_form_no_username(client: TestClient): |
|
||||
response = client.post("/login/", data={"password": "secret"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_post_body_form_no_data(client: TestClient): |
|
||||
response = client.post("/login/") |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_post_body_json(client: TestClient): |
|
||||
response = client.post("/login/", json={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/login/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Login", |
|
||||
"operationId": "login_login__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/x-www-form-urlencoded": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/Body_login_login__post" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Body_login_login__post": { |
|
||||
"title": "Body_login_login__post", |
|
||||
"required": ["username", "password"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"username": {"title": "Username", "type": "string"}, |
|
||||
"password": {"title": "Password", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,242 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.request_forms.tutorial001_an_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body_form(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 200 |
|
||||
assert response.json() == {"username": "Foo"} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body_form_no_password(client: TestClient): |
|
||||
response = client.post("/login/", data={"username": "Foo"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body_form_no_username(client: TestClient): |
|
||||
response = client.post("/login/", data={"password": "secret"}) |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body_form_no_data(client: TestClient): |
|
||||
response = client.post("/login/") |
|
||||
assert response.status_code == 422 |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body_json(client: TestClient): |
|
||||
response = client.post("/login/", json={"username": "Foo", "password": "secret"}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "username"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "password"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/login/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Login", |
|
||||
"operationId": "login_login__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/x-www-form-urlencoded": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/Body_login_login__post" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Body_login_login__post": { |
|
||||
"title": "Body_login_login__post", |
|
||||
"required": ["username", "password"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"username": {"title": "Username", "type": "string"}, |
|
||||
"password": {"title": "Password", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,309 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi import FastAPI |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="app") |
|
||||
def get_app(): |
|
||||
from docs_src.request_forms_and_files.tutorial001_an import app |
|
||||
|
|
||||
return app |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(app: FastAPI): |
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
def test_post_form_no_body(client: TestClient): |
|
||||
response = client.post("/files/") |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "file"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "token"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "file"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "token"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_post_form_no_file(client: TestClient): |
|
||||
response = client.post("/files/", data={"token": "foo"}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "file"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "file"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_post_body_json(client: TestClient): |
|
||||
response = client.post("/files/", json={"file": "Foo", "token": "Bar"}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "file"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "token"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "file"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "token"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_post_file_no_token(tmp_path, app: FastAPI): |
|
||||
path = tmp_path / "test.txt" |
|
||||
path.write_bytes(b"<file content>") |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
with path.open("rb") as file: |
|
||||
response = client.post("/files/", files={"file": file}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "token"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "token"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
def test_post_files_and_token(tmp_path, app: FastAPI): |
|
||||
patha = tmp_path / "test.txt" |
|
||||
pathb = tmp_path / "testb.txt" |
|
||||
patha.write_text("<file content>") |
|
||||
pathb.write_text("<file b content>") |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
with patha.open("rb") as filea, pathb.open("rb") as fileb: |
|
||||
response = client.post( |
|
||||
"/files/", |
|
||||
data={"token": "foo"}, |
|
||||
files={"file": filea, "fileb": ("testb.txt", fileb, "text/plain")}, |
|
||||
) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"file_size": 14, |
|
||||
"token": "foo", |
|
||||
"fileb_content_type": "text/plain", |
|
||||
} |
|
||||
|
|
||||
|
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/files/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Create File", |
|
||||
"operationId": "create_file_files__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"multipart/form-data": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/Body_create_file_files__post" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Body_create_file_files__post": { |
|
||||
"title": "Body_create_file_files__post", |
|
||||
"required": ["file", "fileb", "token"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"file": {"title": "File", "type": "string", "format": "binary"}, |
|
||||
"fileb": { |
|
||||
"title": "Fileb", |
|
||||
"type": "string", |
|
||||
"format": "binary", |
|
||||
}, |
|
||||
"token": {"title": "Token", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,317 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict |
|
||||
from fastapi import FastAPI |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="app") |
|
||||
def get_app(): |
|
||||
from docs_src.request_forms_and_files.tutorial001_an_py39 import app |
|
||||
|
|
||||
return app |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(app: FastAPI): |
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_form_no_body(client: TestClient): |
|
||||
response = client.post("/files/") |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "file"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "token"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "file"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "token"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_form_no_file(client: TestClient): |
|
||||
response = client.post("/files/", data={"token": "foo"}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "file"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "file"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_body_json(client: TestClient): |
|
||||
response = client.post("/files/", json={"file": "Foo", "token": "Bar"}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "file"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "token"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "file"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "token"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_file_no_token(tmp_path, app: FastAPI): |
|
||||
path = tmp_path / "test.txt" |
|
||||
path.write_bytes(b"<file content>") |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
with path.open("rb") as file: |
|
||||
response = client.post("/files/", files={"file": file}) |
|
||||
assert response.status_code == 422, response.text |
|
||||
assert response.json() == IsDict( |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
{ |
|
||||
"type": "missing", |
|
||||
"loc": ["body", "token"], |
|
||||
"msg": "Field required", |
|
||||
"input": None, |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) | IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{ |
|
||||
"detail": [ |
|
||||
{ |
|
||||
"loc": ["body", "fileb"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
{ |
|
||||
"loc": ["body", "token"], |
|
||||
"msg": "field required", |
|
||||
"type": "value_error.missing", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_post_files_and_token(tmp_path, app: FastAPI): |
|
||||
patha = tmp_path / "test.txt" |
|
||||
pathb = tmp_path / "testb.txt" |
|
||||
patha.write_text("<file content>") |
|
||||
pathb.write_text("<file b content>") |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
with patha.open("rb") as filea, pathb.open("rb") as fileb: |
|
||||
response = client.post( |
|
||||
"/files/", |
|
||||
data={"token": "foo"}, |
|
||||
files={"file": filea, "fileb": ("testb.txt", fileb, "text/plain")}, |
|
||||
) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"file_size": 14, |
|
||||
"token": "foo", |
|
||||
"fileb_content_type": "text/plain", |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/files/": { |
|
||||
"post": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Create File", |
|
||||
"operationId": "create_file_files__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"multipart/form-data": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/Body_create_file_files__post" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Body_create_file_files__post": { |
|
||||
"title": "Body_create_file_files__post", |
|
||||
"required": ["file", "fileb", "token"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"file": {"title": "File", "type": "string", "format": "binary"}, |
|
||||
"fileb": { |
|
||||
"title": "Fileb", |
|
||||
"type": "string", |
|
||||
"format": "binary", |
|
||||
}, |
|
||||
"token": {"title": "Token", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,160 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict, IsOneOf |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.response_model.tutorial003_01_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_post_user(client: TestClient): |
|
||||
response = client.post( |
|
||||
"/user/", |
|
||||
json={ |
|
||||
"username": "foo", |
|
||||
"password": "fighter", |
|
||||
"email": "[email protected]", |
|
||||
"full_name": "Grave Dohl", |
|
||||
}, |
|
||||
) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"username": "foo", |
|
||||
"email": "[email protected]", |
|
||||
"full_name": "Grave Dohl", |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/user/": { |
|
||||
"post": { |
|
||||
"summary": "Create User", |
|
||||
"operationId": "create_user_user__post", |
|
||||
"requestBody": { |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/UserIn"} |
|
||||
} |
|
||||
}, |
|
||||
"required": True, |
|
||||
}, |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/BaseUser"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"BaseUser": { |
|
||||
"title": "BaseUser", |
|
||||
"required": IsOneOf( |
|
||||
["username", "email", "full_name"], |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
["username", "email"], |
|
||||
), |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"username": {"title": "Username", "type": "string"}, |
|
||||
"email": { |
|
||||
"title": "Email", |
|
||||
"type": "string", |
|
||||
"format": "email", |
|
||||
}, |
|
||||
"full_name": IsDict( |
|
||||
{ |
|
||||
"title": "Full Name", |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Full Name", "type": "string"} |
|
||||
), |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"UserIn": { |
|
||||
"title": "UserIn", |
|
||||
"required": ["username", "email", "password"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"username": {"title": "Username", "type": "string"}, |
|
||||
"email": { |
|
||||
"title": "Email", |
|
||||
"type": "string", |
|
||||
"format": "email", |
|
||||
}, |
|
||||
"full_name": IsDict( |
|
||||
{ |
|
||||
"title": "Full Name", |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Full Name", "type": "string"} |
|
||||
), |
|
||||
"password": {"title": "Password", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,103 +0,0 @@ |
|||||
import pytest |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.response_model.tutorial003_05_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_get_portal(client: TestClient): |
|
||||
response = client.get("/portal") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == {"message": "Here's your interdimensional portal."} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_get_redirect(client: TestClient): |
|
||||
response = client.get("/portal", params={"teleport": True}, follow_redirects=False) |
|
||||
assert response.status_code == 307, response.text |
|
||||
assert response.headers["location"] == "https://www.youtube.com/watch?v=dQw4w9WgXcQ" |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/portal": { |
|
||||
"get": { |
|
||||
"summary": "Get Portal", |
|
||||
"operationId": "get_portal_portal_get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": False, |
|
||||
"schema": { |
|
||||
"title": "Teleport", |
|
||||
"type": "boolean", |
|
||||
"default": False, |
|
||||
}, |
|
||||
"name": "teleport", |
|
||||
"in": "query", |
|
||||
} |
|
||||
], |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": {"application/json": {"schema": {}}}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,147 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict, IsOneOf |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.response_model.tutorial004_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
@pytest.mark.parametrize( |
|
||||
"url,data", |
|
||||
[ |
|
||||
("/items/foo", {"name": "Foo", "price": 50.2}), |
|
||||
( |
|
||||
"/items/bar", |
|
||||
{"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2}, |
|
||||
), |
|
||||
( |
|
||||
"/items/baz", |
|
||||
{ |
|
||||
"name": "Baz", |
|
||||
"description": None, |
|
||||
"price": 50.2, |
|
||||
"tax": 10.5, |
|
||||
"tags": [], |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
) |
|
||||
def test_get(url, data, client: TestClient): |
|
||||
response = client.get(url) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == data |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Item", |
|
||||
"operationId": "read_item_items__item_id__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"required": IsOneOf( |
|
||||
["name", "description", "price", "tax", "tags"], |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
["name", "price"], |
|
||||
), |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"description": IsDict( |
|
||||
{ |
|
||||
"title": "Description", |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Description", "type": "string"} |
|
||||
), |
|
||||
"tax": {"title": "Tax", "type": "number", "default": 10.5}, |
|
||||
"tags": { |
|
||||
"title": "Tags", |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
"default": [], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,147 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict, IsOneOf |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py39 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.response_model.tutorial004_py39 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
@pytest.mark.parametrize( |
|
||||
"url,data", |
|
||||
[ |
|
||||
("/items/foo", {"name": "Foo", "price": 50.2}), |
|
||||
( |
|
||||
"/items/bar", |
|
||||
{"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2}, |
|
||||
), |
|
||||
( |
|
||||
"/items/baz", |
|
||||
{ |
|
||||
"name": "Baz", |
|
||||
"description": None, |
|
||||
"price": 50.2, |
|
||||
"tax": 10.5, |
|
||||
"tags": [], |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
) |
|
||||
def test_get(url, data, client: TestClient): |
|
||||
response = client.get(url) |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == data |
|
||||
|
|
||||
|
|
||||
@needs_py39 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Item", |
|
||||
"operationId": "read_item_items__item_id__get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"required": IsOneOf( |
|
||||
["name", "description", "price", "tax", "tags"], |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
["name", "price"], |
|
||||
), |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"description": IsDict( |
|
||||
{ |
|
||||
"title": "Description", |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Description", "type": "string"} |
|
||||
), |
|
||||
"tax": {"title": "Tax", "type": "number", "default": 10.5}, |
|
||||
"tags": { |
|
||||
"title": "Tags", |
|
||||
"type": "array", |
|
||||
"items": {"type": "string"}, |
|
||||
"default": [], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
@ -1,166 +0,0 @@ |
|||||
import pytest |
|
||||
from dirty_equals import IsDict, IsOneOf |
|
||||
from fastapi.testclient import TestClient |
|
||||
|
|
||||
from ...utils import needs_py310 |
|
||||
|
|
||||
|
|
||||
@pytest.fixture(name="client") |
|
||||
def get_client(): |
|
||||
from docs_src.response_model.tutorial005_py310 import app |
|
||||
|
|
||||
client = TestClient(app) |
|
||||
return client |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_read_item_name(client: TestClient): |
|
||||
response = client.get("/items/bar/name") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == {"name": "Bar", "description": "The Bar fighters"} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_read_item_public_data(client: TestClient): |
|
||||
response = client.get("/items/bar/public") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"name": "Bar", |
|
||||
"description": "The Bar fighters", |
|
||||
"price": 62, |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@needs_py310 |
|
||||
def test_openapi_schema(client: TestClient): |
|
||||
response = client.get("/openapi.json") |
|
||||
assert response.status_code == 200, response.text |
|
||||
assert response.json() == { |
|
||||
"openapi": "3.1.0", |
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|
||||
"paths": { |
|
||||
"/items/{item_id}/name": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Item Name", |
|
||||
"operationId": "read_item_name_items__item_id__name_get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
}, |
|
||||
"/items/{item_id}/public": { |
|
||||
"get": { |
|
||||
"responses": { |
|
||||
"200": { |
|
||||
"description": "Successful Response", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": {"$ref": "#/components/schemas/Item"} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"422": { |
|
||||
"description": "Validation Error", |
|
||||
"content": { |
|
||||
"application/json": { |
|
||||
"schema": { |
|
||||
"$ref": "#/components/schemas/HTTPValidationError" |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
"summary": "Read Item Public Data", |
|
||||
"operationId": "read_item_public_data_items__item_id__public_get", |
|
||||
"parameters": [ |
|
||||
{ |
|
||||
"required": True, |
|
||||
"schema": {"title": "Item Id", "type": "string"}, |
|
||||
"name": "item_id", |
|
||||
"in": "path", |
|
||||
} |
|
||||
], |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
"components": { |
|
||||
"schemas": { |
|
||||
"Item": { |
|
||||
"title": "Item", |
|
||||
"required": IsOneOf( |
|
||||
["name", "description", "price", "tax"], |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
["name", "price"], |
|
||||
), |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"name": {"title": "Name", "type": "string"}, |
|
||||
"price": {"title": "Price", "type": "number"}, |
|
||||
"description": IsDict( |
|
||||
{ |
|
||||
"title": "Description", |
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|
||||
} |
|
||||
) |
|
||||
| IsDict( |
|
||||
# TODO: remove when deprecating Pydantic v1 |
|
||||
{"title": "Description", "type": "string"} |
|
||||
), |
|
||||
"tax": {"title": "Tax", "type": "number", "default": 10.5}, |
|
||||
}, |
|
||||
}, |
|
||||
"ValidationError": { |
|
||||
"title": "ValidationError", |
|
||||
"required": ["loc", "msg", "type"], |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"loc": { |
|
||||
"title": "Location", |
|
||||
"type": "array", |
|
||||
"items": { |
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|
||||
}, |
|
||||
}, |
|
||||
"msg": {"title": "Message", "type": "string"}, |
|
||||
"type": {"title": "Error Type", "type": "string"}, |
|
||||
}, |
|
||||
}, |
|
||||
"HTTPValidationError": { |
|
||||
"title": "HTTPValidationError", |
|
||||
"type": "object", |
|
||||
"properties": { |
|
||||
"detail": { |
|
||||
"title": "Detail", |
|
||||
"type": "array", |
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
}, |
|
||||
} |
|
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue