git-ents.gitmain
⌘K
foforge
serve.rs135 lines · 5.3 KB · rusthistorycomment on this file
1//! Integration coverage for `git ents serve`'s wiring (`roots.local`):
2//! [`git_ents::commands::serve::build_state`] reuses a real
3//! [`LocalRoot`]'s own seams (the same loose-ref `RefStore` and odb every
4//! other porcelain command uses), signs with the local user's own key
5//! (`roots.web-signing`), and the resulting `ents-web` router carries no
6//! git smart-HTTP surface — all driven in-process via
7//! `tower::ServiceExt::oneshot`, no socket ever bound (`roots.web-agnostic`).
8#![allow(clippy::expect_used, reason = "integration test")]
9
10mod common;
11
12use axum::body::Body;
13use axum::http::{Request, StatusCode};
14use git_ents::commands::members;
15use git_ents::root::LocalRoot;
16use tower::ServiceExt as _;
17
18/// `roots.local`: `git ents serve`'s state is built from an already-open
19/// [`LocalRoot`] (never a second store), and the router it drives exposes
20/// only the web UI — no `/info/refs`, no git wire protocol.
21#[tokio::test]
22// @relation(roots.local, roots.composition, scope=function, role=Verifies)
23async fn serve_reuses_the_local_root_and_exposes_no_git_transport() {
24 let fixture = common::Fixture::new(1);
25 let root = LocalRoot::open(fixture.path()).expect("opens");
26 members::add(&root, "jdc", None, Some(fixture.key_path.clone())).expect("bootstrap");
27
28 let root = LocalRoot::open(fixture.path()).expect("reopen for serve");
29 let state = git_ents::commands::serve::build_state(root, Some(fixture.key_path.clone()))
30 .expect("builds state from the local root");
31 let router = ents_web::router(state);
32
33 let dashboard = router
34 .clone()
35 .oneshot(Request::get("/").body(Body::empty()).expect("request"))
36 .await
37 .expect("in-process call");
38 assert_eq!(dashboard.status(), StatusCode::OK);
39
40 let members_page = router
41 .clone()
42 .oneshot(
43 Request::get("/members")
44 .body(Body::empty())
45 .expect("request"),
46 )
47 .await
48 .expect("in-process call");
49 assert_eq!(members_page.status(), StatusCode::OK);
50
51 let smart_http = router
52 .oneshot(
53 Request::get("/info/refs?service=git-upload-pack")
54 .body(Body::empty())
55 .expect("request"),
56 )
57 .await
58 .expect("in-process call");
59 assert_eq!(
60 smart_http.status(),
61 StatusCode::NOT_FOUND,
62 "git ents serve must never expose git's own smart-HTTP transport"
63 );
64}
65
66/// `roots.web-signing`: the identity chip shows the signer's own enrolled
67/// member username, resolved via `commands::members::find_by_key` (the
68/// same key-match loop `git ents members check` runs) -- not
69/// `actor().name`, which is a fixed `"git-ents"` commit-author wordmark
70/// that would otherwise just duplicate the site logo next to it.
71#[tokio::test]
72// @relation(roots.web-signing, scope=function, role=Verifies)
73async fn serve_identity_chip_shows_the_signers_enrolled_member_username() {
74 let fixture = common::Fixture::new(1);
75 let root = LocalRoot::open(fixture.path()).expect("opens");
76 members::add(&root, "jdc", None, Some(fixture.key_path.clone())).expect("bootstrap");
77
78 let root = LocalRoot::open(fixture.path()).expect("reopen for serve");
79 let state = git_ents::commands::serve::build_state(root, Some(fixture.key_path.clone()))
80 .expect("builds state from the local root");
81 let router = ents_web::router(state);
82
83 let response = router
84 .oneshot(Request::get("/").body(Body::empty()).expect("request"))
85 .await
86 .expect("in-process call");
87 assert_eq!(response.status(), StatusCode::OK);
88 let body = String::from_utf8(
89 axum::body::to_bytes(response.into_body(), usize::MAX)
90 .await
91 .expect("body")
92 .to_vec(),
93 )
94 .expect("utf8 html");
95 assert!(
96 body.contains(r#"class="id-chip" href="/account""#) && body.contains("<span>jdc</span>"),
97 "the id-chip must show the enrolled member's own username: {body}"
98 );
99}
100
101/// A signer whose key names no enrolled member falls back to a short key
102/// fingerprint for the identity chip -- never `actor()`'s `"git-ents"`
103/// wordmark, which would silently duplicate the site logo.
104#[tokio::test]
105// @relation(roots.web-signing, scope=function, role=Verifies)
106async fn serve_identity_chip_falls_back_to_a_fingerprint_when_unenrolled() {
107 let fixture = common::Fixture::new(2);
108 let root = LocalRoot::open(fixture.path()).expect("opens");
109 let state = git_ents::commands::serve::build_state(root, Some(fixture.key_path.clone()))
110 .expect("builds state from the local root");
111 let router = ents_web::router(state);
112
113 let response = router
114 .oneshot(Request::get("/").body(Body::empty()).expect("request"))
115 .await
116 .expect("in-process call");
117 assert_eq!(response.status(), StatusCode::OK);
118 let body = String::from_utf8(
119 axum::body::to_bytes(response.into_body(), usize::MAX)
120 .await
121 .expect("body")
122 .to_vec(),
123 )
124 .expect("utf8 html");
125 let chip = body
126 .split(r#"class="id-chip" href="/account">"#)
127 .nth(1)
128 .and_then(|rest| rest.split("</a>").next())
129 .expect("id-chip renders");
130 assert_ne!(
131 chip, "git-ents",
132 "an unenrolled key must never fall back to the commit-author wordmark"
133 );
134 assert!(!chip.is_empty(), "the fingerprint fallback is never blank");
135}