feat(pubky): integrate pubky_testnet in pubky crate and pass tests

This commit is contained in:
nazeh
2025-02-12 19:12:19 +03:00
parent 863a78e6e1
commit fee569dc32
16 changed files with 168 additions and 144 deletions

24
Cargo.lock generated
View File

@@ -1245,6 +1245,22 @@ dependencies = [
"url",
]
[[package]]
name = "http-relay"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e21bdfe99224d6d446eb1b6ecc139ed96a7a8f0a77a123158b9c31d1dbbbcb08"
dependencies = [
"anyhow",
"axum",
"axum-server",
"futures-util",
"tokio",
"tower-http",
"tracing",
"url",
]
[[package]]
name = "httparse"
version = "1.9.5"
@@ -2082,8 +2098,6 @@ name = "pubky"
version = "0.3.0"
dependencies = [
"anyhow",
"axum",
"axum-server",
"base64 0.22.1",
"bytes",
"cfg_aliases",
@@ -2095,14 +2109,14 @@ dependencies = [
"futures-util",
"js-sys",
"log",
"mainline",
"pkarr",
"pubky-common 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pubky-homeserver",
"pubky-testnet",
"reqwest",
"thiserror 2.0.11",
"tokio",
"tracing",
"tracing-subscriber",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
@@ -2186,9 +2200,11 @@ name = "pubky-testnet"
version = "0.1.0"
dependencies = [
"anyhow",
"http-relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mainline",
"pkarr-relay",
"pubky",
"pubky-homeserver",
"tokio",
"url",
]

View File

@@ -66,7 +66,7 @@ async fn main() -> Result<()> {
println!("PublicKey: {}", keypair.public_key());
let client = if cli.testnet.unwrap_or_default() {
let client = Client::testnet()?;
let client = Client::builder().testnet().build()?;
// For the purposes of this demo, we need to make sure
// the user has an account on the local homeserver.

View File

@@ -28,7 +28,7 @@ async fn main() -> Result<()> {
.init();
let client = if args.testnet {
Client::testnet()?
Client::builder().testnet().build()?
} else {
Client::builder().build()?
};

View File

@@ -25,7 +25,7 @@ pub struct AuthToken {
/// A namespace to ensure this signature can't be used for any
/// other purposes that share the same message structurea by accident.
namespace: [u8; 10],
/// Version of the [AuthToken], in case we need to upgrade it to support unforseen usecases.
/// Version of the [AuthToken], in case we need to upgrade it to support unforeseen usecases.
///
/// Version 0:
/// - Signer is implicitly the same as the root keypair for

View File

@@ -106,6 +106,9 @@ impl Config {
Self {
io: IoConfig {
bootstrap,
http_port: 0,
https_port: 0,
..Default::default()
},
core: CoreConfig::test(),
@@ -244,6 +247,8 @@ mod tests {
io: IoConfig {
bootstrap: Some(vec![]),
http_port: 0,
https_port: 0,
..Default::default()
},

View File

@@ -19,10 +19,10 @@ pub fn router(state: AppState) -> Router<AppState> {
Router::new()
// - Datastore routes
.route("/pub/", get(read::get))
.route("/pub/{path}", get(read::get))
.route("/pub/{path}", head(read::head))
.route("/pub/{path}", put(write::put))
.route("/pub/{path}", delete(write::delete))
.route("/pub/{*path}", get(read::get))
.route("/pub/{*path}", head(read::head))
.route("/pub/{*path}", put(write::put))
.route("/pub/{*path}", delete(write::delete))
// - Session routes
.route("/session", get(session::session))
.route("/session", delete(session::signout))

View File

@@ -12,8 +12,10 @@ categories = ["web-programming", "authentication", "cryptography"]
[dependencies]
anyhow = "1.0.95"
http-relay = "0.1.0"
mainline = "5.2.0"
pkarr-relay = "0.2.0"
pubky = { version = "0.3.0", path = "../pubky" }
pubky-homeserver = { version = "0.1.0", path = "../pubky-homeserver" }
tokio = { version = "1.43.0", features = ["full"] }
url = "2.5.4"

View File

@@ -19,5 +19,5 @@ If you need to run the testnet in a separate process, for example to test Pubky
1. A local DHT with bootstrapping nodes: `&["localhost:6881"]`
3. A Pkarr Relay running on port [15411](pubky_common::constants::testnet_ports::PKARR_RELAY)
2. A Homeserver with address is hardcoded to `8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo`
6. An HTTP relay running on port [15412](pubky_common::constants::testnet_ports::HTTP_RELAY)
2. A Homeserver with address is hardcoded to `8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo`
4. An HTTP relay running on port [15412](pubky_common::constants::testnet_ports::HTTP_RELAY)

View File

@@ -1,4 +1,7 @@
use anyhow::Result;
use http_relay::HttpRelay;
use pubky::ClientBuilder;
use pubky_homeserver::Homeserver;
use url::Url;
pub struct Testnet {
@@ -34,7 +37,31 @@ impl Testnet {
// === Public Methods ===
// pub fn pubky_client() -> std::result::Result<pubky::Client, pubky::errors::BuildError> {}
/// Run a Pubky Homeserver
pub async fn run_homeserver(&self) -> Result<Homeserver> {
Homeserver::run_test(&self.dht.bootstrap).await
}
/// Run an HTTP Relay
pub async fn run_http_relay(&self) -> Result<HttpRelay> {
HttpRelay::builder().build().await
}
/// Create a [ClientBuilder] and configure it to use this local test network.
pub fn client_builder(&self) -> ClientBuilder {
let bootstrap = self.bootstrap();
let relays = self.relays();
let mut builder = pubky::Client::builder();
builder.pkarr(|builder| {
builder
.bootstrap(bootstrap)
.relays(&relays)
.expect("testnet relays should be valid urls")
});
builder
}
/// Run a new Pkarr relay.
///
@@ -42,6 +69,10 @@ impl Testnet {
pub async fn run_pkarr_relay(&mut self) -> Result<Url> {
let relay = pkarr_relay::Relay::run_test(&self.dht).await?;
Ok(relay.local_url())
let url = relay.local_url();
self.relays.push(relay);
Ok(url)
}
}

View File

@@ -11,46 +11,44 @@ keywords = ["web", "dht", "dns", "decentralized", "identity"]
crate-type = ["cdylib", "rlib"]
[dependencies]
thiserror = "2.0.6"
wasm-bindgen = "0.2.99"
thiserror = "2.0.11"
wasm-bindgen = "0.2.100"
url = "2.5.4"
bytes = "^1.9.0"
bytes = "^1.10.0"
base64 = "0.22.1"
pkarr = { version = "3.0.0", features = ["full"] }
mainline = "5.0.0-rc.1"
pubky-common = { version = "0.2.0", path = "../pubky-common" }
pkarr = { version = "3.1.1", features = ["full"] }
cookie = "0.18.1"
tracing = "0.1.41"
cookie_store = { version = "0.21.1", default-features = false }
anyhow = "1.0.94"
anyhow = "1.0.95"
flume = { version = "0.11.1", default-features = false, features = ["async"] }
futures-util = "0.3.31"
pubky-common = "0.2.0"
# Native dependencies
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
reqwest = { version = "0.12.9", features = ["cookies", "rustls-tls"], default-features = false }
tokio = { version = "1.42.0", features = ["full"] }
reqwest = { version = "0.12.12", features = ["cookies", "rustls-tls"], default-features = false }
tokio = { version = "1.43.0", features = ["full"] }
# Wasm dependencies
[target.'cfg(target_arch = "wasm32")'.dependencies]
reqwest = { version = "0.12.9", default-features = false }
futures-lite = { version = "2.5.0", default-features = false }
wasm-bindgen = "0.2.99"
wasm-bindgen-futures = "0.4.49"
reqwest = { version = "0.12.12", default-features = false }
futures-lite = { version = "2.6.0", default-features = false }
wasm-bindgen = "0.2.100"
wasm-bindgen-futures = "0.4.50"
console_log = { version = "1.0.0", features = ["color"] }
log = "0.4.22"
log = "0.4.25"
js-sys = "0.3.76"
web-sys = "0.3.76"
js-sys = "0.3.77"
web-sys = "0.3.77"
[dev-dependencies]
anyhow = "1.0.94"
axum = "0.7.9"
axum-server = "0.7.1"
http-relay = { path = "../http-relay" }
pubky-homeserver = { path = "../pubky-homeserver" }
tokio = "1.42.0"
anyhow = "1.0.95"
futures-lite = "2.6.0"
pubky-testnet = { path = "../pubky-testnet" }
tokio = "1.43.0"
tracing-subscriber = "0.3.19"
[build-dependencies]
cfg_aliases = "0.2.1"

View File

@@ -14,7 +14,7 @@ use pubky::Client;
async fn main () {
// Mainline Dht testnet and a temporary homeserver for unit testing.
let testnet = Testnet::new(10);
let server = Homeserver::start_test(&testnet).await.unwrap();
let server = Homeserver::run_test(&testnet).await.unwrap();
let client = Client::test(&testnet);

View File

@@ -34,3 +34,10 @@ pub use wasm::Client;
// Re-exports
pub use pkarr::{Keypair, PublicKey};
pub use pubky_common::recovery_file;
pub mod errors {
pub use super::*;
#[cfg(not(wasm_browser))]
pub use native::BuildError;
}

View File

@@ -16,9 +16,6 @@ use std::fmt::Debug;
use std::sync::Arc;
use std::time::Duration;
#[cfg(not(wasm_browser))]
use mainline::Testnet;
static DEFAULT_USER_AGENT: &str = concat!("pubky.org", "@", env!("CARGO_PKG_VERSION"),);
#[macro_export]
@@ -41,18 +38,14 @@ pub struct ClientBuilder {
impl ClientBuilder {
#[cfg(not(wasm_browser))]
/// Sets the following:
/// - Pkarr client's DHT bootstrap nodes = `testnet` bootstrap nodes.
/// - Pkarr client's resolvers = `testnet` bootstrap nodes.
/// - Pkarr client's DHT request timeout = 500 milliseconds. (unless in CI, then it is left as default 2000)
pub fn testnet(&mut self, testnet: &Testnet) -> &mut Self {
let bootstrap = testnet.bootstrap.clone();
self.pkarr.no_default_network().bootstrap(&bootstrap);
if std::env::var("CI").is_err() {
self.pkarr.request_timeout(Duration::from_millis(500));
}
/// Creates a client connected to a local test network with hardcoded configurations:
/// 1. local DHT with bootstrapping nodes: `&["localhost:6881"]`
/// 2. Pkarr Relay running on port [15411][pubky_common::constants::testnet_ports::PKARR_RELAY]
pub fn testnet(&mut self) -> &mut Self {
self.pkarr
.bootstrap(&["localhost:6881"])
.relays(&["http://localhost:5411"])
.expect("relays urls infallible");
self
}
@@ -152,22 +145,10 @@ impl Client {
ClientBuilder::default()
}
#[cfg(not(wasm_browser))]
/// Create a client connected to the local network
/// with the bootstrapping node: `localhost:6881`
pub fn testnet() -> Result<Self, BuildError> {
Self::builder()
.testnet(&Testnet {
bootstrap: vec!["localhost:6881".to_string()],
nodes: vec![],
})
.build()
}
// === Getters ===
#[cfg(test)]
#[cfg(not(wasm_browser))]
/// Alias to `pubky::Client::builder().testnet(testnet).build().unwrap()`
pub(crate) fn test(testnet: &Testnet) -> Client {
Client::builder().testnet(testnet).build().unwrap()
/// Returns a reference to the internal Pkarr Client.
pub fn pkarr(&self) -> &pkarr::Client {
&self.pkarr
}
}

View File

@@ -298,21 +298,17 @@ impl AuthRequest {
mod tests {
use std::time::Duration;
use crate::*;
use http_relay::HttpRelay;
use mainline::Testnet;
use pkarr::Keypair;
use pubky_common::capabilities::{Capabilities, Capability};
use pubky_homeserver::Homeserver;
use pubky_testnet::Testnet;
use reqwest::StatusCode;
#[tokio::test]
async fn basic_authn() {
let testnet = Testnet::new(10).unwrap();
let server = Homeserver::start_test(&testnet).await.unwrap();
let testnet = Testnet::run().await.unwrap();
let server = testnet.run_homeserver().await.unwrap();
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let keypair = Keypair::random();
@@ -350,10 +346,10 @@ mod tests {
#[tokio::test]
async fn authz() {
let testnet = Testnet::new(10).unwrap();
let server = Homeserver::start_test(&testnet).await.unwrap();
let testnet = Testnet::run().await.unwrap();
let server = testnet.run_homeserver().await.unwrap();
let http_relay = HttpRelay::builder().build().await.unwrap();
let http_relay = testnet.run_http_relay().await.unwrap();
let http_relay_url = http_relay.local_link_url();
let keypair = Keypair::random();
@@ -363,13 +359,13 @@ mod tests {
let capabilities: Capabilities =
"/pub/pubky.app/:rw,/pub/foo.bar/file:r".try_into().unwrap();
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let pubky_auth_request = client.auth_request(http_relay_url, &capabilities).unwrap();
// Authenticator side
{
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
client.signup(&keypair, &server.public_key()).await.unwrap();
@@ -422,10 +418,10 @@ mod tests {
#[tokio::test]
async fn multiple_users() {
let testnet = Testnet::new(10).unwrap();
let server = Homeserver::start_test(&testnet).await.unwrap();
let testnet = Testnet::run().await.unwrap();
let server = testnet.run_homeserver().await.unwrap();
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let first_keypair = Keypair::random();
let second_keypair = Keypair::random();
@@ -461,10 +457,10 @@ mod tests {
#[tokio::test]
async fn authz_timeout_reconnect() {
let testnet = Testnet::new(10).unwrap();
let server = Homeserver::start_test(&testnet).await.unwrap();
let testnet = Testnet::run().await.unwrap();
let server = testnet.run_homeserver().await.unwrap();
let http_relay = HttpRelay::builder().build().await.unwrap();
let http_relay = testnet.run_http_relay().await.unwrap();
let http_relay_url = http_relay.local_link_url();
let keypair = Keypair::random();
@@ -474,8 +470,8 @@ mod tests {
let capabilities: Capabilities =
"/pub/pubky.app/:rw,/pub/foo.bar/file:r".try_into().unwrap();
let client = Client::builder()
.pkarr(|builder| builder.no_default_network().bootstrap(&testnet.bootstrap))
let client = testnet
.client_builder()
.request_timeout(Duration::from_millis(1000))
.build()
.unwrap();
@@ -486,19 +482,14 @@ mod tests {
{
let url = pubky_auth_request.url().clone();
let client = testnet.client_builder().build().unwrap();
client.signup(&keypair, &server.public_key()).await.unwrap();
tokio::spawn(async move {
loop {
tokio::time::sleep(Duration::from_millis(400)).await;
let client = Client::builder()
.pkarr(|builder| builder.no_default_network().bootstrap(&testnet.bootstrap))
.build()
.unwrap();
client.signup(&keypair, &server.public_key()).await.unwrap();
client.send_auth_token(&keypair, &url).await.unwrap();
}
tokio::time::sleep(Duration::from_millis(400)).await;
// loop {
client.send_auth_token(&keypair, &url).await.unwrap();
// }
});
}

View File

@@ -136,18 +136,14 @@ impl Client {
#[cfg(test)]
mod tests {
use mainline::Testnet;
use pubky_homeserver::Homeserver;
use crate::Client;
use pubky_testnet::Testnet;
#[tokio::test]
async fn http_get_pubky() {
let testnet = Testnet::new(10).unwrap();
let testnet = Testnet::run().await.unwrap();
let homeserver = testnet.run_homeserver().await.unwrap();
let homeserver = Homeserver::start_test(&testnet).await.unwrap();
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let response = client
.get(format!("https://{}/", homeserver.public_key()))
@@ -160,9 +156,9 @@ mod tests {
#[tokio::test]
async fn http_get_icann() {
let testnet = Testnet::new(10).unwrap();
let testnet = Testnet::run().await.unwrap();
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let response = client
.request(Default::default(), "https://example.com/")

View File

@@ -124,20 +124,17 @@ impl<'a> ListBuilder<'a> {
#[cfg(test)]
mod tests {
use crate::*;
use bytes::Bytes;
use mainline::Testnet;
use pkarr::Keypair;
use pubky_homeserver::Homeserver;
use pubky_testnet::Testnet;
use reqwest::{Method, StatusCode};
#[tokio::test]
async fn put_get_delete() {
let testnet = Testnet::new(10).unwrap();
let server = Homeserver::start_test(&testnet).await.unwrap();
let testnet = Testnet::run().await.unwrap();
let server = testnet.run_homeserver().await.unwrap();
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let keypair = Keypair::random();
@@ -174,10 +171,10 @@ mod tests {
#[tokio::test]
async fn unauthorized_put_delete() {
let testnet = Testnet::new(10).unwrap();
let server = Homeserver::start_test(&testnet).await.unwrap();
let testnet = Testnet::run().await.unwrap();
let server = testnet.run_homeserver().await.unwrap();
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let keypair = Keypair::random();
@@ -188,7 +185,7 @@ mod tests {
let url = format!("pubky://{public_key}/pub/foo.txt");
let url = url.as_str();
let other_client = Client::test(&testnet);
let other_client = testnet.client_builder().build().unwrap();
{
let other = Keypair::random();
@@ -239,10 +236,10 @@ mod tests {
#[tokio::test]
async fn list() {
let testnet = Testnet::new(10).unwrap();
let server = Homeserver::start_test(&testnet).await.unwrap();
let testnet = Testnet::run().await.unwrap();
let server = testnet.run_homeserver().await.unwrap();
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let keypair = Keypair::random();
@@ -442,10 +439,10 @@ mod tests {
#[tokio::test]
async fn list_shallow() {
let testnet = Testnet::new(10).unwrap();
let server = Homeserver::start_test(&testnet).await.unwrap();
let testnet = Testnet::run().await.unwrap();
let server = testnet.run_homeserver().await.unwrap();
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let keypair = Keypair::random();
@@ -652,10 +649,10 @@ mod tests {
#[tokio::test]
async fn list_events() {
let testnet = Testnet::new(10).unwrap();
let server = Homeserver::start_test(&testnet).await.unwrap();
let testnet = Testnet::run().await.unwrap();
let server = testnet.run_homeserver().await.unwrap();
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let keypair = Keypair::random();
@@ -683,7 +680,7 @@ mod tests {
let feed_url = format!("https://{}/events/", server.public_key());
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let cursor;
@@ -748,10 +745,10 @@ mod tests {
#[tokio::test]
async fn read_after_event() {
let testnet = Testnet::new(10).unwrap();
let server = Homeserver::start_test(&testnet).await.unwrap();
let testnet = Testnet::run().await.unwrap();
let server = testnet.run_homeserver().await.unwrap();
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let keypair = Keypair::random();
@@ -765,7 +762,7 @@ mod tests {
let feed_url = format!("https://{}/events/", server.public_key());
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
{
let response = client
@@ -798,9 +795,10 @@ mod tests {
#[tokio::test]
async fn dont_delete_shared_blobs() {
let testnet = Testnet::new(10).unwrap();
let homeserver = Homeserver::start_test(&testnet).await.unwrap();
let client = Client::test(&testnet);
let testnet = Testnet::run().await.unwrap();
let homeserver = testnet.run_homeserver().await.unwrap();
let client = testnet.client_builder().build().unwrap();
let homeserver_pubky = homeserver.public_key();
@@ -867,11 +865,10 @@ mod tests {
#[tokio::test]
async fn stream() {
// TODO: test better streaming API
let testnet = Testnet::run().await.unwrap();
let server = testnet.run_homeserver().await.unwrap();
let testnet = Testnet::new(10).unwrap();
let server = Homeserver::start_test(&testnet).await.unwrap();
let client = Client::test(&testnet);
let client = testnet.client_builder().build().unwrap();
let keypair = Keypair::random();