Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 47 additions & 47 deletions src/azure_functions_openapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,22 +122,22 @@ def get_openapi_json(
"""Return JSON after optionally reconciling routes from a completed FunctionApp."""
try:
spec = generate_openapi_spec(
title,
version,
openapi_version,
description,
security_schemes,
route_prefix,
strict,
registry,
hoist_flat_schemas,
infer_auth_level,
servers,
contact,
license,
external_docs,
tags,
app,
title=title,
version=version,
openapi_version=openapi_version,
description=description,
security_schemes=security_schemes,
route_prefix=route_prefix,
strict=strict,
registry=registry,
hoist_flat_schemas=hoist_flat_schemas,
infer_auth_level=infer_auth_level,
servers=servers,
contact=contact,
license=license,
external_docs=external_docs,
tags=tags,
app=app,
)
return json.dumps(spec, indent=2, ensure_ascii=False)
except OpenAPISpecConfigError:
Expand Down Expand Up @@ -167,22 +167,22 @@ def get_openapi_yaml(
"""Return YAML after optionally reconciling routes from a completed FunctionApp."""
try:
spec = generate_openapi_spec(
title,
version,
openapi_version,
description,
security_schemes,
route_prefix,
strict,
registry,
hoist_flat_schemas,
infer_auth_level,
servers,
contact,
license,
external_docs,
tags,
app,
title=title,
version=version,
openapi_version=openapi_version,
description=description,
security_schemes=security_schemes,
route_prefix=route_prefix,
strict=strict,
registry=registry,
hoist_flat_schemas=hoist_flat_schemas,
infer_auth_level=infer_auth_level,
servers=servers,
contact=contact,
license=license,
external_docs=external_docs,
tags=tags,
app=app,
)
return yaml.safe_dump(spec, sort_keys=False, allow_unicode=True)
except OpenAPISpecConfigError:
Expand Down Expand Up @@ -214,21 +214,21 @@ def generate_openapi_report(
registry = OpenAPIRegistry()
unresolved = _prepare_generation(app, route_prefix, registry)
report = _generate_openapi_report(
title,
version,
openapi_version,
description,
security_schemes,
route_prefix,
strict,
registry,
hoist_flat_schemas,
infer_auth_level,
servers,
contact,
license,
external_docs,
tags,
title=title,
version=version,
openapi_version=openapi_version,
description=description,
security_schemes=security_schemes,
route_prefix=route_prefix,
strict=strict,
registry=registry,
hoist_flat_schemas=hoist_flat_schemas,
infer_auth_level=infer_auth_level,
servers=servers,
contact=contact,
license=license,
external_docs=external_docs,
tags=tags,
)
return SpecReport(spec=report.spec, warnings=(*report.warnings, *unresolved))

Expand Down
67 changes: 67 additions & 0 deletions tests/test_facade_forwarding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from __future__ import annotations

from collections.abc import Callable
import inspect
from typing import Any
from unittest.mock import patch

import pytest

import azure_functions_openapi as api
from azure_functions_openapi.registry import OpenAPIRegistry
from azure_functions_openapi.spec import SpecReport


@pytest.mark.parametrize(
"facade", [api.get_openapi_json, api.get_openapi_yaml, api.generate_openapi_report]
)
def test_facade_parameters_match_spec_signature(facade: Callable[..., Any]) -> None:
# Given/When: inspect the explicit public signatures.
actual = inspect.signature(facade).parameters
expected = inspect.signature(api.generate_openapi_spec).parameters
# Then: order, defaults, annotations and positional compatibility agree.
assert actual == expected
assert all(p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD for p in actual.values())


@pytest.mark.parametrize(
"facade", [api.get_openapi_json, api.get_openapi_yaml, api.generate_openapi_report]
)
@pytest.mark.parametrize("positional", [False, True])
def test_facades_forward_every_distinct_argument(
facade: Callable[..., Any], positional: bool
) -> None:
# Given: distinct values exercise every forwarding slot, including adjacent booleans.
options = {
"title": "distinct title",
"version": "2.3",
"openapi_version": "3.2.0",
"description": "description",
"security_schemes": {"key": {"type": "apiKey"}},
"route_prefix": "/custom",
"strict": True,
"registry": OpenAPIRegistry(),
"hoist_flat_schemas": True,
"infer_auth_level": False,
"servers": [{"url": "https://example.test"}],
"contact": {"name": "contact"},
"license": {"name": "license"},
"external_docs": {"url": "https://example.test/docs"},
"tags": [{"name": "tag"}],
"app": None,
}
report_facade = facade is api.generate_openapi_report
target = "_generate_openapi_report" if report_facade else "generate_openapi_spec"
result: SpecReport | dict[str, Any] = SpecReport(spec={}, warnings=()) if report_facade else {}
# When: callers use either supported argument form.
with patch.object(api, target, return_value=result) as generate:
if positional:
facade(*options.values())
else:
facade(**options)
# Then: binding the underlying call recovers every input without reordering or omission.
signature = inspect.signature(api.generate_openapi_spec)
bound = signature.bind(*generate.call_args.args, **generate.call_args.kwargs)
expected = {k: v for k, v in options.items() if not report_facade or k != "app"}
assert bound.arguments == expected
assert generate.call_args.args == ()
Loading