Skip to content

Commit 4abe669

Browse files
rustyconoverclaude
andcommitted
Change default HTTP prefix from "/vgi" to "" (root) and bump version to 0.1.27
Endpoints now live at the root by default (e.g. /bind, /init) instead of under /vgi. Users who need a prefix can still pass --prefix /vgi. - Update all default prefix values across server, client, CLI, and testing - Add prefix auto-detection: http_connect() and friends infer prefix from _SyncTestClient.prefix when not explicitly passed - Handle empty prefix in make_wsgi_app (landing page route, OAuth well-known) - Parameterize test fixtures with ["", "/vgi"] for dual-prefix coverage - Update all test URL references to match new defaults Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent eb4cb1e commit 4abe669

File tree

12 files changed

+230
-201
lines changed

12 files changed

+230
-201
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "vgi-rpc"
3-
version = "0.1.26"
3+
version = "0.1.27"
44
description = "Vector Gateway Interface - RPC framework based on Apache Arrow"
55
readme = "README.md"
66
requires-python = ">=3.13"

tests/test_bad_requests.py

Lines changed: 63 additions & 63 deletions
Large diffs are not rendered by default.

tests/test_bearer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def test_invalid_token_rejected_by_all(self) -> None:
292292
)
293293
# The HTTP layer should return 401
294294
resp = client.post(
295-
"/vgi/whoami",
295+
"/whoami",
296296
content=b"garbage",
297297
headers={"Content-Type": "application/octet-stream"},
298298
)

tests/test_http.py

Lines changed: 90 additions & 81 deletions
Large diffs are not rendered by default.

