use std::path::{Path, PathBuf};
use thiserror::Error;
use zesh_git::{Git, GitError};
use crate::fs::{FsError, FsOperations};
use zellij_rs::{Session, ZellijError, ZellijOperations, options::ZellijOptions};
use zox_rs::{ZoxideError, ZoxideOperations};
#[derive(Debug, Error)]
pub enum ConnectError {
#[error("Zellij error: {0}")]
Zellij(#[from] ZellijError),
#[error("Zoxide error: {0}")]
Zoxide(#[from] ZoxideError),
#[error("Filesystem error: {0}")]
Fs(#[from] FsError),
#[error("Git error: {0}")]
Git(#[from] GitError),
#[error("No matching sessions or directories found for '{0}'")]
NoMatch(String),
#[error("Other error: {0}")]
Other(String),
}
pub struct ConnectService<Z, X, F, G>
where
Z: ZellijOperations,
X: ZoxideOperations,
F: FsOperations,
G: Git,
{
zellij: Z,
zoxide: X,
fs: F,
git: G,
}
impl<Z, X, F, G> ConnectService<Z, X, F, G>
where
Z: ZellijOperations,
X: ZoxideOperations,
F: FsOperations,
G: Git,
{
pub fn new(zellij: Z, zoxide: X, fs: F, git: G) -> Self {
Self {
zellij,
zoxide,
fs,
git,
}
}
pub fn connect(&self, name: &str, options: &ZellijOptions) -> Result<(), ConnectError> {
match self.connect_to_session(name) {
Ok(_) => return Ok(()),
Err(ConnectError::NoMatch(_)) => {}
Err(e) => return Err(e),
}
if let Ok(()) = self.connect_to_directory(name, options) {
return Ok(());
}
self.connect_via_zoxide(name, options)
}
pub fn connect_to_session(&self, name: &str) -> Result<(), ConnectError> {
let sessions = self.zellij.list_sessions()?;
let session_match = sessions.iter().find(|s| s.name == name);
if let Some(session) = session_match {
self.zellij.attach_session(&session.name)?;
Ok(())
} else {
Err(ConnectError::NoMatch(name.to_string()))
}
}
pub fn connect_to_directory(
&self,
dir: &str,
options: &ZellijOptions,
) -> Result<(), ConnectError> {
let path = PathBuf::from(dir);
let (canon_path, _) = self.fs.validate_dir_path(&path)?;
let session_name = self.get_session_name_for_path(&canon_path)?;
let sessions = self.zellij.list_sessions()?;
let session_match = sessions.iter().find(|s| s.name == session_name);
if let Some(session) = session_match {
self.zellij.attach_session(&session.name)?;
} else {
self.fs.set_current_dir(&canon_path)?;
self.zellij.new_session(&session_name, options)?;
}
self.zoxide.add(&canon_path)?;
Ok(())
}
pub fn connect_via_zoxide(
&self,
query: &str,
options: &ZellijOptions,
) -> Result<(), ConnectError> {
let entries = self.zoxide.query(&[query])?;
if entries.is_empty() {
return Err(ConnectError::NoMatch(query.to_string()));
}
let best_match = &entries[0];
let path = &best_match.path;
let session_name = self.get_session_name_for_path(path)?;
let sessions = self.zellij.list_sessions()?;
if sessions.iter().any(|s| s.name == session_name) {
self.zellij.attach_session(&session_name)?;
return Ok(());
}
self.fs.set_current_dir(path)?;
self.zellij.new_session(&session_name, options)?;
self.zoxide.add(path)?;
Ok(())
}
pub fn list_sessions(&self) -> Result<Vec<Session>, ConnectError> {
Ok(self.zellij.list_sessions()?)
}
fn get_session_name_for_path(&self, path: &Path) -> Result<String, ConnectError> {
let path_str = path
.to_str()
.ok_or_else(|| ConnectError::Other("Invalid path".to_string()))?;
match self.git.show_top_level(path_str) {
Ok((true, git_root)) => {
let git_root_path = PathBuf::from(&git_root);
let git_root_name = self.fs.get_dir_name(&git_root_path)?;
if path == git_root_path {
return Ok(git_root_name);
}
match path.strip_prefix(&git_root_path) {
Ok(rel_path) => {
if rel_path == Path::new("") {
Ok(git_root_name)
} else {
Ok(format!("{}_{}", git_root_name, rel_path.display()))
}
}
Err(_) => Ok(self.fs.get_dir_name(path)?), }
}
Ok((false, _)) => {
Ok(self.fs.get_dir_name(path)?)
}
Err(e) => {
eprintln!("Git error: {}", e);
Ok(self.fs.get_dir_name(path)?)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fs::tests::MockFs;
use std::path::PathBuf;
use std::{collections::HashMap, path::Path};
use zellij_rs::{MockZellijClient, Session, ZellijError};
use zox_rs::{MockZoxideClient, ZoxideEntry, ZoxideError};
fn create_service(
zellij_sessions: Option<HashMap<String, bool>>,
zoxide_paths: Option<HashMap<PathBuf, f64>>,
fs_dirs: Option<Vec<(PathBuf, String)>>,
) -> ConnectService<MockZellijClient, MockZoxideClient, MockFs, TestGit> {
let zellij = if let Some(sessions) = zellij_sessions {
MockZellijClient::with_sessions(sessions)
} else {
MockZellijClient::new()
};
let zoxide = if let Some(paths) = zoxide_paths {
MockZoxideClient::with_paths(paths)
} else {
MockZoxideClient::new()
};
let fs = MockFs::new();
if let Some(dirs) = fs_dirs {
for (path, name) in dirs {
fs.with_directory(&path, &name);
}
}
ConnectService::new(zellij, zoxide, fs, TestGit::new(false, "./"))
}
struct FailingZellijClient;
impl ZellijOperations for FailingZellijClient {
fn list_sessions(&self) -> zellij_rs::ZellijResult<Vec<Session>> {
Err(ZellijError::CommandExecution("Command failed".to_string()))
}
fn attach_session(&self, _: &str) -> zellij_rs::ZellijResult<()> {
Err(ZellijError::CommandExecution("Command failed".to_string()))
}
fn new_session(&self, _: &str, _: &ZellijOptions) -> zellij_rs::ZellijResult<()> {
Err(ZellijError::CommandExecution("Command failed".to_string()))
}
fn kill_session(&self, _: &str) -> zellij_rs::ZellijResult<()> {
Err(ZellijError::CommandExecution("Command failed".to_string()))
}
fn list_tabs(&self) -> zellij_rs::ZellijResult<Vec<zellij_rs::Tab>> {
Err(ZellijError::CommandExecution("Command failed".to_string()))
}
fn new_tab(&self, _: Option<&str>) -> zellij_rs::ZellijResult<()> {
Err(ZellijError::CommandExecution("Command failed".to_string()))
}
fn rename_tab(&self, _: &str) -> zellij_rs::ZellijResult<()> {
Err(ZellijError::CommandExecution("Command failed".to_string()))
}
fn close_tab(&self) -> zellij_rs::ZellijResult<()> {
Err(ZellijError::CommandExecution("Command failed".to_string()))
}
fn run_command(&self, _: &str, _: &[&str]) -> zellij_rs::ZellijResult<()> {
Err(ZellijError::CommandExecution("Command failed".to_string()))
}
}
struct FailingZoxideClient;
impl ZoxideOperations for FailingZoxideClient {
fn add<P: AsRef<Path>>(&self, _: P) -> zox_rs::ZoxideResult<()> {
Err(ZoxideError::CommandExecution("Command failed".to_string()))
}
fn list(&self) -> zox_rs::ZoxideResult<Vec<ZoxideEntry>> {
Err(ZoxideError::CommandExecution("Command failed".to_string()))
}
fn query(&self, _: &[&str]) -> zox_rs::ZoxideResult<Vec<ZoxideEntry>> {
Err(ZoxideError::CommandExecution("Command failed".to_string()))
}
}
struct FailingFs;
impl FsOperations for FailingFs {
fn exists(&self, _: &Path) -> bool {
false
}
fn is_dir(&self, _: &Path) -> bool {
false
}
fn canonicalize(&self, _: &Path) -> Result<PathBuf, FsError> {
Err(FsError::Canonicalize(std::io::Error::other(
"Failed to canonicalize",
)))
}
fn get_dir_name(&self, path: &Path) -> Result<String, FsError> {
Err(FsError::NoDirectoryName(path.display().to_string()))
}
fn set_current_dir(&self, _: &Path) -> Result<(), FsError> {
Err(FsError::Other("Failed to set current dir".to_string()))
}
fn current_dir(&self) -> Result<PathBuf, FsError> {
Err(FsError::Other("Failed to get current dir".to_string()))
}
}
#[test]
fn test_connect_to_session_basic() {
let mut sessions = HashMap::new();
sessions.insert("test-session".to_string(), false);
sessions.insert("another-session".to_string(), true);
let service = create_service(Some(sessions), None, None);
let result = service.connect_to_session("test-session");
assert!(result.is_ok());
let updated_sessions = service.list_sessions().unwrap();
let session = updated_sessions
.iter()
.find(|s| s.name == "test-session")
.unwrap();
assert!(session.is_current);
let result = service.connect_to_session("non-existent");
assert!(result.is_err());
if let Err(ConnectError::NoMatch(name)) = result {
assert_eq!(name, "non-existent");
} else {
panic!("Expected ConnectError::NoMatch");
}
}
#[test]
fn test_connect_to_session_error_handling() {
let zellij = FailingZellijClient;
let zoxide = MockZoxideClient::new();
let fs = MockFs::new();
let service = ConnectService::new(zellij, zoxide, fs, TestGit::new(false, "./"));
let result = service.connect_to_session("any-session");
assert!(result.is_err());
if let Err(ConnectError::Zellij(_)) = result {
} else {
panic!("Expected ConnectError::Zellij");
}
}
#[test]
fn test_connect_to_directory_new_session() {
let dir_path = PathBuf::from("/mock/project");
let service = create_service(
None,
None,
Some(vec![(dir_path.clone(), "project".to_string())]),
);
let result = service.connect_to_directory("/mock/project", &ZellijOptions::default());
assert!(result.is_ok());
let sessions = service.list_sessions().unwrap();
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].name, "project");
assert!(sessions[0].is_current);
}
#[test]
fn test_connect_to_directory_existing_session() {
let dir_path = PathBuf::from("/mock/project");
let mut sessions = HashMap::new();
sessions.insert("project".to_string(), false);
let service = create_service(
Some(sessions),
None,
Some(vec![(dir_path.clone(), "project".to_string())]),
);
let result = service.connect_to_directory("/mock/project", &ZellijOptions::default());
assert!(result.is_ok());
let sessions = service.list_sessions().unwrap();
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].name, "project");
assert!(sessions[0].is_current);
}
#[test]
fn test_connect_to_directory_invalid_path() {
let service = create_service(None, None, None);
let result = service.connect_to_directory("/mock/non-existent", &ZellijOptions::default());
assert!(result.is_err());
if let Err(ConnectError::Fs(_)) = result {
} else {
panic!("Expected ConnectError::Fs");
}
let fs = MockFs::new();
fs.with_file(&PathBuf::from("/mock/file.txt"));
let service = ConnectService::new(
MockZellijClient::new(),
MockZoxideClient::new(),
fs,
TestGit::new(false, "./"),
);
let result = service.connect_to_directory("/mock/file.txt", &ZellijOptions::default());
assert!(result.is_err());
if let Err(ConnectError::Fs(_)) = result {
} else {
panic!("Expected ConnectError::Fs");
}
}
#[test]
fn test_connect_to_directory_error_handling() {
let zellij = MockZellijClient::new();
let zoxide = MockZoxideClient::new();
let fs = FailingFs;
let service = ConnectService::new(zellij, zoxide, fs, TestGit::new(false, "./"));
let result = service.connect_to_directory("/any/path", &ZellijOptions::default());
assert!(result.is_err());
if let Err(ConnectError::Fs(_)) = result {
} else {
panic!("Expected ConnectError::Fs");
}
let zellij = FailingZellijClient;
let zoxide = MockZoxideClient::new();
let fs = MockFs::new();
fs.with_directory(&PathBuf::from("/mock/project"), "project");
let service = ConnectService::new(zellij, zoxide, fs, TestGit::new(false, "./"));
let result = service.connect_to_directory("/mock/project", &ZellijOptions::default());
assert!(result.is_err());
if let Err(ConnectError::Zellij(_)) = result {
} else {
panic!("Expected ConnectError::Zellij");
}
let zellij = MockZellijClient::new();
let zoxide = FailingZoxideClient;
let fs = MockFs::new();
fs.with_directory(&PathBuf::from("/mock/project"), "project");
let service = ConnectService::new(zellij, zoxide, fs, TestGit::new(false, "./"));
let result = service.connect_to_directory("/mock/project", &ZellijOptions::default());
assert!(result.is_err());
if let Err(ConnectError::Zoxide(_)) = result {
} else {
panic!("Expected ConnectError::Zoxide");
}
}
#[test]
fn test_connect_to_directory_with_special_chars() {
let dir_path = PathBuf::from("/mock/special project-name!");
let service = create_service(
None,
None,
Some(vec![(
dir_path.clone(),
"special project-name!".to_string(),
)]),
);
let result =
service.connect_to_directory("/mock/special project-name!", &ZellijOptions::default());
assert!(result.is_ok());
let sessions = service.list_sessions().unwrap();
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].name, "special project-name!");
}
#[test]
fn test_connect_via_zoxide_single_match() {
let mut path_scores = HashMap::new();
path_scores.insert(PathBuf::from("/mock/zoxide-dir"), 10.0);
let service = create_service(
None,
Some(path_scores),
Some(vec![(
PathBuf::from("/mock/zoxide-dir"),
"zoxide-dir".to_string(),
)]),
);
let result = service.connect_via_zoxide("zoxide", &ZellijOptions::default());
assert!(result.is_ok());
let sessions = service.list_sessions().unwrap();
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].name, "zoxide-dir");
}
#[test]
fn test_connect_via_zoxide_multiple_matches() {
let mut path_scores = HashMap::new();
path_scores.insert(PathBuf::from("/mock/best-match"), 20.0);
path_scores.insert(PathBuf::from("/mock/second-match"), 10.0);
let service = create_service(
None,
Some(path_scores),
Some(vec![
(PathBuf::from("/mock/best-match"), "best-match".to_string()),
(
PathBuf::from("/mock/second-match"),
"second-match".to_string(),
),
]),
);
let result = service.connect_via_zoxide("match", &ZellijOptions::default());
assert!(result.is_ok());
let sessions = service.list_sessions().unwrap();
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].name, "best-match");
}
#[test]
fn test_connect_via_zoxide_existing_session() {
let mut path_scores = HashMap::new();
path_scores.insert(PathBuf::from("/mock/existing"), 10.0);
let mut sessions = HashMap::new();
sessions.insert("existing".to_string(), false);
let service = create_service(
Some(sessions),
Some(path_scores),
Some(vec![(
PathBuf::from("/mock/existing"),
"existing".to_string(),
)]),
);
let result = service.connect_via_zoxide("existing", &ZellijOptions::default());
assert!(result.is_ok());
let sessions = service.list_sessions().unwrap();
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].name, "existing");
assert!(sessions[0].is_current);
}
#[test]
fn test_connect_via_zoxide_no_matches() {
let service = create_service(None, None, None);
let result = service.connect_via_zoxide("non-existent", &ZellijOptions::default());
assert!(result.is_err());
if let Err(ConnectError::NoMatch(query)) = result {
assert_eq!(query, "non-existent");
} else {
panic!("Expected ConnectError::NoMatch");
}
}
#[test]
fn test_connect_via_zoxide_error_handling() {
let zellij = MockZellijClient::new();
let zoxide = FailingZoxideClient;
let fs = MockFs::new();
let service = ConnectService::new(zellij, zoxide, fs, TestGit::new(false, "./"));
let result = service.connect_via_zoxide("query", &ZellijOptions::default());
assert!(result.is_err());
if let Err(ConnectError::Zoxide(_)) = result {
} else {
panic!("Expected ConnectError::Zoxide");
}
}
#[test]
fn test_connect_success_scenarios() {
let mut sessions = HashMap::new();
sessions.insert("existing-session".to_string(), false);
let mut path_scores = HashMap::new();
path_scores.insert(PathBuf::from("/mock/zoxide-match"), 10.0);
let service = create_service(
Some(sessions),
Some(path_scores),
Some(vec![
(PathBuf::from("/mock/dir-path"), "dir-path".to_string()),
(
PathBuf::from("/mock/zoxide-match"),
"zoxide-match".to_string(),
),
]),
);
let result = service.connect("existing-session", &ZellijOptions::default());
assert!(result.is_ok());
let sessions = service.list_sessions().unwrap();
assert!(
sessions
.iter()
.any(|s| s.name == "existing-session" && s.is_current)
);
let result = service.connect("/mock/dir-path", &ZellijOptions::default());
assert!(result.is_ok());
let sessions = service.list_sessions().unwrap();
assert!(
sessions
.iter()
.any(|s| s.name == "dir-path" && s.is_current)
);
let result = service.connect("zoxide-match", &ZellijOptions::default());
assert!(result.is_ok());
let sessions = service.list_sessions().unwrap();
assert!(
sessions
.iter()
.any(|s| s.name == "zoxide-match" && s.is_current)
);
}
#[test]
fn test_connect_fallback_behavior() {
let service = create_service(
None,
None,
Some(vec![(
PathBuf::from("/mock/valid-dir"),
"valid-dir".to_string(),
)]),
);
let result = service.connect("/mock/valid-dir", &ZellijOptions::default());
assert!(result.is_ok());
let sessions = service.list_sessions().unwrap();
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].name, "valid-dir");
let mut path_scores = HashMap::new();
path_scores.insert(PathBuf::from("/mock/zoxide-path"), 10.0);
let service = create_service(
None,
Some(path_scores),
Some(vec![(
PathBuf::from("/mock/zoxide-path"),
"zoxide-path".to_string(),
)]),
);
let result = service.connect("zoxide", &ZellijOptions::default());
assert!(result.is_ok());
let sessions = service.list_sessions().unwrap();
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].name, "zoxide-path");
}
#[test]
fn test_connect_failure_cases() {
let service = create_service(None, None, None);
let result = service.connect("non-existent", &ZellijOptions::default());
assert!(result.is_err());
if let Err(ConnectError::NoMatch(name)) = result {
assert_eq!(name, "non-existent");
} else {
panic!("Expected ConnectError::NoMatch");
}
let zellij = FailingZellijClient;
let zoxide = MockZoxideClient::new();
let fs = MockFs::new();
let service = ConnectService::new(zellij, zoxide, fs, TestGit::new(false, "./"));
let result = service.connect("anything", &ZellijOptions::default());
assert!(result.is_err());
if let Err(ConnectError::Zellij(_)) = result {
} else {
panic!("Expected ConnectError::Zellij");
}
}
#[test]
fn test_connect_case_sensitivity() {
let mut sessions = HashMap::new();
sessions.insert("Case-Sensitive".to_string(), false);
let service = create_service(Some(sessions), None, None);
let result = service.connect("Case-Sensitive", &ZellijOptions::default());
assert!(result.is_ok());
let result = service.connect("case-sensitive", &ZellijOptions::default());
assert!(result.is_err());
}
#[test]
fn test_list_sessions_error_handling() {
let zellij = FailingZellijClient;
let zoxide = MockZoxideClient::new();
let fs = MockFs::new();
let service = ConnectService::new(zellij, zoxide, fs, TestGit::new(false, "./"));
let result = service.list_sessions();
assert!(result.is_err());
if let Err(ConnectError::Zellij(_)) = result {
} else {
panic!("Expected ConnectError::Zellij");
}
}
#[test]
fn test_complex_workflow() {
let mut sessions = HashMap::new();
sessions.insert("existing".to_string(), true);
let mut path_scores = HashMap::new();
path_scores.insert(PathBuf::from("/mock/project1"), 10.0);
path_scores.insert(PathBuf::from("/mock/project2"), 5.0);
let service = create_service(
Some(sessions.clone()),
Some(path_scores.clone()),
Some(vec![
(PathBuf::from("/mock/project1"), "project1".to_string()),
(PathBuf::from("/mock/project2"), "project2".to_string()),
(PathBuf::from("/mock/project3"), "project3".to_string()),
]),
);
let initial_sessions = service.list_sessions().unwrap();
assert_eq!(initial_sessions.len(), 1);
assert_eq!(initial_sessions[0].name, "existing");
let result = service.connect_to_directory("/mock/project3", &ZellijOptions::default());
assert!(result.is_ok());
let sessions_after_dir = service.list_sessions().unwrap();
assert_eq!(sessions_after_dir.len(), 2);
assert!(
sessions_after_dir
.iter()
.any(|s| s.name == "project3" && s.is_current)
);
assert!(
sessions_after_dir
.iter()
.any(|s| s.name == "existing" && !s.is_current)
);
let result = service.connect_via_zoxide("project1", &ZellijOptions::default());
assert!(result.is_ok());
let sessions_after_zoxide = service.list_sessions().unwrap();
assert_eq!(sessions_after_zoxide.len(), 3);
assert!(
sessions_after_zoxide
.iter()
.any(|s| s.name == "project1" && s.is_current)
);
let result = service.connect_to_session("existing");
assert!(result.is_ok());
let final_sessions = service.list_sessions().unwrap();
assert_eq!(final_sessions.len(), 3);
assert!(
final_sessions
.iter()
.any(|s| s.name == "existing" && s.is_current)
);
assert!(
final_sessions
.iter()
.any(|s| s.name == "project1" && !s.is_current)
);
assert!(
final_sessions
.iter()
.any(|s| s.name == "project3" && !s.is_current)
);
}
struct TestGit {
is_git_repo: bool,
git_root: String,
}
impl TestGit {
fn new(is_git_repo: bool, git_root: &str) -> Self {
Self {
is_git_repo,
git_root: git_root.to_string(),
}
}
}
impl Git for TestGit {
fn show_top_level(&self, _name: &str) -> Result<(bool, String), GitError> {
Ok((self.is_git_repo, self.git_root.clone()))
}
fn git_common_dir(&self, _name: &str) -> Result<(bool, String), GitError> {
Ok((self.is_git_repo, "/mock/repo/common-dir".to_string()))
}
fn clone(&self, _url: &str, _cmd_dir: &str, _dir: &str) -> Result<String, GitError> {
Ok("Mock clone successful".to_string())
}
}
fn create_service_with_git(
zellij_sessions: Option<HashMap<String, bool>>,
zoxide_paths: Option<HashMap<PathBuf, f64>>,
fs_dirs: Option<Vec<(PathBuf, String)>>,
is_git_repo: bool,
git_root: &str,
) -> ConnectService<MockZellijClient, MockZoxideClient, MockFs, TestGit> {
let zellij = if let Some(sessions) = zellij_sessions {
MockZellijClient::with_sessions(sessions)
} else {
MockZellijClient::new()
};
let zoxide = if let Some(paths) = zoxide_paths {
MockZoxideClient::with_paths(paths)
} else {
MockZoxideClient::new()
};
let fs = MockFs::new();
if let Some(dirs) = fs_dirs {
for (path, name) in dirs {
fs.with_directory(&path, &name);
}
}
let git = TestGit::new(is_git_repo, git_root);
ConnectService::new(zellij, zoxide, fs, git)
}
#[test]
fn test_get_session_name_for_git_repo() {
let git_root = PathBuf::from("/mock/foo");
let subdir = PathBuf::from("/mock/foo/bar");
let fs_dirs = vec![
(git_root.clone(), "foo".to_string()),
(subdir.clone(), "bar".to_string()),
];
let service = create_service_with_git(None, None, Some(fs_dirs), true, "/mock/foo");
let name = service.get_session_name_for_path(&git_root).unwrap();
assert_eq!(name, "foo");
let name = service.get_session_name_for_path(&subdir).unwrap();
assert_eq!(name, "foo_bar");
}
#[test]
fn test_get_session_name_for_non_git_path() {
let path = PathBuf::from("/mock/not-git");
let fs_dirs = vec![(path.clone(), "not-git".to_string())];
let service = create_service_with_git(None, None, Some(fs_dirs), false, "");
let name = service.get_session_name_for_path(&path).unwrap();
assert_eq!(name, "not-git");
}
#[test]
fn test_connect_to_git_directory() {
let git_root = PathBuf::from("/mock/project");
let subdir = PathBuf::from("/mock/project/feature");
let fs_dirs = vec![
(git_root.clone(), "project".to_string()),
(subdir.clone(), "feature".to_string()),
];
let service = create_service_with_git(None, None, Some(fs_dirs), true, "/mock/project");
let result =
service.connect_to_directory("/mock/project/feature", &ZellijOptions::default());
assert!(result.is_ok());
let sessions = service.list_sessions().unwrap();
assert_eq!(sessions.len(), 1);
assert_eq!(sessions[0].name, "project_feature");
assert!(sessions[0].is_current);
}
}