fake_git_repo.rs

  1use fs::{FakeFs, Fs};
  2use gpui::{BackgroundExecutor, TestAppContext};
  3use serde_json::json;
  4use std::path::{Path, PathBuf};
  5use util::path;
  6
  7#[gpui::test]
  8async fn test_fake_worktree_lifecycle(cx: &mut TestAppContext) {
  9    let fs = FakeFs::new(cx.executor());
 10    fs.insert_tree("/project", json!({".git": {}, "file.txt": "content"}))
 11        .await;
 12    let repo = fs
 13        .open_repo(Path::new("/project/.git"), None)
 14        .expect("should open fake repo");
 15
 16    // Initially only the main worktree exists
 17    let worktrees = repo.worktrees().await.unwrap();
 18    assert_eq!(worktrees.len(), 1);
 19    assert_eq!(worktrees[0].path, PathBuf::from("/project"));
 20
 21    fs.create_dir("/my-worktrees".as_ref()).await.unwrap();
 22    let worktrees_dir = Path::new("/my-worktrees");
 23
 24    // Create a worktree
 25    let worktree_1_dir = worktrees_dir.join("feature-branch");
 26    repo.create_worktree(
 27        "feature-branch".to_string(),
 28        worktree_1_dir.clone(),
 29        Some("abc123".to_string()),
 30    )
 31    .await
 32    .unwrap();
 33
 34    // List worktrees — should have main + one created
 35    let worktrees = repo.worktrees().await.unwrap();
 36    assert_eq!(worktrees.len(), 2);
 37    assert_eq!(worktrees[0].path, PathBuf::from("/project"));
 38    assert_eq!(worktrees[1].path, worktree_1_dir);
 39    assert_eq!(worktrees[1].ref_name.as_ref(), "refs/heads/feature-branch");
 40    assert_eq!(worktrees[1].sha.as_ref(), "abc123");
 41
 42    // Directory should exist in FakeFs after create
 43    assert!(fs.is_dir(&worktrees_dir.join("feature-branch")).await);
 44
 45    // Create a second worktree (without explicit commit)
 46    let worktree_2_dir = worktrees_dir.join("bugfix-branch");
 47    repo.create_worktree("bugfix-branch".to_string(), worktree_2_dir.clone(), None)
 48        .await
 49        .unwrap();
 50
 51    let worktrees = repo.worktrees().await.unwrap();
 52    assert_eq!(worktrees.len(), 3);
 53    assert!(fs.is_dir(&worktree_2_dir).await);
 54
 55    // Rename the first worktree
 56    repo.rename_worktree(worktree_1_dir, worktrees_dir.join("renamed-branch"))
 57        .await
 58        .unwrap();
 59
 60    let worktrees = repo.worktrees().await.unwrap();
 61    assert_eq!(worktrees.len(), 3);
 62    assert!(
 63        worktrees
 64            .iter()
 65            .any(|w| w.path == worktrees_dir.join("renamed-branch")),
 66    );
 67    assert!(
 68        worktrees
 69            .iter()
 70            .all(|w| w.path != worktrees_dir.join("feature-branch")),
 71    );
 72
 73    // Directory should be moved in FakeFs after rename
 74    assert!(!fs.is_dir(&worktrees_dir.join("feature-branch")).await);
 75    assert!(fs.is_dir(&worktrees_dir.join("renamed-branch")).await);
 76
 77    // Rename a nonexistent worktree should fail
 78    let result = repo
 79        .rename_worktree(PathBuf::from("/nonexistent"), PathBuf::from("/somewhere"))
 80        .await;
 81    assert!(result.is_err());
 82
 83    // Remove a worktree
 84    repo.remove_worktree(worktrees_dir.join("renamed-branch"), false)
 85        .await
 86        .unwrap();
 87
 88    let worktrees = repo.worktrees().await.unwrap();
 89    assert_eq!(worktrees.len(), 2);
 90    assert_eq!(worktrees[0].path, PathBuf::from("/project"));
 91    assert_eq!(worktrees[1].path, worktree_2_dir);
 92
 93    // Directory should be removed from FakeFs after remove
 94    assert!(!fs.is_dir(&worktrees_dir.join("renamed-branch")).await);
 95
 96    // Remove a nonexistent worktree should fail
 97    let result = repo
 98        .remove_worktree(PathBuf::from("/nonexistent"), false)
 99        .await;
100    assert!(result.is_err());
101
102    // Remove the last worktree
103    repo.remove_worktree(worktree_2_dir.clone(), false)
104        .await
105        .unwrap();
106
107    let worktrees = repo.worktrees().await.unwrap();
108    assert_eq!(worktrees.len(), 1);
109    assert_eq!(worktrees[0].path, PathBuf::from("/project"));
110    assert!(!fs.is_dir(&worktree_2_dir).await);
111}
112
113#[gpui::test]
114async fn test_checkpoints(executor: BackgroundExecutor) {
115    let fs = FakeFs::new(executor);
116    fs.insert_tree(
117        path!("/"),
118        json!({
119            "bar": {
120                "baz": "qux"
121            },
122            "foo": {
123                ".git": {},
124                "a": "lorem",
125                "b": "ipsum",
126            },
127        }),
128    )
129    .await;
130    fs.with_git_state(Path::new("/foo/.git"), true, |_git| {})
131        .unwrap();
132    let repository = fs
133        .open_repo(Path::new("/foo/.git"), Some("git".as_ref()))
134        .unwrap();
135
136    let checkpoint_1 = repository.checkpoint().await.unwrap();
137    fs.write(Path::new("/foo/b"), b"IPSUM").await.unwrap();
138    fs.write(Path::new("/foo/c"), b"dolor").await.unwrap();
139    let checkpoint_2 = repository.checkpoint().await.unwrap();
140    let checkpoint_3 = repository.checkpoint().await.unwrap();
141
142    assert!(
143        repository
144            .compare_checkpoints(checkpoint_2.clone(), checkpoint_3.clone())
145            .await
146            .unwrap()
147    );
148    assert!(
149        !repository
150            .compare_checkpoints(checkpoint_1.clone(), checkpoint_2.clone())
151            .await
152            .unwrap()
153    );
154
155    repository.restore_checkpoint(checkpoint_1).await.unwrap();
156    assert_eq!(
157        fs.files_with_contents(Path::new("")),
158        [
159            (Path::new(path!("/bar/baz")).into(), b"qux".into()),
160            (Path::new(path!("/foo/a")).into(), b"lorem".into()),
161            (Path::new(path!("/foo/b")).into(), b"ipsum".into())
162        ]
163    );
164}