diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-03-05 19:57:28 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-03-05 21:31:17 +0000 |
| commit | 83b0886b97e2e90e328f91fcfaeb59726c93308f (patch) | |
| tree | 7e5f17529608738c98dd45c8c01f61e3d12b1b32 /tests/ngit_pr_checkout.rs | |
| parent | 84a197700cac6b2ef72cf0723474ac3185c5d1de (diff) | |
test(pr-checkout): replace broken ngit_list tests with ngit_pr_checkout
tests/ngit_list.rs had 27 tests all failing because the interactive mode
they tested has been replaced by a non-interactive implementation. Replace
the file with a stub documenting the coverage gaps and add
tests/ngit_pr_checkout.rs covering the same proposal branch checkout logic
via `ngit pr checkout <id>`, starting with the fresh-checkout case.
Diffstat (limited to 'tests/ngit_pr_checkout.rs')
| -rw-r--r-- | tests/ngit_pr_checkout.rs | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/tests/ngit_pr_checkout.rs b/tests/ngit_pr_checkout.rs new file mode 100644 index 0000000..f7d7855 --- /dev/null +++ b/tests/ngit_pr_checkout.rs | |||
| @@ -0,0 +1,536 @@ | |||
| 1 | use anyhow::Result; | ||
| 2 | use futures::join; | ||
| 3 | use serial_test::serial; | ||
| 4 | use test_utils::{git::GitTestRepo, relay::Relay, *}; | ||
| 5 | |||
| 6 | /// Run `ngit pr list --json --offline` in `dir` and return the nevent id for | ||
| 7 | /// the proposal whose branch-name matches `branch_name_in_event`. | ||
| 8 | fn get_proposal_id_for_branch(dir: &std::path::Path, branch_name_in_event: &str) -> Result<String> { | ||
| 9 | let output = std::process::Command::new(assert_cmd::cargo::cargo_bin("ngit")) | ||
| 10 | .env("NGITTEST", "TRUE") | ||
| 11 | .current_dir(dir) | ||
| 12 | .args([ | ||
| 13 | "--nsec", | ||
| 14 | TEST_KEY_1_NSEC, | ||
| 15 | "--password", | ||
| 16 | TEST_PASSWORD, | ||
| 17 | "--disable-cli-spinners", | ||
| 18 | "pr", | ||
| 19 | "list", | ||
| 20 | "--json", | ||
| 21 | "--offline", | ||
| 22 | ]) | ||
| 23 | .output()?; | ||
| 24 | let stdout = String::from_utf8(output.stdout)?; | ||
| 25 | let proposals: Vec<serde_json::Value> = serde_json::from_str(&stdout) | ||
| 26 | .map_err(|e| anyhow::anyhow!("failed to parse pr list json: {e}\nstdout: {stdout}"))?; | ||
| 27 | let entry = proposals | ||
| 28 | .iter() | ||
| 29 | .find(|p| { | ||
| 30 | p["branch"] | ||
| 31 | .as_str() | ||
| 32 | .map(|b| b.starts_with(&format!("pr/{branch_name_in_event}("))) | ||
| 33 | .unwrap_or(false) | ||
| 34 | }) | ||
| 35 | .ok_or_else(|| { | ||
| 36 | anyhow::anyhow!( | ||
| 37 | "no proposal found for branch {branch_name_in_event} in: {stdout}" | ||
| 38 | ) | ||
| 39 | })?; | ||
| 40 | Ok(entry["id"].as_str().unwrap_or_default().to_string()) | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Run `ngit pr checkout --offline <id>` (cache must already be populated). | ||
| 44 | fn run_pr_checkout(test_repo: &GitTestRepo, branch_name_in_event: &str) -> Result<()> { | ||
| 45 | run_pr_checkout_with_args(test_repo, branch_name_in_event, &["--offline"]) | ||
| 46 | } | ||
| 47 | |||
| 48 | /// Run `ngit pr checkout --force --offline <id>` (cache must already be populated). | ||
| 49 | fn run_pr_checkout_force(test_repo: &GitTestRepo, branch_name_in_event: &str) -> Result<()> { | ||
| 50 | run_pr_checkout_with_args(test_repo, branch_name_in_event, &["--force", "--offline"]) | ||
| 51 | } | ||
| 52 | |||
| 53 | fn run_pr_checkout_with_args( | ||
| 54 | test_repo: &GitTestRepo, | ||
| 55 | branch_name_in_event: &str, | ||
| 56 | extra_args: &[&str], | ||
| 57 | ) -> Result<()> { | ||
| 58 | let proposal_id = get_proposal_id_for_branch(&test_repo.dir, branch_name_in_event)?; | ||
| 59 | let mut args = vec![ | ||
| 60 | "--nsec", | ||
| 61 | TEST_KEY_1_NSEC, | ||
| 62 | "--password", | ||
| 63 | TEST_PASSWORD, | ||
| 64 | "--disable-cli-spinners", | ||
| 65 | "pr", | ||
| 66 | "checkout", | ||
| 67 | ]; | ||
| 68 | args.extend_from_slice(extra_args); | ||
| 69 | args.push(&proposal_id); | ||
| 70 | // Use std::process::Command directly (not CliTester/rexpect) so that a | ||
| 71 | // non-zero exit code is reliably detected without PTY timeout issues. | ||
| 72 | let status = std::process::Command::new(assert_cmd::cargo::cargo_bin("ngit")) | ||
| 73 | .env("NGITTEST", "TRUE") | ||
| 74 | .current_dir(&test_repo.dir) | ||
| 75 | .args(&args) | ||
| 76 | .status()?; | ||
| 77 | if status.success() { | ||
| 78 | Ok(()) | ||
| 79 | } else { | ||
| 80 | anyhow::bail!("ngit pr checkout exited with {status}") | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Spin up the standard 5-relay set used by all tests in this file. | ||
| 85 | /// Returns the five relay handles in port order (51,52,53,55,56). | ||
| 86 | #[allow(clippy::type_complexity)] | ||
| 87 | fn make_relays() -> ( | ||
| 88 | Relay<'static>, | ||
| 89 | Relay<'static>, | ||
| 90 | Relay<'static>, | ||
| 91 | Relay<'static>, | ||
| 92 | Relay<'static>, | ||
| 93 | ) { | ||
| 94 | let mut r51 = Relay::new(8051, None, None); | ||
| 95 | let r52 = Relay::new(8052, None, None); | ||
| 96 | let r53 = Relay::new(8053, None, None); | ||
| 97 | let mut r55 = Relay::new(8055, None, None); | ||
| 98 | let r56 = Relay::new(8056, None, None); | ||
| 99 | |||
| 100 | r51.events.push(generate_test_key_1_relay_list_event()); | ||
| 101 | r51.events.push(generate_test_key_1_metadata_event("fred")); | ||
| 102 | r51.events.push(generate_repo_ref_event()); | ||
| 103 | |||
| 104 | r55.events.push(generate_repo_ref_event()); | ||
| 105 | r55.events.push(generate_test_key_1_metadata_event("fred")); | ||
| 106 | r55.events.push(generate_test_key_1_relay_list_event()); | ||
| 107 | |||
| 108 | (r51, r52, r53, r55, r56) | ||
| 109 | } | ||
| 110 | |||
| 111 | fn shutdown_relays() -> Result<()> { | ||
| 112 | for port in [51u64, 52, 53, 55, 56] { | ||
| 113 | relay::shutdown_relay(8000 + port)?; | ||
| 114 | } | ||
| 115 | Ok(()) | ||
| 116 | } | ||
| 117 | |||
| 118 | mod when_proposal_branch_doesnt_exist { | ||
| 119 | use super::*; | ||
| 120 | |||
| 121 | async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 122 | let (mut r51, mut r52, mut r53, mut r55, mut r56) = make_relays(); | ||
| 123 | |||
| 124 | let cli_tester_handle = | ||
| 125 | std::thread::spawn(move || -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 126 | let originating_repo = cli_tester_create_proposals()?; | ||
| 127 | |||
| 128 | let test_repo = GitTestRepo::default(); | ||
| 129 | test_repo.populate()?; | ||
| 130 | |||
| 131 | use_ngit_pr_checkout(&test_repo, FEATURE_BRANCH_NAME_1)?; | ||
| 132 | |||
| 133 | shutdown_relays()?; | ||
| 134 | Ok((originating_repo, test_repo)) | ||
| 135 | }); | ||
| 136 | |||
| 137 | let _ = join!( | ||
| 138 | r51.listen_until_close(), | ||
| 139 | r52.listen_until_close(), | ||
| 140 | r53.listen_until_close(), | ||
| 141 | r55.listen_until_close(), | ||
| 142 | r56.listen_until_close(), | ||
| 143 | ); | ||
| 144 | cli_tester_handle.join().unwrap() | ||
| 145 | } | ||
| 146 | |||
| 147 | #[tokio::test] | ||
| 148 | #[serial] | ||
| 149 | async fn proposal_branch_created_with_correct_name() -> Result<()> { | ||
| 150 | let (_, test_repo) = prep_and_run().await?; | ||
| 151 | let expected_branch = get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?; | ||
| 152 | assert!( | ||
| 153 | test_repo.get_local_branch_names()?.contains(&expected_branch), | ||
| 154 | "expected branch {expected_branch} to exist" | ||
| 155 | ); | ||
| 156 | Ok(()) | ||
| 157 | } | ||
| 158 | |||
| 159 | #[tokio::test] | ||
| 160 | #[serial] | ||
| 161 | async fn proposal_branch_checked_out() -> Result<()> { | ||
| 162 | let (_, test_repo) = prep_and_run().await?; | ||
| 163 | assert_eq!( | ||
| 164 | get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?, | ||
| 165 | test_repo.get_checked_out_branch_name()?, | ||
| 166 | ); | ||
| 167 | Ok(()) | ||
| 168 | } | ||
| 169 | |||
| 170 | #[tokio::test] | ||
| 171 | #[serial] | ||
| 172 | async fn proposal_branch_tip_is_most_recent_patch() -> Result<()> { | ||
| 173 | let (originating_repo, test_repo) = prep_and_run().await?; | ||
| 174 | assert_eq!( | ||
| 175 | originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, | ||
| 176 | test_repo.get_tip_of_local_branch( | ||
| 177 | &get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)? | ||
| 178 | )?, | ||
| 179 | ); | ||
| 180 | Ok(()) | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | mod when_proposal_branch_exists_and_is_up_to_date { | ||
| 185 | use super::*; | ||
| 186 | |||
| 187 | async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 188 | let (mut r51, mut r52, mut r53, mut r55, mut r56) = make_relays(); | ||
| 189 | |||
| 190 | let cli_tester_handle = | ||
| 191 | std::thread::spawn(move || -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 192 | let originating_repo = cli_tester_create_proposals()?; | ||
| 193 | |||
| 194 | let test_repo = GitTestRepo::default(); | ||
| 195 | test_repo.populate()?; | ||
| 196 | |||
| 197 | // first checkout creates the branch | ||
| 198 | use_ngit_pr_checkout(&test_repo, FEATURE_BRANCH_NAME_1)?; | ||
| 199 | test_repo.checkout("main")?; | ||
| 200 | |||
| 201 | // second checkout: branch already exists and is up to date | ||
| 202 | run_pr_checkout(&test_repo, FEATURE_BRANCH_NAME_1)?; | ||
| 203 | |||
| 204 | shutdown_relays()?; | ||
| 205 | Ok((originating_repo, test_repo)) | ||
| 206 | }); | ||
| 207 | |||
| 208 | let _ = join!( | ||
| 209 | r51.listen_until_close(), | ||
| 210 | r52.listen_until_close(), | ||
| 211 | r53.listen_until_close(), | ||
| 212 | r55.listen_until_close(), | ||
| 213 | r56.listen_until_close(), | ||
| 214 | ); | ||
| 215 | cli_tester_handle.join().unwrap() | ||
| 216 | } | ||
| 217 | |||
| 218 | #[tokio::test] | ||
| 219 | #[serial] | ||
| 220 | async fn proposal_branch_checked_out() -> Result<()> { | ||
| 221 | let (_, test_repo) = prep_and_run().await?; | ||
| 222 | assert_eq!( | ||
| 223 | get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?, | ||
| 224 | test_repo.get_checked_out_branch_name()?, | ||
| 225 | ); | ||
| 226 | Ok(()) | ||
| 227 | } | ||
| 228 | |||
| 229 | #[tokio::test] | ||
| 230 | #[serial] | ||
| 231 | async fn proposal_branch_tip_unchanged() -> Result<()> { | ||
| 232 | let (originating_repo, test_repo) = prep_and_run().await?; | ||
| 233 | assert_eq!( | ||
| 234 | originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, | ||
| 235 | test_repo.get_tip_of_local_branch( | ||
| 236 | &get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)? | ||
| 237 | )?, | ||
| 238 | ); | ||
| 239 | Ok(()) | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | mod when_proposal_branch_exists_and_is_behind { | ||
| 244 | use super::*; | ||
| 245 | |||
| 246 | async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 247 | let (mut r51, mut r52, mut r53, mut r55, mut r56) = make_relays(); | ||
| 248 | |||
| 249 | let cli_tester_handle = | ||
| 250 | std::thread::spawn(move || -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 251 | let originating_repo = cli_tester_create_proposals()?; | ||
| 252 | |||
| 253 | let test_repo = GitTestRepo::default(); | ||
| 254 | test_repo.populate()?; | ||
| 255 | |||
| 256 | use_ngit_pr_checkout(&test_repo, FEATURE_BRANCH_NAME_1)?; | ||
| 257 | |||
| 258 | // wind the local branch back one commit so it's behind | ||
| 259 | remove_latest_commit_so_proposal_branch_is_behind_and_checkout_main(&test_repo)?; | ||
| 260 | |||
| 261 | // checkout again — should fast-forward to the latest patch | ||
| 262 | run_pr_checkout(&test_repo, FEATURE_BRANCH_NAME_1)?; | ||
| 263 | |||
| 264 | shutdown_relays()?; | ||
| 265 | Ok((originating_repo, test_repo)) | ||
| 266 | }); | ||
| 267 | |||
| 268 | let _ = join!( | ||
| 269 | r51.listen_until_close(), | ||
| 270 | r52.listen_until_close(), | ||
| 271 | r53.listen_until_close(), | ||
| 272 | r55.listen_until_close(), | ||
| 273 | r56.listen_until_close(), | ||
| 274 | ); | ||
| 275 | cli_tester_handle.join().unwrap() | ||
| 276 | } | ||
| 277 | |||
| 278 | #[tokio::test] | ||
| 279 | #[serial] | ||
| 280 | async fn proposal_branch_checked_out() -> Result<()> { | ||
| 281 | let (_, test_repo) = prep_and_run().await?; | ||
| 282 | assert_eq!( | ||
| 283 | get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?, | ||
| 284 | test_repo.get_checked_out_branch_name()?, | ||
| 285 | ); | ||
| 286 | Ok(()) | ||
| 287 | } | ||
| 288 | |||
| 289 | #[tokio::test] | ||
| 290 | #[serial] | ||
| 291 | async fn proposal_branch_tip_is_most_recent_patch() -> Result<()> { | ||
| 292 | let (originating_repo, test_repo) = prep_and_run().await?; | ||
| 293 | assert_eq!( | ||
| 294 | originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, | ||
| 295 | test_repo.get_tip_of_local_branch( | ||
| 296 | &get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)? | ||
| 297 | )?, | ||
| 298 | ); | ||
| 299 | Ok(()) | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | mod when_proposal_branch_has_local_amendments { | ||
| 304 | use super::*; | ||
| 305 | |||
| 306 | async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 307 | let (mut r51, mut r52, mut r53, mut r55, mut r56) = make_relays(); | ||
| 308 | |||
| 309 | let cli_tester_handle = | ||
| 310 | std::thread::spawn(move || -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 311 | let originating_repo = cli_tester_create_proposals()?; | ||
| 312 | |||
| 313 | let test_repo = GitTestRepo::default(); | ||
| 314 | test_repo.populate()?; | ||
| 315 | |||
| 316 | use_ngit_pr_checkout(&test_repo, FEATURE_BRANCH_NAME_1)?; | ||
| 317 | |||
| 318 | // amend: remove the tip and add a different commit in its place | ||
| 319 | amend_last_commit(&test_repo, "add ammended-commit.md")?; | ||
| 320 | test_repo.checkout("main")?; | ||
| 321 | |||
| 322 | // checkout without --force should bail on diverged branch | ||
| 323 | assert!( | ||
| 324 | run_pr_checkout(&test_repo, FEATURE_BRANCH_NAME_1).is_err(), | ||
| 325 | "expected checkout to fail without --force on amended branch" | ||
| 326 | ); | ||
| 327 | |||
| 328 | shutdown_relays()?; | ||
| 329 | Ok((originating_repo, test_repo)) | ||
| 330 | }); | ||
| 331 | |||
| 332 | let _ = join!( | ||
| 333 | r51.listen_until_close(), | ||
| 334 | r52.listen_until_close(), | ||
| 335 | r53.listen_until_close(), | ||
| 336 | r55.listen_until_close(), | ||
| 337 | r56.listen_until_close(), | ||
| 338 | ); | ||
| 339 | cli_tester_handle.join().unwrap() | ||
| 340 | } | ||
| 341 | |||
| 342 | #[tokio::test] | ||
| 343 | #[serial] | ||
| 344 | async fn local_unpublished_commits_are_not_overwritten() -> Result<()> { | ||
| 345 | let (originating_repo, test_repo) = prep_and_run().await?; | ||
| 346 | // the local branch tip must differ from the published tip — local work preserved | ||
| 347 | assert_ne!( | ||
| 348 | originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, | ||
| 349 | test_repo.get_tip_of_local_branch( | ||
| 350 | &get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)? | ||
| 351 | )?, | ||
| 352 | ); | ||
| 353 | Ok(()) | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | mod when_proposal_branch_has_local_commits_on_top { | ||
| 358 | use super::*; | ||
| 359 | |||
| 360 | async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 361 | let (mut r51, mut r52, mut r53, mut r55, mut r56) = make_relays(); | ||
| 362 | |||
| 363 | let cli_tester_handle = | ||
| 364 | std::thread::spawn(move || -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 365 | let originating_repo = cli_tester_create_proposals()?; | ||
| 366 | |||
| 367 | let test_repo = GitTestRepo::default(); | ||
| 368 | test_repo.populate()?; | ||
| 369 | |||
| 370 | use_ngit_pr_checkout(&test_repo, FEATURE_BRANCH_NAME_1)?; | ||
| 371 | |||
| 372 | // add an extra local commit on top of the proposal branch | ||
| 373 | std::fs::write(test_repo.dir.join("local-extra.md"), "local work")?; | ||
| 374 | test_repo.stage_and_commit("add local-extra.md")?; | ||
| 375 | test_repo.checkout("main")?; | ||
| 376 | |||
| 377 | // checkout again — should not discard the extra local commit | ||
| 378 | run_pr_checkout(&test_repo, FEATURE_BRANCH_NAME_1)?; | ||
| 379 | |||
| 380 | shutdown_relays()?; | ||
| 381 | Ok((originating_repo, test_repo)) | ||
| 382 | }); | ||
| 383 | |||
| 384 | let _ = join!( | ||
| 385 | r51.listen_until_close(), | ||
| 386 | r52.listen_until_close(), | ||
| 387 | r53.listen_until_close(), | ||
| 388 | r55.listen_until_close(), | ||
| 389 | r56.listen_until_close(), | ||
| 390 | ); | ||
| 391 | cli_tester_handle.join().unwrap() | ||
| 392 | } | ||
| 393 | |||
| 394 | #[tokio::test] | ||
| 395 | #[serial] | ||
| 396 | async fn proposal_branch_checked_out() -> Result<()> { | ||
| 397 | let (_, test_repo) = prep_and_run().await?; | ||
| 398 | assert_eq!( | ||
| 399 | get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?, | ||
| 400 | test_repo.get_checked_out_branch_name()?, | ||
| 401 | ); | ||
| 402 | Ok(()) | ||
| 403 | } | ||
| 404 | |||
| 405 | #[tokio::test] | ||
| 406 | #[serial] | ||
| 407 | async fn local_commits_are_not_discarded() -> Result<()> { | ||
| 408 | let (originating_repo, test_repo) = prep_and_run().await?; | ||
| 409 | let branch = get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?; | ||
| 410 | let local_tip = test_repo.get_tip_of_local_branch(&branch)?; | ||
| 411 | let published_tip = originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?; | ||
| 412 | // local tip must be ahead of (not equal to) the published tip | ||
| 413 | assert_ne!(local_tip, published_tip, "local commits were discarded"); | ||
| 414 | // and the published tip must be an ancestor of the local tip | ||
| 415 | assert!( | ||
| 416 | test_repo | ||
| 417 | .git_repo | ||
| 418 | .graph_descendant_of(local_tip, published_tip)?, | ||
| 419 | "local branch is not descended from published tip" | ||
| 420 | ); | ||
| 421 | Ok(()) | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | mod when_newer_revision_rebases_proposal { | ||
| 426 | use super::*; | ||
| 427 | |||
| 428 | async fn prep_and_run() -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 429 | let (mut r51, mut r52, mut r53, mut r55, mut r56) = make_relays(); | ||
| 430 | |||
| 431 | let cli_tester_handle = | ||
| 432 | std::thread::spawn(move || -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 433 | let (new_originating_repo, test_repo) = | ||
| 434 | create_proposals_with_rebased_first_proposal()?; | ||
| 435 | |||
| 436 | // refresh test_repo cache so it sees the new rebased revision | ||
| 437 | let mut p = CliTester::new_from_dir( | ||
| 438 | &test_repo.dir, | ||
| 439 | [ | ||
| 440 | "--nsec", | ||
| 441 | TEST_KEY_1_NSEC, | ||
| 442 | "--password", | ||
| 443 | TEST_PASSWORD, | ||
| 444 | "--disable-cli-spinners", | ||
| 445 | "pr", | ||
| 446 | "list", | ||
| 447 | ], | ||
| 448 | ); | ||
| 449 | p.expect_end_eventually()?; | ||
| 450 | |||
| 451 | // checkout without --force should bail on diverged branch | ||
| 452 | assert!( | ||
| 453 | run_pr_checkout(&test_repo, FEATURE_BRANCH_NAME_1).is_err(), | ||
| 454 | "expected checkout to fail without --force on diverged branch" | ||
| 455 | ); | ||
| 456 | // checkout with --force should update to the new rebased revision | ||
| 457 | // (relays still needed for the fetch inside checkout) | ||
| 458 | run_pr_checkout_force(&test_repo, FEATURE_BRANCH_NAME_1)?; | ||
| 459 | |||
| 460 | shutdown_relays()?; | ||
| 461 | Ok((new_originating_repo, test_repo)) | ||
| 462 | }); | ||
| 463 | |||
| 464 | let _ = join!( | ||
| 465 | r51.listen_until_close(), | ||
| 466 | r52.listen_until_close(), | ||
| 467 | r53.listen_until_close(), | ||
| 468 | r55.listen_until_close(), | ||
| 469 | r56.listen_until_close(), | ||
| 470 | ); | ||
| 471 | cli_tester_handle.join().unwrap() | ||
| 472 | } | ||
| 473 | |||
| 474 | #[tokio::test] | ||
| 475 | #[serial] | ||
| 476 | async fn proposal_branch_checked_out() -> Result<()> { | ||
| 477 | let (_, test_repo) = prep_and_run().await?; | ||
| 478 | assert_eq!( | ||
| 479 | get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)?, | ||
| 480 | test_repo.get_checked_out_branch_name()?, | ||
| 481 | ); | ||
| 482 | Ok(()) | ||
| 483 | } | ||
| 484 | |||
| 485 | #[tokio::test] | ||
| 486 | #[serial] | ||
| 487 | async fn proposal_branch_tip_is_most_recent_revision_tip() -> Result<()> { | ||
| 488 | let (new_originating_repo, test_repo) = prep_and_run().await?; | ||
| 489 | assert_eq!( | ||
| 490 | new_originating_repo.get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?, | ||
| 491 | test_repo.get_tip_of_local_branch( | ||
| 492 | &get_proposal_branch_name(&test_repo, FEATURE_BRANCH_NAME_1)? | ||
| 493 | )?, | ||
| 494 | ); | ||
| 495 | Ok(()) | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 499 | /// Creates 3 proposals, checks out proposal 1 in a test repo, then publishes | ||
| 500 | /// a rebased revision of proposal 1 from a second originating repo. Returns | ||
| 501 | /// (new_originating_repo, test_repo) with the test repo still on the old branch. | ||
| 502 | fn create_proposals_with_rebased_first_proposal( | ||
| 503 | ) -> Result<(GitTestRepo, GitTestRepo)> { | ||
| 504 | // create the initial 3 proposals and check out proposal 1 in a test repo | ||
| 505 | let (_, test_repo) = | ||
| 506 | create_proposals_and_repo_with_proposal_branch_checked_out(FEATURE_BRANCH_NAME_1)?; | ||
| 507 | |||
| 508 | // get the original proposal id to use as in_reply_to for the rebased revision | ||
| 509 | let original_proposal_id = | ||
| 510 | get_proposal_id_for_branch(&test_repo.dir, FEATURE_BRANCH_NAME_1)?; | ||
| 511 | |||
| 512 | // publish a rebased revision of proposal 1 from a second originating repo | ||
| 513 | let second_originating_repo = GitTestRepo::default(); | ||
| 514 | second_originating_repo.populate()?; | ||
| 515 | std::fs::write( | ||
| 516 | second_originating_repo.dir.join("amazing.md"), | ||
| 517 | "some content", | ||
| 518 | )?; | ||
| 519 | second_originating_repo.stage_and_commit("commit for rebasing on top of")?; | ||
| 520 | cli_tester_create_proposal( | ||
| 521 | &second_originating_repo, | ||
| 522 | FEATURE_BRANCH_NAME_1, | ||
| 523 | "a", | ||
| 524 | Some((PROPOSAL_TITLE_1, "proposal a description")), | ||
| 525 | Some(original_proposal_id), | ||
| 526 | )?; | ||
| 527 | |||
| 528 | // simulate the test repo having pulled the updated main branch | ||
| 529 | let branch_name = test_repo.get_checked_out_branch_name()?; | ||
| 530 | test_repo.checkout("main")?; | ||
| 531 | std::fs::write(test_repo.dir.join("amazing.md"), "some content")?; | ||
| 532 | test_repo.stage_and_commit("commit for rebasing on top of")?; | ||
| 533 | test_repo.checkout(&branch_name)?; | ||
| 534 | |||
| 535 | Ok((second_originating_repo, test_repo)) | ||
| 536 | } | ||