{"record":{"id":"cb986c87173ae59a","repo":"block/buzz","slug":"database-url-has-path-segment","errorCode":null,"errorMessage":"database URL has path segment","messagePattern":"database URL has path segment","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/buzz-db/src/store/channel_members.rs","lineNumber":3135,"sourceCode":"    }\n\n    #[tokio::test]\n    #[ignore = \"requires Postgres\"]\n    async fn desired_schema_rejects_stale_legacy_roster_role() {\n        use nostr::{EventBuilder, Keys, Kind, Tag, Timestamp};\n\n        let admin = PgPool::connect(&admin_url().await)\n            .await\n            .expect(\"connect admin\");\n        let scratch_name = format!(\"schema_roster_role_{}\", Uuid::new_v4().simple());\n        sqlx::query(sqlx::AssertSqlSafe(format!(\n            \"CREATE DATABASE {scratch_name}\"\n        )))\n        .execute(&admin)\n        .await\n        .expect(\"create desired-schema scratch db\");\n        let base_url = admin_url().await;\n        let slash = base_url.rfind('/').expect(\"database URL has path segment\");\n        let scratch_url = format!(\"{}/{}\", &base_url[..slash], scratch_name);\n        let pool = PgPoolOptions::new()\n            .max_connections(1)\n            .connect(&scratch_url)\n            .await\n            .expect(\"connect desired-schema scratch db\");\n        sqlx::raw_sql(include_str!(\"../../../../schema/schema.sql\"))\n            .execute(&pool)\n            .await\n            .expect(\"apply desired-state schema\");\n\n        let db = Db::from_pool(pool.clone());\n        let community_uuid = Uuid::new_v4();\n        let community = CommunityId::from_uuid(community_uuid);\n        let channel = Uuid::new_v4();\n        let relay_keys = Keys::generate();\n        let owner_keys = Keys::generate();\n        let owner = owner_keys.public_key().to_bytes();","sourceCodeStart":3117,"sourceCodeEnd":3153,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/crates/buzz-db/src/store/channel_members.rs#L3117-L3153","documentation":"The test derives the scratch database URL by finding the last '/' in the admin URL with rfind('/') and expects a path segment (e.g. .../postgres) to strip and replace with the scratch name. The expect panics when the URL contains no '/' after the scheme section, i.e. the admin URL has no database path component, so splitting cannot produce a valid sibling URL.","triggerScenarios":"admin_url() returns a URL like postgres://user:pass@host:5432 (no trailing /postgres), an empty string, or a malformed URL without a path segment — then rfind('/') still matches the scheme's '//' but yields a prefix like 'postgres:' producing a broken scratch URL, or None-equivalent outcomes.","commonSituations":".env DATABASE_URL missing the /postgres database name; someone trimmed the URL for logging; URL written as postgres://host only; URL is postgresql:/// (empty db) or uses a unix socket form the naive rfind split mishandles.","solutions":["Set the admin DATABASE_URL to include a database path, e.g. postgres://user:pass@localhost:5432/postgres","Validate the URL with Url::parse and use url.set_path(scratch_name) instead of string rfind slicing","Log/redact the admin_url before running to confirm it has a path segment","If using a unix socket URL, restructure with the Url builder rather than rfind"],"exampleFix":"// before\nlet slash = base_url.rfind('/').expect(\"database URL has path segment\");\nlet scratch_url = format!(\"{}/{}\", &base_url[..slash], scratch_name);\n// after\nlet mut url = url::Url::parse(&base_url).expect(\"admin URL parses\");\nif url.path() == \"\" || url.path() == \"/\" { panic!(\"admin URL lacks database path segment: {base_url}\"); }\nurl.set_path(&scratch_name);\nlet scratch_url = url.to_string();","handlingStrategy":"validation","validationCode":"let url = url::Url::parse(&base_url)?;\nif url.path().is_empty() || url.path() == \"/\" { panic!(\"admin URL missing database path segment: {base_url}\"); }","typeGuard":"fn has_db_path(u: &str) -> bool {\n    url::Url::parse(u).map(|p| p.path().len() > 1).unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Always include /postgres (or a real db name) in admin DATABASE_URLs","Use url::Url::set_path instead of string rfind slicing","Validate env URLs at startup, not mid-test"],"tags":["config","url-parsing","env-config","test-infrastructure"],"backgroundTag":"missing-env-var","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-08-30T13:49:18.474Z","contentChangedAt":"2026-08-30T13:49:18.474Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}