feat(pubky): pass all unit tests after using pubky in host

This commit is contained in:
nazeh
2024-11-26 17:46:48 +03:00
parent 9b5a4f98f2
commit 6f64831ae7
7 changed files with 241 additions and 479 deletions

View File

@@ -7,7 +7,6 @@ use crate::Client;
mod api;
mod cookies;
mod http;
mod internals;
pub(crate) use cookies::CookieJar;

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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(())
}
}

View File

@@ -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()?;

View File

@@ -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()));
}
}
}

View File

@@ -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(())
}
}