chore(api): drop unused uuid dep + redundant domain dev-dep; test internal exclusion + note list/count race

This commit is contained in:
2026-06-02 13:55:01 +02:00
parent 3c4ada202f
commit 720c7ddbbf
3 changed files with 26 additions and 14 deletions
-2
View File
@@ -10,7 +10,6 @@ serde.workspace = true
utoipa.workspace = true utoipa.workspace = true
db = { path = "../db" } db = { path = "../db" }
domain = { path = "../domain" } domain = { path = "../domain" }
uuid.workspace = true
[dev-dependencies] [dev-dependencies]
tokio.workspace = true tokio.workspace = true
@@ -18,4 +17,3 @@ tower.workspace = true
http-body-util.workspace = true http-body-util.workspace = true
serde_json.workspace = true serde_json.workspace = true
sqlx.workspace = true sqlx.workspace = true
domain = { path = "../domain" }
+3
View File
@@ -86,6 +86,9 @@ pub(crate) async fn list_objects(
) -> Result<Json<PublicObjectPage>, StatusCode> { ) -> Result<Json<PublicObjectPage>, StatusCode> {
let (limit, offset) = (page.limit(), page.offset()); let (limit, offset) = (page.limit(), page.offset());
// `items` and `total` come from two separate queries; under concurrent
// publish/unpublish they can momentarily disagree by one — acceptable for a
// public read surface.
let objects = db::catalog::list_public_objects(state.db.pool(), limit, offset) let objects = db::catalog::list_public_objects(state.db.pool(), limit, offset)
.await .await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
+23 -12
View File
@@ -108,29 +108,40 @@ async fn get_public_object_returns_it(pool: PgPool) {
} }
#[sqlx::test(migrations = "../db/migrations")] #[sqlx::test(migrations = "../db/migrations")]
async fn get_non_public_object_is_404(pool: PgPool) { async fn non_public_objects_are_404(pool: PgPool) {
let db = db::Db::from_pool(pool.clone()); let db = db::Db::from_pool(pool.clone());
let mut tx = db.pool().begin().await.unwrap(); let mut tx = db.pool().begin().await.unwrap();
let id = catalog::create_object( let draft = catalog::create_object(
&mut tx, &mut tx,
AuditActor::System, AuditActor::System,
&object("D-1", "draft vase", Visibility::Draft), &object("D-1", "draft vase", Visibility::Draft),
) )
.await .await
.unwrap(); .unwrap();
let internal = catalog::create_object(
&mut tx,
AuditActor::System,
&object("I-1", "internal vase", Visibility::Internal),
)
.await
.unwrap();
tx.commit().await.unwrap(); tx.commit().await.unwrap();
// both non-public states are hidden behind a 404 — not 403 — so existence isn't leaked
let app = build_app(state(pool)); let app = build_app(state(pool));
let resp = app for id in [draft, internal] {
.oneshot( let resp = app
Request::builder() .clone()
.uri(format!("/api/public/objects/{id}")) .oneshot(
.body(Body::empty()) Request::builder()
.unwrap(), .uri(format!("/api/public/objects/{id}"))
) .body(Body::empty())
.await .unwrap(),
.unwrap(); )
assert_eq!(resp.status(), StatusCode::NOT_FOUND); // not 403 — don't leak existence .await
.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
}
} }
#[sqlx::test(migrations = "../db/migrations")] #[sqlx::test(migrations = "../db/migrations")]