// SPDX-FileCopyrightText: 2026 Ololduck <ololduck@vit.am> and contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
//! Phase 1 tests: instance actor and migrations
mod support;
use support::{Server, free_port};
/// Test: Instance actor endpoint returns Application actor
#[tokio::test]
async fn instance_actor_serves_application_actor() -> miette::Result<()> {
let port = free_port();
let mut server = Server::spawn_federated(&format!("127.0.0.1:{port}"));
server.wait_for_connection(port);
let mut client = support::Client::new(port);
// Request with ActivityPub content negotiation
let response = client.get_ap("/actor")?;
// Should be Application actor
assert_eq!(
response.get("type").and_then(|v| v.as_str()),
Some("Application"),
"should be Application type, got: {:?}",
response
);
// Should have inbox
assert!(
response.get("inbox").is_some(),
"should have inbox, got: {:?}",
response
);
// Should have publicKey
assert!(
response.get("publicKey").is_some(),
"should have publicKey, got: {:?}",
response
);
Ok(())
}
/// Test: Instance actor returns 404 for browser requests
#[tokio::test]
async fn instance_actor_rejects_browser_requests() -> miette::Result<()> {
let port = free_port();
let mut server = Server::spawn_federated(&format!("127.0.0.1:{port}"));
server.wait_for_connection(port);
let mut client = support::Client::new(port);
// Request with browser Accept header (no AP content negotiation)
let response = client.get("/actor");
assert_eq!(response.status, 404, "browser requests should be rejected");
Ok(())
}
/// Test: Instance actor is singleton (same ID on repeated requests)
#[tokio::test]
async fn instance_actor_is_singleton() -> miette::Result<()> {
let port = free_port();
let mut server = Server::spawn_federated(&format!("127.0.0.1:{port}"));
server.wait_for_connection(port);
let mut client = support::Client::new(port);
let response1 = client.get_ap("/actor")?;
let response2 = client.get_ap("/actor")?;
// Extract id from both responses
let id1 = response1.get("id").and_then(|v| v.as_str());
let id2 = response2.get("id").and_then(|v| v.as_str());
assert!(
id1.is_some() && id2.is_some(),
"both responses should have id"
);
assert_eq!(
id1, id2,
"instance actor should be singleton (same id), got: {:?} and {:?}",
id1, id2
);
Ok(())
}
/// Test: Instance actor inbox rejects unsigned requests with 401
#[tokio::test]
async fn instance_actor_inbox_rejects_unsigned_requests() -> miette::Result<()> {
let port = free_port();
let mut server = Server::spawn_federated(&format!("127.0.0.1:{port}"));
server.wait_for_connection(port);
let mut client = support::Client::new(port);
// POST a simple activity to the inbox (unsigned)
let response = client.post_form("/actor/inbox", &[("test", "data")]);
// Should return 401 Unauthorized for unsigned requests
assert_eq!(
response.status, 401,
"expected 401, got: {}",
response.status
);
Ok(())
}