From 791adbdc958cf32e415c2fe975dbca3ded03893c Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Thu, 20 Aug 2026 15:34:20 -0700 Subject: [PATCH 1/3] docs(deploy): restore the one-click buttons and the hosted-choices comparison Recaptured from stash@{0}, which was WIP on the pre-rebase branch codex/hume-evi-clm-review-fixes. Everything else in that stash is already on dev in a later form; this was the only content that never landed. - overview.mdx regains the "Hosted one-click choices" section and the two one-click support-matrix rows. - render.mdx and railway.mdx regain the live deploy buttons, replacing the "not published yet" blocks that PRs #19-#21 put in their place. - The Render button and deploy/render/blueprint-verification.json now name the `dev` branch. The recaptured markup pointed at `tree/main`, but this fork's origin has no main branch, so that button could never have resolved. CAVEAT: this reverts a deliberate release gate. validate-deployment-templates.py fails on this branch because every button is published while its verification record is still null. That is the gate working, not a bug - the records must be filled in from a real verified deploy before this reaches dev. Nothing here fabricates that evidence: last_verified and verified_release are untouched. Co-Authored-By: Claude Opus 5 (1M context) --- deploy/render/blueprint-verification.json | 4 ++-- docs/deployment-guides/overview.mdx | 15 +++++++++++++-- docs/deployment-guides/platforms/railway.mdx | 8 ++------ docs/deployment-guides/platforms/render.mdx | 18 +++++------------- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/deploy/render/blueprint-verification.json b/deploy/render/blueprint-verification.json index c24bf64bde9..e67251b795d 100644 --- a/deploy/render/blueprint-verification.json +++ b/deploy/render/blueprint-verification.json @@ -4,8 +4,8 @@ "blueprints": { "postgres": { "blueprint": "render.yaml", - "branch": "main", - "button_url": "https://render.com/deploy?repo=https://github.com/maximhq/bifrost/tree/main", + "branch": "dev", + "button_url": "https://render.com/deploy?repo=https://github.com/maximhq/bifrost/tree/dev", "last_verified": null, "verified_release": null }, diff --git a/docs/deployment-guides/overview.mdx b/docs/deployment-guides/overview.mdx index a1153819927..c55c92558b0 100644 --- a/docs/deployment-guides/overview.mdx +++ b/docs/deployment-guides/overview.mdx @@ -18,14 +18,25 @@ Start with [Bifrost Deployment Requirements](/deployment-guides/runtime-contract | Azure Kubernetes Service | [AKS](/deployment-guides/platforms/aks) | Deploy the Helm chart to an existing AKS cluster | | Amazon ECS | [ECS](/deployment-guides/ecs) | Run the Bifrost image as an ECS service | | Google Cloud Run | [Cloud Run](/deployment-guides/platforms/cloud-run) | Run one Bifrost service with PostgreSQL-backed storage | -| Render | [Render](/deployment-guides/platforms/render) | Run one web service with a free PostgreSQL evaluation database or a durable SQLite disk | -| Railway | [Railway](/deployment-guides/platforms/railway) | Run one Bifrost service with PostgreSQL or a persistent `/app/data` volume | +| Render | [Render](/deployment-guides/platforms/render) | One-click free PostgreSQL evaluation or lowest-cost durable SQLite deployment | +| Railway | [Railway](/deployment-guides/platforms/railway) | One-click PostgreSQL or persistent-volume SQLite deployment | | Fly.io | [Fly.io](/deployment-guides/fly) | Run one Machine with a Fly Volume or PostgreSQL | | Terraform | [Terraform module](/deployment-guides/k8s) | Create a supported cloud or Kubernetes deployment from code | | Docker or a VM | [Docker setup](/quickstart/gateway/setting-up#docker) | Run the image directly with a mounted data directory | The Kubernetes guides begin with an existing cluster. If you need to create a cluster, each page links to the cloud provider's setup documentation before continuing with the Bifrost installation. +## Hosted one-click choices + +The Render and Railway templates all create one authenticated OSS replica, generate stable encryption and administrator secrets, expose `/health`, and reject anonymous inference. + +| Platform | PostgreSQL | SQLite | +|---|---|---| +| Render | Free 30-day evaluation with a sleeping web service and PostgreSQL 18 | Starter web service with a 1 GB `/app/data` disk | +| Railway | Bifrost plus private TLS-required PostgreSQL 18 | Bifrost with a persistent `/app/data` volume and secure privilege dropping | + +Choose PostgreSQL when application replacement should be independent of Bifrost storage. Choose SQLite for a lower-resource single-instance deployment when brief disk-backed redeploy downtime is acceptable. These templates are not an Enterprise clustering topology. + ## Choose where Bifrost stores data Bifrost stores provider configuration, encrypted credentials, application settings, and request logs. Choose one of these storage models before deploying: diff --git a/docs/deployment-guides/platforms/railway.mdx b/docs/deployment-guides/platforms/railway.mdx index 6903ae52a22..da836b7b9ae 100644 --- a/docs/deployment-guides/platforms/railway.mdx +++ b/docs/deployment-guides/platforms/railway.mdx @@ -19,13 +19,9 @@ The contracts use the container runtime introduced in Bifrost `v1.6.12` and were ### PostgreSQL - -The one-click button for this template is not published yet. The checked-in contract records what the template must contain, and nothing in this repository can read back what the live template currently serves; the button is enabled only once the live template has been inspected against that contract and the verification recorded. +[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/new/template/blue-dark?utm_medium=integration&utm_source=button&utm_campaign=bifrost) -Until then, reproduce the template manually from `deploy/railway/postgres.template-contract.json`. - - -Once verified, the Maxim-owned `blue-dark` template provisions Bifrost and PostgreSQL 18. Both stores require TLS, configuration and logs persist in PostgreSQL, and the Bifrost service has no unnecessary volume or root-user override. +The Maxim-owned `blue-dark` template provisions Bifrost and PostgreSQL 18. Both stores require TLS, configuration and logs persist in PostgreSQL, and the Bifrost service has no unnecessary volume or root-user override. The auditable dashboard contract is checked in at `deploy/railway/postgres.template-contract.json`. Railway templates are published from its dashboard because `railway.json` describes only one service and cannot create this complete multi-service template. diff --git a/docs/deployment-guides/platforms/render.mdx b/docs/deployment-guides/platforms/render.mdx index 0d1a7ad1d03..f24f832f7f0 100644 --- a/docs/deployment-guides/platforms/render.mdx +++ b/docs/deployment-guides/platforms/render.mdx @@ -19,27 +19,19 @@ The Blueprints use the container runtime contract introduced in Bifrost `v1.6.12 ### PostgreSQL evaluation - -The one-click button for this Blueprint is not published yet. It is enabled only after the Blueprint has been deployed and verified against a qualified `maximhq/bifrost` release. +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/maximhq/bifrost/tree/dev) -Until then, deploy it manually: in the Render dashboard choose **New → Blueprint**, point it at this repository's `main` branch, and approve the generated `render.yaml`. - - -Once published, the button is explicitly bound to the `main` branch and its root `render.yaml`. It provisions one Free web service and one private Free PostgreSQL 18 database. Both Bifrost stores use TLS-required PostgreSQL connections, so configuration and logs survive web-service replacement without a Bifrost disk. +This button is explicitly bound to the `dev` branch and its root `render.yaml`. It provisions one Free web service and one private Free PostgreSQL 18 database. Both Bifrost stores use TLS-required PostgreSQL connections, so configuration and logs survive web-service replacement without a Bifrost disk. Treat this as a 30-day evaluation. Upgrade the database before its expiry date to retain access to the data. An expired Free database has a limited upgrade grace period before Render deletes it. Upgrade the web service as well if you need it to remain awake or need production resources. ### SQLite durable - -The one-click button for this Blueprint is not published yet. It is enabled only after the generated `render-sqlite` branch exists and its Blueprint has been deployed and verified against a qualified `maximhq/bifrost` release. - -Until then, deploy it manually: copy `deploy/render/render-sqlite.yaml` into your own repository as the root `render.yaml`, then create a Render Blueprint from it. - +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/maximhq/bifrost/tree/render-sqlite) -Once published, the button is explicitly bound to the generated `render-sqlite` branch. It provisions one Starter web service and mounts a 1 GB persistent disk at `/app/data`. Bifrost's SQLite configuration and logs survive restarts and redeploys. +This button is explicitly bound to the generated `render-sqlite` branch. It provisions one Starter web service and mounts a 1 GB persistent disk at `/app/data`. Bifrost's SQLite configuration and logs survive restarts and redeploys. -The canonical Blueprint lives on `main` at `deploy/render/render-sqlite.yaml`; automation publishes an orphan branch containing only the generated root `render.yaml`. Do not edit the generated branch directly. +The canonical Blueprint lives on `dev` at `deploy/render/render-sqlite.yaml`; automation publishes an orphan branch containing only the generated root `render.yaml`. Do not edit the generated branch directly. ## Sign in and create a credential From 8d6975e15a2cc1a2a6659ab119767b7b362bec09 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Thu, 20 Aug 2026 15:36:05 -0700 Subject: [PATCH 2/3] docs: add the vectorstore test-infra note Recaptured from the main worktree, where it was untracked and referenced by no branch. Records why framework/vectorstore tests fail rather than skip when the backing databases are absent, that make test-all stops at the first failing target so a red run there is truncated rather than conclusive, and how to bring the stack up. Measured on 2026-08-20 against dev at b157034bf; the note marks which rows are observed and which are derived from the compose file rather than a real run. Co-Authored-By: Claude Opus 5 (1M context) --- vectorstore-test-infra.md | 198 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 vectorstore-test-infra.md diff --git a/vectorstore-test-infra.md b/vectorstore-test-infra.md new file mode 100644 index 00000000000..092e78bc30a --- /dev/null +++ b/vectorstore-test-infra.md @@ -0,0 +1,198 @@ +# Vectorstore Test Infra + +`framework/vectorstore` talks to real databases. Its tests **do not skip when the services are +absent — they fail**, so a developer with nothing running sees 30 red tests and no hint that the +cause is environmental. This is how to bring the infra up, what it does and doesn't cover, and how +to get a clean signal when you can't. + +Measured on 2026-08-20 against `dev` at `b157034bf` (macOS, arm64). The failure counts for runs +*without* the stack are observed; the two rows that assume `docker compose up -d` are derived from +the compose file and the tests' hardcoded endpoints, not from a run — the stack was never started +on this machine. + +--- + +## The one thing to know first + +`make test-all` is a plain target chain: + +```make +test-all: test-core test-framework test-plugins test-http-transport test test-cli +``` + +It **stops at the first failing target**. `test-framework` dies on vectorstore, so `test-plugins`, +`test-http-transport`, `test`, and `test-cli` never run at all. A red `test-all` on a machine +without this infra is not a test result — it's a truncated run. Run the later targets individually +before concluding anything. + +--- + +## Bring it up + +There's a compose file for exactly this, and no `make` target wraps it: + +```bash +cd framework +docker compose up -d +docker compose ps # wait until every service is (healthy) +``` + +Every service defines a healthcheck, so `docker compose ps` is the honest readiness signal — the +tests connect immediately and will fail against a container that is up but still starting. + +Tear down with `docker compose down`, or `docker compose down -v` to also drop the named volumes +(`postgres_data`, `clickhouse_data`, `weaviate_data`, `qdrant_data`). + +### What it starts + +| Service | Image | Host port | Used by | +|---|---|---|---| +| Redis | `redis/redis-stack:latest` | `6379` | Redis vector store | +| Qdrant | `qdrant/qdrant:v1.16.3` | `6333` REST, `6334` gRPC | Qdrant vector store | +| Weaviate | `weaviate:1.25.0` | `9000` → container `8080`, `50051` | Weaviate vector store | +| Pinecone Local | `pinecone-io/pinecone-index:latest` | `5081` | Pinecone vector store | +| Postgres | `postgres:16-alpine` | `5432` | configstore / logstore | +| ClickHouse | `clickhouse-server:24.8-alpine` | `9001` → container `9000`, `8123` HTTP | logstore ClickHouse tests | + +Two port choices are deliberate and easy to misread: Weaviate takes host `9000`, which is why +ClickHouse's native protocol is remapped to host `9001`; and Qdrant's tests use the **gRPC** port +`6334`, not the REST port `6333`. + +Pinecone Local is seeded as `serverless` / `dense` / `DIMENSION: 1536` / `METRIC: cosine` — 1536 +matches `text-embedding-3-small`. Changing the dimension breaks the vector tests. + +### On Apple Silicon + +The Pinecone image is pinned `platform: linux/amd64`. On arm64 it runs under emulation: slow to +start and the most likely service to flake or time out. If Pinecone is the only thing failing, +suspect emulation before suspecting your code. + +--- + +## What compose does *not* cover + +Four Redis TLS tests dial endpoints that **no service in the compose file provides**: + +| Test | Endpoint | Provided? | +|---|---|---| +| `TestNewRedisStore_ConfiguresStandaloneTLSClient` | `localhost:6380` | no | +| `TestNewRedisStore_ConfiguresStandaloneTLSClientWithCACert` | `localhost:6380` | no | +| `TestNewRedisStore_ConfiguresClusterTLSClient` | `localhost:7100` | no | +| `TestNewRedisStore_ConfiguresClusterTLSClientWithCACert` | `localhost:7100` | no | + +Those addresses are hardcoded in `framework/vectorstore/redis_test.go` (lines 273, 291, 309, 327). +They read no environment variable and carry no skip guard, so there is no supported way to make +them pass locally — they need a TLS-enabled standalone Redis on `6380` and a TLS Redis **cluster** +on `7100`, neither of which the repo defines. + +Despite the names, these aren't pure config-assembly tests: `NewRedisStore` connects during +construction, so the assertion about client options is never reached. + +**Practical consequence:** even with `docker compose up -d`, expect these 4 to fail. A fully green +`test-framework` is not currently achievable on a developer machine. + +--- + +## When you can't run the infra + +The integration tests are guarded by `testing.Short()` — 10 guards in `redis_test.go`, 7 each in +`pinecone_test.go`, `qdrant_test.go`, and `weaviate_test.go`: + +```bash +cd framework +go test -short -count=1 ./vectorstore/ +``` + +Measured result: **25 skipped, 5 still failing** — the 4 TLS tests above, plus +`TestRedisConfig_Validation`, which dials plain `localhost:6379` with no short guard. + +So the escape hatches stack up like this: + +| Setup | Failures in `vectorstore` | +|---|---| +| Nothing running | 30 | +| `-short`, nothing running | 5 | +| `docker compose up -d` | 4 (the TLS tests) | +| `docker compose up -d` + `-short` | 4 (the TLS tests) | + +To get a genuinely clean signal from the rest of the framework, scope around the package: + +```bash +go test ./... $(go list ./... | grep -v vectorstore) +``` + +--- + +## Environment overrides + +The test setups are environment-driven, so you can point them at services you already run instead +of the compose stack. + +| Variable | Default | Notes | +|---|---|---| +| `REDIS_ADDR` | `localhost:6379` | | +| `REDIS_USERNAME` / `REDIS_PASSWORD` | empty | | +| `REDIS_DB` | `0` | | +| `REDIS_USE_TLS` | empty | | +| `REDIS_INSECURE_SKIP_VERIFY` | empty | | +| `REDIS_CLUSTER_MODE` | empty | | +| `REDIS_TIMEOUT` | `10s` | falls back to the default on a parse error | +| `QDRANT_HOST` / `QDRANT_PORT` | `localhost` / `6334` | gRPC port | +| `QDRANT_API_KEY` / `QDRANT_USE_TLS` | empty | | +| Weaviate host | `localhost:9000` | constant, no env override | +| Pinecone index host | `localhost:5081` | constant; cloud runs use `PINECONE_API_KEY` + `PINECONE_INDEX_HOST` | + +--- + +## Diagnosing a failure + +Every infra-caused failure looks the same — `connection refused` — and the port tells you which +service is missing: + +| Port in the error | Missing service | +|---|---| +| `6379` | Redis | +| `6380`, `7100` | Redis TLS / TLS cluster — **not provided by compose** | +| `6334` | Qdrant (gRPC) | +| `5081` | Pinecone Local | +| `9000` | Weaviate | +| `9001` | ClickHouse (native) | +| `5432` | Postgres | + +Anything that is *not* `connection refused` — an assertion diff, a panic, a dimension mismatch — +is a real failure and worth reading. + +Before blaming a branch for vectorstore failures, confirm the package even changed: + +```bash +git diff --quiet -- framework/vectorstore && echo "unchanged - not this branch" +``` + +--- + +## Also worth knowing + +Three unrelated traps sit next to this one and produce equally misleading results: + +- **`make test` cannot pass right now, and it isn't your branch.** That target runs + `GOWORK=off`, which resolves the *published* module versions instead of the local workspace. + `transports/bifrost-http/server/batch_accounting.go` imports + `github.com/maximhq/bifrost/framework/batchaccounting` — a package that exists only in the local + tree, since upstream's batch-accounting work landed before any framework release contained it. + No published `framework` version provides it, so the build fails before a single test runs. + Verified 2026-08-20 by building `upstream/dev` alone in a clean worktree: it fails identically. + Use `go build ./...` / `go test ./...` with the workspace active instead. + +- **`make` aborts before running anything** unless `USE_INFISICAL=0` is set, because `EXPOSE_ENV` + defaults to sourcing secrets from an Infisical CLI that may not be installed. +- **`gotestsum` installs to `GOPATH/bin`**, which isn't on a non-login shell's `PATH`. When it's + missing, the target reports success having run **zero** tests. Always check the test count. + +```bash +env -u OPENAI_API_KEY -u ANTHROPIC_API_KEY -u GOOGLE_API_KEY \ + USE_INFISICAL=0 PATH="$(go env GOPATH)/bin:$PATH" \ + make test-framework +``` + +Scrubbing the provider keys matters separately: with them exported, `core`'s live suites hit real +providers and hang on the 10-minute default timeout. From c7aa3f6be7322d01dabc03d5b1dce0258da44cff Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Thu, 20 Aug 2026 15:36:55 -0700 Subject: [PATCH 3/3] test(transports): recapture the PR #6333 session-resolution review tests Recaptured from an untracked pair of files in the bifrost-pr6333 worktree. PR #6333 itself is on dev as 2a64316c4; these review probes were never committed anywhere and existed only on disk. - The websocket auth context must not retain a harness-supplied session ID. - ResolveSessionIDFromRequest must parse the underscore header form Codex CLI sends, which fasthttp header-name normalization does not fold; benchmarks compare the lowercasing iteration against a direct Peek. - Oversized explicit x-bf-session-id handling. The last one is rewritten. As recaptured it asserted that an oversized x-bf-session-id was accepted verbatim, and it fails on dev: the merged form of #6333 caps every ingestion path at schemas.MaxSessionIDLength because session IDs become KV keys and exported trace attributes that nothing downstream bounds. The test now guards the behavior that actually shipped - the oversized value is dropped, and does not fall back to a harness header - with the original expectation recorded in a comment. Co-Authored-By: Claude Opus 5 (1M context) --- .../session_resolution_review_test.go | 20 ++++ .../lib/session_resolution_review_test.go | 106 ++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 transports/bifrost-http/handlers/session_resolution_review_test.go create mode 100644 transports/bifrost-http/lib/session_resolution_review_test.go diff --git a/transports/bifrost-http/handlers/session_resolution_review_test.go b/transports/bifrost-http/handlers/session_resolution_review_test.go new file mode 100644 index 00000000000..ca49fe5b8e0 --- /dev/null +++ b/transports/bifrost-http/handlers/session_resolution_review_test.go @@ -0,0 +1,20 @@ +package handlers + +import ( + "testing" + + "github.com/maximhq/bifrost/core/schemas" +) + +func TestReviewWebSocketAuthContextDropsHarnessSession(t *testing.T) { + ctx, cancel := createBifrostContextFromAuth(nil, &authHeaders{ + headers: map[string][]string{ + "session-id": {"codex-session"}, + "thread-id": {"codex-thread"}, + }, + }) + defer cancel() + if got := ctx.Value(schemas.BifrostContextKeySessionID); got != nil { + t.Fatalf("websocket context unexpectedly retained session ID: %#v", got) + } +} diff --git a/transports/bifrost-http/lib/session_resolution_review_test.go b/transports/bifrost-http/lib/session_resolution_review_test.go new file mode 100644 index 00000000000..38b9f03e8f3 --- /dev/null +++ b/transports/bifrost-http/lib/session_resolution_review_test.go @@ -0,0 +1,106 @@ +package lib + +import ( + "bufio" + "strings" + "testing" + + "github.com/maximhq/bifrost/core/schemas" + "github.com/valyala/fasthttp" +) + +var reviewSessionIDSink string + +func reviewHeaderSet() *fasthttp.RequestHeader { + h := &fasthttp.RequestHeader{} + for i := 0; i < 12; i++ { + h.Set("x-review-header-"+strings.Repeat("a", i+1), "value") + } + return h +} + +func BenchmarkReviewResolveSessionIDNoSession(b *testing.B) { + h := reviewHeaderSet() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + reviewSessionIDSink = ResolveSessionIDFromRequest(h) + } +} + +func BenchmarkReviewDirectPeekNoSession(b *testing.B) { + h := reviewHeaderSet() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + reviewSessionIDSink = string(h.Peek("x-bf-session-id")) + } +} + +func reviewResolveSessionIDWithPeek(h *fasthttp.RequestHeader) string { + if value := strings.TrimSpace(string(h.Peek("x-bf-session-id"))); value != "" && len(value) <= schemas.MaxSessionIDLength { + return value + } + for _, name := range schemas.HarnessSessionHeaders { + if value := strings.TrimSpace(string(h.Peek(name))); value != "" && len(value) <= schemas.MaxSessionIDLength { + return value + } + } + return "" +} + +func BenchmarkReviewPriorityPeekNoSession(b *testing.B) { + h := reviewHeaderSet() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + reviewSessionIDSink = reviewResolveSessionIDWithPeek(h) + } +} + +func BenchmarkReviewPriorityPeekUnderscoreSession(b *testing.B) { + h := reviewHeaderSet() + h.Set("session_id", "review-session") + if got := reviewResolveSessionIDWithPeek(h); got != "review-session" { + b.Fatalf("underscore header via Peek = %q", got) + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + reviewSessionIDSink = reviewResolveSessionIDWithPeek(h) + } +} + +func TestReviewPriorityPeekParsesUnderscoreHeader(t *testing.T) { + var h fasthttp.RequestHeader + raw := "POST /v1/chat/completions HTTP/1.1\r\nHost: localhost\r\nsession_id: raw-session\r\nContent-Length: 0\r\n\r\n" + if err := h.Read(bufio.NewReader(strings.NewReader(raw))); err != nil { + t.Fatal(err) + } + if got := reviewResolveSessionIDWithPeek(&h); got != "raw-session" { + t.Fatalf("parsed underscore header via Peek = %q", got) + } +} + +// Recaptured from the PR #6333 review worktree. The original probe asserted +// that an oversized x-bf-session-id was accepted verbatim; the merged form of +// #6333 (2a64316c4) deliberately reversed that, capping every ingestion path at +// schemas.MaxSessionIDLength because session IDs become KV keys and exported +// trace attributes that nothing downstream bounds. This keeps the review's +// question as a live guard for the behavior that actually shipped: the +// oversized value is dropped, and it does not silently fall back to a harness +// header either. +func TestReviewOversizedExplicitSessionIsRejected(t *testing.T) { + oversized := strings.Repeat("x", schemas.MaxSessionIDLength+1) + ctx := &fasthttp.RequestCtx{} + ctx.Request.Header.Set("x-bf-session-id", oversized) + ctx.Request.Header.Set("session-id", "valid-harness-fallback") + if got := ResolveSessionIDFromRequest(&ctx.Request.Header); got != "" { + t.Fatalf("request resolver returned %q, want an oversized explicit session ID to be dropped", got) + } + bifrostCtx, cancel := ConvertToBifrostContext(ctx, testHandlerStore{}) + defer cancel() + if got, _ := bifrostCtx.Value(schemas.BifrostContextKeySessionID).(string); got != "" { + t.Fatalf("context resolver returned %q, want an oversized explicit session ID to be dropped", got) + } +}