Skip to content

Commit 9357290

Browse files
committed
test: impl device flow test case
1 parent 3667774 commit 9357290

File tree

13 files changed

+96
-40
lines changed

13 files changed

+96
-40
lines changed

Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+15-21
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,18 @@ readme = "README"
1212
repository = "https://github.com/ymgyt/syndicationd"
1313

1414
[workspace.dependencies]
15-
anyhow = { version = "1" }
16-
async-trait = { version = "0.1.77" }
17-
chrono = { version = "0.4.31" }
18-
clap = { version = "4.4" }
19-
feed-rs = { version = "1.4" }
20-
futures-util = { version = "0.3.30" }
21-
graphql_client = { version = "0.13.0", default-features = false }
22-
moka = { version = "0.12.4", features = ["future"] }
23-
reqwest = { version = "0.11.23", default-features = false, features = ["rustls-tls", "json"] }
24-
serde = { version = "1", features = ["derive"] }
25-
tokio = { version = "1.35" }
26-
tracing = { version = "0.1.40" }
27-
tracing-subscriber = { version = "0.3.18", features = [
28-
"smallvec",
29-
"fmt",
30-
"ansi",
31-
"std",
32-
"env-filter",
33-
"time",
34-
], default-features = false }
35-
url = { version = "2.5.0" }
15+
anyhow = { version = "1" }
16+
async-trait = { version = "0.1.77" }
17+
axum = { version = "0.7.4" }
18+
chrono = { version = "0.4.31" }
19+
clap = { version = "4.4" }
20+
feed-rs = { version = "1.4" }
21+
futures-util = { version = "0.3.30" }
22+
graphql_client = { version = "0.13.0", default-features = false }
23+
moka = { version = "0.12.4", features = ["future"] }
24+
reqwest = { version = "0.11.23", default-features = false, features = ["rustls-tls", "json"] }
25+
serde = { version = "1", features = ["derive"] }
26+
tokio = { version = "1.35" }
27+
tracing = { version = "0.1.40" }
28+
tracing-subscriber = { version = "0.3.18", features = ["smallvec", "fmt", "ansi", "std", "env-filter", "time"], default-features = false }
29+
url = { version = "2.5.0" }

crates/synd_api/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ async-graphql = { version = "7.0", features = ["tracing"] }
1515
async-graphql-axum = { version = "7.0" }
1616
async-trait = { workspace = true }
1717
# TODO configure features
18-
axum = { version = "0.7.4" }
18+
axum = { workspace = true }
1919
chrono = { workspace = true }
2020
clap = { workspace = true, features = ["derive"] }
2121
feed-rs = { workspace = true }

crates/synd_term/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,5 @@ integration = []
4444

4545
[dev-dependencies]
4646
serial_test = { version = "3.0.0", default_features = false, features = ["async", "file_locks"] }
47+
synd_test = { path = "../synd_test" }
4748
tokio-stream = "0.1.14"

