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        Some("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(
 51        Some("bugfix-branch".to_string()),
 52        worktree_2_dir.clone(),
 53        None,
 54    )
 55    .await
 56    .unwrap();
 57
 58    let worktrees = repo.worktrees().await.unwrap();
 59    assert_eq!(worktrees.len(), 3);
 60    assert!(fs.is_dir(&worktree_2_dir).await);
 61
 62    // Rename the first worktree
 63    repo.rename_worktree(worktree_1_dir, worktrees_dir.join("renamed-branch"))
 64        .await
 65        .unwrap();
 66
 67    let worktrees = repo.worktrees().await.unwrap();
 68    assert_eq!(worktrees.len(), 3);
 69    assert!(
 70        worktrees
 71            .iter()
 72            .any(|w| w.path == worktrees_dir.join("renamed-branch")),
 73    );
 74    assert!(
 75        worktrees
 76            .iter()
 77            .all(|w| w.path != worktrees_dir.join("feature-branch")),
 78    );
 79
 80    // Directory should be moved in FakeFs after rename
 81    assert!(!fs.is_dir(&worktrees_dir.join("feature-branch")).await);
 82    assert!(fs.is_dir(&worktrees_dir.join("renamed-branch")).await);
 83
 84    // Rename a nonexistent worktree should fail
 85    let result = repo
 86        .rename_worktree(PathBuf::from("/nonexistent"), PathBuf::from("/somewhere"))
 87        .await;
 88    assert!(result.is_err());
 89
 90    // Remove a worktree
 91    repo.remove_worktree(worktrees_dir.join("renamed-branch"), false)
 92        .await
 93        .unwrap();
 94
 95    let worktrees = repo.worktrees().await.unwrap();
 96    assert_eq!(worktrees.len(), 2);
 97    assert_eq!(worktrees[0].path, PathBuf::from("/project"));
 98    assert_eq!(worktrees[1].path, worktree_2_dir);
 99
100    // Directory should be removed from FakeFs after remove
101    assert!(!fs.is_dir(&worktrees_dir.join("renamed-branch")).await);
102
103    // Remove a nonexistent worktree should fail
104    let result = repo
105        .remove_worktree(PathBuf::from("/nonexistent"), false)
106        .await;
107    assert!(result.is_err());
108
109    // Remove the last worktree
110    repo.remove_worktree(worktree_2_dir.clone(), false)
111        .await
112        .unwrap();
113
114    let worktrees = repo.worktrees().await.unwrap();
115    assert_eq!(worktrees.len(), 1);
116    assert_eq!(worktrees[0].path, PathBuf::from("/project"));
117    assert!(!fs.is_dir(&worktree_2_dir).await);
118}
119
120#[gpui::test]
121async fn test_checkpoints(executor: BackgroundExecutor) {
122    let fs = FakeFs::new(executor);
123    fs.insert_tree(
124        path!("/"),
125        json!({
126            "bar": {
127                "baz": "qux"
128            },
129            "foo": {
130                ".git": {},
131                "a": "lorem",
132                "b": "ipsum",
133            },
134        }),
135    )
136    .await;
137    fs.with_git_state(Path::new("/foo/.git"), true, |_git| {})
138        .unwrap();
139    let repository = fs
140        .open_repo(Path::new("/foo/.git"), Some("git".as_ref()))
141        .unwrap();
142
143    let checkpoint_1 = repository.checkpoint().await.unwrap();
144    fs.write(Path::new("/foo/b"), b"IPSUM").await.unwrap();
145    fs.write(Path::new("/foo/c"), b"dolor").await.unwrap();
146    let checkpoint_2 = repository.checkpoint().await.unwrap();
147    let checkpoint_3 = repository.checkpoint().await.unwrap();
148
149    assert!(
150        repository
151            .compare_checkpoints(checkpoint_2.clone(), checkpoint_3.clone())
152            .await
153            .unwrap()
154    );
155    assert!(
156        !repository
157            .compare_checkpoints(checkpoint_1.clone(), checkpoint_2.clone())
158            .await
159            .unwrap()
160    );
161
162    repository
163        .restore_checkpoint(checkpoint_1.clone())
164        .await
165        .unwrap();
166    assert_eq!(
167        fs.files_with_contents(Path::new("")),
168        [
169            (Path::new(path!("/bar/baz")).into(), b"qux".into()),
170            (Path::new(path!("/foo/a")).into(), b"lorem".into()),
171            (Path::new(path!("/foo/b")).into(), b"ipsum".into())
172        ]
173    );
174
175    // diff_checkpoints: identical checkpoints produce empty diff
176    let diff = repository
177        .diff_checkpoints(checkpoint_2.clone(), checkpoint_3.clone())
178        .await
179        .unwrap();
180    assert!(
181        diff.is_empty(),
182        "identical checkpoints should produce empty diff"
183    );
184
185    // diff_checkpoints: different checkpoints produce non-empty diff
186    let diff = repository
187        .diff_checkpoints(checkpoint_1.clone(), checkpoint_2.clone())
188        .await
189        .unwrap();
190    assert!(diff.contains("b"), "diff should mention changed file 'b'");
191    assert!(diff.contains("c"), "diff should mention added file 'c'");
192}