upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests/git_remote_nostr
diff options
context:
space:
mode:
Diffstat (limited to 'tests/git_remote_nostr')
-rw-r--r--tests/git_remote_nostr/fetch.rs195
-rw-r--r--tests/git_remote_nostr/list.rs362
-rw-r--r--tests/git_remote_nostr/main.rs2072
-rw-r--r--tests/git_remote_nostr/push.rs1488
4 files changed, 2049 insertions, 2068 deletions
diff --git a/tests/git_remote_nostr/fetch.rs b/tests/git_remote_nostr/fetch.rs
new file mode 100644
index 0000000..9ae17d8
--- /dev/null
+++ b/tests/git_remote_nostr/fetch.rs
@@ -0,0 +1,195 @@
1
2use super::*;
3
4#[tokio::test]
5#[serial]
6async fn fetch_downloads_speficied_commits_from_git_server() -> Result<()> {
7 let source_git_repo = prep_git_repo()?;
8 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
9
10 std::fs::write(source_git_repo.dir.join("commit.md"), "some content")?;
11 let main_commit_id = source_git_repo.stage_and_commit("commit.md")?;
12
13 source_git_repo.create_branch("vnext")?;
14 source_git_repo.checkout("vnext")?;
15 std::fs::write(source_git_repo.dir.join("vnext.md"), "some content")?;
16 let vnext_commit_id = source_git_repo.stage_and_commit("vnext.md")?;
17
18 let git_repo = prep_git_repo()?;
19 let events = vec![
20 generate_test_key_1_metadata_event("fred"),
21 generate_test_key_1_relay_list_event(),
22 generate_repo_ref_event_with_git_server(vec![
23 source_git_repo.dir.to_str().unwrap().to_string(),
24 ]),
25 ];
26 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
27 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
28 Relay::new(8051, None, None),
29 Relay::new(8052, None, None),
30 Relay::new(8053, None, None),
31 Relay::new(8055, None, None),
32 Relay::new(8056, None, None),
33 Relay::new(8057, None, None),
34 );
35 r51.events = events.clone();
36 r55.events = events;
37
38 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
39 assert!(git_repo.git_repo.find_commit(main_commit_id).is_err());
40 assert!(git_repo.git_repo.find_commit(vnext_commit_id).is_err());
41
42 let mut p = cli_tester_after_fetch(&git_repo)?;
43 p.send_line(format!("fetch {main_commit_id} main").as_str())?;
44 p.send_line(format!("fetch {vnext_commit_id} vnext").as_str())?;
45 p.send_line("")?;
46 p.expect(format!("fetching over filesystem from {source_path}...\r\n").as_str())?;
47 p.expect_eventually_and_print("\r\n")?;
48
49 assert!(git_repo.git_repo.find_commit(main_commit_id).is_ok());
50 assert!(git_repo.git_repo.find_commit(vnext_commit_id).is_ok());
51
52 p.exit()?;
53 for p in [51, 52, 53, 55, 56, 57] {
54 relay::shutdown_relay(8000 + p)?;
55 }
56 Ok(())
57 });
58 // launch relays
59 let _ = join!(
60 r51.listen_until_close(),
61 r52.listen_until_close(),
62 r53.listen_until_close(),
63 r55.listen_until_close(),
64 r56.listen_until_close(),
65 r57.listen_until_close(),
66 );
67 cli_tester_handle.join().unwrap()?;
68 Ok(())
69}
70
71mod when_first_git_server_fails_ {
72 use super::*;
73
74 #[tokio::test]
75 #[serial]
76 async fn fetch_downloads_speficied_commits_from_second_git_server() -> Result<()> {
77 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
78 // let source_path = source_git_repo.dir.to_str().unwrap().to_string();
79 let error_path = "./path-doesnt-exist".to_string();
80
81 let main_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
82
83 let git_repo = prep_git_repo_minus_1_commit()?;
84
85 let events = vec![
86 generate_test_key_1_metadata_event("fred"),
87 generate_test_key_1_relay_list_event(),
88 generate_repo_ref_event_with_git_server(vec![
89 error_path.to_string(),
90 source_git_repo.dir.to_str().unwrap().to_string(),
91 ]),
92 state_event,
93 ];
94 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
95 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
96 Relay::new(8051, None, None),
97 Relay::new(8052, None, None),
98 Relay::new(8053, None, None),
99 Relay::new(8055, None, None),
100 Relay::new(8056, None, None),
101 Relay::new(8057, None, None),
102 );
103 r51.events = events.clone();
104 r55.events = events;
105
106 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
107 assert!(git_repo.git_repo.find_commit(main_commit_id).is_err());
108
109 let mut p = cli_tester_after_fetch(&git_repo)?;
110 p.send_line(format!("fetch {main_commit_id} main").as_str())?;
111 p.send_line("")?;
112 p.expect(format!("fetching over filesystem from {error_path}...\r\n").as_str())?;
113 // not sure why the below isn't appearing
114 // p.expect(format!("fetching over filesystem from
115 // {source_path}...\r\n").as_str())?;
116 p.expect_eventually_and_print("\r\n")?;
117 // p.expect("\r\n")?;
118
119 assert!(git_repo.git_repo.find_commit(main_commit_id).is_ok());
120
121 p.exit()?;
122 for p in [51, 52, 53, 55, 56, 57] {
123 relay::shutdown_relay(8000 + p)?;
124 }
125 Ok(())
126 });
127 // launch relays
128 let _ = join!(
129 r51.listen_until_close(),
130 r52.listen_until_close(),
131 r53.listen_until_close(),
132 r55.listen_until_close(),
133 r56.listen_until_close(),
134 r57.listen_until_close(),
135 );
136 cli_tester_handle.join().unwrap()?;
137 Ok(())
138 }
139}
140
141#[tokio::test]
142#[serial]
143async fn creates_commits_from_open_proposal_with_no_warngins_printed() -> Result<()> {
144 let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?;
145 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
146
147 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
148 Relay::new(8051, None, None),
149 Relay::new(8052, None, None),
150 Relay::new(8053, None, None),
151 Relay::new(8055, None, None),
152 Relay::new(8056, None, None),
153 Relay::new(8057, None, None),
154 );
155 r51.events = events.clone();
156 r55.events = events.clone();
157
158 let git_repo = prep_git_repo()?;
159
160 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
161 let branch_name = get_proposal_branch_name_from_events(&events, FEATURE_BRANCH_NAME_1)?;
162 let proposal_tip = cli_tester_create_proposal_branches_ready_to_send()?
163 .get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?;
164
165 assert!(git_repo.git_repo.find_commit(proposal_tip).is_err());
166
167 let mut p = cli_tester_after_fetch(&git_repo)?;
168 p.send_line(format!("fetch {proposal_tip} refs/heads/{branch_name}").as_str())?;
169 p.send_line("")?;
170 p.expect(format!("fetching over filesystem from {source_path}...\r\n").as_str())?;
171 // expect no errors
172 p.expect_after_whitespace("\r\n")?;
173 p.exit()?;
174 for p in [51, 52, 53, 55, 56, 57] {
175 relay::shutdown_relay(8000 + p)?;
176 }
177
178 assert!(git_repo.git_repo.find_commit(proposal_tip).is_ok());
179
180 Ok(())
181 });
182 // launch relays
183 let _ = join!(
184 r51.listen_until_close(),
185 r52.listen_until_close(),
186 r53.listen_until_close(),
187 r55.listen_until_close(),
188 r56.listen_until_close(),
189 r57.listen_until_close(),
190 );
191
192 cli_tester_handle.join().unwrap()?;
193
194 Ok(())
195}
diff --git a/tests/git_remote_nostr/list.rs b/tests/git_remote_nostr/list.rs
new file mode 100644
index 0000000..1b12bbd
--- /dev/null
+++ b/tests/git_remote_nostr/list.rs
@@ -0,0 +1,362 @@
1
2use super::*;
3
4mod without_state_announcement {
5
6 use super::*;
7
8 #[tokio::test]
9 #[serial]
10 async fn lists_head_and_2_branches_and_commit_ids_from_git_server() -> Result<()> {
11 let source_git_repo = prep_git_repo()?;
12 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
13 std::fs::write(source_git_repo.dir.join("commit.md"), "some content")?;
14 let main_commit_id = source_git_repo.stage_and_commit("commit.md")?;
15
16 source_git_repo.create_branch("vnext")?;
17 source_git_repo.checkout("vnext")?;
18 std::fs::write(source_git_repo.dir.join("vnext.md"), "some content")?;
19 let vnext_commit_id = source_git_repo.stage_and_commit("vnext.md")?;
20 source_git_repo.checkout("main")?;
21
22 let git_repo = prep_git_repo()?;
23 let events = vec![
24 generate_test_key_1_metadata_event("fred"),
25 generate_test_key_1_relay_list_event(),
26 generate_repo_ref_event_with_git_server(vec![
27 source_git_repo.dir.to_str().unwrap().to_string(),
28 ]),
29 ];
30 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
31 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
32 Relay::new(8051, None, None),
33 Relay::new(8052, None, None),
34 Relay::new(8053, None, None),
35 Relay::new(8055, None, None),
36 Relay::new(8056, None, None),
37 Relay::new(8057, None, None),
38 );
39 r51.events = events.clone();
40 r55.events = events;
41
42 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
43 let mut p = cli_tester_after_fetch(&git_repo)?;
44 p.send_line("list")?;
45 p.expect(
46 format!(
47 "fetching ref list over filesystem from {}...\r\n",
48 source_path
49 )
50 .as_str(),
51 )?;
52 // println!("{}", p.expect_eventually("\r\n\r\n")?);
53 let res = p.expect_eventually("\r\n\r\n")?;
54 p.exit()?;
55 for p in [51, 52, 53, 55, 56, 57] {
56 relay::shutdown_relay(8000 + p)?;
57 }
58 assert_eq!(
59 res.split("\r\n")
60 .map(|e| e.to_string())
61 .collect::<HashSet<String>>(),
62 HashSet::from([
63 "@refs/heads/main HEAD".to_string(),
64 format!("{} refs/heads/main", main_commit_id),
65 format!("{} refs/heads/vnext", vnext_commit_id),
66 ]),
67 );
68 Ok(())
69 });
70 // launch relays
71 let _ = join!(
72 r51.listen_until_close(),
73 r52.listen_until_close(),
74 r53.listen_until_close(),
75 r55.listen_until_close(),
76 r56.listen_until_close(),
77 r57.listen_until_close(),
78 );
79 cli_tester_handle.join().unwrap()?;
80 Ok(())
81 }
82}
83mod with_state_announcement {
84
85 use super::*;
86
87 mod when_announcement_matches_git_server {
88
89 use super::*;
90
91 #[tokio::test]
92 #[serial]
93 async fn lists_head_and_2_branches_and_commit_ids_announcement() -> Result<()> {
94 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
95 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
96
97 let main_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
98 let example_commit_id = source_git_repo.get_tip_of_local_branch("example-branch")?;
99
100 let git_repo = prep_git_repo()?;
101 let events = vec![
102 generate_test_key_1_metadata_event("fred"),
103 generate_test_key_1_relay_list_event(),
104 generate_repo_ref_event_with_git_server(vec![
105 source_git_repo.dir.to_str().unwrap().to_string(),
106 ]),
107 state_event,
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 let mut p = cli_tester_after_fetch(&git_repo)?;
123 p.send_line("list")?;
124 p.expect(
125 format!(
126 "fetching ref list over filesystem from {}...\r\n",
127 source_path
128 )
129 .as_str(),
130 )?;
131 // println!("{}", p.expect_eventually("\r\n\r\n")?);
132 let res = p.expect_eventually("\r\n\r\n")?;
133 p.exit()?;
134 for p in [51, 52, 53, 55, 56, 57] {
135 relay::shutdown_relay(8000 + p)?;
136 }
137 assert_eq!(
138 res.split("\r\n")
139 .map(|e| e.to_string())
140 .collect::<HashSet<String>>(),
141 HashSet::from([
142 "@refs/heads/main HEAD".to_string(),
143 format!("{} refs/heads/main", main_commit_id),
144 format!("{} refs/heads/example-branch", example_commit_id),
145 ]),
146 );
147
148 Ok(())
149 });
150 // launch relays
151 let _ = join!(
152 r51.listen_until_close(),
153 r52.listen_until_close(),
154 r53.listen_until_close(),
155 r55.listen_until_close(),
156 r56.listen_until_close(),
157 r57.listen_until_close(),
158 );
159 cli_tester_handle.join().unwrap()?;
160 Ok(())
161 }
162 }
163 mod when_announcement_doesnt_match_git_server {
164
165 use super::*;
166
167 #[tokio::test]
168 #[serial]
169 async fn anouncement_state_is_used() -> Result<()> {
170 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
171 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
172 let main_original_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
173
174 {
175 // add commit to main on git server
176 let tmp_repo = GitTestRepo::clone_repo(&source_git_repo)?;
177 std::fs::write(tmp_repo.dir.join("commitx.md"), "some content")?;
178 tmp_repo.stage_and_commit("commitx.md")?;
179 let mut remote = tmp_repo.git_repo.find_remote("origin")?;
180 remote.push(&["refs/heads/main:refs/heads/main"], None)?;
181 }
182
183 let main_updated_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
184 assert_ne!(main_original_commit_id, main_updated_commit_id);
185 let example_commit_id = source_git_repo.get_tip_of_local_branch("example-branch")?;
186
187 let git_repo = prep_git_repo()?;
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 state_event,
195 ];
196 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
197 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
198 Relay::new(8051, None, None),
199 Relay::new(8052, None, None),
200 Relay::new(8053, None, None),
201 Relay::new(8055, None, None),
202 Relay::new(8056, None, None),
203 Relay::new(8057, None, None),
204 );
205 r51.events = events.clone();
206 r55.events = events;
207
208 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
209 let mut p = cli_tester_after_fetch(&git_repo)?;
210 p.send_line("list")?;
211 p.expect(
212 format!(
213 "fetching ref list over filesystem from {}...\r\n",
214 source_path
215 )
216 .as_str(),
217 )?;
218 p.expect(
219 format!(
220 "WARNING: {} refs/heads/main is out of sync with nostr \r\n",
221 source_path
222 )
223 .as_str(),
224 )?;
225
226 // println!("{}", p.expect_eventually("\r\n\r\n")?);
227 let res = p.expect_eventually("\r\n\r\n")?;
228 p.exit()?;
229 for p in [51, 52, 53, 55, 56, 57] {
230 relay::shutdown_relay(8000 + p)?;
231 }
232 assert_eq!(
233 res.split("\r\n")
234 .map(|e| e.to_string())
235 .collect::<HashSet<String>>(),
236 HashSet::from([
237 "@refs/heads/main HEAD".to_string(),
238 format!("{} refs/heads/main", main_original_commit_id),
239 format!("{} refs/heads/example-branch", example_commit_id),
240 ]),
241 );
242 Ok(())
243 });
244 // launch relays
245 let _ = join!(
246 r51.listen_until_close(),
247 r52.listen_until_close(),
248 r53.listen_until_close(),
249 r55.listen_until_close(),
250 r56.listen_until_close(),
251 r57.listen_until_close(),
252 );
253 cli_tester_handle.join().unwrap()?;
254 Ok(())
255 }
256 }
257
258 mod when_there_are_open_proposals {
259
260 use super::*;
261
262 #[tokio::test]
263 #[serial]
264 async fn open_proposal_listed_in_prs_namespace() -> Result<()> {
265 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
266 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
267
268 let main_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
269 let example_commit_id = source_git_repo.get_tip_of_local_branch("example-branch")?;
270
271 let git_repo = prep_git_repo()?;
272
273 let events = vec![
274 generate_test_key_1_metadata_event("fred"),
275 generate_test_key_1_relay_list_event(),
276 generate_repo_ref_event_with_git_server(vec![
277 source_git_repo.dir.to_str().unwrap().to_string(),
278 ]),
279 state_event,
280 ];
281 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
282 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
283 Relay::new(8051, None, None),
284 Relay::new(8052, None, None),
285 Relay::new(8053, None, None),
286 Relay::new(8055, None, None),
287 Relay::new(8056, None, None),
288 Relay::new(8057, None, None),
289 );
290 r51.events = events.clone();
291 r55.events = events;
292
293 let cli_tester_handle = std::thread::spawn(move || -> Result<String> {
294 cli_tester_create_proposals()?;
295
296 let mut p = cli_tester_after_fetch(&git_repo)?;
297 p.send_line("list")?;
298 p.expect(
299 format!(
300 "fetching ref list over filesystem from {}...\r\n",
301 source_path
302 )
303 .as_str(),
304 )?;
305 // println!("{}", p.expect_eventually("\r\n\r\n")?);
306 let res = p.expect_eventually("\r\n\r\n")?;
307
308 p.exit()?;
309 for p in [51, 52, 53, 55, 56, 57] {
310 relay::shutdown_relay(8000 + p)?;
311 }
312 Ok(res)
313 });
314 // launch relays
315 let _ = join!(
316 r51.listen_until_close(),
317 r52.listen_until_close(),
318 r53.listen_until_close(),
319 r55.listen_until_close(),
320 r56.listen_until_close(),
321 r57.listen_until_close(),
322 );
323
324 let res = cli_tester_handle.join().unwrap()?;
325
326 let proposal_creation_repo = cli_tester_create_proposal_branches_ready_to_send()?;
327
328 let mut pr_refs = vec![];
329 for name in [
330 FEATURE_BRANCH_NAME_1,
331 FEATURE_BRANCH_NAME_2,
332 FEATURE_BRANCH_NAME_3,
333 ] {
334 pr_refs.push(format!(
335 "{} refs/heads/{}",
336 proposal_creation_repo.get_tip_of_local_branch(name)?,
337 get_proposal_branch_name_from_events(&r55.events, name)?,
338 ));
339 }
340
341 assert_eq!(
342 res.split("\r\n")
343 .map(|e| e.to_string())
344 .collect::<HashSet<String>>(),
345 [
346 vec![
347 "@refs/heads/main HEAD".to_string(),
348 format!("{} refs/heads/main", main_commit_id),
349 format!("{} refs/heads/example-branch", example_commit_id),
350 ],
351 pr_refs,
352 ]
353 .concat()
354 .iter()
355 .cloned()
356 .collect::<HashSet<String>>()
357 );
358
359 Ok(())
360 }
361 }
362}
diff --git a/tests/git_remote_nostr/main.rs b/tests/git_remote_nostr/main.rs
index 7251204..61a2dc1 100644
--- a/tests/git_remote_nostr/main.rs
+++ b/tests/git_remote_nostr/main.rs
@@ -9,6 +9,10 @@ use relay::Relay;
9use serial_test::serial; 9use serial_test::serial;
10use test_utils::{git::GitTestRepo, *}; 10use test_utils::{git::GitTestRepo, *};
11 11
12mod fetch;
13mod list;
14mod push;
15
12static NOSTR_REMOTE_NAME: &str = "nostr"; 16static NOSTR_REMOTE_NAME: &str = "nostr";
13static STATE_KIND: nostr::Kind = Kind::Custom(30618); 17static STATE_KIND: nostr::Kind = Kind::Custom(30618);
14 18
@@ -268,2071 +272,3 @@ mod initially_runs_fetch {
268 Ok(()) 272 Ok(())
269 } 273 }
270} 274}
271
272mod list {
273
274 use super::*;
275
276 mod without_state_announcement {
277
278 use super::*;
279
280 #[tokio::test]
281 #[serial]
282 async fn lists_head_and_2_branches_and_commit_ids_from_git_server() -> Result<()> {
283 let source_git_repo = prep_git_repo()?;
284 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
285 std::fs::write(source_git_repo.dir.join("commit.md"), "some content")?;
286 let main_commit_id = source_git_repo.stage_and_commit("commit.md")?;
287
288 source_git_repo.create_branch("vnext")?;
289 source_git_repo.checkout("vnext")?;
290 std::fs::write(source_git_repo.dir.join("vnext.md"), "some content")?;
291 let vnext_commit_id = source_git_repo.stage_and_commit("vnext.md")?;
292 source_git_repo.checkout("main")?;
293
294 let git_repo = prep_git_repo()?;
295 let events = vec![
296 generate_test_key_1_metadata_event("fred"),
297 generate_test_key_1_relay_list_event(),
298 generate_repo_ref_event_with_git_server(vec![
299 source_git_repo.dir.to_str().unwrap().to_string(),
300 ]),
301 ];
302 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
303 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
304 Relay::new(8051, None, None),
305 Relay::new(8052, None, None),
306 Relay::new(8053, None, None),
307 Relay::new(8055, None, None),
308 Relay::new(8056, None, None),
309 Relay::new(8057, None, None),
310 );
311 r51.events = events.clone();
312 r55.events = events;
313
314 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
315 let mut p = cli_tester_after_fetch(&git_repo)?;
316 p.send_line("list")?;
317 p.expect(
318 format!(
319 "fetching ref list over filesystem from {}...\r\n",
320 source_path
321 )
322 .as_str(),
323 )?;
324 // println!("{}", p.expect_eventually("\r\n\r\n")?);
325 let res = p.expect_eventually("\r\n\r\n")?;
326 p.exit()?;
327 for p in [51, 52, 53, 55, 56, 57] {
328 relay::shutdown_relay(8000 + p)?;
329 }
330 assert_eq!(
331 res.split("\r\n")
332 .map(|e| e.to_string())
333 .collect::<HashSet<String>>(),
334 HashSet::from([
335 "@refs/heads/main HEAD".to_string(),
336 format!("{} refs/heads/main", main_commit_id),
337 format!("{} refs/heads/vnext", vnext_commit_id),
338 ]),
339 );
340 Ok(())
341 });
342 // launch relays
343 let _ = join!(
344 r51.listen_until_close(),
345 r52.listen_until_close(),
346 r53.listen_until_close(),
347 r55.listen_until_close(),
348 r56.listen_until_close(),
349 r57.listen_until_close(),
350 );
351 cli_tester_handle.join().unwrap()?;
352 Ok(())
353 }
354 }
355 mod with_state_announcement {
356
357 use super::*;
358
359 mod when_announcement_matches_git_server {
360
361 use super::*;
362
363 #[tokio::test]
364 #[serial]
365 async fn lists_head_and_2_branches_and_commit_ids_announcement() -> Result<()> {
366 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
367 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
368
369 let main_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
370 let example_commit_id =
371 source_git_repo.get_tip_of_local_branch("example-branch")?;
372
373 let git_repo = prep_git_repo()?;
374 let events = vec![
375 generate_test_key_1_metadata_event("fred"),
376 generate_test_key_1_relay_list_event(),
377 generate_repo_ref_event_with_git_server(vec![
378 source_git_repo.dir.to_str().unwrap().to_string(),
379 ]),
380 state_event,
381 ];
382 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
383 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
384 Relay::new(8051, None, None),
385 Relay::new(8052, None, None),
386 Relay::new(8053, None, None),
387 Relay::new(8055, None, None),
388 Relay::new(8056, None, None),
389 Relay::new(8057, None, None),
390 );
391 r51.events = events.clone();
392 r55.events = events;
393
394 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
395 let mut p = cli_tester_after_fetch(&git_repo)?;
396 p.send_line("list")?;
397 p.expect(
398 format!(
399 "fetching ref list over filesystem from {}...\r\n",
400 source_path
401 )
402 .as_str(),
403 )?;
404 // println!("{}", p.expect_eventually("\r\n\r\n")?);
405 let res = p.expect_eventually("\r\n\r\n")?;
406 p.exit()?;
407 for p in [51, 52, 53, 55, 56, 57] {
408 relay::shutdown_relay(8000 + p)?;
409 }
410 assert_eq!(
411 res.split("\r\n")
412 .map(|e| e.to_string())
413 .collect::<HashSet<String>>(),
414 HashSet::from([
415 "@refs/heads/main HEAD".to_string(),
416 format!("{} refs/heads/main", main_commit_id),
417 format!("{} refs/heads/example-branch", example_commit_id),
418 ]),
419 );
420
421 Ok(())
422 });
423 // launch relays
424 let _ = join!(
425 r51.listen_until_close(),
426 r52.listen_until_close(),
427 r53.listen_until_close(),
428 r55.listen_until_close(),
429 r56.listen_until_close(),
430 r57.listen_until_close(),
431 );
432 cli_tester_handle.join().unwrap()?;
433 Ok(())
434 }
435 }
436 mod when_announcement_doesnt_match_git_server {
437
438 use super::*;
439
440 #[tokio::test]
441 #[serial]
442 async fn anouncement_state_is_used() -> Result<()> {
443 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
444 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
445 let main_original_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
446
447 {
448 // add commit to main on git server
449 let tmp_repo = GitTestRepo::clone_repo(&source_git_repo)?;
450 std::fs::write(tmp_repo.dir.join("commitx.md"), "some content")?;
451 tmp_repo.stage_and_commit("commitx.md")?;
452 let mut remote = tmp_repo.git_repo.find_remote("origin")?;
453 remote.push(&["refs/heads/main:refs/heads/main"], None)?;
454 }
455
456 let main_updated_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
457 assert_ne!(main_original_commit_id, main_updated_commit_id);
458 let example_commit_id =
459 source_git_repo.get_tip_of_local_branch("example-branch")?;
460
461 let git_repo = prep_git_repo()?;
462 let events = vec![
463 generate_test_key_1_metadata_event("fred"),
464 generate_test_key_1_relay_list_event(),
465 generate_repo_ref_event_with_git_server(vec![
466 source_git_repo.dir.to_str().unwrap().to_string(),
467 ]),
468 state_event,
469 ];
470 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
471 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
472 Relay::new(8051, None, None),
473 Relay::new(8052, None, None),
474 Relay::new(8053, None, None),
475 Relay::new(8055, None, None),
476 Relay::new(8056, None, None),
477 Relay::new(8057, None, None),
478 );
479 r51.events = events.clone();
480 r55.events = events;
481
482 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
483 let mut p = cli_tester_after_fetch(&git_repo)?;
484 p.send_line("list")?;
485 p.expect(
486 format!(
487 "fetching ref list over filesystem from {}...\r\n",
488 source_path
489 )
490 .as_str(),
491 )?;
492 p.expect(
493 format!(
494 "WARNING: {} refs/heads/main is out of sync with nostr \r\n",
495 source_path
496 )
497 .as_str(),
498 )?;
499
500 // println!("{}", p.expect_eventually("\r\n\r\n")?);
501 let res = p.expect_eventually("\r\n\r\n")?;
502 p.exit()?;
503 for p in [51, 52, 53, 55, 56, 57] {
504 relay::shutdown_relay(8000 + p)?;
505 }
506 assert_eq!(
507 res.split("\r\n")
508 .map(|e| e.to_string())
509 .collect::<HashSet<String>>(),
510 HashSet::from([
511 "@refs/heads/main HEAD".to_string(),
512 format!("{} refs/heads/main", main_original_commit_id),
513 format!("{} refs/heads/example-branch", example_commit_id),
514 ]),
515 );
516 Ok(())
517 });
518 // launch relays
519 let _ = join!(
520 r51.listen_until_close(),
521 r52.listen_until_close(),
522 r53.listen_until_close(),
523 r55.listen_until_close(),
524 r56.listen_until_close(),
525 r57.listen_until_close(),
526 );
527 cli_tester_handle.join().unwrap()?;
528 Ok(())
529 }
530 }
531
532 mod when_there_are_open_proposals {
533
534 use super::*;
535
536 #[tokio::test]
537 #[serial]
538 async fn open_proposal_listed_in_prs_namespace() -> Result<()> {
539 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
540 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
541
542 let main_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
543 let example_commit_id =
544 source_git_repo.get_tip_of_local_branch("example-branch")?;
545
546 let git_repo = prep_git_repo()?;
547
548 let events = vec![
549 generate_test_key_1_metadata_event("fred"),
550 generate_test_key_1_relay_list_event(),
551 generate_repo_ref_event_with_git_server(vec![
552 source_git_repo.dir.to_str().unwrap().to_string(),
553 ]),
554 state_event,
555 ];
556 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
557 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
558 Relay::new(8051, None, None),
559 Relay::new(8052, None, None),
560 Relay::new(8053, None, None),
561 Relay::new(8055, None, None),
562 Relay::new(8056, None, None),
563 Relay::new(8057, None, None),
564 );
565 r51.events = events.clone();
566 r55.events = events;
567
568 let cli_tester_handle = std::thread::spawn(move || -> Result<String> {
569 cli_tester_create_proposals()?;
570
571 let mut p = cli_tester_after_fetch(&git_repo)?;
572 p.send_line("list")?;
573 p.expect(
574 format!(
575 "fetching ref list over filesystem from {}...\r\n",
576 source_path
577 )
578 .as_str(),
579 )?;
580 // println!("{}", p.expect_eventually("\r\n\r\n")?);
581 let res = p.expect_eventually("\r\n\r\n")?;
582
583 p.exit()?;
584 for p in [51, 52, 53, 55, 56, 57] {
585 relay::shutdown_relay(8000 + p)?;
586 }
587 Ok(res)
588 });
589 // launch relays
590 let _ = join!(
591 r51.listen_until_close(),
592 r52.listen_until_close(),
593 r53.listen_until_close(),
594 r55.listen_until_close(),
595 r56.listen_until_close(),
596 r57.listen_until_close(),
597 );
598
599 let res = cli_tester_handle.join().unwrap()?;
600
601 let proposal_creation_repo = cli_tester_create_proposal_branches_ready_to_send()?;
602
603 let mut pr_refs = vec![];
604 for name in [
605 FEATURE_BRANCH_NAME_1,
606 FEATURE_BRANCH_NAME_2,
607 FEATURE_BRANCH_NAME_3,
608 ] {
609 pr_refs.push(format!(
610 "{} refs/heads/{}",
611 proposal_creation_repo.get_tip_of_local_branch(name)?,
612 get_proposal_branch_name_from_events(&r55.events, name)?,
613 ));
614 }
615
616 assert_eq!(
617 res.split("\r\n")
618 .map(|e| e.to_string())
619 .collect::<HashSet<String>>(),
620 [
621 vec![
622 "@refs/heads/main HEAD".to_string(),
623 format!("{} refs/heads/main", main_commit_id),
624 format!("{} refs/heads/example-branch", example_commit_id),
625 ],
626 pr_refs,
627 ]
628 .concat()
629 .iter()
630 .cloned()
631 .collect::<HashSet<String>>()
632 );
633
634 Ok(())
635 }
636 }
637 }
638}
639
640mod fetch {
641
642 use super::*;
643
644 #[tokio::test]
645 #[serial]
646 async fn fetch_downloads_speficied_commits_from_git_server() -> Result<()> {
647 let source_git_repo = prep_git_repo()?;
648 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
649
650 std::fs::write(source_git_repo.dir.join("commit.md"), "some content")?;
651 let main_commit_id = source_git_repo.stage_and_commit("commit.md")?;
652
653 source_git_repo.create_branch("vnext")?;
654 source_git_repo.checkout("vnext")?;
655 std::fs::write(source_git_repo.dir.join("vnext.md"), "some content")?;
656 let vnext_commit_id = source_git_repo.stage_and_commit("vnext.md")?;
657
658 let git_repo = prep_git_repo()?;
659 let events = vec![
660 generate_test_key_1_metadata_event("fred"),
661 generate_test_key_1_relay_list_event(),
662 generate_repo_ref_event_with_git_server(vec![
663 source_git_repo.dir.to_str().unwrap().to_string(),
664 ]),
665 ];
666 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
667 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
668 Relay::new(8051, None, None),
669 Relay::new(8052, None, None),
670 Relay::new(8053, None, None),
671 Relay::new(8055, None, None),
672 Relay::new(8056, None, None),
673 Relay::new(8057, None, None),
674 );
675 r51.events = events.clone();
676 r55.events = events;
677
678 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
679 assert!(git_repo.git_repo.find_commit(main_commit_id).is_err());
680 assert!(git_repo.git_repo.find_commit(vnext_commit_id).is_err());
681
682 let mut p = cli_tester_after_fetch(&git_repo)?;
683 p.send_line(format!("fetch {main_commit_id} main").as_str())?;
684 p.send_line(format!("fetch {vnext_commit_id} vnext").as_str())?;
685 p.send_line("")?;
686 p.expect(format!("fetching over filesystem from {source_path}...\r\n").as_str())?;
687 p.expect_eventually_and_print("\r\n")?;
688
689 assert!(git_repo.git_repo.find_commit(main_commit_id).is_ok());
690 assert!(git_repo.git_repo.find_commit(vnext_commit_id).is_ok());
691
692 p.exit()?;
693 for p in [51, 52, 53, 55, 56, 57] {
694 relay::shutdown_relay(8000 + p)?;
695 }
696 Ok(())
697 });
698 // launch relays
699 let _ = join!(
700 r51.listen_until_close(),
701 r52.listen_until_close(),
702 r53.listen_until_close(),
703 r55.listen_until_close(),
704 r56.listen_until_close(),
705 r57.listen_until_close(),
706 );
707 cli_tester_handle.join().unwrap()?;
708 Ok(())
709 }
710
711 mod when_first_git_server_fails_ {
712 use super::*;
713
714 #[tokio::test]
715 #[serial]
716 async fn fetch_downloads_speficied_commits_from_second_git_server() -> Result<()> {
717 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
718 // let source_path = source_git_repo.dir.to_str().unwrap().to_string();
719 let error_path = "./path-doesnt-exist".to_string();
720
721 let main_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
722
723 let git_repo = prep_git_repo_minus_1_commit()?;
724
725 let events = vec![
726 generate_test_key_1_metadata_event("fred"),
727 generate_test_key_1_relay_list_event(),
728 generate_repo_ref_event_with_git_server(vec![
729 error_path.to_string(),
730 source_git_repo.dir.to_str().unwrap().to_string(),
731 ]),
732 state_event,
733 ];
734 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
735 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
736 Relay::new(8051, None, None),
737 Relay::new(8052, None, None),
738 Relay::new(8053, None, None),
739 Relay::new(8055, None, None),
740 Relay::new(8056, None, None),
741 Relay::new(8057, None, None),
742 );
743 r51.events = events.clone();
744 r55.events = events;
745
746 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
747 assert!(git_repo.git_repo.find_commit(main_commit_id).is_err());
748
749 let mut p = cli_tester_after_fetch(&git_repo)?;
750 p.send_line(format!("fetch {main_commit_id} main").as_str())?;
751 p.send_line("")?;
752 p.expect(format!("fetching over filesystem from {error_path}...\r\n").as_str())?;
753 // not sure why the below isn't appearing
754 // p.expect(format!("fetching over filesystem from
755 // {source_path}...\r\n").as_str())?;
756 p.expect_eventually_and_print("\r\n")?;
757 // p.expect("\r\n")?;
758
759 assert!(git_repo.git_repo.find_commit(main_commit_id).is_ok());
760
761 p.exit()?;
762 for p in [51, 52, 53, 55, 56, 57] {
763 relay::shutdown_relay(8000 + p)?;
764 }
765 Ok(())
766 });
767 // launch relays
768 let _ = join!(
769 r51.listen_until_close(),
770 r52.listen_until_close(),
771 r53.listen_until_close(),
772 r55.listen_until_close(),
773 r56.listen_until_close(),
774 r57.listen_until_close(),
775 );
776 cli_tester_handle.join().unwrap()?;
777 Ok(())
778 }
779 }
780
781 #[tokio::test]
782 #[serial]
783 async fn creates_commits_from_open_proposal_with_no_warngins_printed() -> Result<()> {
784 let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?;
785 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
786
787 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
788 Relay::new(8051, None, None),
789 Relay::new(8052, None, None),
790 Relay::new(8053, None, None),
791 Relay::new(8055, None, None),
792 Relay::new(8056, None, None),
793 Relay::new(8057, None, None),
794 );
795 r51.events = events.clone();
796 r55.events = events.clone();
797
798 let git_repo = prep_git_repo()?;
799
800 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
801 let branch_name = get_proposal_branch_name_from_events(&events, FEATURE_BRANCH_NAME_1)?;
802 let proposal_tip = cli_tester_create_proposal_branches_ready_to_send()?
803 .get_tip_of_local_branch(FEATURE_BRANCH_NAME_1)?;
804
805 assert!(git_repo.git_repo.find_commit(proposal_tip).is_err());
806
807 let mut p = cli_tester_after_fetch(&git_repo)?;
808 p.send_line(format!("fetch {proposal_tip} refs/heads/{branch_name}").as_str())?;
809 p.send_line("")?;
810 p.expect(format!("fetching over filesystem from {source_path}...\r\n").as_str())?;
811 // expect no errors
812 p.expect_after_whitespace("\r\n")?;
813 p.exit()?;
814 for p in [51, 52, 53, 55, 56, 57] {
815 relay::shutdown_relay(8000 + p)?;
816 }
817
818 assert!(git_repo.git_repo.find_commit(proposal_tip).is_ok());
819
820 Ok(())
821 });
822 // launch relays
823 let _ = join!(
824 r51.listen_until_close(),
825 r52.listen_until_close(),
826 r53.listen_until_close(),
827 r55.listen_until_close(),
828 r56.listen_until_close(),
829 r57.listen_until_close(),
830 );
831
832 cli_tester_handle.join().unwrap()?;
833
834 Ok(())
835 }
836}
837
838mod push {
839
840 use super::*;
841
842 #[tokio::test]
843 #[serial]
844 async fn new_branch_when_no_state_event_exists() -> Result<()> {
845 generate_repo_with_state_event().await?;
846 Ok(())
847 }
848 mod two_branches_in_batch_one_added_one_updated {
849
850 use super::*;
851
852 #[tokio::test]
853 #[serial]
854 async fn updates_branch_on_git_server() -> Result<()> {
855 let git_repo = prep_git_repo()?;
856 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
857
858 std::fs::write(git_repo.dir.join("commit.md"), "some content")?;
859 let main_commit_id = git_repo.stage_and_commit("commit.md")?;
860
861 git_repo.create_branch("vnext")?;
862 git_repo.checkout("vnext")?;
863 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
864 let vnext_commit_id = git_repo.stage_and_commit("vnext.md")?;
865
866 let events = vec![
867 generate_test_key_1_metadata_event("fred"),
868 generate_test_key_1_relay_list_event(),
869 generate_repo_ref_event_with_git_server(vec![
870 source_git_repo.dir.to_str().unwrap().to_string(),
871 ]),
872 ];
873 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
874 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
875 Relay::new(8051, None, None),
876 Relay::new(8052, None, None),
877 Relay::new(8053, None, None),
878 Relay::new(8055, None, None),
879 Relay::new(8056, None, None),
880 Relay::new(8057, None, None),
881 );
882 r51.events = events.clone();
883 r55.events = events;
884
885 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
886 assert_ne!(
887 source_git_repo.get_tip_of_local_branch("main")?,
888 main_commit_id
889 );
890
891 let mut p =
892 cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
893
894 p.send_line("push refs/heads/main:refs/heads/main")?;
895 p.send_line("push refs/heads/vnext:refs/heads/vnext")?;
896 p.send_line("")?;
897 p.expect_eventually("\r\n\r\n")?;
898 p.exit()?;
899 for p in [51, 52, 53, 55, 56, 57] {
900 relay::shutdown_relay(8000 + p)?;
901 }
902
903 assert_eq!(
904 source_git_repo.get_tip_of_local_branch("main")?,
905 main_commit_id
906 );
907
908 assert_eq!(
909 source_git_repo.get_tip_of_local_branch("vnext")?,
910 vnext_commit_id
911 );
912
913 Ok(())
914 });
915 // launch relays
916 let _ = join!(
917 r51.listen_until_close(),
918 r52.listen_until_close(),
919 r53.listen_until_close(),
920 r55.listen_until_close(),
921 r56.listen_until_close(),
922 r57.listen_until_close(),
923 );
924 cli_tester_handle.join().unwrap()?;
925 Ok(())
926 }
927
928 #[tokio::test]
929 #[serial]
930 async fn remote_refs_updated_in_local_git() -> Result<()> {
931 let git_repo = prep_git_repo()?;
932 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
933
934 std::fs::write(git_repo.dir.join("commit.md"), "some content")?;
935 let main_commit_id = git_repo.stage_and_commit("commit.md")?;
936
937 git_repo.create_branch("vnext")?;
938 git_repo.checkout("vnext")?;
939 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
940 let vnext_commit_id = git_repo.stage_and_commit("vnext.md")?;
941
942 let events = vec![
943 generate_test_key_1_metadata_event("fred"),
944 generate_test_key_1_relay_list_event(),
945 generate_repo_ref_event_with_git_server(vec![
946 source_git_repo.dir.to_str().unwrap().to_string(),
947 ]),
948 ];
949 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
950 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
951 Relay::new(8051, None, None),
952 Relay::new(8052, None, None),
953 Relay::new(8053, None, None),
954 Relay::new(8055, None, None),
955 Relay::new(8056, None, None),
956 Relay::new(8057, None, None),
957 );
958 r51.events = events.clone();
959 r55.events = events;
960
961 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
962 assert_ne!(
963 source_git_repo.get_tip_of_local_branch("main")?,
964 main_commit_id
965 );
966
967 let mut p =
968 cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
969 p.send_line("push refs/heads/main:refs/heads/main")?;
970 p.send_line("push refs/heads/vnext:refs/heads/vnext")?;
971 p.send_line("")?;
972 p.expect_eventually("\r\n\r\n")?;
973 p.exit()?;
974 for p in [51, 52, 53, 55, 56, 57] {
975 relay::shutdown_relay(8000 + p)?;
976 }
977
978 assert_eq!(
979 git_repo
980 .git_repo
981 .find_reference("refs/remotes/nostr/main")?
982 .peel_to_commit()?
983 .id(),
984 main_commit_id,
985 );
986
987 assert_eq!(
988 git_repo
989 .git_repo
990 .find_reference("refs/remotes/nostr/vnext")?
991 .peel_to_commit()?
992 .id(),
993 vnext_commit_id
994 );
995
996 p.exit()?;
997 for p in [51, 52, 53, 55, 56, 57] {
998 relay::shutdown_relay(8000 + p)?;
999 }
1000 Ok(())
1001 });
1002 // launch relays
1003 let _ = join!(
1004 r51.listen_until_close(),
1005 r52.listen_until_close(),
1006 r53.listen_until_close(),
1007 r55.listen_until_close(),
1008 r56.listen_until_close(),
1009 r57.listen_until_close(),
1010 );
1011 cli_tester_handle.join().unwrap()?;
1012 Ok(())
1013 }
1014
1015 #[tokio::test]
1016 #[serial]
1017 async fn prints_git_helper_ok_respose() -> Result<()> {
1018 let git_repo = prep_git_repo()?;
1019 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
1020
1021 std::fs::write(git_repo.dir.join("commit.md"), "some content")?;
1022 let main_commit_id = git_repo.stage_and_commit("commit.md")?;
1023
1024 git_repo.create_branch("vnext")?;
1025 git_repo.checkout("vnext")?;
1026 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
1027 git_repo.stage_and_commit("vnext.md")?;
1028
1029 let events = vec![
1030 generate_test_key_1_metadata_event("fred"),
1031 generate_test_key_1_relay_list_event(),
1032 generate_repo_ref_event_with_git_server(vec![
1033 source_git_repo.dir.to_str().unwrap().to_string(),
1034 ]),
1035 ];
1036 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
1037 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1038 Relay::new(8051, None, None),
1039 Relay::new(8052, None, None),
1040 Relay::new(8053, None, None),
1041 Relay::new(8055, None, None),
1042 Relay::new(8056, None, None),
1043 Relay::new(8057, None, None),
1044 );
1045 r51.events = events.clone();
1046 r55.events = events;
1047
1048 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1049 assert_ne!(
1050 source_git_repo.get_tip_of_local_branch("main")?,
1051 main_commit_id
1052 );
1053
1054 let mut p =
1055 cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
1056
1057 p.send_line("push refs/heads/main:refs/heads/main")?;
1058 p.send_line("push refs/heads/vnext:refs/heads/vnext")?;
1059 p.send_line("")?;
1060 p.expect("ok refs/heads/main\r\n")?;
1061 p.expect("ok refs/heads/vnext\r\n")?;
1062 p.expect("\r\n")?;
1063 p.exit()?;
1064 for p in [51, 52, 53, 55, 56, 57] {
1065 relay::shutdown_relay(8000 + p)?;
1066 }
1067 Ok(())
1068 });
1069 // launch relays
1070 let _ = join!(
1071 r51.listen_until_close(),
1072 r52.listen_until_close(),
1073 r53.listen_until_close(),
1074 r55.listen_until_close(),
1075 r56.listen_until_close(),
1076 r57.listen_until_close(),
1077 );
1078 cli_tester_handle.join().unwrap()?;
1079 Ok(())
1080 }
1081
1082 #[tokio::test]
1083 #[serial]
1084 async fn when_no_existing_state_event_state_on_git_server_published_in_nostr_state_event()
1085 -> Result<()> {
1086 let git_repo = prep_git_repo()?;
1087 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
1088
1089 std::fs::write(git_repo.dir.join("commit.md"), "some content")?;
1090 let main_commit_id = git_repo.stage_and_commit("commit.md")?;
1091
1092 git_repo.create_branch("vnext")?;
1093 git_repo.checkout("vnext")?;
1094 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
1095 let vnext_commit_id = git_repo.stage_and_commit("vnext.md")?;
1096
1097 let events = vec![
1098 generate_test_key_1_metadata_event("fred"),
1099 generate_test_key_1_relay_list_event(),
1100 generate_repo_ref_event_with_git_server(vec![
1101 source_git_repo.dir.to_str().unwrap().to_string(),
1102 ]),
1103 ];
1104 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
1105 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1106 Relay::new(8051, None, None),
1107 Relay::new(8052, None, None),
1108 Relay::new(8053, None, None),
1109 Relay::new(8055, None, None),
1110 Relay::new(8056, None, None),
1111 Relay::new(8057, None, None),
1112 );
1113 r51.events = events.clone();
1114 r55.events = events;
1115
1116 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1117 let mut p =
1118 cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
1119 p.send_line("push refs/heads/main:refs/heads/main")?;
1120 p.send_line("push refs/heads/vnext:refs/heads/vnext")?;
1121 p.send_line("")?;
1122 p.expect_eventually_and_print("\r\n\r\n")?;
1123 p.exit()?;
1124 for p in [51, 52, 53, 55, 56, 57] {
1125 relay::shutdown_relay(8000 + p)?;
1126 }
1127 Ok(())
1128 });
1129 // launch relays
1130 let _ = join!(
1131 r51.listen_until_close(),
1132 r52.listen_until_close(),
1133 r53.listen_until_close(),
1134 r55.listen_until_close(),
1135 r56.listen_until_close(),
1136 r57.listen_until_close(),
1137 );
1138 cli_tester_handle.join().unwrap()?;
1139
1140 let state_event = r56
1141 .events
1142 .iter()
1143 .find(|e| e.kind().eq(&STATE_KIND))
1144 .context("state event not created")?;
1145
1146 assert_eq!(
1147 state_event.identifier(),
1148 generate_repo_ref_event().identifier(),
1149 );
1150 // println!("{:#?}", state_event);
1151 assert_eq!(
1152 state_event
1153 .tags
1154 .iter()
1155 .filter(|t| t.kind().to_string().as_str().ne("d"))
1156 .map(|t| t.as_vec().to_vec())
1157 .collect::<HashSet<Vec<String>>>(),
1158 HashSet::from([
1159 vec!["HEAD".to_string(), "ref: refs/heads/main".to_string()],
1160 vec!["refs/heads/main".to_string(), main_commit_id.to_string()],
1161 vec!["refs/heads/vnext".to_string(), vnext_commit_id.to_string()],
1162 ]),
1163 );
1164 Ok(())
1165 }
1166
1167 #[tokio::test]
1168 #[serial]
1169 async fn existing_state_event_published_in_nostr_state_event() -> Result<()> {
1170 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
1171
1172 let git_repo = prep_git_repo()?;
1173 let example_branch_commit_id = git_repo.get_tip_of_local_branch("main")?.to_string(); // same as example
1174
1175 std::fs::write(git_repo.dir.join("new.md"), "some content")?;
1176 let main_commit_id = git_repo.stage_and_commit("new.md")?;
1177 git_repo.create_branch("vnext")?;
1178 git_repo.checkout("vnext")?;
1179 std::fs::write(git_repo.dir.join("more.md"), "some content")?;
1180 let vnext_commit_id = git_repo.stage_and_commit("more.md")?;
1181
1182 let events = vec![
1183 generate_test_key_1_metadata_event("fred"),
1184 generate_test_key_1_relay_list_event(),
1185 generate_repo_ref_event_with_git_server(vec![
1186 source_git_repo.dir.to_str().unwrap().to_string(),
1187 ]),
1188 state_event.clone(),
1189 ];
1190
1191 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
1192 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1193 Relay::new(8051, None, None),
1194 Relay::new(8052, None, None),
1195 Relay::new(8053, None, None),
1196 Relay::new(8055, None, None),
1197 Relay::new(8056, None, None),
1198 Relay::new(8057, None, None),
1199 );
1200 r51.events = events.clone();
1201 r55.events = events;
1202
1203 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1204 let mut p =
1205 cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
1206 p.send_line("push refs/heads/main:refs/heads/main")?;
1207 p.send_line("push refs/heads/vnext:refs/heads/vnext")?;
1208 p.send_line("")?;
1209 p.expect_eventually_and_print("\r\n\r\n")?;
1210 p.exit()?;
1211 for p in [51, 52, 53, 55, 56, 57] {
1212 relay::shutdown_relay(8000 + p)?;
1213 }
1214 // local refs updated
1215 assert_eq!(
1216 git_repo
1217 .git_repo
1218 .find_reference("refs/remotes/nostr/main")?
1219 .peel_to_commit()?
1220 .id(),
1221 main_commit_id,
1222 );
1223
1224 assert_eq!(
1225 git_repo
1226 .git_repo
1227 .find_reference("refs/remotes/nostr/vnext")?
1228 .peel_to_commit()?
1229 .id(),
1230 vnext_commit_id
1231 );
1232 Ok(())
1233 });
1234 // launch relays
1235 let _ = join!(
1236 r51.listen_until_close(),
1237 r52.listen_until_close(),
1238 r53.listen_until_close(),
1239 r55.listen_until_close(),
1240 r56.listen_until_close(),
1241 r57.listen_until_close(),
1242 );
1243
1244 cli_tester_handle.join().unwrap()?;
1245
1246 // git_server updated
1247 assert_eq!(
1248 source_git_repo.get_tip_of_local_branch("main")?,
1249 main_commit_id
1250 );
1251
1252 assert_eq!(
1253 source_git_repo.get_tip_of_local_branch("vnext")?,
1254 vnext_commit_id
1255 );
1256
1257 // state annoucement updated
1258 let state_event = r56
1259 .events
1260 .iter()
1261 .find(|e| e.kind().eq(&STATE_KIND))
1262 .context("state event not created")?;
1263
1264 // println!("{:#?}", state_event);
1265 assert_eq!(
1266 state_event
1267 .tags
1268 .iter()
1269 .filter(|t| t.kind().to_string().as_str().ne("d"))
1270 .map(|t| t.as_vec().to_vec())
1271 .collect::<HashSet<Vec<String>>>(),
1272 HashSet::from([
1273 vec!["HEAD".to_string(), "ref: refs/heads/main".to_string()],
1274 vec!["refs/heads/main".to_string(), main_commit_id.to_string()],
1275 vec![
1276 "refs/heads/example-branch".to_string(),
1277 example_branch_commit_id.to_string()
1278 ],
1279 vec!["refs/heads/vnext".to_string(), vnext_commit_id.to_string()],
1280 ]),
1281 );
1282 Ok(())
1283 }
1284 }
1285 mod delete_one_branch {
1286
1287 use super::*;
1288
1289 #[tokio::test]
1290 #[serial]
1291 async fn deletes_branch_on_git_server() -> Result<()> {
1292 let git_repo = prep_git_repo()?;
1293
1294 git_repo.create_branch("vnext")?;
1295 git_repo.checkout("vnext")?;
1296 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
1297 let vnext_commit_id = git_repo.stage_and_commit("vnext.md")?;
1298
1299 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
1300
1301 let events = vec![
1302 generate_test_key_1_metadata_event("fred"),
1303 generate_test_key_1_relay_list_event(),
1304 generate_repo_ref_event_with_git_server(vec![
1305 source_git_repo.dir.to_str().unwrap().to_string(),
1306 ]),
1307 ];
1308 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
1309 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1310 Relay::new(8051, None, None),
1311 Relay::new(8052, None, None),
1312 Relay::new(8053, None, None),
1313 Relay::new(8055, None, None),
1314 Relay::new(8056, None, None),
1315 Relay::new(8057, None, None),
1316 );
1317 r51.events = events.clone();
1318 r55.events = events;
1319
1320 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1321 assert_eq!(
1322 source_git_repo
1323 .git_repo
1324 .find_reference("refs/heads/vnext")?
1325 .peel_to_commit()?
1326 .id(),
1327 vnext_commit_id
1328 );
1329
1330 let mut p =
1331 cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
1332 p.send_line("push :refs/heads/vnext")?;
1333 p.send_line("")?;
1334 p.expect_eventually_and_print("\r\n\r\n")?;
1335 p.exit()?;
1336 for p in [51, 52, 53, 55, 56, 57] {
1337 relay::shutdown_relay(8000 + p)?;
1338 }
1339
1340 assert!(
1341 source_git_repo
1342 .git_repo
1343 .find_reference("refs/heads/vnext")
1344 .is_err()
1345 );
1346 Ok(())
1347 });
1348 // launch relays
1349 let _ = join!(
1350 r51.listen_until_close(),
1351 r52.listen_until_close(),
1352 r53.listen_until_close(),
1353 r55.listen_until_close(),
1354 r56.listen_until_close(),
1355 r57.listen_until_close(),
1356 );
1357 cli_tester_handle.join().unwrap()?;
1358 Ok(())
1359 }
1360
1361 #[tokio::test]
1362 #[serial]
1363 async fn remote_refs_updated_in_local_git() -> Result<()> {
1364 let git_repo = prep_git_repo()?;
1365
1366 git_repo.create_branch("vnext")?;
1367 git_repo.checkout("vnext")?;
1368 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
1369 let vnext_commit_id = git_repo.stage_and_commit("vnext.md")?;
1370
1371 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
1372
1373 git_repo
1374 .git_repo
1375 .reference("refs/remotes/nostr/vnext", vnext_commit_id, true, "")?;
1376
1377 let events = vec![
1378 generate_test_key_1_metadata_event("fred"),
1379 generate_test_key_1_relay_list_event(),
1380 generate_repo_ref_event_with_git_server(vec![
1381 source_git_repo.dir.to_str().unwrap().to_string(),
1382 ]),
1383 ];
1384 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
1385 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1386 Relay::new(8051, None, None),
1387 Relay::new(8052, None, None),
1388 Relay::new(8053, None, None),
1389 Relay::new(8055, None, None),
1390 Relay::new(8056, None, None),
1391 Relay::new(8057, None, None),
1392 );
1393 r51.events = events.clone();
1394 r55.events = events;
1395
1396 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1397 assert_eq!(
1398 git_repo
1399 .git_repo
1400 .find_reference("refs/remotes/nostr/vnext")?
1401 .peel_to_commit()?
1402 .id(),
1403 vnext_commit_id
1404 );
1405
1406 let mut p =
1407 cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
1408 p.send_line("push :refs/heads/vnext")?;
1409 p.send_line("")?;
1410 p.expect_eventually("\r\n\r\n")?;
1411 p.exit()?;
1412 for p in [51, 52, 53, 55, 56, 57] {
1413 relay::shutdown_relay(8000 + p)?;
1414 }
1415 assert!(
1416 git_repo
1417 .git_repo
1418 .find_reference("refs/remotes/nostr/vnext")
1419 .is_err()
1420 );
1421 Ok(())
1422 });
1423 // launch relays
1424 let _ = join!(
1425 r51.listen_until_close(),
1426 r52.listen_until_close(),
1427 r53.listen_until_close(),
1428 r55.listen_until_close(),
1429 r56.listen_until_close(),
1430 r57.listen_until_close(),
1431 );
1432 cli_tester_handle.join().unwrap()?;
1433 Ok(())
1434 }
1435
1436 #[tokio::test]
1437 #[serial]
1438 async fn prints_git_helper_ok_respose() -> Result<()> {
1439 let git_repo = prep_git_repo()?;
1440
1441 git_repo.create_branch("vnext")?;
1442 git_repo.checkout("vnext")?;
1443 std::fs::write(git_repo.dir.join("vnext.md"), "some content")?;
1444 let vnext_commit_id = git_repo.stage_and_commit("vnext.md")?;
1445
1446 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
1447
1448 git_repo
1449 .git_repo
1450 .reference("refs/remotes/nostr/vnext", vnext_commit_id, true, "")?;
1451
1452 let events = vec![
1453 generate_test_key_1_metadata_event("fred"),
1454 generate_test_key_1_relay_list_event(),
1455 generate_repo_ref_event_with_git_server(vec![
1456 source_git_repo.dir.to_str().unwrap().to_string(),
1457 ]),
1458 ];
1459 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
1460 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1461 Relay::new(8051, None, None),
1462 Relay::new(8052, None, None),
1463 Relay::new(8053, None, None),
1464 Relay::new(8055, None, None),
1465 Relay::new(8056, None, None),
1466 Relay::new(8057, None, None),
1467 );
1468 r51.events = events.clone();
1469 r55.events = events;
1470
1471 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1472 let mut p =
1473 cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
1474 p.send_line("push :refs/heads/vnext")?;
1475 p.send_line("")?;
1476 // let res = p.expect_eventually("\r\n\r\n")?;
1477 // println!("{res}");
1478 p.expect("ok refs/heads/vnext\r\n")?;
1479 p.expect("\r\n")?;
1480 p.exit()?;
1481 for p in [51, 52, 53, 55, 56, 57] {
1482 relay::shutdown_relay(8000 + p)?;
1483 }
1484 Ok(())
1485 });
1486 // launch relays
1487 let _ = join!(
1488 r51.listen_until_close(),
1489 r52.listen_until_close(),
1490 r53.listen_until_close(),
1491 r55.listen_until_close(),
1492 r56.listen_until_close(),
1493 r57.listen_until_close(),
1494 );
1495 cli_tester_handle.join().unwrap()?;
1496 Ok(())
1497 }
1498
1499 mod when_existing_state_event {
1500 use super::*;
1501
1502 #[tokio::test]
1503 #[serial]
1504 async fn state_event_updated_and_branch_deleted_and_ok_printed() -> Result<()> {
1505 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
1506
1507 let git_repo = prep_git_repo()?;
1508 let main_commit_id = git_repo.get_tip_of_local_branch("main")?.to_string(); // same as example
1509
1510 let events = vec![
1511 generate_test_key_1_metadata_event("fred"),
1512 generate_test_key_1_relay_list_event(),
1513 generate_repo_ref_event_with_git_server(vec![
1514 source_git_repo.dir.to_str().unwrap().to_string(),
1515 ]),
1516 state_event.clone(),
1517 ];
1518
1519 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
1520 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1521 Relay::new(8051, None, None),
1522 Relay::new(8052, None, None),
1523 Relay::new(8053, None, None),
1524 Relay::new(8055, None, None),
1525 Relay::new(8056, None, None),
1526 Relay::new(8057, None, None),
1527 );
1528 r51.events = events.clone();
1529 r55.events = events;
1530
1531 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1532 let mut p =
1533 cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
1534 p.send_line("push :refs/heads/example-branch")?;
1535 p.send_line("")?;
1536 p.expect("ok refs/heads/example-branch\r\n")?;
1537 p.expect("\r\n")?;
1538 p.exit()?;
1539 for p in [51, 52, 53, 55, 56, 57] {
1540 relay::shutdown_relay(8000 + p)?;
1541 }
1542 Ok(())
1543 });
1544 // launch relays
1545 let _ = join!(
1546 r51.listen_until_close(),
1547 r52.listen_until_close(),
1548 r53.listen_until_close(),
1549 r55.listen_until_close(),
1550 r56.listen_until_close(),
1551 r57.listen_until_close(),
1552 );
1553
1554 cli_tester_handle.join().unwrap()?;
1555
1556 let state_event = r56
1557 .events
1558 .iter()
1559 .find(|e| e.kind().eq(&STATE_KIND))
1560 .context("state event not created")?;
1561
1562 // println!("{:#?}", state_event);
1563 assert_eq!(
1564 state_event
1565 .tags
1566 .iter()
1567 .filter(|t| t.kind().to_string().as_str().ne("d"))
1568 .map(|t| t.as_vec().to_vec())
1569 .collect::<HashSet<Vec<String>>>(),
1570 HashSet::from([
1571 vec!["HEAD".to_string(), "ref: refs/heads/main".to_string()],
1572 vec!["refs/heads/main".to_string(), main_commit_id.to_string()],
1573 ]),
1574 );
1575 Ok(())
1576 }
1577
1578 mod already_deleted_on_git_server {
1579 use super::*;
1580
1581 #[tokio::test]
1582 #[serial]
1583 async fn existing_state_event_updated_and_ok_printed() -> Result<()> {
1584 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
1585
1586 {
1587 // delete branch on git server
1588 let tmp_repo = GitTestRepo::clone_repo(&source_git_repo)?;
1589 let mut remote = tmp_repo.git_repo.find_remote("origin")?;
1590 remote.push(&[":refs/heads/example-branch"], None)?;
1591 }
1592
1593 let git_repo = prep_git_repo()?;
1594 let main_commit_id = git_repo.get_tip_of_local_branch("main")?.to_string(); // same as example
1595
1596 let events = vec![
1597 generate_test_key_1_metadata_event("fred"),
1598 generate_test_key_1_relay_list_event(),
1599 generate_repo_ref_event_with_git_server(vec![
1600 source_git_repo.dir.to_str().unwrap().to_string(),
1601 ]),
1602 state_event.clone(),
1603 ];
1604
1605 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
1606 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1607 Relay::new(8051, None, None),
1608 Relay::new(8052, None, None),
1609 Relay::new(8053, None, None),
1610 Relay::new(8055, None, None),
1611 Relay::new(8056, None, None),
1612 Relay::new(8057, None, None),
1613 );
1614 r51.events = events.clone();
1615 r55.events = events;
1616
1617 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1618 let mut p = cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(
1619 &git_repo,
1620 )?;
1621 p.send_line("push :refs/heads/example-branch")?;
1622 p.send_line("")?;
1623 p.expect("ok refs/heads/example-branch\r\n")?;
1624 p.expect("\r\n")?;
1625 p.exit()?;
1626 for p in [51, 52, 53, 55, 56, 57] {
1627 relay::shutdown_relay(8000 + p)?;
1628 }
1629 Ok(())
1630 });
1631 // launch relays
1632 let _ = join!(
1633 r51.listen_until_close(),
1634 r52.listen_until_close(),
1635 r53.listen_until_close(),
1636 r55.listen_until_close(),
1637 r56.listen_until_close(),
1638 r57.listen_until_close(),
1639 );
1640
1641 cli_tester_handle.join().unwrap()?;
1642
1643 let state_event = r56
1644 .events
1645 .iter()
1646 .find(|e| e.kind().eq(&STATE_KIND))
1647 .context("state event not created")?;
1648
1649 // println!("{:#?}", state_event);
1650 assert_eq!(
1651 state_event
1652 .tags
1653 .iter()
1654 .filter(|t| t.kind().to_string().as_str().ne("d"))
1655 .map(|t| t.as_vec().to_vec())
1656 .collect::<HashSet<Vec<String>>>(),
1657 HashSet::from([
1658 vec!["HEAD".to_string(), "ref: refs/heads/main".to_string()],
1659 vec!["refs/heads/main".to_string(), main_commit_id.to_string()],
1660 ]),
1661 );
1662 Ok(())
1663 }
1664 }
1665 }
1666 }
1667
1668 #[tokio::test]
1669 #[serial]
1670 async fn pushes_to_all_git_servers_listed_and_ok_printed() -> Result<()> {
1671 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
1672 let second_source_git_repo = GitTestRepo::duplicate(&source_git_repo)?;
1673
1674 // uppdate announcement with extra git server
1675
1676 let git_repo = prep_git_repo()?;
1677
1678 std::fs::write(git_repo.dir.join("new.md"), "some content")?;
1679 let main_commit_id = git_repo.stage_and_commit("new.md")?;
1680
1681 let events = vec![
1682 generate_test_key_1_metadata_event("fred"),
1683 generate_test_key_1_relay_list_event(),
1684 generate_repo_ref_event_with_git_server(vec![
1685 source_git_repo.dir.to_str().unwrap().to_string(),
1686 second_source_git_repo.dir.to_str().unwrap().to_string(),
1687 ]),
1688 state_event.clone(),
1689 ];
1690
1691 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
1692 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1693 Relay::new(8051, None, None),
1694 Relay::new(8052, None, None),
1695 Relay::new(8053, None, None),
1696 Relay::new(8055, None, None),
1697 Relay::new(8056, None, None),
1698 Relay::new(8057, None, None),
1699 );
1700 r51.events = events.clone();
1701 r55.events = events;
1702
1703 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
1704 let mut p = cli_tester_after_nostr_fetch_and_sent_list_for_push_responds(&git_repo)?;
1705 p.send_line("push refs/heads/main:refs/heads/main")?;
1706 p.send_line("")?;
1707 p.expect("ok refs/heads/main\r\n")?;
1708 p.expect("\r\n")?;
1709 p.exit()?;
1710 for p in [51, 52, 53, 55, 56, 57] {
1711 relay::shutdown_relay(8000 + p)?;
1712 }
1713 Ok(())
1714 });
1715 // launch relays
1716 let _ = join!(
1717 r51.listen_until_close(),
1718 r52.listen_until_close(),
1719 r53.listen_until_close(),
1720 r55.listen_until_close(),
1721 r56.listen_until_close(),
1722 r57.listen_until_close(),
1723 );
1724
1725 cli_tester_handle.join().unwrap()?;
1726
1727 // git_server updated
1728 assert_eq!(
1729 source_git_repo.get_tip_of_local_branch("main")?,
1730 main_commit_id
1731 );
1732 assert_eq!(
1733 second_source_git_repo.get_tip_of_local_branch("main")?,
1734 main_commit_id
1735 );
1736
1737 Ok(())
1738 }
1739
1740 #[tokio::test]
1741 #[serial]
1742 async fn proposal_merge_commit_pushed_to_main_leads_to_status_event_issued() -> Result<()> {
1743 //
1744 let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?;
1745 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
1746
1747 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1748 Relay::new(8051, None, None),
1749 Relay::new(8052, None, None),
1750 Relay::new(8053, None, None),
1751 Relay::new(8055, None, None),
1752 Relay::new(8056, None, None),
1753 Relay::new(8057, None, None),
1754 );
1755 r51.events = events.clone();
1756 r55.events = events.clone();
1757
1758 #[allow(clippy::mutable_key_type)]
1759 let before = r55.events.iter().cloned().collect::<HashSet<Event>>();
1760
1761 let cli_tester_handle = std::thread::spawn(move || -> Result<(String, Oid)> {
1762 let branch_name = get_proposal_branch_name_from_events(&events, FEATURE_BRANCH_NAME_1)?;
1763
1764 let git_repo = clone_git_repo_with_nostr_url()?;
1765 git_repo.checkout_remote_branch(&branch_name)?;
1766 git_repo.checkout("refs/heads/main")?;
1767
1768 std::fs::write(git_repo.dir.join("new.md"), "some content")?;
1769 git_repo.stage_and_commit("new.md")?;
1770
1771 CliTester::new_git_with_remote_helper_from_dir(
1772 &git_repo.dir,
1773 ["merge", &branch_name, "-m", "proposal merge commit message"],
1774 )
1775 .expect_end_eventually_and_print()?;
1776
1777 let oid = git_repo.get_tip_of_local_branch("main")?;
1778
1779 let mut p = CliTester::new_git_with_remote_helper_from_dir(&git_repo.dir, ["push"]);
1780 cli_expect_nostr_fetch(&mut p)?;
1781 p.expect(
1782 format!(
1783 "fetching ref list over filesystem from {}...\r\n",
1784 source_path
1785 )
1786 .as_str(),
1787 )?;
1788
1789 p.expect("merge commit ")?;
1790 // shorthand merge commit id appears in this gap
1791 p.expect_eventually(": create nostr proposal status event\r\n")?;
1792 p.expect(format!("To {}\r\n", get_nostr_remote_url()?).as_str())?;
1793 let output = p.expect_end_eventually()?;
1794
1795 for p in [51, 52, 53, 55, 56, 57] {
1796 relay::shutdown_relay(8000 + p)?;
1797 }
1798
1799 Ok((output, oid))
1800 });
1801 // launch relays
1802 let _ = join!(
1803 r51.listen_until_close(),
1804 r52.listen_until_close(),
1805 r53.listen_until_close(),
1806 r55.listen_until_close(),
1807 r56.listen_until_close(),
1808 r57.listen_until_close(),
1809 );
1810
1811 let (output, oid) = cli_tester_handle.join().unwrap()?;
1812
1813 assert_eq!(
1814 output,
1815 format!(" 431b84e..{} main -> main\r\n", &oid.to_string()[..7])
1816 );
1817
1818 let new_events = r55
1819 .events
1820 .iter()
1821 .cloned()
1822 .collect::<HashSet<Event>>()
1823 .difference(&before)
1824 .cloned()
1825 .collect::<Vec<Event>>();
1826
1827 assert_eq!(new_events.len(), 2, "{new_events:?}");
1828
1829 let proposal = r55
1830 .events
1831 .iter()
1832 .find(|e| {
1833 e.tags()
1834 .iter()
1835 .find(|t| t.as_vec()[0].eq("branch-name"))
1836 .is_some_and(|t| t.as_vec()[1].eq(FEATURE_BRANCH_NAME_1))
1837 })
1838 .unwrap();
1839
1840 let merge_status = new_events
1841 .iter()
1842 .find(|e| e.kind().eq(&Kind::GitStatusApplied))
1843 .unwrap();
1844
1845 assert_eq!(
1846 oid.to_string(),
1847 merge_status
1848 .tags
1849 .iter()
1850 .find(|t| t.as_vec()[0].eq("merge-commit-id"))
1851 .unwrap()
1852 .as_vec()[1],
1853 "status sets correct merge-commit-id tag"
1854 );
1855
1856 let proposal_tip = r55
1857 .events
1858 .iter()
1859 .filter(|e| {
1860 e.tags()
1861 .iter()
1862 .any(|t| t.as_vec()[1].eq(&proposal.id().to_string()))
1863 && e.kind().eq(&Kind::GitPatch)
1864 })
1865 .last()
1866 .unwrap();
1867
1868 assert_eq!(
1869 proposal_tip.id().to_string(),
1870 merge_status
1871 .tags
1872 .iter()
1873 .find(|t| t.as_vec().len().eq(&4) && t.as_vec()[3].eq("mention"))
1874 .unwrap()
1875 .as_vec()[1],
1876 "status mentions proposal tip event \r\nmerge status:\r\n{}\r\nproposal tip:\r\n{}",
1877 merge_status.as_json(),
1878 proposal_tip.as_json(),
1879 );
1880
1881 assert_eq!(
1882 proposal.id().to_string(),
1883 merge_status
1884 .tags
1885 .iter()
1886 .find(|t| t.is_root())
1887 .unwrap()
1888 .as_vec()[1],
1889 "status tags proposal id as root \r\nmerge status:\r\n{}\r\nproposal:\r\n{}",
1890 merge_status.as_json(),
1891 proposal.as_json(),
1892 );
1893
1894 Ok(())
1895 }
1896
1897 #[tokio::test]
1898 #[serial]
1899 async fn push_2_commits_to_existing_proposal() -> Result<()> {
1900 let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?;
1901 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
1902
1903 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
1904 Relay::new(8051, None, None),
1905 Relay::new(8052, None, None),
1906 Relay::new(8053, None, None),
1907 Relay::new(8055, None, None),
1908 Relay::new(8056, None, None),
1909 Relay::new(8057, None, None),
1910 );
1911 r51.events = events.clone();
1912 r55.events = events.clone();
1913
1914 #[allow(clippy::mutable_key_type)]
1915 let before = r55.events.iter().cloned().collect::<HashSet<Event>>();
1916
1917 let cli_tester_handle = std::thread::spawn(move || -> Result<(String, String)> {
1918 let branch_name = get_proposal_branch_name_from_events(&events, FEATURE_BRANCH_NAME_1)?;
1919
1920 let git_repo = clone_git_repo_with_nostr_url()?;
1921 git_repo.checkout_remote_branch(&branch_name)?;
1922
1923 std::fs::write(git_repo.dir.join("new.md"), "some content")?;
1924 git_repo.stage_and_commit("new.md")?;
1925
1926 std::fs::write(git_repo.dir.join("new2.md"), "some content")?;
1927 git_repo.stage_and_commit("new2.md")?;
1928
1929 let mut p = CliTester::new_git_with_remote_helper_from_dir(&git_repo.dir, ["push"]);
1930 cli_expect_nostr_fetch(&mut p)?;
1931 p.expect(
1932 format!(
1933 "fetching ref list over filesystem from {}...\r\n",
1934 source_path
1935 )
1936 .as_str(),
1937 )?;
1938 p.expect(format!("To {}\r\n", get_nostr_remote_url()?).as_str())?;
1939 let output = p.expect_end_eventually()?;
1940
1941 for p in [51, 52, 53, 55, 56, 57] {
1942 relay::shutdown_relay(8000 + p)?;
1943 }
1944
1945 Ok((output, branch_name))
1946 });
1947 // launch relays
1948 let _ = join!(
1949 r51.listen_until_close(),
1950 r52.listen_until_close(),
1951 r53.listen_until_close(),
1952 r55.listen_until_close(),
1953 r56.listen_until_close(),
1954 r57.listen_until_close(),
1955 );
1956
1957 let (output, branch_name) = cli_tester_handle.join().unwrap()?;
1958
1959 assert_eq!(
1960 output,
1961 format!(" eb5d678..7de5e41 {branch_name} -> {branch_name}\r\n").as_str(),
1962 );
1963
1964 let new_events = r55
1965 .events
1966 .iter()
1967 .cloned()
1968 .collect::<HashSet<Event>>()
1969 .difference(&before)
1970 .cloned()
1971 .collect::<Vec<Event>>();
1972 assert_eq!(new_events.len(), 2);
1973 let first_new_patch = new_events
1974 .iter()
1975 .find(|e| e.content.contains("new.md"))
1976 .unwrap();
1977 let second_new_patch = new_events
1978 .iter()
1979 .find(|e| e.content.contains("new2.md"))
1980 .unwrap();
1981 assert!(
1982 first_new_patch.content.contains("[PATCH 3/4]"),
1983 "first patch labeled with [PATCH 3/4]"
1984 );
1985 assert!(
1986 second_new_patch.content.contains("[PATCH 4/4]"),
1987 "second patch labeled with [PATCH 4/4]"
1988 );
1989
1990 let proposal = r55
1991 .events
1992 .iter()
1993 .find(|e| {
1994 e.tags()
1995 .iter()
1996 .find(|t| t.as_vec()[0].eq("branch-name"))
1997 .is_some_and(|t| t.as_vec()[1].eq(FEATURE_BRANCH_NAME_1))
1998 })
1999 .unwrap();
2000
2001 assert_eq!(
2002 proposal.id().to_string(),
2003 first_new_patch
2004 .tags
2005 .iter()
2006 .find(|t| t.is_root())
2007 .unwrap()
2008 .as_vec()[1],
2009 "first patch sets proposal id as root"
2010 );
2011
2012 assert_eq!(
2013 first_new_patch.id().to_string(),
2014 second_new_patch
2015 .tags
2016 .iter()
2017 .find(|t| t.is_reply())
2018 .unwrap()
2019 .as_vec()[1],
2020 "second new patch replies to the first new patch"
2021 );
2022
2023 let previous_proposal_tip_event = r55
2024 .events
2025 .iter()
2026 .find(|e| {
2027 e.tags()
2028 .iter()
2029 .any(|t| t.as_vec()[1].eq(&proposal.id().to_string()))
2030 && e.content.contains("[PATCH 2/2]")
2031 })
2032 .unwrap();
2033
2034 assert_eq!(
2035 previous_proposal_tip_event.id().to_string(),
2036 first_new_patch
2037 .tags
2038 .iter()
2039 .find(|t| t.is_reply())
2040 .unwrap()
2041 .as_vec()[1],
2042 "first patch replies to the previous tip of proposal"
2043 );
2044
2045 Ok(())
2046 }
2047
2048 #[tokio::test]
2049 #[serial]
2050 async fn force_push_creates_proposal_revision() -> Result<()> {
2051 let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?;
2052 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
2053
2054 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
2055 Relay::new(8051, None, None),
2056 Relay::new(8052, None, None),
2057 Relay::new(8053, None, None),
2058 Relay::new(8055, None, None),
2059 Relay::new(8056, None, None),
2060 Relay::new(8057, None, None),
2061 );
2062 r51.events = events.clone();
2063 r55.events = events.clone();
2064
2065 #[allow(clippy::mutable_key_type)]
2066 let before = r55.events.iter().cloned().collect::<HashSet<Event>>();
2067
2068 let cli_tester_handle = std::thread::spawn(move || -> Result<(String, String)> {
2069 let branch_name = get_proposal_branch_name_from_events(&events, FEATURE_BRANCH_NAME_1)?;
2070
2071 let git_repo = clone_git_repo_with_nostr_url()?;
2072 let oid = git_repo.checkout_remote_branch(&branch_name)?;
2073 // remove last commit
2074 git_repo.checkout("main")?;
2075 git_repo.git_repo.branch(
2076 &branch_name,
2077 &git_repo.git_repo.find_commit(oid)?.parent(0)?,
2078 true,
2079 )?;
2080 git_repo.checkout(&branch_name)?;
2081
2082 std::fs::write(git_repo.dir.join("new.md"), "some content")?;
2083 git_repo.stage_and_commit("new.md")?;
2084
2085 std::fs::write(git_repo.dir.join("new2.md"), "some content")?;
2086 git_repo.stage_and_commit("new2.md")?;
2087
2088 let mut p =
2089 CliTester::new_git_with_remote_helper_from_dir(&git_repo.dir, ["push", "--force"]);
2090 cli_expect_nostr_fetch(&mut p)?;
2091 p.expect(
2092 format!(
2093 "fetching ref list over filesystem from {}...\r\n",
2094 source_path
2095 )
2096 .as_str(),
2097 )?;
2098 p.expect(format!("To {}\r\n", get_nostr_remote_url()?).as_str())?;
2099 let output = p.expect_end_eventually()?;
2100
2101 for p in [51, 52, 53, 55, 56, 57] {
2102 relay::shutdown_relay(8000 + p)?;
2103 }
2104
2105 Ok((output, branch_name))
2106 });
2107 // launch relays
2108 let _ = join!(
2109 r51.listen_until_close(),
2110 r52.listen_until_close(),
2111 r53.listen_until_close(),
2112 r55.listen_until_close(),
2113 r56.listen_until_close(),
2114 r57.listen_until_close(),
2115 );
2116
2117 let (output, branch_name) = cli_tester_handle.join().unwrap()?;
2118
2119 assert_eq!(
2120 output,
2121 format!(" + eb5d678...8a296c8 {branch_name} -> {branch_name} (forced update)\r\n")
2122 .as_str(),
2123 );
2124
2125 let new_events = r55
2126 .events
2127 .iter()
2128 .cloned()
2129 .collect::<HashSet<Event>>()
2130 .difference(&before)
2131 .cloned()
2132 .collect::<Vec<Event>>();
2133 assert_eq!(new_events.len(), 3);
2134
2135 let proposal = r55
2136 .events
2137 .iter()
2138 .find(|e| {
2139 e.tags()
2140 .iter()
2141 .find(|t| t.as_vec()[0].eq("branch-name"))
2142 .is_some_and(|t| t.as_vec()[1].eq(FEATURE_BRANCH_NAME_1))
2143 })
2144 .unwrap();
2145
2146 let revision_root_patch = new_events
2147 .iter()
2148 .find(|e| e.tags().iter().any(|t| t.as_vec()[1].eq("revision-root")))
2149 .unwrap();
2150
2151 assert_eq!(
2152 proposal.id().to_string(),
2153 revision_root_patch
2154 .tags
2155 .iter()
2156 .find(|t| t.is_reply())
2157 .unwrap()
2158 .as_vec()[1],
2159 "revision root patch replies to original proposal"
2160 );
2161
2162 assert!(
2163 revision_root_patch.content.contains("[PATCH 1/3]"),
2164 "revision root labeled with [PATCH 1/3] event: {revision_root_patch:?}",
2165 );
2166
2167 let second_patch = new_events
2168 .iter()
2169 .find(|e| e.content.contains("new.md"))
2170 .unwrap();
2171 let third_patch = new_events
2172 .iter()
2173 .find(|e| e.content.contains("new2.md"))
2174 .unwrap();
2175 assert!(
2176 second_patch.content.contains("[PATCH 2/3]"),
2177 "second patch labeled with [PATCH 2/3]"
2178 );
2179 assert!(
2180 third_patch.content.contains("[PATCH 3/3]"),
2181 "third patch labeled with [PATCH 3/3]"
2182 );
2183
2184 assert_eq!(
2185 revision_root_patch.id().to_string(),
2186 second_patch
2187 .tags
2188 .iter()
2189 .find(|t| t.is_root())
2190 .unwrap()
2191 .as_vec()[1],
2192 "second patch sets revision id as root"
2193 );
2194
2195 assert_eq!(
2196 second_patch.id().to_string(),
2197 third_patch
2198 .tags
2199 .iter()
2200 .find(|t| t.is_reply())
2201 .unwrap()
2202 .as_vec()[1],
2203 "third patch replies to the second new patch"
2204 );
2205
2206 Ok(())
2207 }
2208
2209 #[tokio::test]
2210 #[serial]
2211 async fn push_new_pr_branch_creates_proposal() -> Result<()> {
2212 let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?;
2213 let source_path = source_git_repo.dir.to_str().unwrap().to_string();
2214
2215 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
2216 Relay::new(8051, None, None),
2217 Relay::new(8052, None, None),
2218 Relay::new(8053, None, None),
2219 Relay::new(8055, None, None),
2220 Relay::new(8056, None, None),
2221 Relay::new(8057, None, None),
2222 );
2223 r51.events = events.clone();
2224 r55.events = events.clone();
2225
2226 #[allow(clippy::mutable_key_type)]
2227 let before = r55.events.iter().cloned().collect::<HashSet<Event>>();
2228 let branch_name = "pr/my-new-proposal";
2229
2230 let cli_tester_handle = std::thread::spawn(move || -> Result<String> {
2231 let mut git_repo = clone_git_repo_with_nostr_url()?;
2232 git_repo.delete_dir_on_drop = false;
2233 git_repo.create_branch(branch_name)?;
2234 git_repo.checkout(branch_name)?;
2235
2236 std::fs::write(git_repo.dir.join("new.md"), "some content")?;
2237 git_repo.stage_and_commit("new.md")?;
2238
2239 std::fs::write(git_repo.dir.join("new2.md"), "some content")?;
2240 git_repo.stage_and_commit("new2.md")?;
2241
2242 let mut p = CliTester::new_git_with_remote_helper_from_dir(
2243 &git_repo.dir,
2244 ["push", "-u", "origin", branch_name],
2245 );
2246 cli_expect_nostr_fetch(&mut p)?;
2247 p.expect(
2248 format!(
2249 "fetching ref list over filesystem from {}...\r\n",
2250 source_path
2251 )
2252 .as_str(),
2253 )?;
2254 p.expect(format!("To {}\r\n", get_nostr_remote_url()?).as_str())?;
2255 let output = p.expect_end_eventually()?;
2256
2257 for p in [51, 52, 53, 55, 56, 57] {
2258 relay::shutdown_relay(8000 + p)?;
2259 }
2260
2261 Ok(output)
2262 });
2263 // launch relays
2264 let _ = join!(
2265 r51.listen_until_close(),
2266 r52.listen_until_close(),
2267 r53.listen_until_close(),
2268 r55.listen_until_close(),
2269 r56.listen_until_close(),
2270 r57.listen_until_close(),
2271 );
2272
2273 let output = cli_tester_handle.join().unwrap()?;
2274
2275 assert_eq!(
2276 output,
2277 format!(" * [new branch] {branch_name} -> {branch_name}\r\nbranch '{branch_name}' set up to track 'origin/{branch_name}'.\r\n").as_str(),
2278 );
2279
2280 let new_events = r55
2281 .events
2282 .iter()
2283 .cloned()
2284 .collect::<HashSet<Event>>()
2285 .difference(&before)
2286 .cloned()
2287 .collect::<Vec<Event>>();
2288 assert_eq!(new_events.len(), 2);
2289
2290 let proposal = new_events
2291 .iter()
2292 .find(|e| e.tags().iter().any(|t| t.as_vec()[1].eq("root")))
2293 .unwrap();
2294
2295 assert!(
2296 proposal.content.contains("new.md"),
2297 "first patch is proposal root"
2298 );
2299
2300 assert!(
2301 proposal.content.contains("[PATCH 1/2]"),
2302 "proposal root labeled with[PATCH 1/2] event: {proposal:?}",
2303 );
2304
2305 assert_eq!(
2306 proposal
2307 .tags()
2308 .iter()
2309 .find(|t| t.as_vec()[0].eq("branch-name"))
2310 .unwrap()
2311 .as_vec()[1],
2312 branch_name.replace("pr/", ""),
2313 );
2314
2315 let second_patch = new_events
2316 .iter()
2317 .find(|e| e.content.contains("new2.md"))
2318 .unwrap();
2319
2320 assert!(
2321 second_patch.content.contains("[PATCH 2/2]"),
2322 "second patch labeled with [PATCH 2/2]"
2323 );
2324
2325 assert_eq!(
2326 proposal.id().to_string(),
2327 second_patch
2328 .tags
2329 .iter()
2330 .find(|t| t.is_root())
2331 .unwrap()
2332 .as_vec()[1],
2333 "second patch sets proposal id as root"
2334 );
2335
2336 Ok(())
2337 }
2338}
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}