crates/synd_term/src/application/mod.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::{
99
auth::{
1010
self,
1111
device_flow::{DeviceAccessTokenResponse, DeviceAuthorizationResponse},
12+
github::DeviceFlow,
1213
AuthenticationProvider, Credential,
1314
},
1415
client::Client,
@@ -37,12 +38,14 @@ pub enum EventLoopControlFlow {
3738

3839
pub struct Config {
3940
pub idle_timer_interval: Duration,
41+
pub github_device_flow: DeviceFlow,
4042
}
4143

4244
impl Default for Config {
4345
fn default() -> Self {
4446
Self {
4547
idle_timer_interval: Duration::from_secs(250),
48+
github_device_flow: DeviceFlow::new(),
4649
}
4750
}
4851
}
@@ -435,12 +438,10 @@ impl Application {
435438
tracing::info!("Start authenticate");
436439
match provider {
437440
AuthenticationProvider::Github => {
441+
let device_flow = self.config.github_device_flow.clone();
438442
let fut = async move {
439443
// TODO: error handling
440-
let res = auth::github::DeviceFlow::new()
441-
.device_authorize_request()
442-
.await
443-
.unwrap();
444+
let res = device_flow.device_authorize_request().await.unwrap();
444445

445446
Ok(Command::DeviceAuthorizationFlow(res))
446447
}

crates/synd_term/src/auth/device_flow.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1+
use std::borrow::Cow;
2+
13
use http::Uri;
24
use serde::{Deserialize, Serialize};
35

46
/// https://datatracker.ietf.org/doc/html/rfc8628#section-3.1
5-
#[derive(Serialize)]
7+
#[derive(Serialize, Deserialize, Debug)]
68
pub struct DeviceAuthorizationRequest<'s> {
7-
pub client_id: &'s str,
8-
pub scope: &'s str,
9+
pub client_id: Cow<'s, str>,
10+
pub scope: Cow<'s, str>,
911
}
1012

1113
/// https://datatracker.ietf.org/doc/html/rfc8628#section-3.2
12-
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
14+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1315
pub struct DeviceAuthorizationResponse {
1416
/// device verification code
1517
pub device_code: String,

crates/synd_term/src/auth/github.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ use crate::{
1313
};
1414

1515
/// https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow
16+
#[derive(Clone)]
1617
pub struct DeviceFlow {
1718
client: Client,
1819
client_id: &'static str,
20+
endpoint: Option<&'static str>,
1921
}
2022

2123
impl DeviceFlow {
@@ -32,6 +34,14 @@ impl DeviceFlow {
3234
Self {
3335
client,
3436
client_id: config::github::CLIENT_ID,
37+
endpoint: None,
38+
}
39+
}
40+
41+
pub fn with_endpoint(self, endpoint: &'static str) -> Self {
42+
Self {
43+
endpoint: Some(endpoint),
44+
..self
3545
}
3646
}
3747

@@ -44,11 +54,11 @@ impl DeviceFlow {
4454

4555
let response = self
4656
.client
47-
.post(Self::DEVICE_AUTHORIZATION_ENDPOINT)
57+
.post(self.endpoint.unwrap_or(Self::DEVICE_AUTHORIZATION_ENDPOINT))
4858
.header(http::header::ACCEPT, "application/json")
4959
.form(&DeviceAuthorizationRequest {
50-
client_id: self.client_id,
51-
scope,
60+
client_id: self.client_id.into(),
61+
scope: scope.into(),
5262
})
5363
.send()
5464
.await?

crates/synd_term/tests/integration.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,38 @@ mod test {
1212
use serial_test::file_serial;
1313
use synd_term::{
1414
application::{Application, Config},
15+
auth::github::DeviceFlow,
1516
client::Client,
1617
ui::theme::Theme,
1718
};
19+
use tokio::net::TcpListener;
1820
use tokio_stream::wrappers::UnboundedReceiverStream;
19-
use tracing::Level;
21+
use tracing_subscriber::EnvFilter;
2022

2123
#[tokio::test(flavor = "multi_thread")]
2224
#[file_serial(a)]
2325
async fn hello_world() -> anyhow::Result<()> {
2426
// TODO: wrap once
2527
tracing_subscriber::fmt()
26-
.with_max_level(Level::DEBUG)
28+
.with_env_filter(EnvFilter::from_default_env())
2729
.with_line_number(true)
2830
.with_file(true)
31+
.with_target(false)
2932
.init();
3033

3134
tracing::info!("TEST hello_world run");
3235

36+
let oauth_addr = ("127.0.0.1", 6000);
37+
let oauth_listener = TcpListener::bind(oauth_addr).await?;
38+
tokio::spawn(synd_test::oauth::serve(oauth_listener));
39+
3340
let endpoint = "http://localhost:5960".parse().unwrap();
3441
let terminal = helper::new_test_terminal();
3542
let client = Client::new(endpoint).unwrap();
3643
let config = Config {
3744
idle_timer_interval: Duration::from_millis(2000),
45+
github_device_flow: DeviceFlow::new()
46+
.with_endpoint("http://localhost:6000/github/login/device/code"),
3847
};
3948
// or mpsc and tokio_stream ReceiverStream
4049
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();

crates/synd_test/Cargo.toml

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ readme.workspace = true
99
repository.workspace = true
1010
version = "0.1.0"
1111

12-
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13-
1412
[dependencies]
13+
anyhow = { workspace = true }
14+
axum = { workspace = true }
15+
synd_term = { path = "../synd_term" }
16+
tokio = { workspace = true, features = ["rt-multi-thread", "net"] }

crates/synd_test/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod oauth;

crates/synd_test/src/oauth.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use axum::{http::StatusCode, routing::post, Form, Json, Router};
2+
use synd_term::auth::device_flow::{DeviceAuthorizationRequest, DeviceAuthorizationResponse};
3+
use tokio::net::TcpListener;
4+
5+
async fn device_authorization(
6+
Form(DeviceAuthorizationRequest { scope, .. }): Form<DeviceAuthorizationRequest<'static>>,
7+
) -> Result<Json<DeviceAuthorizationResponse>, StatusCode> {
8+
if scope != "user:email" {
9+
return Err(StatusCode::BAD_REQUEST);
10+
}
11+
let res = DeviceAuthorizationResponse {
12+
device_code: "DC001".into(),
13+
user_code: "UC123456".into(),
14+
verification_uri: "https://syndicationd.ymgyt.io/test".parse().unwrap(),
15+
verification_uri_complete: None,
16+
expires_in: 3600,
17+
interval: None,
18+
};
19+
20+
Ok(Json(res))
21+
}
22+
23+
pub async fn serve(listener: TcpListener) -> anyhow::Result<()> {
24+
let router = Router::new().route("/github/login/device/code", post(device_authorization));
25+
26+
axum::serve(listener, router).await?;
27+
28+
Ok(())
29+
}

justfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ lint:
2020

2121
# Format toml files
2222
fmt-toml:
23-
taplo fmt **.toml
23+
taplo fmt --config taplo.toml **.toml
2424

2525
# Run integration test
2626
integration:
27-
RUST_LOG="synd_term,integration=debug" cargo nextest run --package syndterm --features integration --test integration --no-capture
27+
RUST_LOG="synd_term,integration=debug" cargo nextest run --package synd_term --features integration --test integration --no-capture
2828

2929
update-gql-schema:
3030
@graphql-client introspect-schema http://localhost:5959/graphql \

taplo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ include = ["Cargo.toml", "crates/**/Cargo.toml"]
22

33
[formatting]
44
align_entries = true
5-
column_width = 120
5+
column_width = 200
66
reorder_keys = true

0 commit comments

Comments
 (0)