From 7b13303724691611d243c67793466cfe1c72fd81 Mon Sep 17 00:00:00 2001 From: Hare Date: Fri, 2 Oct 2026 14:34:51 +0900 Subject: [PATCH] fix: preserve repository key for backend validation --- crates/tui/src/backend_workspace_picker.rs | 24 +++++++++++- .../workspace-server/src/workspace_catalog.rs | 39 +++++++++++-------- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/crates/tui/src/backend_workspace_picker.rs b/crates/tui/src/backend_workspace_picker.rs index af917b668..b4b95755a 100644 --- a/crates/tui/src/backend_workspace_picker.rs +++ b/crates/tui/src/backend_workspace_picker.rs @@ -195,12 +195,14 @@ fn prompt_create_request_with_io( let Some(display_name) = prompt_line(input, output, "Workspace display name: ")? else { return Ok(None); }; + let display_name = display_name.trim().to_string(); if display_name.is_empty() { return Ok(None); } let Some(uri) = prompt_line(input, output, "Initial repository absolute path/URI: ")? else { return Ok(None); }; + let uri = uri.trim().to_string(); if uri.is_empty() { writeln!(output, "Repository path/URI is required.")?; return Ok(None); @@ -210,7 +212,7 @@ fn prompt_create_request_with_io( else { return Ok(None); }; - if repository_key.is_empty() { + if repository_key.trim().is_empty() { writeln!(output, "Repository key is required.")?; continue; } @@ -220,6 +222,7 @@ fn prompt_create_request_with_io( else { return Ok(None); }; + let default_ref = default_ref.trim().to_string(); let operation_key = format!( "tui-workspace-create-{}-{}", std::process::id(), @@ -250,7 +253,13 @@ fn prompt_line( if input.read_line(&mut value)? == 0 { return Ok(None); } - Ok(Some(value.trim().to_string())) + if value.ends_with('\n') { + value.pop(); + if value.ends_with('\r') { + value.pop(); + } + } + Ok(Some(value)) } #[cfg(test)] @@ -300,6 +309,17 @@ mod tests { assert_eq!(output.matches("Repository key (required): ").count(), 3); } + #[test] + fn workspace_creation_does_not_normalize_a_nonblank_repository_key() { + let (request, _) = prompt_create("Workspace\n/srv/repository\n platform \n\n"); + + assert_eq!( + request.unwrap().repository.repository_key, + " platform ", + "the Backend must validate the creator's exact Repository key" + ); + } + #[test] fn workspace_creation_accepts_explicit_main_repository_key() { let (request, _) = prompt_create("Workspace\n/srv/repository\nmain\n\n"); diff --git a/crates/workspace-server/src/workspace_catalog.rs b/crates/workspace-server/src/workspace_catalog.rs index ac98cae2e..d9fce280e 100644 --- a/crates/workspace-server/src/workspace_catalog.rs +++ b/crates/workspace-server/src/workspace_catalog.rs @@ -336,7 +336,7 @@ mod tests { } #[test] - fn create_rejects_blank_repository_key_without_persisting_a_workspace() { + fn create_rejects_noncanonical_repository_keys_without_persisting_a_workspace() { let store = Arc::new(SqliteWorkspaceStore::in_memory().unwrap()); let service = WorkspaceCatalogService::new( store.clone(), @@ -345,23 +345,30 @@ mod tests { ), ); let repository = git_repository(); - let request = WorkspaceCreateRequest { - operation_key: "blank-repository-key".to_string(), - display_name: "Workspace A".to_string(), - repository: InitialRepositoryIntent { - uri: repository.path().display().to_string(), - repository_key: " ".to_string(), - default_ref: None, - }, - }; + let owner_account_id = owner_account(store.as_ref()); - let error = service - .create(request, owner_account(store.as_ref())) - .unwrap_err() - .to_string(); + for (operation_key, repository_key) in [ + ("blank-repository-key", " "), + ("spaced-repository-key", " platform "), + ] { + let request = WorkspaceCreateRequest { + operation_key: operation_key.to_string(), + display_name: "Workspace A".to_string(), + repository: InitialRepositoryIntent { + uri: repository.path().display().to_string(), + repository_key: repository_key.to_string(), + default_ref: None, + }, + }; - assert!(error.contains("invalid Repository key"), "{error}"); - assert!(store.list_workspaces().unwrap().is_empty()); + let error = service + .create(request, owner_account_id.clone()) + .unwrap_err() + .to_string(); + + assert!(error.contains("invalid Repository key"), "{error}"); + assert!(store.list_workspaces().unwrap().is_empty()); + } } #[tokio::test]