pinakes-server: add more integration tests
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I7c6c8eaad569404c7a13cfa8114d84516a6a6964
This commit is contained in:
parent
b1ddb32ff0
commit
bac79a2c08
6 changed files with 517 additions and 1 deletions
BIN
Cargo.lock
generated
BIN
Cargo.lock
generated
Binary file not shown.
|
|
@ -41,4 +41,5 @@ workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
http-body-util = "0.1.3"
|
http-body-util = "0.1.3"
|
||||||
tempfile = "3.25.0"
|
reqwest = { workspace = true }
|
||||||
|
tempfile = { workspace = true }
|
||||||
|
|
|
||||||
65
crates/pinakes-server/tests/e2e.rs
Normal file
65
crates/pinakes-server/tests/e2e.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
/// End-to-end tests that bind a real TCP listener and exercise the HTTP layer.
|
||||||
|
///
|
||||||
|
/// These tests differ from the router-level `oneshot` tests in that they verify
|
||||||
|
/// the full Axum `serve` path: `TcpListener` binding, HTTP framing, and response
|
||||||
|
/// serialization. Each test spins up a server on an ephemeral port, issues a
|
||||||
|
/// real HTTP request via reqwest, then shuts down.
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
|
/// Bind a listener on an ephemeral port, spawn the server in the background,
|
||||||
|
/// and return the bound address as a string.
|
||||||
|
///
|
||||||
|
/// Uses `into_make_service_with_connect_info` so that the governor rate
|
||||||
|
/// limiter can extract `ConnectInfo<SocketAddr>` from real TCP connections.
|
||||||
|
async fn bind_and_serve() -> String {
|
||||||
|
let app = common::setup_app().await;
|
||||||
|
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||||
|
let addr = listener.local_addr().unwrap();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
axum::serve(
|
||||||
|
listener,
|
||||||
|
app.into_make_service_with_connect_info::<SocketAddr>(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
format!("http://{addr}")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn health_endpoint_responds_over_real_tcp() {
|
||||||
|
let base = bind_and_serve().await;
|
||||||
|
let resp = reqwest::get(format!("{base}/api/v1/health"))
|
||||||
|
.await
|
||||||
|
.expect("health request failed");
|
||||||
|
assert_eq!(resp.status(), 200);
|
||||||
|
let body: serde_json::Value = resp.json().await.expect("body not JSON");
|
||||||
|
assert!(
|
||||||
|
body["status"].is_string(),
|
||||||
|
"expected health response to contain 'status' field"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn media_list_responds_over_real_tcp() {
|
||||||
|
// setup_app has authentication_disabled=true; verifies the router serves
|
||||||
|
// real TCP traffic, not just in-process oneshot requests.
|
||||||
|
let base = bind_and_serve().await;
|
||||||
|
let resp = reqwest::get(format!("{base}/api/v1/media"))
|
||||||
|
.await
|
||||||
|
.expect("media list request failed");
|
||||||
|
assert_eq!(resp.status(), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn unknown_route_returns_404_over_real_tcp() {
|
||||||
|
let base = bind_and_serve().await;
|
||||||
|
let resp = reqwest::get(format!("{base}/api/v1/nonexistent-route"))
|
||||||
|
.await
|
||||||
|
.expect("request failed");
|
||||||
|
assert_eq!(resp.status(), 404);
|
||||||
|
}
|
||||||
143
crates/pinakes-server/tests/enrichment.rs
Normal file
143
crates/pinakes-server/tests/enrichment.rs
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
use common::{
|
||||||
|
get,
|
||||||
|
get_authed,
|
||||||
|
post_json_authed,
|
||||||
|
setup_app,
|
||||||
|
setup_app_with_auth,
|
||||||
|
};
|
||||||
|
use tower::ServiceExt;
|
||||||
|
|
||||||
|
// GET /api/v1/media/{id}/metadata/external (viewer)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_external_metadata_requires_auth() {
|
||||||
|
let (app, ..) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(get(
|
||||||
|
"/api/v1/media/00000000-0000-0000-0000-000000000000/external-metadata",
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_external_metadata_viewer_ok() {
|
||||||
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(get_authed(
|
||||||
|
"/api/v1/media/00000000-0000-0000-0000-000000000000/external-metadata",
|
||||||
|
&viewer_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
// Media does not exist; 200 with empty array or 404 are both valid
|
||||||
|
assert!(
|
||||||
|
response.status() == StatusCode::OK
|
||||||
|
|| response.status() == StatusCode::NOT_FOUND
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /api/v1/media/{id}/enrich (editor)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn trigger_enrichment_requires_editor() {
|
||||||
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(post_json_authed(
|
||||||
|
"/api/v1/media/00000000-0000-0000-0000-000000000000/enrich",
|
||||||
|
"{}",
|
||||||
|
&viewer_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn trigger_enrichment_editor_accepted() {
|
||||||
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(post_json_authed(
|
||||||
|
"/api/v1/media/00000000-0000-0000-0000-000000000000/enrich",
|
||||||
|
"{}",
|
||||||
|
&editor_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
// Route is accessible to editors; media not found returns 404, job queued
|
||||||
|
// returns 200
|
||||||
|
assert!(
|
||||||
|
response.status() == StatusCode::OK
|
||||||
|
|| response.status() == StatusCode::NOT_FOUND
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /api/v1/jobs/enrich (editor, batch)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn batch_enrich_requires_editor() {
|
||||||
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(post_json_authed(
|
||||||
|
"/api/v1/jobs/enrich",
|
||||||
|
r#"{"media_ids":["00000000-0000-0000-0000-000000000000"]}"#,
|
||||||
|
&viewer_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn batch_enrich_empty_ids_rejected() {
|
||||||
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(post_json_authed(
|
||||||
|
"/api/v1/jobs/enrich",
|
||||||
|
r#"{"media_ids":[]}"#,
|
||||||
|
&editor_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
// Validation requires 1-1000 ids
|
||||||
|
assert!(
|
||||||
|
response.status() == StatusCode::BAD_REQUEST
|
||||||
|
|| response.status() == StatusCode::UNPROCESSABLE_ENTITY
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn batch_enrich_editor_accepted() {
|
||||||
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(post_json_authed(
|
||||||
|
"/api/v1/jobs/enrich",
|
||||||
|
r#"{"media_ids":["00000000-0000-0000-0000-000000000000"]}"#,
|
||||||
|
&editor_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
// Job is queued and a job_id is returned
|
||||||
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No-auth coverage (exercises setup_app and get)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_external_metadata_auth_disabled() {
|
||||||
|
let app = setup_app().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(get(
|
||||||
|
"/api/v1/media/00000000-0000-0000-0000-000000000000/external-metadata",
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
response.status() == StatusCode::OK
|
||||||
|
|| response.status() == StatusCode::NOT_FOUND
|
||||||
|
);
|
||||||
|
}
|
||||||
217
crates/pinakes-server/tests/users.rs
Normal file
217
crates/pinakes-server/tests/users.rs
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
use common::{
|
||||||
|
delete_authed,
|
||||||
|
get,
|
||||||
|
get_authed,
|
||||||
|
patch_json_authed,
|
||||||
|
post_json_authed,
|
||||||
|
response_body,
|
||||||
|
setup_app,
|
||||||
|
setup_app_with_auth,
|
||||||
|
};
|
||||||
|
use tower::ServiceExt;
|
||||||
|
|
||||||
|
// GET /api/v1/users (admin)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn list_users_requires_admin() {
|
||||||
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(get_authed("/api/v1/users", &editor_token))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn list_users_viewer_forbidden() {
|
||||||
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(get_authed("/api/v1/users", &viewer_token))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn list_users_admin_ok() {
|
||||||
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(get_authed("/api/v1/users", &admin_token))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let status = response.status();
|
||||||
|
let body = response_body(response).await;
|
||||||
|
assert_eq!(status, StatusCode::OK);
|
||||||
|
let users = body.as_array().expect("users is array");
|
||||||
|
// setup_app_with_auth seeds three users
|
||||||
|
assert_eq!(users.len(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /api/v1/users (admin)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_user_requires_admin() {
|
||||||
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(post_json_authed(
|
||||||
|
"/api/v1/users",
|
||||||
|
r#"{"username":"newuser","password":"password123","role":"viewer"}"#,
|
||||||
|
&editor_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_user_admin_ok() {
|
||||||
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(post_json_authed(
|
||||||
|
"/api/v1/users",
|
||||||
|
r#"{"username":"newuser","password":"password123","role":"viewer"}"#,
|
||||||
|
&admin_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let status = response.status();
|
||||||
|
let body = response_body(response).await;
|
||||||
|
assert!(
|
||||||
|
status == StatusCode::OK || status == StatusCode::CREATED,
|
||||||
|
"unexpected status: {status}"
|
||||||
|
);
|
||||||
|
assert!(body["id"].is_string(), "expected id field, got: {body}");
|
||||||
|
assert_eq!(body["username"].as_str().unwrap(), "newuser");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_user_duplicate_username() {
|
||||||
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
||||||
|
// "admin" already exists
|
||||||
|
let response = app
|
||||||
|
.oneshot(post_json_authed(
|
||||||
|
"/api/v1/users",
|
||||||
|
r#"{"username":"admin","password":"password123","role":"viewer"}"#,
|
||||||
|
&admin_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::CONFLICT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_user_password_too_short() {
|
||||||
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(post_json_authed(
|
||||||
|
"/api/v1/users",
|
||||||
|
r#"{"username":"shortpass","password":"short","role":"viewer"}"#,
|
||||||
|
&admin_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
// Password minimum is 8 chars; should be rejected
|
||||||
|
assert!(
|
||||||
|
response.status() == StatusCode::BAD_REQUEST
|
||||||
|
|| response.status() == StatusCode::UNPROCESSABLE_ENTITY
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/v1/users/{id} (admin)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_user_requires_admin() {
|
||||||
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(get_authed(
|
||||||
|
"/api/v1/users/00000000-0000-0000-0000-000000000000",
|
||||||
|
&viewer_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_user_not_found() {
|
||||||
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(get_authed(
|
||||||
|
"/api/v1/users/00000000-0000-0000-0000-000000000000",
|
||||||
|
&admin_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PATCH /api/v1/users/{id} (admin)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_user_requires_admin() {
|
||||||
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(patch_json_authed(
|
||||||
|
"/api/v1/users/00000000-0000-0000-0000-000000000000",
|
||||||
|
r#"{"role":"editor"}"#,
|
||||||
|
&viewer_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /api/v1/users/{id} (admin)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn delete_user_requires_admin() {
|
||||||
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(delete_authed(
|
||||||
|
"/api/v1/users/00000000-0000-0000-0000-000000000000",
|
||||||
|
&viewer_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn delete_user_not_found() {
|
||||||
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(delete_authed(
|
||||||
|
"/api/v1/users/00000000-0000-0000-0000-000000000000",
|
||||||
|
&admin_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/v1/users/{id}/libraries (admin)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_user_libraries_requires_admin() {
|
||||||
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(get_authed(
|
||||||
|
"/api/v1/users/00000000-0000-0000-0000-000000000000/libraries",
|
||||||
|
&editor_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No-auth coverage (exercises setup_app and get helpers)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn media_list_no_auth_users_file() {
|
||||||
|
let app = setup_app().await;
|
||||||
|
let response = app.oneshot(get("/api/v1/media")).await.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
90
crates/pinakes-server/tests/webhooks.rs
Normal file
90
crates/pinakes-server/tests/webhooks.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
use common::{
|
||||||
|
get,
|
||||||
|
get_authed,
|
||||||
|
post_json_authed,
|
||||||
|
response_body,
|
||||||
|
setup_app,
|
||||||
|
setup_app_with_auth,
|
||||||
|
};
|
||||||
|
use tower::ServiceExt;
|
||||||
|
|
||||||
|
// GET /api/v1/webhooks (viewer)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn list_webhooks_requires_auth() {
|
||||||
|
let (app, ..) = setup_app_with_auth().await;
|
||||||
|
let response = app.oneshot(get("/api/v1/webhooks")).await.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn list_webhooks_viewer_ok() {
|
||||||
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(get_authed("/api/v1/webhooks", &viewer_token))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let status = response.status();
|
||||||
|
let body = response_body(response).await;
|
||||||
|
assert_eq!(status, StatusCode::OK);
|
||||||
|
// No webhooks configured in test config: empty array
|
||||||
|
assert!(body.is_array(), "expected array, got: {body}");
|
||||||
|
assert_eq!(body.as_array().unwrap().len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn list_webhooks_no_auth_disabled_ok() {
|
||||||
|
// Auth disabled (setup_app): viewer-level route still accessible
|
||||||
|
let app = setup_app().await;
|
||||||
|
let response = app.oneshot(get("/api/v1/webhooks")).await.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /api/v1/webhooks/test (editor)
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_webhook_requires_editor() {
|
||||||
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(post_json_authed(
|
||||||
|
"/api/v1/webhooks/test",
|
||||||
|
"{}",
|
||||||
|
&viewer_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_webhook_no_dispatcher_returns_ok() {
|
||||||
|
// No webhook dispatcher in test setup; route should return 200 with
|
||||||
|
// "no webhooks configured" message rather than erroring.
|
||||||
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(post_json_authed(
|
||||||
|
"/api/v1/webhooks/test",
|
||||||
|
"{}",
|
||||||
|
&editor_token,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
// Either OK or the route returns a structured response about no webhooks
|
||||||
|
assert!(
|
||||||
|
response.status() == StatusCode::OK
|
||||||
|
|| response.status() == StatusCode::BAD_REQUEST
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_webhook_requires_auth() {
|
||||||
|
let (app, ..) = setup_app_with_auth().await;
|
||||||
|
let response = app
|
||||||
|
.oneshot(post_json_authed("/api/v1/webhooks/test", "{}", "badtoken"))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue