upleb.uk

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

summaryrefslogtreecommitdiff
path: root/grasp-audit/src/specs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-12-01 23:47:27 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-12-01 23:47:27 +0000
commit59dbbf0f2986e8d969cc30b57d70f76984a272e3 (patch)
tree303601548a7444dbfbe797684a8cd3371005b0ae /grasp-audit/src/specs
parente6c056023bac4a83930b9c40f4a9513c3680cb67 (diff)
add repo land page and 404 page per GRASP-01
Diffstat (limited to 'grasp-audit/src/specs')
-rw-r--r--grasp-audit/src/specs/grasp01/repository_creation.rs193
1 files changed, 193 insertions, 0 deletions
diff --git a/grasp-audit/src/specs/grasp01/repository_creation.rs b/grasp-audit/src/specs/grasp01/repository_creation.rs
index 0b3eed5..1014aa3 100644
--- a/grasp-audit/src/specs/grasp01/repository_creation.rs
+++ b/grasp-audit/src/specs/grasp01/repository_creation.rs
@@ -27,6 +27,8 @@ impl RepositoryCreationTests {
27 let mut results = crate::AuditResult::new("GRASP-01 Repository Creation Tests"); 27 let mut results = crate::AuditResult::new("GRASP-01 Repository Creation Tests");
28 28
29 results.add(Self::test_bare_repo_created_on_announcement(client, relay_domain).await); 29 results.add(Self::test_bare_repo_created_on_announcement(client, relay_domain).await);
30 results.add(Self::test_webpage_served_for_existing_repo(client, relay_domain).await);
31 results.add(Self::test_404_for_nonexistent_repo(client, relay_domain).await);
30 32
31 results 33 results
32 } 34 }
@@ -109,6 +111,144 @@ impl RepositoryCreationTests {
109 ) 111 )
110 .pass() 112 .pass()
111 } 113 }
114
115 /// Test that a webpage is served for an existing repository
116 ///
117 /// This test verifies:
118 /// 1. Creates a valid repository announcement
119 /// 2. Accesses the repository URL without git service parameters
120 /// 3. Verifies a webpage is returned (any 2xx status with HTML content)
121 ///
122 /// GRASP-01: "SHOULD serve a webpage at the same endpoint linking to git nostr client(s)"
123 pub async fn test_webpage_served_for_existing_repo(
124 client: &AuditClient,
125 relay_domain: &str,
126 ) -> TestResult {
127 let test_name = "test_webpage_served_for_existing_repo";
128 let ctx = TestContext::new(client);
129
130 // Create a repository announcement
131 let repo = match ctx.get_fixture(FixtureKind::ValidRepo).await {
132 Ok(r) => r,
133 Err(e) => {
134 return TestResult::new(
135 test_name,
136 "GRASP-01",
137 "Relay SHOULD serve a webpage for existing repositories",
138 )
139 .fail(format!("Failed to create repo fixture: {}", e))
140 }
141 };
142
143 // Wait for repository creation
144 tokio::time::sleep(std::time::Duration::from_millis(200)).await;
145
146 // Extract repo identifier and npub
147 let repo_id = match repo
148 .tags
149 .iter()
150 .find(|t| t.kind() == TagKind::d())
151 .and_then(|t| t.content())
152 {
153 Some(id) => id.to_string(),
154 None => {
155 return TestResult::new(
156 test_name,
157 "GRASP-01",
158 "Relay SHOULD serve a webpage for existing repositories",
159 )
160 .fail("Repository announcement missing d tag")
161 }
162 };
163
164 let npub = match repo.pubkey.to_bech32() {
165 Ok(n) => n,
166 Err(e) => {
167 return TestResult::new(
168 test_name,
169 "GRASP-01",
170 "Relay SHOULD serve a webpage for existing repositories",
171 )
172 .fail(format!("Failed to convert pubkey to npub: {}", e))
173 }
174 };
175
176 // Check that a webpage is served at the repository URL
177 if let Err(e) = check_webpage_served(relay_domain, &npub, &repo_id).await {
178 return TestResult::new(
179 test_name,
180 "GRASP-01",
181 "Relay SHOULD serve a webpage for existing repositories",
182 )
183 .fail(format!("Webpage not served: {}", e));
184 }
185
186 TestResult::new(
187 test_name,
188 "GRASP-01",
189 "Relay SHOULD serve a webpage for existing repositories",
190 )
191 .pass()
192 }
193
194 /// Test that 404 is returned for non-existent repositories
195 ///
196 /// This test verifies:
197 /// 1. Accesses a URL for a repository that doesn't exist
198 /// 2. Verifies a 404 status is returned
199 ///
200 /// GRASP-01: "...and a 404 page for repositories it doesn't host"
201 pub async fn test_404_for_nonexistent_repo(
202 client: &AuditClient,
203 relay_domain: &str,
204 ) -> TestResult {
205 let test_name = "test_404_for_nonexistent_repo";
206
207 let ctx = TestContext::new(client);
208
209 let repo = match ctx.get_fixture(FixtureKind::ValidRepo).await {
210 Ok(r) => r,
211 Err(e) => {
212 return TestResult::new(
213 test_name,
214 "GRASP-01",
215 "Relay SHOULD serve a webpage for existing repositories",
216 )
217 .fail(format!("Failed to create repo fixture: {}", e))
218 }
219 };
220
221 let npub = match repo.pubkey.to_bech32() {
222 Ok(n) => n,
223 Err(e) => {
224 return TestResult::new(
225 test_name,
226 "GRASP-01",
227 "Relay SHOULD serve a webpage for existing repositories",
228 )
229 .fail(format!("Failed to convert pubkey to npub: {}", e))
230 }
231 };
232 // Use a clearly non-existent repo id but real npub
233 let fake_repo_id = "nonexistent-repo-12345";
234
235 // Check that 404 is returned
236 if let Err(e) = check_404_for_nonexistent_repo(relay_domain, &npub, fake_repo_id).await {
237 return TestResult::new(
238 test_name,
239 "GRASP-01",
240 "Relay SHOULD return 404 for repositories it doesn't host",
241 )
242 .fail(format!("Expected 404, got: {}", e));
243 }
244
245 TestResult::new(
246 test_name,
247 "GRASP-01",
248 "Relay SHOULD return 404 for repositories it doesn't host",
249 )
250 .pass()
251 }
112} 252}
113 253
114/// Helper function to check if a repository is accessible via Smart HTTP service 254/// Helper function to check if a repository is accessible via Smart HTTP service
@@ -156,3 +296,56 @@ async fn check_repo_accessible_via_http(
156 296
157 Ok(()) 297 Ok(())
158} 298}
299
300/// Helper function to check if a webpage is served for an existing repository
301///
302/// Verifies that accessing the repository URL returns a webpage (2xx status)
303/// URL format: http://domain/npub/identifier.git
304async fn check_webpage_served(relay_domain: &str, npub: &str, repo_id: &str) -> Result<(), String> {
305 let repo_url = format!("http://{}/{}/{}.git", relay_domain, npub, repo_id);
306
307 let http_client = reqwest::Client::new();
308 let response = http_client
309 .get(&repo_url)
310 .send()
311 .await
312 .map_err(|e| format!("HTTP request failed: {}", e))?;
313
314 if !response.status().is_success() {
315 return Err(format!(
316 "Expected 2xx status for existing repo webpage, got {} for URL: {}",
317 response.status(),
318 repo_url
319 ));
320 }
321
322 Ok(())
323}
324
325/// Helper function to check that 404 is returned for non-existent repository
326///
327/// Verifies that accessing a non-existent repository URL returns 404
328async fn check_404_for_nonexistent_repo(
329 relay_domain: &str,
330 npub: &str,
331 repo_id: &str,
332) -> Result<(), String> {
333 let repo_url = format!("http://{}/{}/{}.git", relay_domain, npub, repo_id);
334
335 let http_client = reqwest::Client::new();
336 let response = http_client
337 .get(&repo_url)
338 .send()
339 .await
340 .map_err(|e| format!("HTTP request failed: {}", e))?;
341
342 if response.status().as_u16() != 404 {
343 return Err(format!(
344 "Expected 404 status for non-existent repo, got {} for URL: {}",
345 response.status(),
346 repo_url
347 ));
348 }
349
350 Ok(())
351}