upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/tests/git_remote_nostr/push.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/git_remote_nostr/push.rs')
-rw-r--r--tests/git_remote_nostr/push.rs1488
1 files changed, 1488 insertions, 0 deletions
diff --git a/tests/git_remote_nostr/push.rs b/tests/git_remote_nostr/push.rs
new file mode 100644
index 0000000..f0d519e
--- /dev/null
+++ b/tests/git_remote_nostr/push.rs
@@ -0,0 +1,1488 @@
1use super::*;
2
3#[tokio::test]
4#[serial]
5async fn new_branch_when_no_state_event_exists() -> Result<()> {
6 generate_repo_with_state_event().await?;
7 Ok(())
8}
9mod two_branches_in_batch_one_added_one_updated {
10
11 use super::*;
12
13 #[tokio::test]
14 #[serial]
15 async fn updates_branch_on_git_server() -> Result<()> {
16 let git_repo = prep_git_repo()?;
17 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
18
19 std::fs::write(git_repo.dir.join("commit.md"), "some content")?;
20 let main_commit_id = git_repo.stage_and_commit("commit.md")?;
21
22 git_repo.create_branch("vnext")?;
23 git_repo.checkout("vnext")?;
24 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
25 let vnext_commit_id = git_repo.stage_and_commit("vnext.md")?;
26
27 let events = vec![
28 generate_test_key_1_metadata_event("fred"),
29 generate_test_key_1_relay_list_event(),
30 generate_repo_ref_event_with_git_server(vec![
31 source_git_repo.dir.to_str().unwrap().to_string(),
32 ]),
33 ];
34 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
35 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
36 Relay::new(8051, None, None),
37 Relay::new(8052, None, None),
38 Relay::new(8053, None, None),
39 Relay::new(8055, None, None),
40 Relay::new(8056, None, None),
41 Relay::new(8057, None, None),
42 );
43 r51.events = events.clone();
44 r55.events = events;
45
46 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
47 assert_ne!(
48 source_git_repo.get_tip_of_local_branch("main")?,
49 main_commit_id
50 );
51
52 let mut p = cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
53
54 p.send_line("push refs/heads/main:refs/heads/main")?;
55 p.send_line("push refs/heads/vnext:refs/heads/vnext")?;
56 p.send_line("")?;
57 p.expect_eventually("\r\n\r\n")?;
58 p.exit()?;
59 for p in [51, 52, 53, 55, 56, 57] {
60 relay::shutdown_relay(8000 + p)?;
61 }
62
63 assert_eq!(
64 source_git_repo.get_tip_of_local_branch("main")?,
65 main_commit_id
66 );
67
68 assert_eq!(
69 source_git_repo.get_tip_of_local_branch("vnext")?,
70 vnext_commit_id
71 );
72
73 Ok(())
74 });
75 // launch relays
76 let _ = join!(
77 r51.listen_until_close(),
78 r52.listen_until_close(),
79 r53.listen_until_close(),
80 r55.listen_until_close(),
81 r56.listen_until_close(),
82 r57.listen_until_close(),
83 );
84 cli_tester_handle.join().unwrap()?;
85 Ok(())
86 }
87
88 #[tokio::test]
89 #[serial]
90 async fn remote_refs_updated_in_local_git() -> Result<()> {
91 let git_repo = prep_git_repo()?;
92 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
93
94 std::fs::write(git_repo.dir.join("commit.md"), "some content")?;
95 let main_commit_id = git_repo.stage_and_commit("commit.md")?;
96
97 git_repo.create_branch("vnext")?;
98 git_repo.checkout("vnext")?;
99 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
100 let vnext_commit_id = git_repo.stage_and_commit("vnext.md")?;
101
102 let events = vec![
103 generate_test_key_1_metadata_event("fred"),
104 generate_test_key_1_relay_list_event(),
105 generate_repo_ref_event_with_git_server(vec![
106 source_git_repo.dir.to_str().unwrap().to_string(),
107 ]),
108 ];
109 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
110 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
111 Relay::new(8051, None, None),
112 Relay::new(8052, None, None),
113 Relay::new(8053, None, None),
114 Relay::new(8055, None, None),
115 Relay::new(8056, None, None),
116 Relay::new(8057, None, None),
117 );
118 r51.events = events.clone();
119 r55.events = events;
120
121 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
122 assert_ne!(
123 source_git_repo.get_tip_of_local_branch("main")?,
124 main_commit_id
125 );
126
127 let mut p = cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
128 p.send_line("push refs/heads/main:refs/heads/main")?;
129 p.send_line("push refs/heads/vnext:refs/heads/vnext")?;
130 p.send_line("")?;
131 p.expect_eventually("\r\n\r\n")?;
132 p.exit()?;
133 for p in [51, 52, 53, 55, 56, 57] {
134 relay::shutdown_relay(8000 + p)?;
135 }
136
137 assert_eq!(
138 git_repo
139 .git_repo
140 .find_reference("refs/remotes/nostr/main")?
141 .peel_to_commit()?
142 .id(),
143 main_commit_id,
144 );
145
146 assert_eq!(
147 git_repo
148 .git_repo
149 .find_reference("refs/remotes/nostr/vnext")?
150 .peel_to_commit()?
151 .id(),
152 vnext_commit_id
153 );
154
155 p.exit()?;
156 for p in [51, 52, 53, 55, 56, 57] {
157 relay::shutdown_relay(8000 + p)?;
158 }
159 Ok(())
160 });
161 // launch relays
162 let _ = join!(
163 r51.listen_until_close(),
164 r52.listen_until_close(),
165 r53.listen_until_close(),
166 r55.listen_until_close(),
167 r56.listen_until_close(),
168 r57.listen_until_close(),
169 );
170 cli_tester_handle.join().unwrap()?;
171 Ok(())
172 }
173
174 #[tokio::test]
175 #[serial]
176 async fn prints_git_helper_ok_respose() -> Result<()> {
177 let git_repo = prep_git_repo()?;
178 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
179
180 std::fs::write(git_repo.dir.join("commit.md"), "some content")?;
181 let main_commit_id = git_repo.stage_and_commit("commit.md")?;
182
183 git_repo.create_branch("vnext")?;
184 git_repo.checkout("vnext")?;
185 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
186 git_repo.stage_and_commit("vnext.md")?;
187
188 let events = vec![
189 generate_test_key_1_metadata_event("fred"),
190 generate_test_key_1_relay_list_event(),
191 generate_repo_ref_event_with_git_server(vec![
192 source_git_repo.dir.to_str().unwrap().to_string(),
193 ]),
194 ];
195 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
196 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
197 Relay::new(8051, None, None),
198 Relay::new(8052, None, None),
199 Relay::new(8053, None, None),
200 Relay::new(8055, None, None),
201 Relay::new(8056, None, None),
202 Relay::new(8057, None, None),
203 );
204 r51.events = events.clone();
205 r55.events = events;
206
207 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
208 assert_ne!(
209 source_git_repo.get_tip_of_local_branch("main")?,
210 main_commit_id
211 );
212
213 let mut p = cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
214
215 p.send_line("push refs/heads/main:refs/heads/main")?;
216 p.send_line("push refs/heads/vnext:refs/heads/vnext")?;
217 p.send_line("")?;
218 p.expect("ok refs/heads/main\r\n")?;
219 p.expect("ok refs/heads/vnext\r\n")?;
220 p.expect("\r\n")?;
221 p.exit()?;
222 for p in [51, 52, 53, 55, 56, 57] {
223 relay::shutdown_relay(8000 + p)?;
224 }
225 Ok(())
226 });
227 // launch relays
228 let _ = join!(
229 r51.listen_until_close(),
230 r52.listen_until_close(),
231 r53.listen_until_close(),
232 r55.listen_until_close(),
233 r56.listen_until_close(),
234 r57.listen_until_close(),
235 );
236 cli_tester_handle.join().unwrap()?;
237 Ok(())
238 }
239
240 #[tokio::test]
241 #[serial]
242 async fn when_no_existing_state_event_state_on_git_server_published_in_nostr_state_event()
243 -> Result<()> {
244 let git_repo = prep_git_repo()?;
245 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
246
247 std::fs::write(git_repo.dir.join("commit.md"), "some content")?;
248 let main_commit_id = git_repo.stage_and_commit("commit.md")?;
249
250 git_repo.create_branch("vnext")?;
251 git_repo.checkout("vnext")?;
252 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
253 let vnext_commit_id = git_repo.stage_and_commit("vnext.md")?;
254
255 let events = vec![
256 generate_test_key_1_metadata_event("fred"),
257 generate_test_key_1_relay_list_event(),
258 generate_repo_ref_event_with_git_server(vec![
259 source_git_repo.dir.to_str().unwrap().to_string(),
260 ]),
261 ];
262 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
263 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
264 Relay::new(8051, None, None),
265 Relay::new(8052, None, None),
266 Relay::new(8053, None, None),
267 Relay::new(8055, None, None),
268 Relay::new(8056, None, None),
269 Relay::new(8057, None, None),
270 );
271 r51.events = events.clone();
272 r55.events = events;
273
274 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
275 let mut p = cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
276 p.send_line("push refs/heads/main:refs/heads/main")?;
277 p.send_line("push refs/heads/vnext:refs/heads/vnext")?;
278 p.send_line("")?;
279 p.expect_eventually_and_print("\r\n\r\n")?;
280 p.exit()?;
281 for p in [51, 52, 53, 55, 56, 57] {
282 relay::shutdown_relay(8000 + p)?;
283 }
284 Ok(())
285 });
286 // launch relays
287 let _ = join!(
288 r51.listen_until_close(),
289 r52.listen_until_close(),
290 r53.listen_until_close(),
291 r55.listen_until_close(),
292 r56.listen_until_close(),
293 r57.listen_until_close(),
294 );
295 cli_tester_handle.join().unwrap()?;
296
297 let state_event = r56
298 .events
299 .iter()
300 .find(|e| e.kind().eq(&STATE_KIND))
301 .context("state event not created")?;
302
303 assert_eq!(
304 state_event.identifier(),
305 generate_repo_ref_event().identifier(),
306 );
307 // println!("{:#?}", state_event);
308 assert_eq!(
309 state_event
310 .tags
311 .iter()
312 .filter(|t| t.kind().to_string().as_str().ne("d"))
313 .map(|t| t.as_vec().to_vec())
314 .collect::<HashSet<Vec<String>>>(),
315 HashSet::from([
316 vec!["HEAD".to_string(), "ref: refs/heads/main".to_string()],
317 vec!["refs/heads/main".to_string(), main_commit_id.to_string()],
318 vec!["refs/heads/vnext".to_string(), vnext_commit_id.to_string()],
319 ]),
320 );
321 Ok(())
322 }
323
324 #[tokio::test]
325 #[serial]
326 async fn existing_state_event_published_in_nostr_state_event() -> Result<()> {
327 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
328
329 let git_repo = prep_git_repo()?;
330 let example_branch_commit_id = git_repo.get_tip_of_local_branch("main")?.to_string(); // same as example
331
332 std::fs::write(git_repo.dir.join("new.md"), "some content")?;
333 let main_commit_id = git_repo.stage_and_commit("new.md")?;
334 git_repo.create_branch("vnext")?;
335 git_repo.checkout("vnext")?;
336 std::fs::write(git_repo.dir.join("more.md"), "some content")?;
337 let vnext_commit_id = git_repo.stage_and_commit("more.md")?;
338
339 let events = vec![
340 generate_test_key_1_metadata_event("fred"),
341 generate_test_key_1_relay_list_event(),
342 generate_repo_ref_event_with_git_server(vec![
343 source_git_repo.dir.to_str().unwrap().to_string(),
344 ]),
345 state_event.clone(),
346 ];
347
348 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
349 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
350 Relay::new(8051, None, None),
351 Relay::new(8052, None, None),
352 Relay::new(8053, None, None),
353 Relay::new(8055, None, None),
354 Relay::new(8056, None, None),
355 Relay::new(8057, None, None),
356 );
357 r51.events = events.clone();
358 r55.events = events;
359
360 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
361 let mut p = cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
362 p.send_line("push refs/heads/main:refs/heads/main")?;
363 p.send_line("push refs/heads/vnext:refs/heads/vnext")?;
364 p.send_line("")?;
365 p.expect_eventually_and_print("\r\n\r\n")?;
366 p.exit()?;
367 for p in [51, 52, 53, 55, 56, 57] {
368 relay::shutdown_relay(8000 + p)?;
369 }
370 // local refs updated
371 assert_eq!(
372 git_repo
373 .git_repo
374 .find_reference("refs/remotes/nostr/main")?
375 .peel_to_commit()?
376 .id(),
377 main_commit_id,
378 );
379
380 assert_eq!(
381 git_repo
382 .git_repo
383 .find_reference("refs/remotes/nostr/vnext")?
384 .peel_to_commit()?
385 .id(),
386 vnext_commit_id
387 );
388 Ok(())
389 });
390 // launch relays
391 let _ = join!(
392 r51.listen_until_close(),
393 r52.listen_until_close(),
394 r53.listen_until_close(),
395 r55.listen_until_close(),
396 r56.listen_until_close(),
397 r57.listen_until_close(),
398 );
399
400 cli_tester_handle.join().unwrap()?;
401
402 // git_server updated
403 assert_eq!(
404 source_git_repo.get_tip_of_local_branch("main")?,
405 main_commit_id
406 );
407
408 assert_eq!(
409 source_git_repo.get_tip_of_local_branch("vnext")?,
410 vnext_commit_id
411 );
412
413 // state annoucement updated
414 let state_event = r56
415 .events
416 .iter()
417 .find(|e| e.kind().eq(&STATE_KIND))
418 .context("state event not created")?;
419
420 // println!("{:#?}", state_event);
421 assert_eq!(
422 state_event
423 .tags
424 .iter()
425 .filter(|t| t.kind().to_string().as_str().ne("d"))
426 .map(|t| t.as_vec().to_vec())
427 .collect::<HashSet<Vec<String>>>(),
428 HashSet::from([
429 vec!["HEAD".to_string(), "ref: refs/heads/main".to_string()],
430 vec!["refs/heads/main".to_string(), main_commit_id.to_string()],
431 vec![
432 "refs/heads/example-branch".to_string(),
433 example_branch_commit_id.to_string()
434 ],
435 vec!["refs/heads/vnext".to_string(), vnext_commit_id.to_string()],
436 ]),
437 );
438 Ok(())
439 }
440}
441mod delete_one_branch {
442
443 use super::*;
444
445 #[tokio::test]
446 #[serial]
447 async fn deletes_branch_on_git_server() -> Result<()> {
448 let git_repo = prep_git_repo()?;
449
450 git_repo.create_branch("vnext")?;
451 git_repo.checkout("vnext")?;
452 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
453 let vnext_commit_id = git_repo.stage_and_commit("vnext.md")?;
454
455 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
456
457 let events = vec![
458 generate_test_key_1_metadata_event("fred"),
459 generate_test_key_1_relay_list_event(),
460 generate_repo_ref_event_with_git_server(vec![
461 source_git_repo.dir.to_str().unwrap().to_string(),
462 ]),
463 ];
464 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
465 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
466 Relay::new(8051, None, None),
467 Relay::new(8052, None, None),
468 Relay::new(8053, None, None),
469 Relay::new(8055, None, None),
470 Relay::new(8056, None, None),
471 Relay::new(8057, None, None),
472 );
473 r51.events = events.clone();
474 r55.events = events;
475
476 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
477 assert_eq!(
478 source_git_repo
479 .git_repo
480 .find_reference("refs/heads/vnext")?
481 .peel_to_commit()?
482 .id(),
483 vnext_commit_id
484 );
485
486 let mut p = cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
487 p.send_line("push :refs/heads/vnext")?;
488 p.send_line("")?;
489 p.expect_eventually_and_print("\r\n\r\n")?;
490 p.exit()?;
491 for p in [51, 52, 53, 55, 56, 57] {
492 relay::shutdown_relay(8000 + p)?;
493 }
494
495 assert!(
496 source_git_repo
497 .git_repo
498 .find_reference("refs/heads/vnext")
499 .is_err()
500 );
501 Ok(())
502 });
503 // launch relays
504 let _ = join!(
505 r51.listen_until_close(),
506 r52.listen_until_close(),
507 r53.listen_until_close(),
508 r55.listen_until_close(),
509 r56.listen_until_close(),
510 r57.listen_until_close(),
511 );
512 cli_tester_handle.join().unwrap()?;
513 Ok(())
514 }
515
516 #[tokio::test]
517 #[serial]
518 async fn remote_refs_updated_in_local_git() -> Result<()> {
519 let git_repo = prep_git_repo()?;
520
521 git_repo.create_branch("vnext")?;
522 git_repo.checkout("vnext")?;
523 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
524 let vnext_commit_id = git_repo.stage_and_commit("vnext.md")?;
525
526 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
527
528 git_repo
529 .git_repo
530 .reference("refs/remotes/nostr/vnext", vnext_commit_id, true, "")?;
531
532 let events = vec![
533 generate_test_key_1_metadata_event("fred"),
534 generate_test_key_1_relay_list_event(),
535 generate_repo_ref_event_with_git_server(vec![
536 source_git_repo.dir.to_str().unwrap().to_string(),
537 ]),
538 ];
539 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
540 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
541 Relay::new(8051, None, None),
542 Relay::new(8052, None, None),
543 Relay::new(8053, None, None),
544 Relay::new(8055, None, None),
545 Relay::new(8056, None, None),
546 Relay::new(8057, None, None),
547 );
548 r51.events = events.clone();
549 r55.events = events;
550
551 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
552 assert_eq!(
553 git_repo
554 .git_repo
555 .find_reference("refs/remotes/nostr/vnext")?
556 .peel_to_commit()?
557 .id(),
558 vnext_commit_id
559 );
560
561 let mut p = cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
562 p.send_line("push :refs/heads/vnext")?;
563 p.send_line("")?;
564 p.expect_eventually("\r\n\r\n")?;
565 p.exit()?;
566 for p in [51, 52, 53, 55, 56, 57] {
567 relay::shutdown_relay(8000 + p)?;
568 }
569 assert!(
570 git_repo
571 .git_repo
572 .find_reference("refs/remotes/nostr/vnext")
573 .is_err()
574 );
575 Ok(())
576 });
577 // launch relays
578 let _ = join!(
579 r51.listen_until_close(),
580 r52.listen_until_close(),
581 r53.listen_until_close(),
582 r55.listen_until_close(),
583 r56.listen_until_close(),
584 r57.listen_until_close(),
585 );
586 cli_tester_handle.join().unwrap()?;
587 Ok(())
588 }
589
590 #[tokio::test]
591 #[serial]
592 async fn prints_git_helper_ok_respose() -> Result<()> {
593 let git_repo = prep_git_repo()?;
594
595 git_repo.create_branch("vnext")?;
596 git_repo.checkout("vnext")?;
597 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
598 let vnext_commit_id = git_repo.stage_and_commit("vnext.md")?;
599
600 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
601
602 git_repo
603 .git_repo
604 .reference("refs/remotes/nostr/vnext", vnext_commit_id, true, "")?;
605
606 let events = vec![
607 generate_test_key_1_metadata_event("fred"),
608 generate_test_key_1_relay_list_event(),
609 generate_repo_ref_event_with_git_server(vec![
610 source_git_repo.dir.to_str().unwrap().to_string(),
611 ]),
612 ];
613 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
614 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
615 Relay::new(8051, None, None),
616 Relay::new(8052, None, None),
617 Relay::new(8053, None, None),
618 Relay::new(8055, None, None),
619 Relay::new(8056, None, None),
620 Relay::new(8057, None, None),
621 );
622 r51.events = events.clone();
623 r55.events = events;
624
625 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
626 let mut p = cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
627 p.send_line("push :refs/heads/vnext")?;
628 p.send_line("")?;
629 // let res = p.expect_eventually("\r\n\r\n")?;
630 // println!("{res}");
631 p.expect("ok refs/heads/vnext\r\n")?;
632 p.expect("\r\n")?;
633 p.exit()?;
634 for p in [51, 52, 53, 55, 56, 57] {
635 relay::shutdown_relay(8000 + p)?;
636 }
637 Ok(())
638 });
639 // launch relays
640 let _ = join!(
641 r51.listen_until_close(),
642 r52.listen_until_close(),
643 r53.listen_until_close(),
644 r55.listen_until_close(),
645 r56.listen_until_close(),
646 r57.listen_until_close(),
647 );
648 cli_tester_handle.join().unwrap()?;
649 Ok(())
650 }
651
652 mod when_existing_state_event {
653 use super::*;
654
655 #[tokio::test]
656 #[serial]
657 async fn state_event_updated_and_branch_deleted_and_ok_printed() -> Result<()> {
658 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
659
660 let git_repo = prep_git_repo()?;
661 let main_commit_id = git_repo.get_tip_of_local_branch("main")?.to_string(); // same as example
662
663 let events = vec![
664 generate_test_key_1_metadata_event("fred"),
665 generate_test_key_1_relay_list_event(),
666 generate_repo_ref_event_with_git_server(vec![
667 source_git_repo.dir.to_str().unwrap().to_string(),
668 ]),
669 state_event.clone(),
670 ];
671
672 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
673 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
674 Relay::new(8051, None, None),
675 Relay::new(8052, None, None),
676 Relay::new(8053, None, None),
677 Relay::new(8055, None, None),
678 Relay::new(8056, None, None),
679 Relay::new(8057, None, None),
680 );
681 r51.events = events.clone();
682 r55.events = events;
683
684 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
685 let mut p =
686 cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
687 p.send_line("push :refs/heads/example-branch")?;
688 p.send_line("")?;
689 p.expect("ok refs/heads/example-branch\r\n")?;
690 p.expect("\r\n")?;
691 p.exit()?;
692 for p in [51, 52, 53, 55, 56, 57] {
693 relay::shutdown_relay(8000 + p)?;
694 }
695 Ok(())
696 });
697 // launch relays
698 let _ = join!(
699 r51.listen_until_close(),
700 r52.listen_until_close(),
701 r53.listen_until_close(),
702 r55.listen_until_close(),
703 r56.listen_until_close(),
704 r57.listen_until_close(),
705 );
706
707 cli_tester_handle.join().unwrap()?;
708
709 let state_event = r56
710 .events
711 .iter()
712 .find(|e| e.kind().eq(&STATE_KIND))
713 .context("state event not created")?;
714
715 // println!("{:#?}", state_event);
716 assert_eq!(
717 state_event
718 .tags
719 .iter()
720 .filter(|t| t.kind().to_string().as_str().ne("d"))
721 .map(|t| t.as_vec().to_vec())
722 .collect::<HashSet<Vec<String>>>(),
723 HashSet::from([
724 vec!["HEAD".to_string(), "ref: refs/heads/main".to_string()],
725 vec!["refs/heads/main".to_string(), main_commit_id.to_string()],
726 ]),
727 );
728 Ok(())
729 }
730
731 mod already_deleted_on_git_server {
732 use super::*;
733
734 #[tokio::test]
735 #[serial]
736 async fn existing_state_event_updated_and_ok_printed() -> Result<()> {
737 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
738
739 {
740 // delete branch on git server
741 let tmp_repo = GitTestRepo::clone_repo(&source_git_repo)?;
742 let mut remote = tmp_repo.git_repo.find_remote("origin")?;
743 remote.push(&[":refs/heads/example-branch"], None)?;
744 }
745
746 let git_repo = prep_git_repo()?;
747 let main_commit_id = git_repo.get_tip_of_local_branch("main")?.to_string(); // same as example
748
749 let events = vec![
750 generate_test_key_1_metadata_event("fred"),
751 generate_test_key_1_relay_list_event(),
752 generate_repo_ref_event_with_git_server(vec![
753 source_git_repo.dir.to_str().unwrap().to_string(),
754 ]),
755 state_event.clone(),
756 ];
757
758 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
759 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
760 Relay::new(8051, None, None),
761 Relay::new(8052, None, None),
762 Relay::new(8053, None, None),
763 Relay::new(8055, None, None),
764 Relay::new(8056, None, None),
765 Relay::new(8057, None, None),
766 );
767 r51.events = events.clone();
768 r55.events = events;
769
770 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
771 let mut p =
772 cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
773 p.send_line("push :refs/heads/example-branch")?;
774 p.send_line("")?;
775 p.expect("ok refs/heads/example-branch\r\n")?;
776 p.expect("\r\n")?;
777 p.exit()?;
778 for p in [51, 52, 53, 55, 56, 57] {
779 relay::shutdown_relay(8000 + p)?;
780 }
781 Ok(())
782 });
783 // launch relays
784 let _ = join!(
785 r51.listen_until_close(),
786 r52.listen_until_close(),
787 r53.listen_until_close(),
788 r55.listen_until_close(),
789 r56.listen_until_close(),
790 r57.listen_until_close(),
791 );
792
793 cli_tester_handle.join().unwrap()?;
794
795 let state_event = r56
796 .events
797 .iter()
798 .find(|e| e.kind().eq(&STATE_KIND))
799 .context("state event not created")?;
800
801 // println!("{:#?}", state_event);
802 assert_eq!(
803 state_event
804 .tags
805 .iter()
806 .filter(|t| t.kind().to_string().as_str().ne("d"))
807 .map(|t| t.as_vec().to_vec())
808 .collect::<HashSet<Vec<String>>>(),
809 HashSet::from([
810 vec!["HEAD".to_string(), "ref: refs/heads/main".to_string()],
811 vec!["refs/heads/main".to_string(), main_commit_id.to_string()],
812 ]),
813 );
814 Ok(())
815 }
816 }
817 }
818}
819
820#[tokio::test]
821#[serial]
822async fn pushes_to_all_git_servers_listed_and_ok_printed() -> Result<()> {
823 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
824 let second_source_git_repo = GitTestRepo::duplicate(&source_git_repo)?;
825
826 // uppdate announcement with extra git server
827
828 let git_repo = prep_git_repo()?;
829
830 std::fs::write(git_repo.dir.join("new.md"), "some content")?;
831 let main_commit_id = git_repo.stage_and_commit("new.md")?;
832
833 let events = vec![
834 generate_test_key_1_metadata_event("fred"),
835 generate_test_key_1_relay_list_event(),
836 generate_repo_ref_event_with_git_server(vec![
837 source_git_repo.dir.to_str().unwrap().to_string(),
838 second_source_git_repo.dir.to_str().unwrap().to_string(),
839 ]),
840 state_event.clone(),
841 ];
842
843 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
844 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
845 Relay::new(8051, None, None),
846 Relay::new(8052, None, None),
847 Relay::new(8053, None, None),
848 Relay::new(8055, None, None),
849 Relay::new(8056, None, None),
850 Relay::new(8057, None, None),
851 );
852 r51.events = events.clone();
853 r55.events = events;
854
855 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
856 let mut p = cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
857 p.send_line("push refs/heads/main:refs/heads/main")?;
858 p.send_line("")?;
859 p.expect("ok refs/heads/main\r\n")?;
860 p.expect("\r\n")?;
861 p.exit()?;
862 for p in [51, 52, 53, 55, 56, 57] {
863 relay::shutdown_relay(8000 + p)?;
864 }
865 Ok(())
866 });
867 // launch relays
868 let _ = join!(
869 r51.listen_until_close(),
870 r52.listen_until_close(),
871 r53.listen_until_close(),
872 r55.listen_until_close(),
873 r56.listen_until_close(),
874 r57.listen_until_close(),
875 );
876
877 cli_tester_handle.join().unwrap()?;
878
879 // git_server updated
880 assert_eq!(
881 source_git_repo.get_tip_of_local_branch("main")?,
882 main_commit_id
883 );
884 assert_eq!(
885 second_source_git_repo.get_tip_of_local_branch("main")?,
886 main_commit_id
887 );
888
889 Ok(())
890}
891
892#[tokio::test]
893#[serial]
894async fn proposal_merge_commit_pushed_to_main_leads_to_status_event_issued() -> Result<()> {
895 //
896 let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?;
897 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
898
899 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
900 Relay::new(8051, None, None),
901 Relay::new(8052, None, None),
902 Relay::new(8053, None, None),
903 Relay::new(8055, None, None),
904 Relay::new(8056, None, None),
905 Relay::new(8057, None, None),
906 );
907 r51.events = events.clone();
908 r55.events = events.clone();
909
910 #[allow(clippy::mutable_key_type)]
911 let before = r55.events.iter().cloned().collect::<HashSet<Event>>();
912
913 let cli_tester_handle = std::thread::spawn(move || -> Result<(String, Oid)> {
914 let branch_name = get_proposal_branch_name_from_events(&events, FEATURE_BRANCH_NAME_1)?;
915
916 let git_repo = clone_git_repo_with_nostr_url()?;
917 git_repo.checkout_remote_branch(&branch_name)?;
918 git_repo.checkout("refs/heads/main")?;
919
920 std::fs::write(git_repo.dir.join("new.md"), "some content")?;
921 git_repo.stage_and_commit("new.md")?;
922
923 CliTester::new_git_with_remote_helper_from_dir(
924 &git_repo.dir,
925 ["merge", &branch_name, "-m", "proposal merge commit message"],
926 )
927 .expect_end_eventually_and_print()?;
928
929 let oid = git_repo.get_tip_of_local_branch("main")?;
930
931 let mut p = CliTester::new_git_with_remote_helper_from_dir(&git_repo.dir, ["push"]);
932 cli_expect_nostr_fetch(&mut p)?;
933 p.expect(
934 format!(
935 "fetching ref list over filesystem from {}...\r\n",
936 source_path
937 )
938 .as_str(),
939 )?;
940
941 p.expect("merge commit ")?;
942 // shorthand merge commit id appears in this gap
943 p.expect_eventually(": create nostr proposal status event\r\n")?;
944 p.expect(format!("To {}\r\n", get_nostr_remote_url()?).as_str())?;
945 let output = p.expect_end_eventually()?;
946
947 for p in [51, 52, 53, 55, 56, 57] {
948 relay::shutdown_relay(8000 + p)?;
949 }
950
951 Ok((output, oid))
952 });
953 // launch relays
954 let _ = join!(
955 r51.listen_until_close(),
956 r52.listen_until_close(),
957 r53.listen_until_close(),
958 r55.listen_until_close(),
959 r56.listen_until_close(),
960 r57.listen_until_close(),
961 );
962
963 let (output, oid) = cli_tester_handle.join().unwrap()?;
964
965 assert_eq!(
966 output,
967 format!(" 431b84e..{} main -> main\r\n", &oid.to_string()[..7])
968 );
969
970 let new_events = r55
971 .events
972 .iter()
973 .cloned()
974 .collect::<HashSet<Event>>()
975 .difference(&before)
976 .cloned()
977 .collect::<Vec<Event>>();
978
979 assert_eq!(new_events.len(), 2, "{new_events:?}");
980
981 let proposal = r55
982 .events
983 .iter()
984 .find(|e| {
985 e.tags()
986 .iter()
987 .find(|t| t.as_vec()[0].eq("branch-name"))
988 .is_some_and(|t| t.as_vec()[1].eq(FEATURE_BRANCH_NAME_1))
989 })
990 .unwrap();
991
992 let merge_status = new_events
993 .iter()
994 .find(|e| e.kind().eq(&Kind::GitStatusApplied))
995 .unwrap();
996
997 assert_eq!(
998 oid.to_string(),
999 merge_status
1000 .tags
1001 .iter()
1002 .find(|t| t.as_vec()[0].eq("merge-commit-id"))
1003 .unwrap()
1004 .as_vec()[1],
1005 "status sets correct merge-commit-id tag"
1006 );
1007
1008 let proposal_tip = r55
1009 .events
1010 .iter()
1011 .filter(|e| {
1012 e.tags()
1013 .iter()
1014 .any(|t| t.as_vec()[1].eq(&proposal.id().to_string()))
1015 && e.kind().eq(&Kind::GitPatch)
1016 })
1017 .last()
1018 .unwrap();
1019
1020 assert_eq!(
1021 proposal_tip.id().to_string(),
1022 merge_status
1023 .tags
1024 .iter()
1025 .find(|t| t.as_vec().len().eq(&4) && t.as_vec()[3].eq("mention"))
1026 .unwrap()
1027 .as_vec()[1],
1028 "status mentions proposal tip event \r\nmerge status:\r\n{}\r\nproposal tip:\r\n{}",
1029 merge_status.as_json(),
1030 proposal_tip.as_json(),
1031 );
1032
1033 assert_eq!(
1034 proposal.id().to_string(),
1035 merge_status
1036 .tags
1037 .iter()
1038 .find(|t| t.is_root())
1039 .unwrap()
1040 .as_vec()[1],
1041 "status tags proposal id as root \r\nmerge status:\r\n{}\r\nproposal:\r\n{}",
1042 merge_status.as_json(),
1043 proposal.as_json(),
1044 );
1045
1046 Ok(())
1047}
1048
1049#[tokio::test]
1050#[serial]
1051async fn push_2_commits_to_existing_proposal() -> Result<()> {
1052 let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?;
1053 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
1054
1055 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1056 Relay::new(8051, None, None),
1057 Relay::new(8052, None, None),
1058 Relay::new(8053, None, None),
1059 Relay::new(8055, None, None),
1060 Relay::new(8056, None, None),
1061 Relay::new(8057, None, None),
1062 );
1063 r51.events = events.clone();
1064 r55.events = events.clone();
1065
1066 #[allow(clippy::mutable_key_type)]
1067 let before = r55.events.iter().cloned().collect::<HashSet<Event>>();
1068
1069 let cli_tester_handle = std::thread::spawn(move || -> Result<(String, String)> {
1070 let branch_name = get_proposal_branch_name_from_events(&events, FEATURE_BRANCH_NAME_1)?;
1071
1072 let git_repo = clone_git_repo_with_nostr_url()?;
1073 git_repo.checkout_remote_branch(&branch_name)?;
1074
1075 std::fs::write(git_repo.dir.join("new.md"), "some content")?;
1076 git_repo.stage_and_commit("new.md")?;
1077
1078 std::fs::write(git_repo.dir.join("new2.md"), "some content")?;
1079 git_repo.stage_and_commit("new2.md")?;
1080
1081 let mut p = CliTester::new_git_with_remote_helper_from_dir(&git_repo.dir, ["push"]);
1082 cli_expect_nostr_fetch(&mut p)?;
1083 p.expect(
1084 format!(
1085 "fetching ref list over filesystem from {}...\r\n",
1086 source_path
1087 )
1088 .as_str(),
1089 )?;
1090 p.expect(format!("To {}\r\n", get_nostr_remote_url()?).as_str())?;
1091 let output = p.expect_end_eventually()?;
1092
1093 for p in [51, 52, 53, 55, 56, 57] {
1094 relay::shutdown_relay(8000 + p)?;
1095 }
1096
1097 Ok((output, branch_name))
1098 });
1099 // launch relays
1100 let _ = join!(
1101 r51.listen_until_close(),
1102 r52.listen_until_close(),
1103 r53.listen_until_close(),
1104 r55.listen_until_close(),
1105 r56.listen_until_close(),
1106 r57.listen_until_close(),
1107 );
1108
1109 let (output, branch_name) = cli_tester_handle.join().unwrap()?;
1110
1111 assert_eq!(
1112 output,
1113 format!(" eb5d678..7de5e41 {branch_name} -> {branch_name}\r\n").as_str(),
1114 );
1115
1116 let new_events = r55
1117 .events
1118 .iter()
1119 .cloned()
1120 .collect::<HashSet<Event>>()
1121 .difference(&before)
1122 .cloned()
1123 .collect::<Vec<Event>>();
1124 assert_eq!(new_events.len(), 2);
1125 let first_new_patch = new_events
1126 .iter()
1127 .find(|e| e.content.contains("new.md"))
1128 .unwrap();
1129 let second_new_patch = new_events
1130 .iter()
1131 .find(|e| e.content.contains("new2.md"))
1132 .unwrap();
1133 assert!(
1134 first_new_patch.content.contains("[PATCH 3/4]"),
1135 "first patch labeled with [PATCH 3/4]"
1136 );
1137 assert!(
1138 second_new_patch.content.contains("[PATCH 4/4]"),
1139 "second patch labeled with [PATCH 4/4]"
1140 );
1141
1142 let proposal = r55
1143 .events
1144 .iter()
1145 .find(|e| {
1146 e.tags()
1147 .iter()
1148 .find(|t| t.as_vec()[0].eq("branch-name"))
1149 .is_some_and(|t| t.as_vec()[1].eq(FEATURE_BRANCH_NAME_1))
1150 })
1151 .unwrap();
1152
1153 assert_eq!(
1154 proposal.id().to_string(),
1155 first_new_patch
1156 .tags
1157 .iter()
1158 .find(|t| t.is_root())
1159 .unwrap()
1160 .as_vec()[1],
1161 "first patch sets proposal id as root"
1162 );
1163
1164 assert_eq!(
1165 first_new_patch.id().to_string(),
1166 second_new_patch
1167 .tags
1168 .iter()
1169 .find(|t| t.is_reply())
1170 .unwrap()
1171 .as_vec()[1],
1172 "second new patch replies to the first new patch"
1173 );
1174
1175 let previous_proposal_tip_event = r55
1176 .events
1177 .iter()
1178 .find(|e| {
1179 e.tags()
1180 .iter()
1181 .any(|t| t.as_vec()[1].eq(&proposal.id().to_string()))
1182 && e.content.contains("[PATCH 2/2]")
1183 })
1184 .unwrap();
1185
1186 assert_eq!(
1187 previous_proposal_tip_event.id().to_string(),
1188 first_new_patch
1189 .tags
1190 .iter()
1191 .find(|t| t.is_reply())
1192 .unwrap()
1193 .as_vec()[1],
1194 "first patch replies to the previous tip of proposal"
1195 );
1196
1197 Ok(())
1198}
1199
1200#[tokio::test]
1201#[serial]
1202async fn force_push_creates_proposal_revision() -> Result<()> {
1203 let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?;
1204 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
1205
1206 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1207 Relay::new(8051, None, None),
1208 Relay::new(8052, None, None),
1209 Relay::new(8053, None, None),
1210 Relay::new(8055, None, None),
1211 Relay::new(8056, None, None),
1212 Relay::new(8057, None, None),
1213 );
1214 r51.events = events.clone();
1215 r55.events = events.clone();
1216
1217 #[allow(clippy::mutable_key_type)]
1218 let before = r55.events.iter().cloned().collect::<HashSet<Event>>();
1219
1220 let cli_tester_handle = std::thread::spawn(move || -> Result<(String, String)> {
1221 let branch_name = get_proposal_branch_name_from_events(&events, FEATURE_BRANCH_NAME_1)?;
1222
1223 let git_repo = clone_git_repo_with_nostr_url()?;
1224 let oid = git_repo.checkout_remote_branch(&branch_name)?;
1225 // remove last commit
1226 git_repo.checkout("main")?;
1227 git_repo.git_repo.branch(
1228 &branch_name,
1229 &git_repo.git_repo.find_commit(oid)?.parent(0)?,
1230 true,
1231 )?;
1232 git_repo.checkout(&branch_name)?;
1233
1234 std::fs::write(git_repo.dir.join("new.md"), "some content")?;
1235 git_repo.stage_and_commit("new.md")?;
1236
1237 std::fs::write(git_repo.dir.join("new2.md"), "some content")?;
1238 git_repo.stage_and_commit("new2.md")?;
1239
1240 let mut p =
1241 CliTester::new_git_with_remote_helper_from_dir(&git_repo.dir, ["push", "--force"]);
1242 cli_expect_nostr_fetch(&mut p)?;
1243 p.expect(
1244 format!(
1245 "fetching ref list over filesystem from {}...\r\n",
1246 source_path
1247 )
1248 .as_str(),
1249 )?;
1250 p.expect(format!("To {}\r\n", get_nostr_remote_url()?).as_str())?;
1251 let output = p.expect_end_eventually()?;
1252
1253 for p in [51, 52, 53, 55, 56, 57] {
1254 relay::shutdown_relay(8000 + p)?;
1255 }
1256
1257 Ok((output, branch_name))
1258 });
1259 // launch relays
1260 let _ = join!(
1261 r51.listen_until_close(),
1262 r52.listen_until_close(),
1263 r53.listen_until_close(),
1264 r55.listen_until_close(),
1265 r56.listen_until_close(),
1266 r57.listen_until_close(),
1267 );
1268
1269 let (output, branch_name) = cli_tester_handle.join().unwrap()?;
1270
1271 assert_eq!(
1272 output,
1273 format!(" + eb5d678...8a296c8 {branch_name} -> {branch_name} (forced update)\r\n").as_str(),
1274 );
1275
1276 let new_events = r55
1277 .events
1278 .iter()
1279 .cloned()
1280 .collect::<HashSet<Event>>()
1281 .difference(&before)
1282 .cloned()
1283 .collect::<Vec<Event>>();
1284 assert_eq!(new_events.len(), 3);
1285
1286 let proposal = r55
1287 .events
1288 .iter()
1289 .find(|e| {
1290 e.tags()
1291 .iter()
1292 .find(|t| t.as_vec()[0].eq("branch-name"))
1293 .is_some_and(|t| t.as_vec()[1].eq(FEATURE_BRANCH_NAME_1))
1294 })
1295 .unwrap();
1296
1297 let revision_root_patch = new_events
1298 .iter()
1299 .find(|e| e.tags().iter().any(|t| t.as_vec()[1].eq("revision-root")))
1300 .unwrap();
1301
1302 assert_eq!(
1303 proposal.id().to_string(),
1304 revision_root_patch
1305 .tags
1306 .iter()
1307 .find(|t| t.is_reply())
1308 .unwrap()
1309 .as_vec()[1],
1310 "revision root patch replies to original proposal"
1311 );
1312
1313 assert!(
1314 revision_root_patch.content.contains("[PATCH 1/3]"),
1315 "revision root labeled with [PATCH 1/3] event: {revision_root_patch:?}",
1316 );
1317
1318 let second_patch = new_events
1319 .iter()
1320 .find(|e| e.content.contains("new.md"))
1321 .unwrap();
1322 let third_patch = new_events
1323 .iter()
1324 .find(|e| e.content.contains("new2.md"))
1325 .unwrap();
1326 assert!(
1327 second_patch.content.contains("[PATCH 2/3]"),
1328 "second patch labeled with [PATCH 2/3]"
1329 );
1330 assert!(
1331 third_patch.content.contains("[PATCH 3/3]"),
1332 "third patch labeled with [PATCH 3/3]"
1333 );
1334
1335 assert_eq!(
1336 revision_root_patch.id().to_string(),
1337 second_patch
1338 .tags
1339 .iter()
1340 .find(|t| t.is_root())
1341 .unwrap()
1342 .as_vec()[1],
1343 "second patch sets revision id as root"
1344 );
1345
1346 assert_eq!(
1347 second_patch.id().to_string(),
1348 third_patch
1349 .tags
1350 .iter()
1351 .find(|t| t.is_reply())
1352 .unwrap()
1353 .as_vec()[1],
1354 "third patch replies to the second new patch"
1355 );
1356
1357 Ok(())
1358}
1359
1360#[tokio::test]
1361#[serial]
1362async fn push_new_pr_branch_creates_proposal() -> Result<()> {
1363 let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?;
1364 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
1365
1366 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1367 Relay::new(8051, None, None),
1368 Relay::new(8052, None, None),
1369 Relay::new(8053, None, None),
1370 Relay::new(8055, None, None),
1371 Relay::new(8056, None, None),
1372 Relay::new(8057, None, None),
1373 );
1374 r51.events = events.clone();
1375 r55.events = events.clone();
1376
1377 #[allow(clippy::mutable_key_type)]
1378 let before = r55.events.iter().cloned().collect::<HashSet<Event>>();
1379 let branch_name = "pr/my-new-proposal";
1380
1381 let cli_tester_handle = std::thread::spawn(move || -> Result<String> {
1382 let mut git_repo = clone_git_repo_with_nostr_url()?;
1383 git_repo.delete_dir_on_drop = false;
1384 git_repo.create_branch(branch_name)?;
1385 git_repo.checkout(branch_name)?;
1386
1387 std::fs::write(git_repo.dir.join("new.md"), "some content")?;
1388 git_repo.stage_and_commit("new.md")?;
1389
1390 std::fs::write(git_repo.dir.join("new2.md"), "some content")?;
1391 git_repo.stage_and_commit("new2.md")?;
1392
1393 let mut p = CliTester::new_git_with_remote_helper_from_dir(
1394 &git_repo.dir,
1395 ["push", "-u", "origin", branch_name],
1396 );
1397 cli_expect_nostr_fetch(&mut p)?;
1398 p.expect(
1399 format!(
1400 "fetching ref list over filesystem from {}...\r\n",
1401 source_path
1402 )
1403 .as_str(),
1404 )?;
1405 p.expect(format!("To {}\r\n", get_nostr_remote_url()?).as_str())?;
1406 let output = p.expect_end_eventually()?;
1407
1408 for p in [51, 52, 53, 55, 56, 57] {
1409 relay::shutdown_relay(8000 + p)?;
1410 }
1411
1412 Ok(output)
1413 });
1414 // launch relays
1415 let _ = join!(
1416 r51.listen_until_close(),
1417 r52.listen_until_close(),
1418 r53.listen_until_close(),
1419 r55.listen_until_close(),
1420 r56.listen_until_close(),
1421 r57.listen_until_close(),
1422 );
1423
1424 let output = cli_tester_handle.join().unwrap()?;
1425
1426 assert_eq!(
1427 output,
1428 format!(" * [new branch] {branch_name} -> {branch_name}\r\nbranch '{branch_name}' set up to track 'origin/{branch_name}'.\r\n").as_str(),
1429 );
1430
1431 let new_events = r55
1432 .events
1433 .iter()
1434 .cloned()
1435 .collect::<HashSet<Event>>()
1436 .difference(&before)
1437 .cloned()
1438 .collect::<Vec<Event>>();
1439 assert_eq!(new_events.len(), 2);
1440
1441 let proposal = new_events
1442 .iter()
1443 .find(|e| e.tags().iter().any(|t| t.as_vec()[1].eq("root")))
1444 .unwrap();
1445
1446 assert!(
1447 proposal.content.contains("new.md"),
1448 "first patch is proposal root"
1449 );
1450
1451 assert!(
1452 proposal.content.contains("[PATCH 1/2]"),
1453 "proposal root labeled with[PATCH 1/2] event: {proposal:?}",
1454 );
1455
1456 assert_eq!(
1457 proposal
1458 .tags()
1459 .iter()
1460 .find(|t| t.as_vec()[0].eq("branch-name"))
1461 .unwrap()
1462 .as_vec()[1],
1463 branch_name.replace("pr/", ""),
1464 );
1465
1466 let second_patch = new_events
1467 .iter()
1468 .find(|e| e.content.contains("new2.md"))
1469 .unwrap();
1470
1471 assert!(
1472 second_patch.content.contains("[PATCH 2/2]"),
1473 "second patch labeled with [PATCH 2/2]"
1474 );
1475
1476 assert_eq!(
1477 proposal.id().to_string(),
1478 second_patch
1479 .tags
1480 .iter()
1481 .find(|t| t.is_root())
1482 .unwrap()
1483 .as_vec()[1],
1484 "second patch sets proposal id as root"
1485 );
1486
1487 Ok(())
1488}