tests/test_http_retry.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ def test_no_config_no_retry(self, real_client: _SyncTestClient) -> None:
387387
wrapper = _TransientFailureClient(real_client, failure_status=502, failures=0)
388388
resp = _post_with_retry(
389389
wrapper, # type: ignore[arg-type]
390-
"/vgi/add",
390+
"/add",
391391
content=b"",
392392
headers={"Content-Type": "application/octet-stream"},
393393
config=None,
@@ -399,7 +399,7 @@ def test_retries_on_502(self, real_client: _SyncTestClient) -> None:
399399
wrapper = _TransientFailureClient(real_client, failure_status=502, failures=2)
400400
resp = _post_with_retry(
401401
wrapper, # type: ignore[arg-type]
402-
"/vgi/add",
402+
"/add",
403403
content=_make_add_request(),
404404
headers={"Content-Type": "application/vnd.apache.arrow.stream"},
405405
config=HttpRetryConfig(max_retries=3, backoff_base=0.001),
@@ -414,7 +414,7 @@ def test_exhausted_raises_transient_error(self, real_client: _SyncTestClient) ->
414414
with pytest.raises(HttpTransientError) as exc_info:
415415
_post_with_retry(
416416
wrapper, # type: ignore[arg-type]
417-
"/vgi/add",
417+
"/add",
418418
content=b"data",
419419
headers={"Content-Type": "application/octet-stream"},
420420
config=HttpRetryConfig(max_retries=2, backoff_base=0.001),
@@ -428,7 +428,7 @@ def test_no_retry_on_non_transient(self, real_client: _SyncTestClient) -> None:
428428
wrapper = _TransientFailureClient(real_client, failure_status=400, failures=5)
429429
resp = _post_with_retry(
430430
wrapper, # type: ignore[arg-type]
431-
"/vgi/add",
431+
"/add",
432432
content=b"data",
433433
headers={"Content-Type": "application/octet-stream"},
434434
config=HttpRetryConfig(max_retries=3, backoff_base=0.001),
@@ -443,7 +443,7 @@ def test_max_retries_zero(self, real_client: _SyncTestClient) -> None:
443443
with pytest.raises(HttpTransientError) as exc_info:
444444
_post_with_retry(
445445
wrapper, # type: ignore[arg-type]
446-
"/vgi/add",
446+
"/add",
447447
content=b"data",
448448
headers={"Content-Type": "application/octet-stream"},
449449
config=HttpRetryConfig(max_retries=0),
@@ -458,7 +458,7 @@ def test_retry_after_preserved_in_error(self, real_client: _SyncTestClient) -> N
458458
with pytest.raises(HttpTransientError) as exc_info:
459459
_post_with_retry(
460460
wrapper, # type: ignore[arg-type]
461-
"/vgi/add",
461+
"/add",
462462
content=b"data",
463463
headers={"Content-Type": "application/octet-stream"},
464464
config=HttpRetryConfig(max_retries=1, backoff_base=0.001),
@@ -473,7 +473,7 @@ def test_custom_retryable_codes(self, real_client: _SyncTestClient) -> None:
473473
# 500 is not in default retryable set — should NOT retry
474474
resp = _post_with_retry(
475475
wrapper, # type: ignore[arg-type]
476-
"/vgi/add",
476+
"/add",
477477
content=b"data",
478478
headers={"Content-Type": "application/octet-stream"},
479479
config=HttpRetryConfig(max_retries=3, backoff_base=0.001),
@@ -487,7 +487,7 @@ def test_custom_retryable_codes(self, real_client: _SyncTestClient) -> None:
487487
with pytest.raises(HttpTransientError):
488488
_post_with_retry(
489489
wrapper2, # type: ignore[arg-type]
490-
"/vgi/add",
490+
"/add",
491491
content=b"data",
492492
headers={"Content-Type": "application/octet-stream"},
493493
config=HttpRetryConfig(max_retries=2, retryable_status_codes=frozenset({500}), backoff_base=0.001),
@@ -501,7 +501,7 @@ def test_each_retryable_code(self, real_client: _SyncTestClient) -> None:
501501
wrapper = _TransientFailureClient(real_client, failure_status=code, failures=1)
502502
resp = _post_with_retry(
503503
wrapper, # type: ignore[arg-type]
504-
"/vgi/add",
504+
"/add",
505505
content=_make_add_request(),
506506
headers={"Content-Type": "application/vnd.apache.arrow.stream"},
507507
config=HttpRetryConfig(max_retries=3, backoff_base=0.001),
@@ -517,7 +517,7 @@ def test_backoff_delay_called(self, real_client: _SyncTestClient) -> None:
517517
with pytest.raises(HttpTransientError):
518518
_post_with_retry(
519519
wrapper, # type: ignore[arg-type]
520-
"/vgi/add",
520+
"/add",
521521
content=b"data",
522522
headers={"Content-Type": "application/octet-stream"},
523523
config=HttpRetryConfig(max_retries=2, backoff_base=1.0, backoff_max=100.0),
@@ -535,7 +535,7 @@ def test_connect_error_retried_and_succeeds(self, real_client: _SyncTestClient)
535535
wrapper = _ConnectionErrorClient(real_client, failures=2)
536536
resp = _post_with_retry(
537537
wrapper, # type: ignore[arg-type]
538-
"/vgi/add",
538+
"/add",
539539
content=_make_add_request(),
540540
headers={"Content-Type": "application/vnd.apache.arrow.stream"},
541541
config=HttpRetryConfig(max_retries=3, backoff_base=0.001),
@@ -550,7 +550,7 @@ def test_connect_error_exhausted_raises(self, real_client: _SyncTestClient) -> N
550550
with pytest.raises(httpx.ConnectError):
551551
_post_with_retry(
552552
wrapper, # type: ignore[arg-type]
553-
"/vgi/add",
553+
"/add",
554554
content=b"data",
555555
headers={"Content-Type": "application/octet-stream"},
556556
config=HttpRetryConfig(max_retries=2, backoff_base=0.001),
@@ -564,7 +564,7 @@ def test_connect_error_no_retry_when_disabled(self, real_client: _SyncTestClient
564564
with pytest.raises(httpx.ConnectError):
565565
_post_with_retry(
566566
wrapper, # type: ignore[arg-type]
567-
"/vgi/add",
567+
"/add",
568568
content=b"data",
569569
headers={"Content-Type": "application/octet-stream"},
570570
config=HttpRetryConfig(max_retries=3, retry_on_connection_error=False, backoff_base=0.001),
@@ -577,7 +577,7 @@ def test_timeout_error_retried_and_succeeds(self, real_client: _SyncTestClient)
577577
wrapper = _TimeoutErrorClient(real_client, failures=1)
578578
resp = _post_with_retry(
579579
wrapper, # type: ignore[arg-type]
580-
"/vgi/add",
580+
"/add",
581581
content=_make_add_request(),
582582
headers={"Content-Type": "application/vnd.apache.arrow.stream"},
583583
config=HttpRetryConfig(max_retries=3, backoff_base=0.001),
@@ -592,7 +592,7 @@ def test_timeout_no_retry_when_disabled(self, real_client: _SyncTestClient) -> N
592592
with pytest.raises(httpx.ReadTimeout):
593593
_post_with_retry(
594594
wrapper, # type: ignore[arg-type]
595-
"/vgi/add",
595+
"/add",
596596
content=b"data",
597597
headers={"Content-Type": "application/octet-stream"},
598598
config=HttpRetryConfig(max_retries=3, retry_on_connection_error=False, backoff_base=0.001),
@@ -609,7 +609,7 @@ def test_retries_on_503(self, real_client: _SyncTestClient) -> None:
609609
wrapper = _TransientFailureClient(real_client, failure_status=503, failures=1)
610610
resp = _options_with_retry(
611611
wrapper, # type: ignore[arg-type]
612-
"/vgi/__capabilities__",
612+
"/__capabilities__",
613613
config=HttpRetryConfig(max_retries=3, backoff_base=0.001),
614614
_sleep=lambda _: None,
615615
)
@@ -622,7 +622,7 @@ def test_exhausted_raises(self, real_client: _SyncTestClient) -> None:
622622
with pytest.raises(HttpTransientError) as exc_info:
623623
_options_with_retry(
624624
wrapper, # type: ignore[arg-type]
625-
"/vgi/__capabilities__",
625+
"/__capabilities__",
626626
config=HttpRetryConfig(max_retries=1, backoff_base=0.001),
627627
_sleep=lambda _: None,
628628
)

tests/test_oauth.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def test_well_known_returns_metadata_json(self) -> None:
161161
def test_well_known_path_variant(self) -> None:
162162
"""GET /.well-known/oauth-protected-resource/vgi works with prefix."""
163163
server = RpcServer(_EchoService, _EchoImpl())
164-
client = make_sync_client(server, signing_key=b"k", oauth_resource_metadata=_METADATA)
164+
client = make_sync_client(server, signing_key=b"k", prefix="/vgi", oauth_resource_metadata=_METADATA)
165165
resp = client.get("/.well-known/oauth-protected-resource/vgi")
166166
assert resp.status_code == 200
167167
body = json.loads(resp.content)
@@ -233,7 +233,7 @@ def test_401_includes_www_authenticate(self) -> None:
233233
)
234234
# POST without auth should get 401 with WWW-Authenticate
235235
resp = client.post(
236-
"/vgi/echo",
236+
"/echo",
237237
content=b"garbage",
238238
headers={"Content-Type": "application/octet-stream"},
239239
)
@@ -253,7 +253,7 @@ def test_401_no_www_authenticate_without_metadata(self) -> None:
253253
authenticate=auth_fn,
254254
)
255255
resp = client.post(
256-
"/vgi/echo",
256+
"/echo",
257257
content=b"garbage",
258258
headers={"Content-Type": "application/octet-stream"},
259259
)
@@ -343,7 +343,7 @@ def test_client_id_in_www_authenticate(self) -> None:
343343
oauth_resource_metadata=_METADATA_WITH_CLIENT_ID,
344344
)
345345
resp = client.post(
346-
"/vgi/echo",
346+
"/echo",
347347
content=b"garbage",
348348
headers={"Content-Type": "application/octet-stream"},
349349
)
@@ -363,7 +363,7 @@ def test_client_id_absent_from_www_authenticate(self) -> None:
363363
oauth_resource_metadata=_METADATA,
364364
)
365365
resp = client.post(
366-
"/vgi/echo",
366+
"/echo",
367367
content=b"garbage",
368368
headers={"Content-Type": "application/octet-stream"},
369369
)
@@ -428,7 +428,7 @@ def test_client_secret_in_www_authenticate(self) -> None:
428428
oauth_resource_metadata=_METADATA_WITH_CLIENT_SECRET,
429429
)
430430
resp = client.post(
431-
"/vgi/echo",
431+
"/echo",
432432
content=b"garbage",
433433
headers={"Content-Type": "application/octet-stream"},
434434
)
@@ -448,7 +448,7 @@ def test_client_secret_absent_from_www_authenticate(self) -> None:
448448
oauth_resource_metadata=_METADATA,
449449
)
450450
resp = client.post(
451-
"/vgi/echo",
451+
"/echo",
452452
content=b"garbage",
453453
headers={"Content-Type": "application/octet-stream"},
454454
)
@@ -503,7 +503,7 @@ def test_use_id_token_as_bearer_in_www_authenticate(self) -> None:
503503
oauth_resource_metadata=_METADATA_WITH_ID_TOKEN,
504504
)
505505
resp = client.post(
506-
"/vgi/echo",
506+
"/echo",
507507
content=b"garbage",
508508
headers={"Content-Type": "application/octet-stream"},
509509
)
@@ -523,7 +523,7 @@ def test_use_id_token_as_bearer_absent_from_www_authenticate(self) -> None:
523523
oauth_resource_metadata=_METADATA,
524524
)
525525
resp = client.post(
526-
"/vgi/echo",
526+
"/echo",
527527
content=b"garbage",
528528
headers={"Content-Type": "application/octet-stream"},
529529
)
@@ -590,7 +590,7 @@ def test_device_code_client_id_in_www_authenticate(self) -> None:
590590
oauth_resource_metadata=_METADATA_WITH_DEVICE_CODE_CLIENT_ID,
591591
)
592592
resp = client.post(
593-
"/vgi/echo",
593+
"/echo",
594594
content=b"garbage",
595595
headers={"Content-Type": "application/octet-stream"},
596596
)
@@ -610,7 +610,7 @@ def test_device_code_client_id_absent_from_www_authenticate(self) -> None:
610610
oauth_resource_metadata=_METADATA,
611611
)
612612
resp = client.post(
613-
"/vgi/echo",
613+
"/echo",
614614
content=b"garbage",
615615
headers={"Content-Type": "application/octet-stream"},
616616
)
@@ -679,7 +679,7 @@ def test_device_code_client_secret_in_www_authenticate(self) -> None:
679679
oauth_resource_metadata=_METADATA_WITH_DEVICE_CODE_CLIENT_SECRET,
680680
)
681681
resp = client.post(
682-
"/vgi/echo",
682+
"/echo",
683683
content=b"garbage",
684684
headers={"Content-Type": "application/octet-stream"},
685685
)
@@ -699,7 +699,7 @@ def test_device_code_client_secret_absent_from_www_authenticate(self) -> None:
699699
oauth_resource_metadata=_METADATA,
700700
)
701701
resp = client.post(
702-
"/vgi/echo",
702+
"/echo",
703703
content=b"garbage",
704704
headers={"Content-Type": "application/octet-stream"},
705705
)
@@ -744,7 +744,7 @@ def test_401_discovery_flow(self) -> None:
744744
)
745745
# Step 1: Make an unauthenticated request, get 401
746746
resp = client.post(
747-
"/vgi/echo",
747+
"/echo",
748748
content=b"garbage",
749749
headers={"Content-Type": "application/octet-stream"},
750750
)

vgi_rpc/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ class _CliConfig:
126126
url: str | None = None
127127
cmd: str | None = None
128128
unix: str | None = None
129-
prefix: str = "/vgi"
129+
prefix: str = ""
130130
format: OutputFormat = OutputFormat.auto
131131
output: str | None = None
132132
verbose: bool = False
@@ -203,7 +203,7 @@ def _main(
203203
url: Annotated[str | None, typer.Option("--url", "-u", help="HTTP base URL")] = None,
204204
cmd: Annotated[str | None, typer.Option("--cmd", "-c", help="Subprocess command")] = None,
205205
unix: Annotated[str | None, typer.Option("--unix", help="Unix domain socket path")] = None,
206-
prefix: Annotated[str, typer.Option("--prefix", "-p", help="URL path prefix")] = "/vgi",
206+
prefix: Annotated[str, typer.Option("--prefix", "-p", help="URL path prefix")] = "",
207207
fmt: Annotated[OutputFormat, typer.Option("--format", "-f", help="Output format")] = OutputFormat.auto,
208208
output: Annotated[str | None, typer.Option("--output", "-o", help="Output file path (default: stdout)")] = None,
209209
verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Show server log messages on stderr")] = False,

vgi_rpc/conformance/_test_cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def _build_parser() -> argparse.ArgumentParser:
292292
transport_excl.add_argument("--cmd", "-c", metavar="CMD", help="Subprocess command to test (pipe transport)")
293293
transport_excl.add_argument("--url", "-u", metavar="URL", help="HTTP base URL to test")
294294
transport_excl.add_argument("--unix", metavar="PATH", help="Unix domain socket path to test")
295-
transport_group.add_argument("--prefix", default="/vgi", help="URL path prefix (default: /vgi)")
295+
transport_group.add_argument("--prefix", default="", help="URL path prefix (default: none)")
296296
transport_group.add_argument(
297297
"--shm",
298298
type=int,

0 commit comments

Comments
 (0)