Files
dify/api/tests/unit_tests/controllers/openapi/test_app_info.py
GareArc 86ba361ff1 feat(openapi): app reads + canonical pagination envelope
Read-side surface for difyctl describe / get / list:

- GET /openapi/v1/apps              paginated list (workspace_id required)
- GET /openapi/v1/apps/<id>         single app summary
- GET /openapi/v1/apps/<id>/parameters  port of service_api parameters
- GET /openapi/v1/apps/<id>/describe    merged { info, parameters }

All gated by validate_bearer(ACCEPT_USER_ANY) + require_scope(APPS_READ) +
require_workspace_member(ctx, tenant_id). SSO subjects 404 (account-only
helper account_or_404 deduplicates the guard across the four endpoints).

PaginationEnvelope[T] (page, limit, total, has_more, data) is the canonical
shape for every /openapi/v1/* list endpoint. has_more is computed by the
server from page * limit < total. /account/sessions migrates from the
legacy { sessions: [...] } shape to the envelope; integration tests assert
the legacy key is gone.
2026-05-05 18:08:12 -07:00

58 lines
1.5 KiB
Python

import builtins
from flask import Flask
from flask.views import MethodView
from controllers.openapi import bp as openapi_bp
from controllers.openapi.app_info import AppInfoApi
if not hasattr(builtins, "MethodView"):
builtins.MethodView = MethodView # type: ignore[attr-defined]
def _openapi_app() -> Flask:
app = Flask(__name__)
app.config["TESTING"] = True
app.register_blueprint(openapi_bp)
return app
def _rule(app: Flask, path: str):
return next(r for r in app.url_map.iter_rules() if r.rule == path)
def test_app_info_route_registered():
rules = {r.rule for r in _openapi_app().url_map.iter_rules()}
assert "/openapi/v1/apps/<string:app_id>/info" in rules
def test_app_info_dispatches_to_class():
app = _openapi_app()
rule = _rule(app, "/openapi/v1/apps/<string:app_id>/info")
assert app.view_functions[rule.endpoint].view_class is AppInfoApi
assert "GET" in rule.methods
def test_app_info_payload_shape():
from types import SimpleNamespace
from controllers.openapi.apps import app_info_payload
app_obj = SimpleNamespace(
id="app1",
name="X",
description="d",
mode="chat",
author_name="alice",
tags=[SimpleNamespace(name="prod")],
)
payload = app_info_payload(app_obj)
assert payload == {
"id": "app1",
"name": "X",
"description": "d",
"mode": "chat",
"author": "alice",
"tags": [{"name": "prod"}],
}