use super::support::*;
use crate::proto::udb::core::asset::services::v1 as asset_pb;
use crate::proto::udb::core::asset::services::v1::asset_service_server::AssetService;
use crate::proto::udb::core::storage::services::v1 as storage_pb;
use crate::proto::udb::core::storage::services::v1::storage_service_server::StorageService;
use crate::proto::udb::core::webrtc::services::v1 as webrtc_pb;
use crate::proto::udb::core::webrtc::services::v1::room_service_server::RoomService;
use tonic::Request;
use uuid::Uuid;
const OUTBOX_RELATION: &str = "udb_system.outbox_events";
async fn ensure_outbox_table(pool: &sqlx::PgPool) {
sqlx::query("CREATE SCHEMA IF NOT EXISTS udb_system")
.execute(pool)
.await
.expect("create udb_system schema");
sqlx::query("DROP TABLE IF EXISTS udb_system.outbox_events CASCADE")
.execute(pool)
.await
.expect("drop outbox");
sqlx::query(
"CREATE TABLE udb_system.outbox_events ( \
event_seq BIGSERIAL PRIMARY KEY, \
event_id UUID NOT NULL UNIQUE, \
topic TEXT NOT NULL, \
partition_key TEXT NOT NULL DEFAULT '', \
payload JSONB NOT NULL, \
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() )",
)
.execute(pool)
.await
.expect("create outbox table");
}
async fn assert_outbox_topic(pool: &sqlx::PgPool, topic: &str) -> serde_json::Value {
let (event_id, got_topic, payload): (Uuid, String, serde_json::Value) = sqlx::query_as(
"SELECT event_id, topic, payload FROM udb_system.outbox_events \
WHERE topic = $1 ORDER BY event_seq DESC LIMIT 1",
)
.bind(topic)
.fetch_one(pool)
.await
.unwrap_or_else(|err| panic!("no outbox row written for declared topic {topic}: {err}"));
assert_eq!(got_topic, topic);
assert_eq!(
payload["event_type"], topic,
"envelope event_type must equal the versioned topic the proto `emits[]` declares"
);
assert_eq!(payload["event_id"], event_id.to_string());
payload
}
#[tokio::test]
#[ignore = "requires live Postgres; UDB_LIVE_AUTH_TESTS=1 cargo test --lib live_storage_finalize_emits_declared_topic -- --ignored --nocapture"]
async fn live_storage_finalize_emits_declared_topic() {
let _guard = live_native_service_db_lock().lock().await;
let pool = live_pg_pool().await;
migrate_native_service_db(&pool).await;
ensure_outbox_table(&pool).await;
let tenant_id = Uuid::new_v4().to_string();
let svc = storage_service(pool.clone())
.await
.with_outbox(Some(OUTBOX_RELATION.to_string()));
let reg = svc
.register_upload(Request::new(storage_pb::RegisterUploadRequest {
tenant_id: tenant_id.clone(),
filename: "report.pdf".to_string(),
content_type: "application/pdf".to_string(),
file_type: "DOCUMENT".to_string(),
..Default::default()
}))
.await
.expect("register_upload")
.into_inner();
put_storage_object(®.object_key, "application/pdf", b"%PDF-udb-event").await;
svc.finalize_upload(Request::new(storage_pb::FinalizeUploadRequest {
tenant_id: tenant_id.clone(),
file_id: reg.file_id.clone(),
content_type: "application/pdf".to_string(),
is_public: Some(true),
size_bytes: 2048,
..Default::default()
}))
.await
.expect("finalize_upload");
assert_outbox_topic(&pool, "udb.storage.file.finalized.v1").await;
cleanup_native_service_db(&pool).await;
}
#[tokio::test]
#[ignore = "requires live Postgres; UDB_LIVE_AUTH_TESTS=1 cargo test --lib live_asset_register_emits_declared_topic -- --ignored --nocapture"]
async fn live_asset_register_emits_declared_topic() {
let _guard = live_native_service_db_lock().lock().await;
let pool = live_pg_pool().await;
migrate_native_service_db(&pool).await;
ensure_outbox_table(&pool).await;
let tenant_id = Uuid::new_v4().to_string();
let file_id = seed_storage_file(&pool, &tenant_id).await;
let svc = asset_service(pool.clone())
.await
.with_outbox(Some(OUTBOX_RELATION.to_string()));
svc.register_asset(Request::new(asset_pb::RegisterAssetRequest {
tenant_id: tenant_id.clone(),
file_id,
name: "notes.txt".to_string(),
media_type: "text".to_string(),
..Default::default()
}))
.await
.expect("register_asset");
assert_outbox_topic(&pool, "udb.asset.asset.registered.v1").await;
cleanup_native_service_db(&pool).await;
}
#[tokio::test]
#[ignore = "requires live Postgres; UDB_LIVE_AUTH_TESTS=1 cargo test --lib live_webrtc_create_room_emits_declared_topic -- --ignored --nocapture"]
async fn live_webrtc_create_room_emits_declared_topic() {
let _guard = live_native_service_db_lock().lock().await;
let pool = live_pg_pool().await;
migrate_native_service_db(&pool).await;
ensure_outbox_table(&pool).await;
let tenant_id = Uuid::new_v4().to_string();
let svc = webrtc_service(pool.clone())
.await
.with_outbox(Some(OUTBOX_RELATION.to_string()));
let room = RoomService::create_room(
&svc,
Request::new(webrtc_pb::CreateRoomRequest {
tenant_id: tenant_id.clone(),
name: "standup".to_string(),
max_participants: 10,
..Default::default()
}),
)
.await
.expect("create_room")
.into_inner();
assert!(!room.room_id.is_empty());
assert_outbox_topic(&pool, "udb.webrtc.room.created.v1").await;
cleanup_native_service_db(&pool).await;
}