mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-09-19 00:34:15 +00:00
Half-ass attempt at the local-first appview endpoints like ref impl
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
mod common;
|
||||
|
||||
use common::{base_url, client, create_account_and_login};
|
||||
use reqwest::StatusCode;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_author_feed_returns_appview_data() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (jwt, did) = create_account_and_login(&client).await;
|
||||
|
||||
let res = client
|
||||
.get(format!(
|
||||
"{}/xrpc/app.bsky.feed.getAuthorFeed?actor={}",
|
||||
base, did
|
||||
))
|
||||
.header("Authorization", format!("Bearer {}", jwt))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
|
||||
let body: Value = res.json().await.unwrap();
|
||||
assert!(body["feed"].is_array(), "Response should have feed array");
|
||||
let feed = body["feed"].as_array().unwrap();
|
||||
assert_eq!(feed.len(), 1, "Feed should have 1 post from appview");
|
||||
assert_eq!(
|
||||
feed[0]["post"]["record"]["text"].as_str(),
|
||||
Some("Author feed post from appview"),
|
||||
"Post text should match appview response"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_actor_likes_returns_appview_data() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (jwt, did) = create_account_and_login(&client).await;
|
||||
|
||||
let res = client
|
||||
.get(format!(
|
||||
"{}/xrpc/app.bsky.feed.getActorLikes?actor={}",
|
||||
base, did
|
||||
))
|
||||
.header("Authorization", format!("Bearer {}", jwt))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
|
||||
let body: Value = res.json().await.unwrap();
|
||||
assert!(body["feed"].is_array(), "Response should have feed array");
|
||||
let feed = body["feed"].as_array().unwrap();
|
||||
assert_eq!(feed.len(), 1, "Feed should have 1 liked post from appview");
|
||||
assert_eq!(
|
||||
feed[0]["post"]["record"]["text"].as_str(),
|
||||
Some("Liked post from appview"),
|
||||
"Post text should match appview response"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_post_thread_returns_appview_data() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (jwt, did) = create_account_and_login(&client).await;
|
||||
|
||||
let res = client
|
||||
.get(format!(
|
||||
"{}/xrpc/app.bsky.feed.getPostThread?uri=at://{}/app.bsky.feed.post/test123",
|
||||
base, did
|
||||
))
|
||||
.header("Authorization", format!("Bearer {}", jwt))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
|
||||
let body: Value = res.json().await.unwrap();
|
||||
assert!(body["thread"].is_object(), "Response should have thread object");
|
||||
assert_eq!(
|
||||
body["thread"]["$type"].as_str(),
|
||||
Some("app.bsky.feed.defs#threadViewPost"),
|
||||
"Thread should be a threadViewPost"
|
||||
);
|
||||
assert_eq!(
|
||||
body["thread"]["post"]["record"]["text"].as_str(),
|
||||
Some("Thread post from appview"),
|
||||
"Post text should match appview response"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_feed_returns_appview_data() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (jwt, _did) = create_account_and_login(&client).await;
|
||||
|
||||
let res = client
|
||||
.get(format!(
|
||||
"{}/xrpc/app.bsky.feed.getFeed?feed=at://did:plc:test/app.bsky.feed.generator/test",
|
||||
base
|
||||
))
|
||||
.header("Authorization", format!("Bearer {}", jwt))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
|
||||
let body: Value = res.json().await.unwrap();
|
||||
assert!(body["feed"].is_array(), "Response should have feed array");
|
||||
let feed = body["feed"].as_array().unwrap();
|
||||
assert_eq!(feed.len(), 1, "Feed should have 1 post from appview");
|
||||
assert_eq!(
|
||||
feed[0]["post"]["record"]["text"].as_str(),
|
||||
Some("Custom feed post from appview"),
|
||||
"Post text should match appview response"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_register_push_proxies_to_appview() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (jwt, _did) = create_account_and_login(&client).await;
|
||||
|
||||
let res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/app.bsky.notification.registerPush",
|
||||
base
|
||||
))
|
||||
.header("Authorization", format!("Bearer {}", jwt))
|
||||
.json(&json!({
|
||||
"serviceDid": "did:web:example.com",
|
||||
"token": "test-push-token",
|
||||
"platform": "ios",
|
||||
"appId": "xyz.bsky.app"
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
}
|
||||
@@ -233,6 +233,122 @@ async fn setup_mock_appview(mock_server: &MockServer) {
|
||||
})))
|
||||
.mount(mock_server)
|
||||
.await;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/xrpc/app.bsky.feed.getTimeline"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.insert_header("atproto-repo-rev", "0")
|
||||
.set_body_json(json!({
|
||||
"feed": [],
|
||||
"cursor": null
|
||||
}))
|
||||
)
|
||||
.mount(mock_server)
|
||||
.await;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/xrpc/app.bsky.feed.getAuthorFeed"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.insert_header("atproto-repo-rev", "0")
|
||||
.set_body_json(json!({
|
||||
"feed": [{
|
||||
"post": {
|
||||
"uri": "at://did:plc:mock-author/app.bsky.feed.post/from-appview-author",
|
||||
"cid": "bafyappview123",
|
||||
"author": {"did": "did:plc:mock-author", "handle": "mock.author"},
|
||||
"record": {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"text": "Author feed post from appview",
|
||||
"createdAt": "2025-01-01T00:00:00Z"
|
||||
},
|
||||
"indexedAt": "2025-01-01T00:00:00Z"
|
||||
}
|
||||
}],
|
||||
"cursor": "author-cursor"
|
||||
})),
|
||||
)
|
||||
.mount(mock_server)
|
||||
.await;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/xrpc/app.bsky.feed.getActorLikes"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.insert_header("atproto-repo-rev", "0")
|
||||
.set_body_json(json!({
|
||||
"feed": [{
|
||||
"post": {
|
||||
"uri": "at://did:plc:mock-likes/app.bsky.feed.post/liked-post",
|
||||
"cid": "bafyliked123",
|
||||
"author": {"did": "did:plc:mock-likes", "handle": "mock.likes"},
|
||||
"record": {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"text": "Liked post from appview",
|
||||
"createdAt": "2025-01-01T00:00:00Z"
|
||||
},
|
||||
"indexedAt": "2025-01-01T00:00:00Z"
|
||||
}
|
||||
}],
|
||||
"cursor": null
|
||||
})),
|
||||
)
|
||||
.mount(mock_server)
|
||||
.await;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/xrpc/app.bsky.feed.getPostThread"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.insert_header("atproto-repo-rev", "0")
|
||||
.set_body_json(json!({
|
||||
"thread": {
|
||||
"$type": "app.bsky.feed.defs#threadViewPost",
|
||||
"post": {
|
||||
"uri": "at://did:plc:mock/app.bsky.feed.post/thread-post",
|
||||
"cid": "bafythread123",
|
||||
"author": {"did": "did:plc:mock", "handle": "mock.handle"},
|
||||
"record": {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"text": "Thread post from appview",
|
||||
"createdAt": "2025-01-01T00:00:00Z"
|
||||
},
|
||||
"indexedAt": "2025-01-01T00:00:00Z"
|
||||
},
|
||||
"replies": []
|
||||
}
|
||||
})),
|
||||
)
|
||||
.mount(mock_server)
|
||||
.await;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/xrpc/app.bsky.feed.getFeed"))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
|
||||
"feed": [{
|
||||
"post": {
|
||||
"uri": "at://did:plc:mock-feed/app.bsky.feed.post/custom-feed-post",
|
||||
"cid": "bafyfeed123",
|
||||
"author": {"did": "did:plc:mock-feed", "handle": "mock.feed"},
|
||||
"record": {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"text": "Custom feed post from appview",
|
||||
"createdAt": "2025-01-01T00:00:00Z"
|
||||
},
|
||||
"indexedAt": "2025-01-01T00:00:00Z"
|
||||
}
|
||||
}],
|
||||
"cursor": null
|
||||
})))
|
||||
.mount(mock_server)
|
||||
.await;
|
||||
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/xrpc/app.bsky.notification.registerPush"))
|
||||
.respond_with(ResponseTemplate::new(200))
|
||||
.mount(mock_server)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn spawn_app(database_url: String) -> String {
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
mod common;
|
||||
|
||||
use common::{base_url, client, create_account_and_login};
|
||||
use serde_json::json;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_timeline_requires_auth() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
|
||||
let res = client
|
||||
.get(format!("{}/xrpc/app.bsky.feed.getTimeline", base))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), 401);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_author_feed_requires_actor() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (jwt, _did) = create_account_and_login(&client).await;
|
||||
|
||||
let res = client
|
||||
.get(format!("{}/xrpc/app.bsky.feed.getAuthorFeed", base))
|
||||
.header("Authorization", format!("Bearer {}", jwt))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), 400);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_actor_likes_requires_actor() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (jwt, _did) = create_account_and_login(&client).await;
|
||||
|
||||
let res = client
|
||||
.get(format!("{}/xrpc/app.bsky.feed.getActorLikes", base))
|
||||
.header("Authorization", format!("Bearer {}", jwt))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), 400);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_post_thread_requires_uri() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (jwt, _did) = create_account_and_login(&client).await;
|
||||
|
||||
let res = client
|
||||
.get(format!("{}/xrpc/app.bsky.feed.getPostThread", base))
|
||||
.header("Authorization", format!("Bearer {}", jwt))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), 400);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_feed_requires_auth() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
|
||||
let res = client
|
||||
.get(format!(
|
||||
"{}/xrpc/app.bsky.feed.getFeed?feed=at://did:plc:test/app.bsky.feed.generator/test",
|
||||
base
|
||||
))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), 401);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_feed_requires_feed_param() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (jwt, _did) = create_account_and_login(&client).await;
|
||||
|
||||
let res = client
|
||||
.get(format!("{}/xrpc/app.bsky.feed.getFeed", base))
|
||||
.header("Authorization", format!("Bearer {}", jwt))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), 400);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_register_push_requires_auth() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
|
||||
let res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/app.bsky.notification.registerPush",
|
||||
base
|
||||
))
|
||||
.json(&json!({
|
||||
"serviceDid": "did:web:example.com",
|
||||
"token": "test-token",
|
||||
"platform": "ios",
|
||||
"appId": "xyz.bsky.app"
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), 401);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ fn write_varint(buf: &mut Vec<u8>, mut value: u64) {
|
||||
async fn test_import_rejects_car_for_different_user() {
|
||||
let client = client();
|
||||
|
||||
let (token_a, did_a) = create_account_and_login(&client).await;
|
||||
let (token_a, _did_a) = create_account_and_login(&client).await;
|
||||
let (_token_b, did_b) = create_account_and_login(&client).await;
|
||||
|
||||
let export_res = client
|
||||
|
||||
Reference in New Issue
Block a user