mirror of
https://github.com/aljazceru/pubky-core.git
synced 2026-01-07 16:24:20 +01:00
feat(pubky): pass all unit tests after using pubky in host
This commit is contained in:
@@ -7,7 +7,6 @@ use crate::Client;
|
||||
mod api;
|
||||
mod cookies;
|
||||
mod http;
|
||||
mod internals;
|
||||
|
||||
pub(crate) use cookies::CookieJar;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use url::Url;
|
||||
use reqwest::IntoUrl;
|
||||
|
||||
use crate::{error::Result, shared::list_builder::ListBuilder, Client};
|
||||
|
||||
@@ -6,7 +6,7 @@ impl Client {
|
||||
/// Returns a [ListBuilder] to help pass options before calling [ListBuilder::send].
|
||||
///
|
||||
/// `url` sets the path you want to lest within.
|
||||
pub fn list<T: TryInto<Url>>(&self, url: T) -> Result<ListBuilder> {
|
||||
pub fn list<T: IntoUrl>(&self, url: T) -> Result<ListBuilder> {
|
||||
self.inner_list(url)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
use reqwest::RequestBuilder;
|
||||
use url::Url;
|
||||
|
||||
use crate::Client;
|
||||
|
||||
impl Client {
|
||||
// === HTTP ===
|
||||
|
||||
/// A wrapper around [reqwest::Client::request], with the same signature between native and wasm.
|
||||
pub(crate) async fn inner_request(&self, method: reqwest::Method, url: Url) -> RequestBuilder {
|
||||
self.http.request(method, url)
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ impl Client {
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
|
||||
self.publish_pubky_homeserver(keypair, &homeserver.to_string())
|
||||
self.publish_homeserver(keypair, &homeserver.to_string())
|
||||
.await?;
|
||||
|
||||
// Store the cookie to the correct URL.
|
||||
@@ -213,21 +213,25 @@ mod tests {
|
||||
use reqwest::StatusCode;
|
||||
|
||||
#[tokio::test]
|
||||
async fn basic_authn() -> anyhow::Result<()> {
|
||||
async fn basic_authn() {
|
||||
let testnet = Testnet::new(10).unwrap();
|
||||
let server = Homeserver::start_test(&testnet).await?;
|
||||
let server = Homeserver::start_test(&testnet).await.unwrap();
|
||||
|
||||
let client = Client::test(&testnet);
|
||||
|
||||
let keypair = Keypair::random();
|
||||
|
||||
client.signup(&keypair, &server.public_key()).await?;
|
||||
client.signup(&keypair, &server.public_key()).await.unwrap();
|
||||
|
||||
let session = client.session(&keypair.public_key()).await?.unwrap();
|
||||
let session = client
|
||||
.session(&keypair.public_key())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert!(session.capabilities().contains(&Capability::root()));
|
||||
|
||||
client.signout(&keypair.public_key()).await?;
|
||||
client.signout(&keypair.public_key()).await.unwrap();
|
||||
|
||||
{
|
||||
let session = client.session(&keypair.public_key()).await.unwrap();
|
||||
@@ -238,17 +242,19 @@ mod tests {
|
||||
client.signin(&keypair).await.unwrap();
|
||||
|
||||
{
|
||||
let session = client.session(&keypair.public_key()).await?.unwrap();
|
||||
let session = client
|
||||
.session(&keypair.public_key())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(session.pubky(), &keypair.public_key());
|
||||
assert!(session.capabilities().contains(&Capability::root()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn authz() -> anyhow::Result<()> {
|
||||
async fn authz() {
|
||||
let testnet = Testnet::new(10).unwrap();
|
||||
let server = Homeserver::start_test(&testnet).await.unwrap();
|
||||
|
||||
@@ -279,7 +285,7 @@ mod tests {
|
||||
|
||||
assert_eq!(&public_key, &pubky);
|
||||
|
||||
let session = client.session(&pubky).await?.unwrap();
|
||||
let session = client.session(&pubky).await.unwrap().unwrap();
|
||||
assert_eq!(session.capabilities(), &capabilities.0);
|
||||
|
||||
// Test access control enforcement
|
||||
@@ -288,15 +294,18 @@ mod tests {
|
||||
.put(format!("pubky://{pubky}/pub/pubky.app/foo"))
|
||||
.body(vec![])
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
.await
|
||||
.unwrap()
|
||||
.error_for_status()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
client
|
||||
.put(format!("pubky://{pubky}/pub/pubky.app"))
|
||||
.body(vec![])
|
||||
.send()
|
||||
.await?
|
||||
.await
|
||||
.unwrap()
|
||||
.status(),
|
||||
StatusCode::FORBIDDEN
|
||||
);
|
||||
@@ -306,11 +315,10 @@ mod tests {
|
||||
.put(format!("pubky://{pubky}/pub/foo.bar/file"))
|
||||
.body(vec![])
|
||||
.send()
|
||||
.await?
|
||||
.await
|
||||
.unwrap()
|
||||
.status(),
|
||||
StatusCode::FORBIDDEN
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use reqwest::Method;
|
||||
use url::Url;
|
||||
use reqwest::{IntoUrl, Method};
|
||||
|
||||
use crate::{error::Result, Client};
|
||||
|
||||
/// Helper struct to edit Pubky homeserver's list API options before sending them.
|
||||
#[derive(Debug)]
|
||||
pub struct ListBuilder<'a> {
|
||||
url: Url,
|
||||
url: String,
|
||||
reverse: bool,
|
||||
limit: Option<u16>,
|
||||
cursor: Option<&'a str>,
|
||||
@@ -16,10 +15,10 @@ pub struct ListBuilder<'a> {
|
||||
|
||||
impl<'a> ListBuilder<'a> {
|
||||
/// Create a new List request builder
|
||||
pub(crate) fn new(client: &'a Client, url: Url) -> Self {
|
||||
pub(crate) fn new<T: IntoUrl>(client: &'a Client, url: T) -> Self {
|
||||
Self {
|
||||
client,
|
||||
url,
|
||||
url: url.as_str().to_string(),
|
||||
limit: None,
|
||||
cursor: None,
|
||||
reverse: false,
|
||||
@@ -59,7 +58,7 @@ impl<'a> ListBuilder<'a> {
|
||||
/// respecting [ListBuilder::reverse], [ListBuilder::limit] and [ListBuilder::cursor]
|
||||
/// options.
|
||||
pub async fn send(self) -> Result<Vec<String>> {
|
||||
let mut url = self.client.pubky_to_http(self.url).await?;
|
||||
let mut url = url::Url::parse(&self.url)?;
|
||||
|
||||
if !url.path().ends_with('/') {
|
||||
let path = url.path().to_string();
|
||||
@@ -91,12 +90,7 @@ impl<'a> ListBuilder<'a> {
|
||||
|
||||
drop(query);
|
||||
|
||||
let response = self
|
||||
.client
|
||||
.inner_request(Method::GET, url)
|
||||
.await
|
||||
.send()
|
||||
.await?;
|
||||
let response = self.client.request(Method::GET, url).send().await?;
|
||||
|
||||
response.error_for_status_ref()?;
|
||||
|
||||
|
||||
@@ -1,24 +1,15 @@
|
||||
use url::Url;
|
||||
|
||||
use pkarr::{
|
||||
dns::{rdata::SVCB, Packet},
|
||||
Keypair, PublicKey, SignedPacket,
|
||||
Keypair, SignedPacket,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
Client,
|
||||
};
|
||||
|
||||
const MAX_ENDPOINT_RESOLUTION_RECURSION: u8 = 3;
|
||||
use crate::{error::Result, Client};
|
||||
|
||||
impl Client {
|
||||
/// Publish the SVCB record for `_pubky.<public_key>`.
|
||||
pub(crate) async fn publish_pubky_homeserver(
|
||||
&self,
|
||||
keypair: &Keypair,
|
||||
host: &str,
|
||||
) -> Result<()> {
|
||||
/// Publish the HTTPS record for `_pubky.<public_key>`.
|
||||
pub(crate) async fn publish_homeserver(&self, keypair: &Keypair, host: &str) -> Result<()> {
|
||||
// TODO: Before making public, consider the effect on other records and other mirrors
|
||||
|
||||
let existing = self.pkarr.resolve(&keypair.public_key()).await?;
|
||||
|
||||
let mut packet = Packet::new_reply(0);
|
||||
@@ -46,264 +37,4 @@ impl Client {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolve the homeserver for a pubky.
|
||||
pub(crate) async fn resolve_pubky_homeserver(&self, pubky: &PublicKey) -> Result<Endpoint> {
|
||||
let target = format!("_pubky.{pubky}");
|
||||
|
||||
self.resolve_endpoint(&target)
|
||||
.await
|
||||
.map_err(|_| Error::Generic("Could not resolve homeserver".to_string()))
|
||||
}
|
||||
|
||||
/// Resolve a service's public_key and "non-pkarr url" from a Pubky domain
|
||||
///
|
||||
/// "non-pkarr" url is any URL where the hostname isn't a 52 z-base32 character,
|
||||
/// usually an IPv4, IPv6 or ICANN domain, but could also be any other unknown hostname.
|
||||
///
|
||||
/// Recursively resolve SVCB and HTTPS endpoints, with [MAX_ENDPOINT_RESOLUTION_RECURSION] limit.
|
||||
pub(crate) async fn resolve_endpoint(&self, target: &str) -> Result<Endpoint> {
|
||||
let original_target = target;
|
||||
// TODO: cache the result of this function?
|
||||
|
||||
let mut target = target.to_string();
|
||||
|
||||
let mut endpoint_public_key = None;
|
||||
let mut origin = target.clone();
|
||||
|
||||
let mut step = 0;
|
||||
|
||||
// PublicKey is very good at extracting the Pkarr TLD from a string.
|
||||
while let Ok(public_key) = PublicKey::try_from(target.clone()) {
|
||||
if step >= MAX_ENDPOINT_RESOLUTION_RECURSION {
|
||||
break;
|
||||
};
|
||||
step += 1;
|
||||
|
||||
if let Some(signed_packet) = self
|
||||
.pkarr
|
||||
.resolve(&public_key)
|
||||
.await
|
||||
.map_err(|_| Error::ResolveEndpoint(original_target.into()))?
|
||||
{
|
||||
// Choose most prior SVCB record
|
||||
let svcb = signed_packet.resource_records(&target).fold(
|
||||
None,
|
||||
|prev: Option<SVCB>, answer| {
|
||||
if let Some(svcb) = match &answer.rdata {
|
||||
pkarr::dns::rdata::RData::SVCB(svcb) => Some(svcb),
|
||||
pkarr::dns::rdata::RData::HTTPS(curr) => Some(&curr.0),
|
||||
_ => None,
|
||||
} {
|
||||
let curr = svcb.clone();
|
||||
|
||||
if curr.priority == 0 {
|
||||
return Some(curr);
|
||||
}
|
||||
if let Some(prev) = &prev {
|
||||
// TODO return random if priority is the same
|
||||
if curr.priority >= prev.priority {
|
||||
return Some(curr);
|
||||
}
|
||||
} else {
|
||||
return Some(curr);
|
||||
}
|
||||
}
|
||||
|
||||
prev
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(svcb) = svcb {
|
||||
endpoint_public_key = Some(public_key.clone());
|
||||
target = svcb.target.to_string();
|
||||
|
||||
if let Some(port) = svcb.get_param(pkarr::dns::rdata::SVCB::PORT) {
|
||||
if port.len() < 2 {
|
||||
// TODO: debug! Error encoding port!
|
||||
}
|
||||
let port = u16::from_be_bytes([port[0], port[1]]);
|
||||
|
||||
origin = format!("{target}:{port}");
|
||||
} else {
|
||||
origin.clone_from(&target);
|
||||
};
|
||||
|
||||
if step >= MAX_ENDPOINT_RESOLUTION_RECURSION {
|
||||
continue;
|
||||
};
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if PublicKey::try_from(origin.as_str()).is_ok() {
|
||||
return Err(Error::ResolveEndpoint(original_target.into()));
|
||||
}
|
||||
|
||||
if endpoint_public_key.is_some() {
|
||||
let url = Url::parse(&format!(
|
||||
"{}://{}",
|
||||
if origin.starts_with("localhost") {
|
||||
"http"
|
||||
} else {
|
||||
"https"
|
||||
},
|
||||
origin
|
||||
))?;
|
||||
|
||||
return Ok(Endpoint { url });
|
||||
}
|
||||
|
||||
Err(Error::ResolveEndpoint(original_target.into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Endpoint {
|
||||
pub url: Url,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use pkarr::{
|
||||
dns::{
|
||||
rdata::{HTTPS, SVCB},
|
||||
Packet,
|
||||
},
|
||||
mainline::Testnet,
|
||||
Keypair, SignedPacket,
|
||||
};
|
||||
use pubky_homeserver::Homeserver;
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolve_endpoint_https() {
|
||||
let testnet = Testnet::new(10).unwrap();
|
||||
|
||||
let pkarr_client = pkarr::Client::builder().testnet(&testnet).build().unwrap();
|
||||
|
||||
let domain = "example.com";
|
||||
let mut target;
|
||||
|
||||
// Server
|
||||
{
|
||||
let keypair = Keypair::random();
|
||||
|
||||
let https = HTTPS(SVCB::new(0, domain.try_into().unwrap()));
|
||||
|
||||
let mut packet = Packet::new_reply(0);
|
||||
|
||||
packet.answers.push(pkarr::dns::ResourceRecord::new(
|
||||
"foo".try_into().unwrap(),
|
||||
pkarr::dns::CLASS::IN,
|
||||
60 * 60,
|
||||
pkarr::dns::rdata::RData::HTTPS(https),
|
||||
));
|
||||
|
||||
let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap();
|
||||
|
||||
pkarr_client.publish(&signed_packet).await.unwrap();
|
||||
|
||||
target = format!("foo.{}", keypair.public_key());
|
||||
}
|
||||
|
||||
// intermediate
|
||||
{
|
||||
let keypair = Keypair::random();
|
||||
|
||||
let svcb = SVCB::new(0, target.as_str().try_into().unwrap());
|
||||
|
||||
let mut packet = Packet::new_reply(0);
|
||||
|
||||
packet.answers.push(pkarr::dns::ResourceRecord::new(
|
||||
"bar".try_into().unwrap(),
|
||||
pkarr::dns::CLASS::IN,
|
||||
60 * 60,
|
||||
pkarr::dns::rdata::RData::SVCB(svcb),
|
||||
));
|
||||
|
||||
let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap();
|
||||
|
||||
pkarr_client.publish(&signed_packet).await.unwrap();
|
||||
|
||||
target = format!("bar.{}", keypair.public_key())
|
||||
}
|
||||
|
||||
{
|
||||
let keypair = Keypair::random();
|
||||
|
||||
let svcb = SVCB::new(0, target.as_str().try_into().unwrap());
|
||||
|
||||
let mut packet = Packet::new_reply(0);
|
||||
|
||||
packet.answers.push(pkarr::dns::ResourceRecord::new(
|
||||
"pubky".try_into().unwrap(),
|
||||
pkarr::dns::CLASS::IN,
|
||||
60 * 60,
|
||||
pkarr::dns::rdata::RData::SVCB(svcb),
|
||||
));
|
||||
|
||||
let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap();
|
||||
|
||||
pkarr_client.publish(&signed_packet).await.unwrap();
|
||||
|
||||
target = format!("pubky.{}", keypair.public_key())
|
||||
}
|
||||
|
||||
let client = Client::test(&testnet);
|
||||
|
||||
let endpoint = client.resolve_endpoint(&target).await.unwrap();
|
||||
|
||||
assert_eq!(endpoint.url.host_str().unwrap(), domain);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolve_homeserver() {
|
||||
let testnet = Testnet::new(10).unwrap();
|
||||
let server = Homeserver::start_test(&testnet).await.unwrap();
|
||||
|
||||
// Publish an intermediate controller of the homeserver
|
||||
let pkarr_client = pkarr::Client::builder().testnet(&testnet).build().unwrap();
|
||||
|
||||
let intermediate = Keypair::random();
|
||||
|
||||
let mut packet = Packet::new_reply(0);
|
||||
|
||||
let server_tld = server.public_key().to_string();
|
||||
|
||||
let svcb = SVCB::new(0, server_tld.as_str().try_into().unwrap());
|
||||
|
||||
packet.answers.push(pkarr::dns::ResourceRecord::new(
|
||||
"pubky".try_into().unwrap(),
|
||||
pkarr::dns::CLASS::IN,
|
||||
60 * 60,
|
||||
pkarr::dns::rdata::RData::SVCB(svcb),
|
||||
));
|
||||
|
||||
let signed_packet = SignedPacket::from_packet(&intermediate, &packet).unwrap();
|
||||
|
||||
pkarr_client.publish(&signed_packet).await.unwrap();
|
||||
|
||||
{
|
||||
let client = Client::test(&testnet);
|
||||
|
||||
let pubky = Keypair::random();
|
||||
|
||||
client
|
||||
.publish_pubky_homeserver(&pubky, &format!("pubky.{}", &intermediate.public_key()))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let Endpoint { url, .. } = client
|
||||
.resolve_pubky_homeserver(&pubky.public_key())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(url.host_str(), Some("localhost"));
|
||||
assert_eq!(url.port(), Some(server.port()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,17 @@
|
||||
use pkarr::PublicKey;
|
||||
use url::Url;
|
||||
use reqwest::IntoUrl;
|
||||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
Client,
|
||||
};
|
||||
use crate::{error::Result, Client};
|
||||
|
||||
use super::{list_builder::ListBuilder, pkarr::Endpoint};
|
||||
use super::list_builder::ListBuilder;
|
||||
|
||||
impl Client {
|
||||
pub(crate) fn inner_list<T: TryInto<Url>>(&self, url: T) -> Result<ListBuilder> {
|
||||
Ok(ListBuilder::new(
|
||||
self,
|
||||
url.try_into().map_err(|_| Error::InvalidUrl)?,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) async fn pubky_to_http<T: TryInto<Url>>(&self, url: T) -> Result<Url> {
|
||||
let original_url: Url = url.try_into().map_err(|_| Error::InvalidUrl)?;
|
||||
|
||||
let pubky = original_url
|
||||
.host_str()
|
||||
.ok_or(Error::Generic("Missing Pubky Url host".to_string()))?;
|
||||
|
||||
if let Ok(public_key) = PublicKey::try_from(pubky) {
|
||||
let Endpoint { mut url, .. } = self.resolve_pubky_homeserver(&public_key).await?;
|
||||
|
||||
// TODO: remove if we move to subdomains instead of paths.
|
||||
if original_url.scheme() == "pubky" {
|
||||
let path = original_url.path_segments();
|
||||
|
||||
let mut split = url.path_segments_mut().unwrap();
|
||||
split.push(pubky);
|
||||
if let Some(segments) = path {
|
||||
for segment in segments {
|
||||
split.push(segment);
|
||||
}
|
||||
}
|
||||
drop(split);
|
||||
}
|
||||
|
||||
return Ok(url);
|
||||
}
|
||||
|
||||
Ok(original_url)
|
||||
pub(crate) fn inner_list<T: IntoUrl>(&self, url: T) -> Result<ListBuilder> {
|
||||
Ok(ListBuilder::new(self, url))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::*;
|
||||
|
||||
use bytes::Bytes;
|
||||
@@ -58,15 +20,15 @@ mod tests {
|
||||
use reqwest::{Method, StatusCode};
|
||||
|
||||
#[tokio::test]
|
||||
async fn put_get_delete() -> anyhow::Result<()> {
|
||||
let testnet = Testnet::new(10)?;
|
||||
let server = Homeserver::start_test(&testnet).await?;
|
||||
async fn put_get_delete() {
|
||||
let testnet = Testnet::new(10).unwrap();
|
||||
let server = Homeserver::start_test(&testnet).await.unwrap();
|
||||
|
||||
let client = Client::test(&testnet);
|
||||
|
||||
let keypair = Keypair::random();
|
||||
|
||||
client.signup(&keypair, &server.public_key()).await?;
|
||||
client.signup(&keypair, &server.public_key()).await.unwrap();
|
||||
|
||||
let url = format!("pubky://{}/pub/foo.txt", keypair.public_key());
|
||||
let url = url.as_str();
|
||||
@@ -75,32 +37,38 @@ mod tests {
|
||||
.put(url)
|
||||
.body(vec![0, 1, 2, 3, 4])
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
.await
|
||||
.unwrap()
|
||||
.error_for_status()
|
||||
.unwrap();
|
||||
|
||||
let response = client.get(url).send().await?.bytes().await?;
|
||||
let response = client.get(url).send().await.unwrap().bytes().await.unwrap();
|
||||
|
||||
assert_eq!(response, bytes::Bytes::from(vec![0, 1, 2, 3, 4]));
|
||||
|
||||
client.delete(url).send().await?.error_for_status()?;
|
||||
client
|
||||
.delete(url)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.error_for_status()
|
||||
.unwrap();
|
||||
|
||||
let response = client.get(url).send().await?;
|
||||
let response = client.get(url).send().await.unwrap();
|
||||
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn unauthorized_put_delete() -> anyhow::Result<()> {
|
||||
let testnet = Testnet::new(10)?;
|
||||
let server = Homeserver::start_test(&testnet).await?;
|
||||
async fn unauthorized_put_delete() {
|
||||
let testnet = Testnet::new(10).unwrap();
|
||||
let server = Homeserver::start_test(&testnet).await.unwrap();
|
||||
|
||||
let client = Client::test(&testnet);
|
||||
|
||||
let keypair = Keypair::random();
|
||||
|
||||
client.signup(&keypair, &server.public_key()).await?;
|
||||
client.signup(&keypair, &server.public_key()).await.unwrap();
|
||||
|
||||
let public_key = keypair.public_key();
|
||||
|
||||
@@ -112,50 +80,60 @@ mod tests {
|
||||
let other = Keypair::random();
|
||||
|
||||
// TODO: remove extra client after switching to subdomains.
|
||||
other_client.signup(&other, &server.public_key()).await?;
|
||||
other_client
|
||||
.signup(&other, &server.public_key())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
other_client
|
||||
.put(url)
|
||||
.body(vec![0, 1, 2, 3, 4])
|
||||
.send()
|
||||
.await?
|
||||
.await
|
||||
.unwrap()
|
||||
.status(),
|
||||
StatusCode::UNAUTHORIZED
|
||||
);
|
||||
}
|
||||
|
||||
client.put(url).body(vec![0, 1, 2, 3, 4]).send().await?;
|
||||
client
|
||||
.put(url)
|
||||
.body(vec![0, 1, 2, 3, 4])
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let other = Keypair::random();
|
||||
|
||||
// TODO: remove extra client after switching to subdomains.
|
||||
other_client.signup(&other, &server.public_key()).await?;
|
||||
other_client
|
||||
.signup(&other, &server.public_key())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
other_client.delete(url).send().await?.status(),
|
||||
other_client.delete(url).send().await.unwrap().status(),
|
||||
StatusCode::UNAUTHORIZED
|
||||
);
|
||||
}
|
||||
|
||||
let response = client.get(url).send().await?.bytes().await?;
|
||||
let response = client.get(url).send().await.unwrap().bytes().await.unwrap();
|
||||
|
||||
assert_eq!(response, bytes::Bytes::from(vec![0, 1, 2, 3, 4]));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list() -> anyhow::Result<()> {
|
||||
let testnet = Testnet::new(10)?;
|
||||
let server = Homeserver::start_test(&testnet).await?;
|
||||
async fn list() {
|
||||
let testnet = Testnet::new(10).unwrap();
|
||||
let server = Homeserver::start_test(&testnet).await.unwrap();
|
||||
|
||||
let client = Client::test(&testnet);
|
||||
|
||||
let keypair = Keypair::random();
|
||||
|
||||
client.signup(&keypair, &server.public_key()).await?;
|
||||
client.signup(&keypair, &server.public_key()).await.unwrap();
|
||||
|
||||
let pubky = keypair.public_key();
|
||||
|
||||
@@ -171,14 +149,13 @@ mod tests {
|
||||
];
|
||||
|
||||
for url in urls {
|
||||
client.put(url).body(vec![0]).send().await?;
|
||||
client.put(url).body(vec![0]).send().await.unwrap();
|
||||
}
|
||||
|
||||
let url = format!("pubky://{pubky}/pub/example.com/extra");
|
||||
let url = url.as_str();
|
||||
|
||||
{
|
||||
let list = client.list(url)?.send().await?;
|
||||
let list = client.list(&url).unwrap().send().await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -194,7 +171,7 @@ mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let list = client.list(url)?.limit(2).send().await?;
|
||||
let list = client.list(&url).unwrap().limit(2).send().await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -207,7 +184,14 @@ mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let list = client.list(url)?.limit(2).cursor("a.txt").send().await?;
|
||||
let list = client
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.limit(2)
|
||||
.cursor("a.txt")
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -221,11 +205,13 @@ mod tests {
|
||||
|
||||
{
|
||||
let list = client
|
||||
.list(url)?
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.limit(2)
|
||||
.cursor("cc-nested/")
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -239,11 +225,13 @@ mod tests {
|
||||
|
||||
{
|
||||
let list = client
|
||||
.list(url)?
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.limit(2)
|
||||
.cursor(&format!("pubky://{pubky}/pub/example.com/a.txt"))
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -256,7 +244,14 @@ mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let list = client.list(url)?.limit(2).cursor("/a.txt").send().await?;
|
||||
let list = client
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.limit(2)
|
||||
.cursor("/a.txt")
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -269,7 +264,13 @@ mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let list = client.list(url)?.reverse(true).send().await?;
|
||||
let list = client
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.reverse(true)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -285,7 +286,14 @@ mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let list = client.list(url)?.reverse(true).limit(2).send().await?;
|
||||
let list = client
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.reverse(true)
|
||||
.limit(2)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -299,12 +307,14 @@ mod tests {
|
||||
|
||||
{
|
||||
let list = client
|
||||
.list(url)?
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.reverse(true)
|
||||
.limit(2)
|
||||
.cursor("d.txt")
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -315,20 +325,18 @@ mod tests {
|
||||
"reverse list with limit and cursor"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_shallow() -> anyhow::Result<()> {
|
||||
let testnet = Testnet::new(10)?;
|
||||
let server = Homeserver::start_test(&testnet).await?;
|
||||
async fn list_shallow() {
|
||||
let testnet = Testnet::new(10).unwrap();
|
||||
let server = Homeserver::start_test(&testnet).await.unwrap();
|
||||
|
||||
let client = Client::test(&testnet);
|
||||
|
||||
let keypair = Keypair::random();
|
||||
|
||||
client.signup(&keypair, &server.public_key()).await?;
|
||||
client.signup(&keypair, &server.public_key()).await.unwrap();
|
||||
|
||||
let pubky = keypair.public_key();
|
||||
|
||||
@@ -346,14 +354,19 @@ mod tests {
|
||||
];
|
||||
|
||||
for url in urls {
|
||||
client.put(url).body(vec![0]).send().await?;
|
||||
client.put(url).body(vec![0]).send().await.unwrap();
|
||||
}
|
||||
|
||||
let url = format!("pubky://{pubky}/pub/");
|
||||
let url = url.as_str();
|
||||
|
||||
{
|
||||
let list = client.list(url)?.shallow(true).send().await?;
|
||||
let list = client
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.shallow(true)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -371,7 +384,14 @@ mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let list = client.list(url)?.shallow(true).limit(2).send().await?;
|
||||
let list = client
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.shallow(true)
|
||||
.limit(2)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -385,12 +405,14 @@ mod tests {
|
||||
|
||||
{
|
||||
let list = client
|
||||
.list(url)?
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.shallow(true)
|
||||
.limit(2)
|
||||
.cursor("example.com/a.txt")
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -404,12 +426,14 @@ mod tests {
|
||||
|
||||
{
|
||||
let list = client
|
||||
.list(url)?
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.shallow(true)
|
||||
.limit(3)
|
||||
.cursor("example.com/")
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -423,7 +447,14 @@ mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let list = client.list(url)?.reverse(true).shallow(true).send().await?;
|
||||
let list = client
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.reverse(true)
|
||||
.shallow(true)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -442,12 +473,14 @@ mod tests {
|
||||
|
||||
{
|
||||
let list = client
|
||||
.list(url)?
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.reverse(true)
|
||||
.shallow(true)
|
||||
.limit(2)
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -461,13 +494,15 @@ mod tests {
|
||||
|
||||
{
|
||||
let list = client
|
||||
.list(url)?
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.shallow(true)
|
||||
.reverse(true)
|
||||
.limit(2)
|
||||
.cursor("file2")
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -481,13 +516,15 @@ mod tests {
|
||||
|
||||
{
|
||||
let list = client
|
||||
.list(url)?
|
||||
.list(&url)
|
||||
.unwrap()
|
||||
.shallow(true)
|
||||
.reverse(true)
|
||||
.limit(2)
|
||||
.cursor("example.con/")
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
list,
|
||||
@@ -498,20 +535,18 @@ mod tests {
|
||||
"reverse list shallow with limit and a directory cursor"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_events() -> anyhow::Result<()> {
|
||||
let testnet = Testnet::new(10)?;
|
||||
let server = Homeserver::start_test(&testnet).await?;
|
||||
async fn list_events() {
|
||||
let testnet = Testnet::new(10).unwrap();
|
||||
let server = Homeserver::start_test(&testnet).await.unwrap();
|
||||
|
||||
let client = Client::test(&testnet);
|
||||
|
||||
let keypair = Keypair::random();
|
||||
|
||||
client.signup(&keypair, &server.public_key()).await?;
|
||||
client.signup(&keypair, &server.public_key()).await.unwrap();
|
||||
|
||||
let pubky = keypair.public_key();
|
||||
|
||||
@@ -529,12 +564,11 @@ mod tests {
|
||||
];
|
||||
|
||||
for url in urls {
|
||||
client.put(&url).body(vec![0]).send().await?;
|
||||
client.delete(url).send().await?;
|
||||
client.put(&url).body(vec![0]).send().await.unwrap();
|
||||
client.delete(url).send().await.unwrap();
|
||||
}
|
||||
|
||||
let feed_url = format!("http://localhost:{}/events/", server.port());
|
||||
let feed_url = feed_url.as_str();
|
||||
let feed_url = format!("https://{}/events/", server.public_key());
|
||||
|
||||
let client = Client::test(&testnet);
|
||||
|
||||
@@ -544,9 +578,10 @@ mod tests {
|
||||
let response = client
|
||||
.request(Method::GET, format!("{feed_url}?limit=10"))
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let text = response.text().await?;
|
||||
let text = response.text().await.unwrap();
|
||||
let lines = text.split('\n').collect::<Vec<_>>();
|
||||
|
||||
cursor = lines.last().unwrap().split(" ").last().unwrap().to_string();
|
||||
@@ -573,9 +608,10 @@ mod tests {
|
||||
let response = client
|
||||
.request(Method::GET, format!("{feed_url}?limit=10&cursor={cursor}"))
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let text = response.text().await?;
|
||||
let text = response.text().await.unwrap();
|
||||
let lines = text.split('\n').collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
@@ -595,30 +631,26 @@ mod tests {
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn read_after_event() -> anyhow::Result<()> {
|
||||
let testnet = Testnet::new(10)?;
|
||||
let server = Homeserver::start_test(&testnet).await?;
|
||||
async fn read_after_event() {
|
||||
let testnet = Testnet::new(10).unwrap();
|
||||
let server = Homeserver::start_test(&testnet).await.unwrap();
|
||||
|
||||
let client = Client::test(&testnet);
|
||||
|
||||
let keypair = Keypair::random();
|
||||
|
||||
client.signup(&keypair, &server.public_key()).await?;
|
||||
client.signup(&keypair, &server.public_key()).await.unwrap();
|
||||
|
||||
let pubky = keypair.public_key();
|
||||
|
||||
let url = format!("pubky://{pubky}/pub/a.com/a.txt");
|
||||
let url = url.as_str();
|
||||
|
||||
client.put(url).body(vec![0]).send().await?;
|
||||
client.put(&url).body(vec![0]).send().await.unwrap();
|
||||
|
||||
let feed_url = format!("http://localhost:{}/events/", server.port());
|
||||
let feed_url = feed_url.as_str();
|
||||
let feed_url = format!("https://{}/events/", server.public_key());
|
||||
|
||||
let client = Client::test(&testnet);
|
||||
|
||||
@@ -626,9 +658,10 @@ mod tests {
|
||||
let response = client
|
||||
.request(Method::GET, format!("{feed_url}?limit=10"))
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let text = response.text().await?;
|
||||
let text = response.text().await.unwrap();
|
||||
let lines = text.split('\n').collect::<Vec<_>>();
|
||||
|
||||
let cursor = lines.last().unwrap().split(" ").last().unwrap().to_string();
|
||||
@@ -642,17 +675,15 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
let get = client.get(url).send().await?.bytes().await?;
|
||||
let get = client.get(url).send().await.unwrap().bytes().await.unwrap();
|
||||
|
||||
assert_eq!(get.as_ref(), &[0]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn dont_delete_shared_blobs() -> anyhow::Result<()> {
|
||||
let testnet = Testnet::new(10)?;
|
||||
let homeserver = Homeserver::start_test(&testnet).await?;
|
||||
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 homeserver_pubky = homeserver.public_key();
|
||||
@@ -660,8 +691,8 @@ mod tests {
|
||||
let user_1 = Keypair::random();
|
||||
let user_2 = Keypair::random();
|
||||
|
||||
client.signup(&user_1, &homeserver_pubky).await?;
|
||||
client.signup(&user_2, &homeserver_pubky).await?;
|
||||
client.signup(&user_1, &homeserver_pubky).await.unwrap();
|
||||
client.signup(&user_2, &homeserver_pubky).await.unwrap();
|
||||
|
||||
let user_1_id = user_1.public_key();
|
||||
let user_2_id = user_2.public_key();
|
||||
@@ -670,24 +701,40 @@ mod tests {
|
||||
let url_2 = format!("pubky://{user_2_id}/pub/pubky.app/file/file_1");
|
||||
|
||||
let file = vec![1];
|
||||
client.put(&url_1).body(file.clone()).send().await?;
|
||||
client.put(&url_2).body(file.clone()).send().await?;
|
||||
client.put(&url_1).body(file.clone()).send().await.unwrap();
|
||||
client.put(&url_2).body(file.clone()).send().await.unwrap();
|
||||
|
||||
// Delete file 1
|
||||
client.delete(url_1).send().await?.error_for_status()?;
|
||||
client
|
||||
.delete(url_1)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.error_for_status()
|
||||
.unwrap();
|
||||
|
||||
let blob = client.get(url_2).send().await?.bytes().await?;
|
||||
let blob = client
|
||||
.get(url_2)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.bytes()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(blob, file);
|
||||
|
||||
let feed_url = format!("http://localhost:{}/events/", homeserver.port());
|
||||
let feed_url = format!("https://{}/events/", homeserver.public_key());
|
||||
|
||||
let response = client
|
||||
.request(Method::GET, format!("{feed_url}"))
|
||||
.request(Method::GET, feed_url)
|
||||
.send()
|
||||
.await?;
|
||||
.await
|
||||
.unwrap()
|
||||
.error_for_status()
|
||||
.unwrap();
|
||||
|
||||
let text = response.text().await?;
|
||||
let text = response.text().await.unwrap();
|
||||
let lines = text.split('\n').collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
@@ -699,40 +746,36 @@ mod tests {
|
||||
lines.last().unwrap().to_string()
|
||||
]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stream() -> anyhow::Result<()> {
|
||||
async fn stream() {
|
||||
// TODO: test better streaming API
|
||||
|
||||
let testnet = Testnet::new(10)?;
|
||||
let server = Homeserver::start_test(&testnet).await?;
|
||||
let testnet = Testnet::new(10).unwrap();
|
||||
let server = Homeserver::start_test(&testnet).await.unwrap();
|
||||
|
||||
let client = Client::test(&testnet);
|
||||
|
||||
let keypair = Keypair::random();
|
||||
|
||||
client.signup(&keypair, &server.public_key()).await?;
|
||||
client.signup(&keypair, &server.public_key()).await.unwrap();
|
||||
|
||||
let url = format!("pubky://{}/pub/foo.txt", keypair.public_key());
|
||||
let url = url.as_str();
|
||||
|
||||
let bytes = Bytes::from(vec![0; 1024 * 1024]);
|
||||
|
||||
client.put(url).body(bytes.clone()).send().await?;
|
||||
client.put(url).body(bytes.clone()).send().await.unwrap();
|
||||
|
||||
let response = client.get(url).send().await?.bytes().await?;
|
||||
let response = client.get(url).send().await.unwrap().bytes().await.unwrap();
|
||||
|
||||
assert_eq!(response, bytes);
|
||||
|
||||
client.delete(url).send().await?;
|
||||
client.delete(url).send().await.unwrap();
|
||||
|
||||
let response = client.get(url).send().await?;
|
||||
let response = client.get(url).send().await.unwrap();
|
||||
|
||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user