6 changed files with 652 additions and 0 deletions
@ -0,0 +1,191 @@ |
|||
import importlib |
|||
|
|||
import pytest |
|||
from fastapi.testclient import TestClient |
|||
from inline_snapshot import snapshot |
|||
|
|||
|
|||
@pytest.fixture( |
|||
name="client", |
|||
params=[ |
|||
pytest.param("tutorial001_py310"), |
|||
], |
|||
) |
|||
def get_client(request: pytest.FixtureRequest): |
|||
mod = importlib.import_module(f"docs_src.server_sent_events.{request.param}") |
|||
|
|||
client = TestClient(mod.app) |
|||
return client |
|||
|
|||
|
|||
@pytest.mark.parametrize( |
|||
"path", |
|||
[ |
|||
"/items/stream", |
|||
"/items/stream-no-async", |
|||
"/items/stream-no-annotation", |
|||
"/items/stream-no-async-no-annotation", |
|||
], |
|||
) |
|||
def test_stream_items(client: TestClient, path: str): |
|||
response = client.get(path) |
|||
assert response.status_code == 200, response.text |
|||
assert response.headers["content-type"] == "text/event-stream; charset=utf-8" |
|||
data_lines = [ |
|||
line for line in response.text.strip().split("\n") if line.startswith("data: ") |
|||
] |
|||
assert len(data_lines) == 3 |
|||
|
|||
|
|||
def test_openapi_schema(client: TestClient): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == snapshot( |
|||
{ |
|||
"openapi": "3.1.0", |
|||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/items/stream": { |
|||
"get": { |
|||
"summary": "Sse Items", |
|||
"operationId": "sse_items_items_stream_get", |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"text/event-stream": { |
|||
"itemSchema": { |
|||
"type": "object", |
|||
"properties": { |
|||
"data": { |
|||
"type": "string", |
|||
"contentMediaType": "application/json", |
|||
"contentSchema": { |
|||
"$ref": "#/components/schemas/Item" |
|||
}, |
|||
}, |
|||
"event": {"type": "string"}, |
|||
"id": {"type": "string"}, |
|||
"retry": { |
|||
"type": "integer", |
|||
"minimum": 0, |
|||
}, |
|||
}, |
|||
"required": ["data"], |
|||
} |
|||
} |
|||
}, |
|||
} |
|||
}, |
|||
} |
|||
}, |
|||
"/items/stream-no-async": { |
|||
"get": { |
|||
"summary": "Sse Items No Async", |
|||
"operationId": "sse_items_no_async_items_stream_no_async_get", |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"text/event-stream": { |
|||
"itemSchema": { |
|||
"type": "object", |
|||
"properties": { |
|||
"data": { |
|||
"type": "string", |
|||
"contentMediaType": "application/json", |
|||
"contentSchema": { |
|||
"$ref": "#/components/schemas/Item" |
|||
}, |
|||
}, |
|||
"event": {"type": "string"}, |
|||
"id": {"type": "string"}, |
|||
"retry": { |
|||
"type": "integer", |
|||
"minimum": 0, |
|||
}, |
|||
}, |
|||
"required": ["data"], |
|||
} |
|||
} |
|||
}, |
|||
} |
|||
}, |
|||
} |
|||
}, |
|||
"/items/stream-no-annotation": { |
|||
"get": { |
|||
"summary": "Sse Items No Annotation", |
|||
"operationId": "sse_items_no_annotation_items_stream_no_annotation_get", |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"text/event-stream": { |
|||
"itemSchema": { |
|||
"type": "object", |
|||
"properties": { |
|||
"data": {"type": "string"}, |
|||
"event": {"type": "string"}, |
|||
"id": {"type": "string"}, |
|||
"retry": { |
|||
"type": "integer", |
|||
"minimum": 0, |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
} |
|||
}, |
|||
} |
|||
}, |
|||
"/items/stream-no-async-no-annotation": { |
|||
"get": { |
|||
"summary": "Sse Items No Async No Annotation", |
|||
"operationId": "sse_items_no_async_no_annotation_items_stream_no_async_no_annotation_get", |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"text/event-stream": { |
|||
"itemSchema": { |
|||
"type": "object", |
|||
"properties": { |
|||
"data": {"type": "string"}, |
|||
"event": {"type": "string"}, |
|||
"id": {"type": "string"}, |
|||
"retry": { |
|||
"type": "integer", |
|||
"minimum": 0, |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
} |
|||
}, |
|||
} |
|||
}, |
|||
}, |
|||
"components": { |
|||
"schemas": { |
|||
"Item": { |
|||
"properties": { |
|||
"name": {"type": "string", "title": "Name"}, |
|||
"description": { |
|||
"anyOf": [ |
|||
{"type": "string"}, |
|||
{"type": "null"}, |
|||
], |
|||
"title": "Description", |
|||
}, |
|||
}, |
|||
"type": "object", |
|||
"required": ["name", "description"], |
|||
"title": "Item", |
|||
} |
|||
} |
|||
}, |
|||
} |
|||
) |
|||
@ -0,0 +1,83 @@ |
|||
import importlib |
|||
|
|||
import pytest |
|||
from fastapi.testclient import TestClient |
|||
from inline_snapshot import snapshot |
|||
|
|||
|
|||
@pytest.fixture( |
|||
name="client", |
|||
params=[ |
|||
pytest.param("tutorial002_py310"), |
|||
], |
|||
) |
|||
def get_client(request: pytest.FixtureRequest): |
|||
mod = importlib.import_module(f"docs_src.server_sent_events.{request.param}") |
|||
client = TestClient(mod.app) |
|||
return client |
|||
|
|||
|
|||
def test_stream_items(client: TestClient): |
|||
response = client.get("/items/stream") |
|||
assert response.status_code == 200, response.text |
|||
assert response.headers["content-type"] == "text/event-stream; charset=utf-8" |
|||
|
|||
lines = response.text.strip().split("\n") |
|||
|
|||
# First event is a comment-only event |
|||
assert lines[0] == ": stream of item updates" |
|||
|
|||
# Remaining lines contain event:, data:, id:, retry: fields |
|||
event_lines = [line for line in lines if line.startswith("event: ")] |
|||
assert len(event_lines) == 3 |
|||
assert all(line == "event: item_update" for line in event_lines) |
|||
|
|||
data_lines = [line for line in lines if line.startswith("data: ")] |
|||
assert len(data_lines) == 3 |
|||
|
|||
id_lines = [line for line in lines if line.startswith("id: ")] |
|||
assert id_lines == ["id: 1", "id: 2", "id: 3"] |
|||
|
|||
retry_lines = [line for line in lines if line.startswith("retry: ")] |
|||
assert len(retry_lines) == 3 |
|||
assert all(line == "retry: 5000" for line in retry_lines) |
|||
|
|||
|
|||
def test_openapi_schema(client: TestClient): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == snapshot( |
|||
{ |
|||
"openapi": "3.1.0", |
|||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/items/stream": { |
|||
"get": { |
|||
"summary": "Stream Items", |
|||
"operationId": "stream_items_items_stream_get", |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"text/event-stream": { |
|||
"itemSchema": { |
|||
"type": "object", |
|||
"properties": { |
|||
"data": {"type": "string"}, |
|||
"event": {"type": "string"}, |
|||
"id": {"type": "string"}, |
|||
"retry": { |
|||
"type": "integer", |
|||
"minimum": 0, |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
} |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
} |
|||
) |
|||
@ -0,0 +1,73 @@ |
|||
import importlib |
|||
|
|||
import pytest |
|||
from fastapi.testclient import TestClient |
|||
from inline_snapshot import snapshot |
|||
|
|||
|
|||
@pytest.fixture( |
|||
name="client", |
|||
params=[ |
|||
pytest.param("tutorial003_py310"), |
|||
], |
|||
) |
|||
def get_client(request: pytest.FixtureRequest): |
|||
mod = importlib.import_module(f"docs_src.server_sent_events.{request.param}") |
|||
client = TestClient(mod.app) |
|||
return client |
|||
|
|||
|
|||
def test_stream_logs(client: TestClient): |
|||
response = client.get("/logs/stream") |
|||
assert response.status_code == 200, response.text |
|||
assert response.headers["content-type"] == "text/event-stream; charset=utf-8" |
|||
|
|||
data_lines = [ |
|||
line for line in response.text.strip().split("\n") if line.startswith("data: ") |
|||
] |
|||
assert len(data_lines) == 3 |
|||
|
|||
# raw_data is sent without JSON encoding (no quotes around the string) |
|||
assert data_lines[0] == "data: 2025-01-01 INFO Application started" |
|||
assert data_lines[1] == "data: 2025-01-01 DEBUG Connected to database" |
|||
assert data_lines[2] == "data: 2025-01-01 WARN High memory usage detected" |
|||
|
|||
|
|||
def test_openapi_schema(client: TestClient): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == snapshot( |
|||
{ |
|||
"openapi": "3.1.0", |
|||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/logs/stream": { |
|||
"get": { |
|||
"summary": "Stream Logs", |
|||
"operationId": "stream_logs_logs_stream_get", |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"text/event-stream": { |
|||
"itemSchema": { |
|||
"type": "object", |
|||
"properties": { |
|||
"data": {"type": "string"}, |
|||
"event": {"type": "string"}, |
|||
"id": {"type": "string"}, |
|||
"retry": { |
|||
"type": "integer", |
|||
"minimum": 0, |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
} |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
} |
|||
) |
|||
@ -0,0 +1,164 @@ |
|||
import importlib |
|||
|
|||
import pytest |
|||
from fastapi.testclient import TestClient |
|||
from inline_snapshot import snapshot |
|||
|
|||
|
|||
@pytest.fixture( |
|||
name="client", |
|||
params=[ |
|||
pytest.param("tutorial004_py310"), |
|||
], |
|||
) |
|||
def get_client(request: pytest.FixtureRequest): |
|||
mod = importlib.import_module(f"docs_src.server_sent_events.{request.param}") |
|||
client = TestClient(mod.app) |
|||
return client |
|||
|
|||
|
|||
def test_stream_all_items(client: TestClient): |
|||
response = client.get("/items/stream") |
|||
assert response.status_code == 200, response.text |
|||
|
|||
data_lines = [ |
|||
line for line in response.text.strip().split("\n") if line.startswith("data: ") |
|||
] |
|||
assert len(data_lines) == 3 |
|||
|
|||
id_lines = [ |
|||
line for line in response.text.strip().split("\n") if line.startswith("id: ") |
|||
] |
|||
assert id_lines == ["id: 0", "id: 1", "id: 2"] |
|||
|
|||
|
|||
def test_resume_from_last_event_id(client: TestClient): |
|||
response = client.get( |
|||
"/items/stream", |
|||
headers={"last-event-id": "0"}, |
|||
) |
|||
assert response.status_code == 200, response.text |
|||
|
|||
data_lines = [ |
|||
line for line in response.text.strip().split("\n") if line.startswith("data: ") |
|||
] |
|||
assert len(data_lines) == 2 |
|||
|
|||
id_lines = [ |
|||
line for line in response.text.strip().split("\n") if line.startswith("id: ") |
|||
] |
|||
assert id_lines == ["id: 1", "id: 2"] |
|||
|
|||
|
|||
def test_resume_from_last_item(client: TestClient): |
|||
response = client.get( |
|||
"/items/stream", |
|||
headers={"last-event-id": "1"}, |
|||
) |
|||
assert response.status_code == 200, response.text |
|||
|
|||
data_lines = [ |
|||
line for line in response.text.strip().split("\n") if line.startswith("data: ") |
|||
] |
|||
assert len(data_lines) == 1 |
|||
|
|||
id_lines = [ |
|||
line for line in response.text.strip().split("\n") if line.startswith("id: ") |
|||
] |
|||
assert id_lines == ["id: 2"] |
|||
|
|||
|
|||
def test_openapi_schema(client: TestClient): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == snapshot( |
|||
{ |
|||
"openapi": "3.1.0", |
|||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/items/stream": { |
|||
"get": { |
|||
"summary": "Stream Items", |
|||
"operationId": "stream_items_items_stream_get", |
|||
"parameters": [ |
|||
{ |
|||
"name": "last-event-id", |
|||
"in": "header", |
|||
"required": False, |
|||
"schema": { |
|||
"anyOf": [{"type": "integer"}, {"type": "null"}], |
|||
"title": "Last-Event-Id", |
|||
}, |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"text/event-stream": { |
|||
"itemSchema": { |
|||
"type": "object", |
|||
"properties": { |
|||
"data": {"type": "string"}, |
|||
"event": {"type": "string"}, |
|||
"id": {"type": "string"}, |
|||
"retry": { |
|||
"type": "integer", |
|||
"minimum": 0, |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
"components": { |
|||
"schemas": { |
|||
"HTTPValidationError": { |
|||
"properties": { |
|||
"detail": { |
|||
"items": { |
|||
"$ref": "#/components/schemas/ValidationError" |
|||
}, |
|||
"type": "array", |
|||
"title": "Detail", |
|||
} |
|||
}, |
|||
"type": "object", |
|||
"title": "HTTPValidationError", |
|||
}, |
|||
"ValidationError": { |
|||
"properties": { |
|||
"loc": { |
|||
"items": { |
|||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|||
}, |
|||
"type": "array", |
|||
"title": "Location", |
|||
}, |
|||
"msg": {"type": "string", "title": "Message"}, |
|||
"type": {"type": "string", "title": "Error Type"}, |
|||
"input": {"title": "Input"}, |
|||
"ctx": {"type": "object", "title": "Context"}, |
|||
}, |
|||
"type": "object", |
|||
"required": ["loc", "msg", "type"], |
|||
"title": "ValidationError", |
|||
}, |
|||
} |
|||
}, |
|||
} |
|||
) |
|||
@ -0,0 +1,141 @@ |
|||
import importlib |
|||
|
|||
import pytest |
|||
from fastapi.testclient import TestClient |
|||
from inline_snapshot import snapshot |
|||
|
|||
|
|||
@pytest.fixture( |
|||
name="client", |
|||
params=[ |
|||
pytest.param("tutorial005_py310"), |
|||
], |
|||
) |
|||
def get_client(request: pytest.FixtureRequest): |
|||
mod = importlib.import_module(f"docs_src.server_sent_events.{request.param}") |
|||
client = TestClient(mod.app) |
|||
return client |
|||
|
|||
|
|||
def test_stream_chat(client: TestClient): |
|||
response = client.post( |
|||
"/chat/stream", |
|||
json={"text": "hello world"}, |
|||
) |
|||
assert response.status_code == 200, response.text |
|||
assert response.headers["content-type"] == "text/event-stream; charset=utf-8" |
|||
|
|||
lines = response.text.strip().split("\n") |
|||
|
|||
event_lines = [line for line in lines if line.startswith("event: ")] |
|||
assert event_lines == [ |
|||
"event: token", |
|||
"event: token", |
|||
"event: done", |
|||
] |
|||
|
|||
data_lines = [line for line in lines if line.startswith("data: ")] |
|||
assert data_lines == [ |
|||
'data: "hello"', |
|||
'data: "world"', |
|||
"data: [DONE]", |
|||
] |
|||
|
|||
|
|||
def test_openapi_schema(client: TestClient): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == snapshot( |
|||
{ |
|||
"openapi": "3.1.0", |
|||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/chat/stream": { |
|||
"post": { |
|||
"summary": "Stream Chat", |
|||
"operationId": "stream_chat_chat_stream_post", |
|||
"requestBody": { |
|||
"content": { |
|||
"application/json": { |
|||
"schema": {"$ref": "#/components/schemas/Prompt"} |
|||
} |
|||
}, |
|||
"required": True, |
|||
}, |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": { |
|||
"text/event-stream": { |
|||
"itemSchema": { |
|||
"type": "object", |
|||
"properties": { |
|||
"data": {"type": "string"}, |
|||
"event": {"type": "string"}, |
|||
"id": {"type": "string"}, |
|||
"retry": { |
|||
"type": "integer", |
|||
"minimum": 0, |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
"components": { |
|||
"schemas": { |
|||
"HTTPValidationError": { |
|||
"properties": { |
|||
"detail": { |
|||
"items": { |
|||
"$ref": "#/components/schemas/ValidationError" |
|||
}, |
|||
"type": "array", |
|||
"title": "Detail", |
|||
} |
|||
}, |
|||
"type": "object", |
|||
"title": "HTTPValidationError", |
|||
}, |
|||
"Prompt": { |
|||
"properties": {"text": {"type": "string", "title": "Text"}}, |
|||
"type": "object", |
|||
"required": ["text"], |
|||
"title": "Prompt", |
|||
}, |
|||
"ValidationError": { |
|||
"properties": { |
|||
"loc": { |
|||
"items": { |
|||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|||
}, |
|||
"type": "array", |
|||
"title": "Location", |
|||
}, |
|||
"msg": {"type": "string", "title": "Message"}, |
|||
"type": {"type": "string", "title": "Error Type"}, |
|||
"input": {"title": "Input"}, |
|||
"ctx": {"type": "object", "title": "Context"}, |
|||
}, |
|||
"type": "object", |
|||
"required": ["loc", "msg", "type"], |
|||
"title": "ValidationError", |
|||
}, |
|||
} |
|||
}, |
|||
} |
|||
) |
|||
Loading…
Reference in new issue