Skip to content

Commit 2400c77

Browse files
authored
Merge pull request #2258 from DeusData/distill/1245-websocket-route-suffixes
fix(routes): register FastAPI and Starlette WebSocket decorators
2 parents 97ebf18 + 7ac6396 commit 2400c77

2 files changed

Lines changed: 56 additions & 0 deletions

File tree

‎internal/cbm/service_patterns.c‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,11 @@ static const method_suffix_t route_reg_suffixes[] = {
577577
{".add_route", "ANY"},
578578
{".add_api_route", "ANY"},
579579
{".add_api_websocket_route", "ANY"},
580+
/* FastAPI/Starlette WebSocket decorators (`@router.websocket('/ws')`,
581+
* `@app.websocket_route('/events')`): not an HTTP verb, so ANY — mirrors
582+
* .add_api_websocket_route above (#1245). */
583+
{".websocket", "ANY"},
584+
{".websocket_route", "ANY"},
580585
{NULL, NULL},
581586
};
582587

‎tests/test_pipeline.c‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8230,6 +8230,56 @@ TEST(pipeline_capitalized_axios_import_is_http_client_sequential) {
82308230
return capitalized_axios_case(0);
82318231
}
82328232

8233+
/* FastAPI/Starlette WebSocket route registration (distilled from #1245). The
8234+
* route-registration suffix table knew `.add_api_websocket_route` but not the
8235+
* decorator forms `.websocket` / `.websocket_route`, so a WebSocket endpoint
8236+
* registered via `@router.websocket('/ws')` produced no Route node at all —
8237+
* the endpoint was invisible to the graph. `@app.get('/health')` is the
8238+
* positive control (already a route suffix). Fewer than 50 files exercises
8239+
* pass_calls.c; the same suffix table drives the parallel resolver. */
8240+
TEST(pipeline_python_websocket_decorators_register_routes) {
8241+
char tmp[256];
8242+
snprintf(tmp, sizeof(tmp), "/tmp/cbm_py_ws_routes_XXXXXX");
8243+
if (!cbm_mkdtemp(tmp)) {
8244+
FAIL("tmpdir");
8245+
}
8246+
8247+
write_temp_file(tmp, "app.py",
8248+
"from fastapi import APIRouter, FastAPI, WebSocket\n\n"
8249+
"app = FastAPI()\n"
8250+
"router = APIRouter()\n\n"
8251+
"@app.get('/health')\n"
8252+
"def health():\n"
8253+
" return {'ok': True}\n\n"
8254+
"@router.websocket('/ws')\n"
8255+
"async def ws_endpoint(websocket: WebSocket):\n"
8256+
" await websocket.accept()\n\n"
8257+
"@app.websocket_route('/events')\n"
8258+
"async def events_endpoint(websocket: WebSocket):\n"
8259+
" await websocket.accept()\n");
8260+
8261+
char db_path[512];
8262+
snprintf(db_path, sizeof(db_path), "%s/py_ws.db", tmp);
8263+
cbm_pipeline_t *p = cbm_pipeline_new(tmp, db_path, CBM_MODE_FULL);
8264+
ASSERT_NOT_NULL(p);
8265+
ASSERT_EQ(cbm_pipeline_run(p), 0);
8266+
const char *project = cbm_pipeline_project_name(p);
8267+
8268+
cbm_store_t *s = cbm_store_open_path(db_path);
8269+
ASSERT_NOT_NULL(s);
8270+
8271+
/* Positive control: the HTTP decorator mints its Route node. */
8272+
ASSERT_GTE(count_nodes_named(s, project, "/health"), 1);
8273+
/* The two WebSocket decorator forms must mint Route nodes too. */
8274+
ASSERT_GTE(count_nodes_named(s, project, "/ws"), 1);
8275+
ASSERT_GTE(count_nodes_named(s, project, "/events"), 1);
8276+
8277+
cbm_store_close(s);
8278+
cbm_pipeline_free(p);
8279+
th_rmtree(tmp);
8280+
PASS();
8281+
}
8282+
82338283
/* Python bare-call local-binding suppression, sequential path. The bare-call
82348284
* counterpart of the receiver guard above: `run` is a PARAMETER, so `run()`
82358285
* cannot be the module-level `run` and must not bind SatoriLive.run.
@@ -18410,6 +18460,7 @@ SUITE(pipeline) {
1841018460
RUN_TEST(pipeline_tsjs_receiver_parallel_keeps_service_edges);
1841118461
RUN_TEST(pipeline_capitalized_axios_import_is_http_client);
1841218462
RUN_TEST(pipeline_capitalized_axios_import_is_http_client_sequential);
18463+
RUN_TEST(pipeline_python_websocket_decorators_register_routes);
1841318464
RUN_TEST(pipeline_python_receiver_parallel_suppresses_weak_method_edges);
1841418465
RUN_TEST(pipeline_python_bare_local_binding_suppresses_weak_edge);
1841518466
RUN_TEST(pipeline_python_bare_local_binding_parallel_suppresses_weak_edge);

0 commit comments

Comments
 (0)