Skip to main content

max / makenotwork

7.6 KB · 208 lines History Blame Raw
1 //! SSH management command integration tests.
2 //!
3 //! Tests repo CRUD (via DB functions used by the SSH handlers),
4 //! SSH key delete-by-fingerprint, and issue counts after repo operations.
5
6 use crate::harness::TestHarness;
7 use makenotwork::db;
8
9 // ── Repo CRUD ──
10
11 #[tokio::test]
12 async fn ssh_repo_list_and_info() {
13 let mut h = TestHarness::new().await;
14 h.signup("alice", "alice@example.com", "password123").await;
15 let user = db::users::get_user_by_username(&h.db, &db::Username::from_trusted("alice".into()))
16 .await
17 .unwrap()
18 .unwrap();
19
20 // No repos initially
21 let repos = db::git_repos::get_repos_by_user(&h.db, user.id).await.unwrap();
22 assert!(repos.is_empty());
23
24 // Create a repo
25 let repo = db::git_repos::create_repo(&h.db, user.id, "myproject").await.unwrap();
26 assert_eq!(repo.name, "myproject");
27 assert_eq!(repo.visibility, "public");
28
29 // List should show 1 repo
30 let repos = db::git_repos::get_repos_by_user(&h.db, user.id).await.unwrap();
31 assert_eq!(repos.len(), 1);
32
33 // Info: issue counts should be zero
34 let (open, closed) = db::issues::get_issue_counts(&h.db, repo.id).await.unwrap();
35 assert_eq!(open, 0);
36 assert_eq!(closed, 0);
37 }
38
39 #[tokio::test]
40 async fn ssh_repo_set_visibility() {
41 let mut h = TestHarness::new().await;
42 h.signup("bob", "bob@example.com", "password123").await;
43 let user = db::users::get_user_by_username(&h.db, &db::Username::from_trusted("bob".into()))
44 .await
45 .unwrap()
46 .unwrap();
47
48 let repo = db::git_repos::create_repo(&h.db, user.id, "secret").await.unwrap();
49 assert_eq!(repo.visibility, db::Visibility::Public);
50
51 // Update visibility
52 db::git_repos::update_visibility(&h.db, repo.id, db::Visibility::Private).await.unwrap();
53
54 let updated = db::git_repos::get_repo_by_id(&h.db, repo.id).await.unwrap().unwrap();
55 assert_eq!(updated.visibility, db::Visibility::Private);
56
57 // Also test unlisted
58 db::git_repos::update_visibility(&h.db, repo.id, db::Visibility::Unlisted).await.unwrap();
59 let updated = db::git_repos::get_repo_by_id(&h.db, repo.id).await.unwrap().unwrap();
60 assert_eq!(updated.visibility, db::Visibility::Unlisted);
61 }
62
63 #[tokio::test]
64 async fn ssh_repo_set_description() {
65 let mut h = TestHarness::new().await;
66 h.signup("carol", "carol@example.com", "password123").await;
67 let user = db::users::get_user_by_username(&h.db, &db::Username::from_trusted("carol".into()))
68 .await
69 .unwrap()
70 .unwrap();
71
72 let repo = db::git_repos::create_repo(&h.db, user.id, "docengine").await.unwrap();
73 assert!(repo.description.is_empty());
74
75 db::git_repos::update_repo_settings(&h.db, repo.id, "Markdown rendering engine", repo.visibility)
76 .await
77 .unwrap();
78
79 let updated = db::git_repos::get_repo_by_id(&h.db, repo.id).await.unwrap().unwrap();
80 assert_eq!(updated.description, "Markdown rendering engine");
81 }
82
83 #[tokio::test]
84 async fn ssh_repo_delete_cascades_issues() {
85 let tmp = tempfile::TempDir::new().unwrap();
86 let mut h = TestHarness::with_git_repos(tmp.path().to_str().unwrap().to_string()).await;
87 h.signup("dave", "dave@example.com", "password123").await;
88 h.login("dave", "password123").await;
89
90 let user = db::users::get_user_by_username(&h.db, &db::Username::from_trusted("dave".into()))
91 .await
92 .unwrap()
93 .unwrap();
94
95 // Create a bare repo on disk + DB entry
96 let bare_path = tmp.path().join("dave").join("deleteme.git");
97 std::fs::create_dir_all(&bare_path).unwrap();
98 git2::Repository::init_bare(&bare_path).unwrap();
99
100 // Make a commit so the repo page works
101 {
102 let bare = git2::Repository::open(&bare_path).unwrap();
103 let sig = git2::Signature::now("Test", "test@example.com").unwrap();
104 let blob = bare.blob(b"# Delete Me\n").unwrap();
105 let mut tb = bare.treebuilder(None).unwrap();
106 tb.insert("README.md", blob, 0o100644).unwrap();
107 let tree_oid = tb.write().unwrap();
108 let tree = bare.find_tree(tree_oid).unwrap();
109 bare.commit(Some("refs/heads/main"), &sig, &sig, "init", &tree, &[]).unwrap();
110 bare.set_head("refs/heads/main").unwrap();
111 }
112
113 // Visit to auto-register
114 let resp = h.client.get("/git/dave/deleteme").await;
115 assert!(resp.status.is_success());
116
117 let repo = db::git_repos::get_repo_by_user_and_name(&h.db, user.id, "deleteme")
118 .await
119 .unwrap()
120 .unwrap();
121
122 // Create an issue via direct DB insert (issues are email-only, no web write path)
123 sqlx::query(
124 "INSERT INTO issues (repo_id, number, author_user_id, title, body_markdown, body_html) VALUES ($1, 1, $2, 'TestIssue', 'body', '<p>body</p>')"
125 )
126 .bind(repo.id)
127 .bind(user.id)
128 .execute(&h.db)
129 .await
130 .unwrap();
131
132 let (open, _) = db::issues::get_issue_counts(&h.db, repo.id).await.unwrap();
133 assert_eq!(open, 1);
134
135 // Delete the repo — issues should cascade
136 db::git_repos::delete_repo(&h.db, repo.id).await.unwrap();
137
138 // Verify repo is gone
139 let gone = db::git_repos::get_repo_by_id(&h.db, repo.id).await.unwrap();
140 assert!(gone.is_none());
141 }
142
143 // ── SSH key delete by fingerprint ──
144
145 #[tokio::test]
146 async fn ssh_key_delete_by_fingerprint() {
147 let mut h = TestHarness::new().await;
148 h.signup("eve", "eve@example.com", "password123").await;
149 h.login("eve", "password123").await;
150
151 // Add a key via the API
152 let test_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGrJSsFMsNzFqLOsNjMoVMtQ3fMM4JhPmLPWVOmBsBzq test@example.com";
153 let body = format!("public_key={}&label=laptop", urlencoding::encode(test_key));
154 let resp = h.client.post_form("/api/users/me/ssh-keys", &body).await;
155 assert!(resp.status.is_success(), "Add key failed: {}", resp.text);
156 let json: serde_json::Value = resp.json();
157 let fingerprint = json["fingerprint"].as_str().unwrap().to_string();
158
159 let user = db::users::get_user_by_username(&h.db, &db::Username::from_trusted("eve".into()))
160 .await
161 .unwrap()
162 .unwrap();
163
164 // Delete by fingerprint
165 let deleted = db::ssh_keys::delete_key_by_fingerprint(&h.db, user.id, &fingerprint)
166 .await
167 .unwrap();
168 assert!(deleted);
169
170 // Should not be found again
171 let deleted_again = db::ssh_keys::delete_key_by_fingerprint(&h.db, user.id, &fingerprint)
172 .await
173 .unwrap();
174 assert!(!deleted_again);
175
176 // Verify key list is empty
177 let keys = db::ssh_keys::list_keys_by_user(&h.db, user.id).await.unwrap();
178 assert!(keys.is_empty());
179 }
180
181 #[tokio::test]
182 async fn ssh_key_delete_by_fingerprint_wrong_user() {
183 let mut h = TestHarness::new().await;
184 h.signup("frank", "frank@example.com", "password123").await;
185 h.login("frank", "password123").await;
186
187 let test_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGrJSsFMsNzFqLOsNjMoVMtQ3fMM4JhPmLPWVOmBsBzq test@example.com";
188 let body = format!("public_key={}&label=mykey", urlencoding::encode(test_key));
189 let resp = h.client.post_form("/api/users/me/ssh-keys", &body).await;
190 assert!(resp.status.is_success());
191 let json: serde_json::Value = resp.json();
192 let fingerprint = json["fingerprint"].as_str().unwrap().to_string();
193
194 // Create another user
195 h.client.post_form("/logout", "").await;
196 h.signup("grace", "grace@example.com", "password123").await;
197 let other_user = db::users::get_user_by_username(&h.db, &db::Username::from_trusted("grace".into()))
198 .await
199 .unwrap()
200 .unwrap();
201
202 // Other user can't delete Frank's key
203 let deleted = db::ssh_keys::delete_key_by_fingerprint(&h.db, other_user.id, &fingerprint)
204 .await
205 .unwrap();
206 assert!(!deleted);
207 }
208