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!(
 40        worktrees[1].ref_name,
 41        Some("refs/heads/feature-branch".into())
 42    );
 43    assert_eq!(worktrees[1].sha.as_ref(), "abc123");
 44
 45    // Directory should exist in FakeFs after create
 46    assert!(fs.is_dir(&worktrees_dir.join("feature-branch")).await);
 47
 48    // Create a second worktree (without explicit commit)
 49    let worktree_2_dir = worktrees_dir.join("bugfix-branch");
 50    repo.create_worktree("bugfix-branch".to_string(), worktree_2_dir.clone(), None)
 51        .await
 52        .unwrap();
 53
 54    let worktrees = repo.worktrees().await.unwrap();
 55    assert_eq!(worktrees.len(), 3);
 56    assert!(fs.is_dir(&worktree_2_dir).await);
 57
 58    // Rename the first worktree
 59    repo.rename_worktree(worktree_1_dir, worktrees_dir.join("renamed-branch"))
 60        .await
 61        .unwrap();
 62
 63    let worktrees = repo.worktrees().await.unwrap();
 64    assert_eq!(worktrees.len(), 3);
 65    assert!(
 66        worktrees
 67            .iter()
 68            .any(|w| w.path == worktrees_dir.join("renamed-branch")),
 69    );
 70    assert!(
 71        worktrees
 72            .iter()
 73            .all(|w| w.path != worktrees_dir.join("feature-branch")),
 74    );
 75
 76    // Directory should be moved in FakeFs after rename
 77    assert!(!fs.is_dir(&worktrees_dir.join("feature-branch")).await);
 78    assert!(fs.is_dir(&worktrees_dir.join("renamed-branch")).await);
 79
 80    // Rename a nonexistent worktree should fail
 81    let result = repo
 82        .rename_worktree(PathBuf::from("/nonexistent"), PathBuf::from("/somewhere"))
 83        .await;
 84    assert!(result.is_err());
 85
 86    // Remove a worktree
 87    repo.remove_worktree(worktrees_dir.join("renamed-branch"), false)
 88        .await
 89        .unwrap();
 90
 91    let worktrees = repo.worktrees().await.unwrap();
 92    assert_eq!(worktrees.len(), 2);
 93    assert_eq!(worktrees[0].path, PathBuf::from("/project"));
 94    assert_eq!(worktrees[1].path, worktree_2_dir);
 95
 96    // Directory should be removed from FakeFs after remove
 97    assert!(!fs.is_dir(&worktrees_dir.join("renamed-branch")).await);
 98
 99    // Remove a nonexistent worktree should fail
100    let result = repo
101        .remove_worktree(PathBuf::from("/nonexistent"), false)
102        .await;
103    assert!(result.is_err());
104
105    // Remove the last worktree
106    repo.remove_worktree(worktree_2_dir.clone(), false)
107        .await
108        .unwrap();
109
110    let worktrees = repo.worktrees().await.unwrap();
111    assert_eq!(worktrees.len(), 1);
112    assert_eq!(worktrees[0].path, PathBuf::from("/project"));
113    assert!(!fs.is_dir(&worktree_2_dir).await);
114}
115
116#[gpui::test]
117async fn test_checkpoints(executor: BackgroundExecutor) {
118    let fs = FakeFs::new(executor);
119    fs.insert_tree(
120        path!("/"),
121        json!({
122            "bar": {
123                "baz": "qux"
124            },
125            "foo": {
126                ".git": {},
127                "a": "lorem",
128                "b": "ipsum",
129            },
130        }),
131    )
132    .await;
133    fs.with_git_state(Path::new("/foo/.git"), true, |_git| {})
134        .unwrap();
135    let repository = fs
136        .open_repo(Path::new("/foo/.git"), Some("git".as_ref()))
137        .unwrap();
138
139    let checkpoint_1 = repository.checkpoint().await.unwrap();
140    fs.write(Path::new("/foo/b"), b"IPSUM").await.unwrap();
141    fs.write(Path::new("/foo/c"), b"dolor").await.unwrap();
142    let checkpoint_2 = repository.checkpoint().await.unwrap();
143    let checkpoint_3 = repository.checkpoint().await.unwrap();
144
145    assert!(
146        repository
147            .compare_checkpoints(checkpoint_2.clone(), checkpoint_3.clone())
148            .await
149            .unwrap()
150    );
151    assert!(
152        !repository
153            .compare_checkpoints(checkpoint_1.clone(), checkpoint_2.clone())
154            .await
155            .unwrap()
156    );
157
158    repository.restore_checkpoint(checkpoint_1).await.unwrap();
159    assert_eq!(
160        fs.files_with_contents(Path::new("")),
161        [
162            (Path::new(path!("/bar/baz")).into(), b"qux".into()),
163            (Path::new(path!("/foo/a")).into(), b"lorem".into()),
164            (Path::new(path!("/foo/b")).into(), b"ipsum".into())
165        ]
166    );
167}