refactor: 移除独立 hub/proxy/executor/gateway crate,统一为 gateway tunnel 架构

- 删除 aether-hub、aether-proxy 独立项目及其 Dockerfile/配置
- 删除 crates/aether-executor 和 crates/aether-gateway 全部模块
- 新增 apps/ 目录作为应用入口
- 将 hub 概念重构为 gateway tunnel transport
- 将 executor 重构为 execution runtime
- 新增 tunnel.rs 合约定义和 testkit tunnel/execution_runtime 模块
- 更新 Python 服务层和测试适配新架构命名
This commit is contained in:
fawney19
2026-04-03 14:59:58 +08:00
parent ddf18fed9a
commit 8f26e1a31f
983 changed files with 103097 additions and 105836 deletions
@@ -0,0 +1,102 @@
use super::*;
pub(super) fn sample_transport(
base_url: &str,
provider_api_format: &str,
) -> LocalVideoTaskTransport {
let url = match provider_api_format {
"openai:video" => format!("{base_url}/v1/videos"),
"gemini:video" => {
format!("{base_url}/v1beta/models/veo-3:predictLongRunning")
}
_ => panic!("unsupported provider api format"),
};
LocalVideoTaskTransport::from_plan(&sample_plan(&url, provider_api_format))
.expect("transport should build")
}
pub(super) fn sample_persistence(provider_api_format: &str) -> LocalVideoTaskPersistence {
LocalVideoTaskPersistence {
request_id: "request-123".to_string(),
username: Some("user".to_string()),
api_key_name: Some("primary".to_string()),
client_api_format: provider_api_format.to_string(),
provider_api_format: provider_api_format.to_string(),
original_request_body: json!({
"prompt": "hello",
"seconds": 4,
"resolution": "720p",
"aspect_ratio": "16:9",
"size": "1280x720"
}),
format_converted: false,
}
}
pub(super) fn sample_auth_context() -> GatewayControlAuthContext {
GatewayControlAuthContext {
user_id: "user-123".to_string(),
api_key_id: "key-123".to_string(),
balance_remaining: None,
access_allowed: true,
user_rate_limit: None,
api_key_rate_limit: None,
api_key_is_standalone: false,
local_rejection: None,
allowed_models: None,
}
}
pub(super) fn sample_plan(url: &str, provider_api_format: &str) -> ExecutionPlan {
ExecutionPlan {
request_id: "req-123".to_string(),
candidate_id: None,
provider_name: Some(
provider_api_format
.split(':')
.next()
.expect("provider name should exist")
.to_string(),
),
provider_id: "provider-123".to_string(),
endpoint_id: "endpoint-123".to_string(),
key_id: "key-123".to_string(),
method: "POST".to_string(),
url: url.to_string(),
headers: BTreeMap::from([(
"authorization".to_string(),
"Bearer upstream-key".to_string(),
)]),
content_type: Some("application/json".to_string()),
content_encoding: None,
body: RequestBody::from_json(json!({})),
stream: false,
client_api_format: provider_api_format.to_string(),
provider_api_format: provider_api_format.to_string(),
model_name: Some(
if provider_api_format == "gemini:video" {
"veo-3"
} else {
"sora-2"
}
.to_string(),
),
proxy: Some(ProxySnapshot {
enabled: Some(false),
mode: Some("direct".to_string()),
node_id: None,
label: None,
url: None,
extra: None,
}),
tls_profile: Some("chrome".to_string()),
timeouts: Some(ExecutionTimeouts {
connect_ms: Some(10_000),
read_ms: Some(30_000),
first_byte_ms: None,
write_ms: Some(30_000),
pool_ms: Some(10_000),
total_ms: Some(300_000),
}),
}
}
@@ -0,0 +1,403 @@
use super::fixtures::*;
use super::*;
#[test]
fn rust_authoritative_service_builds_openai_cancel_follow_up_plan() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
service.record_snapshot(LocalVideoTaskSnapshot::OpenAi(OpenAiVideoTaskSeed {
local_task_id: "task-local-123".to_string(),
upstream_task_id: "ext-video-task-123".to_string(),
created_at_unix_secs: 1712345678,
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: Some("sora-2".to_string()),
prompt: None,
size: None,
seconds: None,
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Submitted,
progress_percent: 0,
completed_at_unix_secs: None,
expires_at_unix_secs: None,
error_code: None,
error_message: None,
video_url: None,
persistence: sample_persistence("openai:video"),
transport: sample_transport("https://api.openai.example", "openai:video"),
}));
let follow_up = service
.prepare_follow_up_sync_plan(
"openai_video_cancel_sync",
"/v1/videos/task-local-123/cancel",
None,
Some(&sample_auth_context()),
"trace-openai-cancel-123",
)
.expect("follow-up plan should build");
assert_eq!(follow_up.plan.method, "DELETE");
assert_eq!(
follow_up.plan.url,
"https://api.openai.example/v1/videos/ext-video-task-123"
);
assert_eq!(follow_up.plan.provider_api_format, "openai:video");
assert!(follow_up.plan.body.json_body.is_none());
assert_eq!(
follow_up.report_kind.as_deref(),
Some("openai_video_cancel_sync_finalize")
);
assert_eq!(
follow_up
.report_context
.as_ref()
.and_then(Value::as_object)
.and_then(|value| value.get("task_id"))
.and_then(Value::as_str),
Some("task-local-123")
);
}
#[test]
fn rust_authoritative_service_builds_openai_remix_follow_up_plan() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
service.record_snapshot(LocalVideoTaskSnapshot::OpenAi(OpenAiVideoTaskSeed {
local_task_id: "task-local-123".to_string(),
upstream_task_id: "ext-video-task-123".to_string(),
created_at_unix_secs: 1712345678,
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: Some("sora-2".to_string()),
prompt: None,
size: None,
seconds: None,
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Completed,
progress_percent: 100,
completed_at_unix_secs: Some(1712345688),
expires_at_unix_secs: None,
error_code: None,
error_message: None,
video_url: Some("https://cdn.example.com/original.mp4".to_string()),
persistence: sample_persistence("openai:video"),
transport: sample_transport("https://api.openai.example", "openai:video"),
}));
let remix_body = json!({
"prompt": "remix this",
"model": "sora-2",
});
let follow_up = service
.prepare_follow_up_sync_plan(
"openai_video_remix_sync",
"/v1/videos/task-local-123/remix",
Some(&remix_body),
Some(&sample_auth_context()),
"trace-openai-remix-123",
)
.expect("follow-up plan should build");
assert_eq!(follow_up.plan.method, "POST");
assert_eq!(
follow_up.plan.url,
"https://api.openai.example/v1/videos/ext-video-task-123/remix"
);
assert_eq!(follow_up.plan.provider_api_format, "openai:video");
assert_eq!(follow_up.plan.body.json_body, Some(remix_body.clone()));
assert_eq!(
follow_up.report_kind.as_deref(),
Some("openai_video_remix_sync_finalize")
);
assert_eq!(
follow_up
.report_context
.as_ref()
.and_then(Value::as_object)
.and_then(|value| value.get("task_id"))
.and_then(Value::as_str),
Some("task-local-123")
);
}
#[test]
fn rust_authoritative_service_builds_openai_delete_follow_up_plan() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
service.record_snapshot(LocalVideoTaskSnapshot::OpenAi(OpenAiVideoTaskSeed {
local_task_id: "task-local-123".to_string(),
upstream_task_id: "ext-video-task-123".to_string(),
created_at_unix_secs: 1712345678,
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: Some("sora-2".to_string()),
prompt: None,
size: None,
seconds: None,
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Completed,
progress_percent: 100,
completed_at_unix_secs: Some(1712345688),
expires_at_unix_secs: None,
error_code: None,
error_message: None,
video_url: None,
persistence: sample_persistence("openai:video"),
transport: sample_transport("https://api.openai.example", "openai:video"),
}));
let follow_up = service
.prepare_follow_up_sync_plan(
"openai_video_delete_sync",
"/v1/videos/task-local-123",
None,
Some(&sample_auth_context()),
"trace-openai-delete-123",
)
.expect("follow-up plan should build");
assert_eq!(follow_up.plan.method, "DELETE");
assert_eq!(
follow_up.plan.url,
"https://api.openai.example/v1/videos/ext-video-task-123"
);
assert_eq!(follow_up.plan.provider_api_format, "openai:video");
assert!(follow_up.plan.body.json_body.is_none());
assert_eq!(
follow_up.report_kind.as_deref(),
Some("openai_video_delete_sync_finalize")
);
assert_eq!(
follow_up
.report_context
.as_ref()
.and_then(Value::as_object)
.and_then(|value| value.get("task_id"))
.and_then(Value::as_str),
Some("task-local-123")
);
}
#[test]
fn rust_authoritative_service_builds_gemini_cancel_follow_up_plan() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
service.record_snapshot(LocalVideoTaskSnapshot::Gemini(GeminiVideoTaskSeed {
local_short_id: "localshort123".to_string(),
upstream_operation_name: "operations/ext-video-123".to_string(),
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: "veo-3".to_string(),
status: LocalVideoTaskStatus::Submitted,
progress_percent: 0,
error_code: None,
error_message: None,
metadata: json!({}),
persistence: sample_persistence("gemini:video"),
transport: sample_transport("https://generativelanguage.googleapis.com", "gemini:video"),
}));
let follow_up = service
.prepare_follow_up_sync_plan(
"gemini_video_cancel_sync",
"/v1beta/models/veo-3/operations/localshort123:cancel",
None,
Some(&sample_auth_context()),
"trace-gemini-cancel-123",
)
.expect("follow-up plan should build");
assert_eq!(follow_up.plan.method, "POST");
assert_eq!(
follow_up.plan.url,
"https://generativelanguage.googleapis.com/v1beta/models/veo-3/operations/ext-video-123:cancel"
);
assert_eq!(follow_up.plan.provider_api_format, "gemini:video");
assert_eq!(follow_up.plan.body.json_body, Some(json!({})));
assert_eq!(
follow_up.report_kind.as_deref(),
Some("gemini_video_cancel_sync_finalize")
);
assert_eq!(
follow_up
.report_context
.as_ref()
.and_then(Value::as_object)
.and_then(|value| value.get("task_id"))
.and_then(Value::as_str),
Some("localshort123")
);
}
#[test]
fn rust_authoritative_service_builds_openai_read_refresh_plan() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
service.record_snapshot(LocalVideoTaskSnapshot::OpenAi(OpenAiVideoTaskSeed {
local_task_id: "task-local-123".to_string(),
upstream_task_id: "ext-video-task-123".to_string(),
created_at_unix_secs: 1712345678,
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: Some("sora-2".to_string()),
prompt: None,
size: None,
seconds: None,
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Submitted,
progress_percent: 0,
completed_at_unix_secs: None,
expires_at_unix_secs: None,
error_code: None,
error_message: None,
video_url: None,
persistence: sample_persistence("openai:video"),
transport: sample_transport("https://api.openai.example", "openai:video"),
}));
let refresh = service
.prepare_read_refresh_sync_plan(
Some("openai"),
"/v1/videos/task-local-123",
"trace-openai-read-123",
)
.expect("read refresh plan should build");
assert_eq!(refresh.plan.method, "GET");
assert_eq!(
refresh.plan.url,
"https://api.openai.example/v1/videos/ext-video-task-123"
);
assert!(refresh.plan.body.json_body.is_none());
}
#[test]
fn rust_authoritative_service_builds_gemini_read_refresh_plan() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
service.record_snapshot(LocalVideoTaskSnapshot::Gemini(GeminiVideoTaskSeed {
local_short_id: "localshort123".to_string(),
upstream_operation_name: "operations/ext-video-123".to_string(),
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: "veo-3".to_string(),
status: LocalVideoTaskStatus::Submitted,
progress_percent: 0,
error_code: None,
error_message: None,
metadata: json!({}),
persistence: sample_persistence("gemini:video"),
transport: sample_transport("https://generativelanguage.googleapis.com", "gemini:video"),
}));
let refresh = service
.prepare_read_refresh_sync_plan(
Some("gemini"),
"/v1beta/models/veo-3/operations/localshort123",
"trace-gemini-read-123",
)
.expect("read refresh plan should build");
assert_eq!(refresh.plan.method, "GET");
assert_eq!(
refresh.plan.url,
"https://generativelanguage.googleapis.com/v1beta/models/veo-3/operations/ext-video-123"
);
assert!(refresh.plan.body.json_body.is_none());
}
#[test]
fn rust_authoritative_service_builds_poll_refresh_batch_for_active_tasks_only() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
service.record_snapshot(LocalVideoTaskSnapshot::OpenAi(OpenAiVideoTaskSeed {
local_task_id: "task-active-123".to_string(),
upstream_task_id: "ext-video-task-123".to_string(),
created_at_unix_secs: 1712345678,
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: Some("sora-2".to_string()),
prompt: None,
size: None,
seconds: None,
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Processing,
progress_percent: 42,
completed_at_unix_secs: None,
expires_at_unix_secs: None,
error_code: None,
error_message: None,
video_url: None,
persistence: sample_persistence("openai:video"),
transport: sample_transport("https://api.openai.example", "openai:video"),
}));
service.record_snapshot(LocalVideoTaskSnapshot::OpenAi(OpenAiVideoTaskSeed {
local_task_id: "task-completed-123".to_string(),
upstream_task_id: "ext-video-task-999".to_string(),
created_at_unix_secs: 1712345678,
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: Some("sora-2".to_string()),
prompt: None,
size: None,
seconds: None,
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Completed,
progress_percent: 100,
completed_at_unix_secs: Some(1712345688),
expires_at_unix_secs: None,
error_code: None,
error_message: None,
video_url: Some("https://cdn.example.com/ext-video-task-999.mp4".to_string()),
persistence: sample_persistence("openai:video"),
transport: sample_transport("https://api.openai.example", "openai:video"),
}));
let batch = service.prepare_poll_refresh_batch(10, "trace-poller");
assert_eq!(batch.len(), 1);
assert_eq!(batch[0].plan.method, "GET");
assert_eq!(
batch[0].plan.url,
"https://api.openai.example/v1/videos/ext-video-task-123"
);
}
#[test]
fn file_video_task_store_persists_snapshots_across_service_rebuilds() {
let store_path =
std::env::temp_dir().join(format!("aether-video-task-store-{}.json", Uuid::new_v4()));
let service =
VideoTaskService::with_file_store(VideoTaskTruthSourceMode::RustAuthoritative, &store_path)
.expect("file-backed service should build");
service.record_snapshot(LocalVideoTaskSnapshot::OpenAi(OpenAiVideoTaskSeed {
local_task_id: "task-file-123".to_string(),
upstream_task_id: "ext-video-task-123".to_string(),
created_at_unix_secs: 1712345678,
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: Some("sora-2".to_string()),
prompt: Some("hello".to_string()),
size: None,
seconds: None,
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Submitted,
progress_percent: 0,
completed_at_unix_secs: None,
expires_at_unix_secs: None,
error_code: None,
error_message: None,
video_url: None,
persistence: sample_persistence("openai:video"),
transport: sample_transport("https://api.openai.example", "openai:video"),
}));
drop(service);
let reopened =
VideoTaskService::with_file_store(VideoTaskTruthSourceMode::RustAuthoritative, &store_path)
.expect("reopened file-backed service should build");
let response = reopened
.read_response(Some("openai"), "/v1/videos/task-file-123")
.expect("persisted read response should exist");
assert_eq!(response.status_code, 200);
assert_eq!(
response.body_json.get("id").and_then(Value::as_str),
Some("task-file-123")
);
let _ = std::fs::remove_file(store_path);
}
@@ -0,0 +1,246 @@
use super::fixtures::*;
use super::*;
#[test]
fn rust_authoritative_service_projects_openai_status_into_local_read_response() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
service.record_snapshot(LocalVideoTaskSnapshot::OpenAi(OpenAiVideoTaskSeed {
local_task_id: "task-local-123".to_string(),
upstream_task_id: "ext-video-task-123".to_string(),
created_at_unix_secs: 1712345678,
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: Some("sora-2".to_string()),
prompt: Some("hello".to_string()),
size: None,
seconds: None,
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Submitted,
progress_percent: 0,
completed_at_unix_secs: None,
expires_at_unix_secs: None,
error_code: None,
error_message: None,
video_url: None,
persistence: sample_persistence("openai:video"),
transport: sample_transport("https://api.openai.example", "openai:video"),
}));
assert!(service.project_openai_task_response(
"task-local-123",
json!({
"status": "processing",
"progress": 42
})
.as_object()
.expect("provider body should be object"),
));
let processing = service
.read_response(Some("openai"), "/v1/videos/task-local-123")
.expect("processing read response should exist");
assert_eq!(processing.status_code, 200);
assert_eq!(
processing.body_json.get("status"),
Some(&json!("processing"))
);
assert_eq!(processing.body_json.get("progress"), Some(&json!(42)));
assert!(service.project_openai_task_response(
"task-local-123",
json!({
"status": "failed",
"progress": 100,
"completed_at": 1712345688u64,
"error": {
"code": "upstream_failed",
"message": "provider failed"
}
})
.as_object()
.expect("provider body should be object"),
));
let failed = service
.read_response(Some("openai"), "/v1/videos/task-local-123")
.expect("failed read response should exist");
assert_eq!(failed.status_code, 200);
assert_eq!(failed.body_json.get("status"), Some(&json!("failed")));
assert_eq!(
failed.body_json.get("completed_at"),
Some(&json!(1712345688u64))
);
assert_eq!(
failed
.body_json
.get("error")
.and_then(Value::as_object)
.and_then(|value| value.get("code")),
Some(&json!("upstream_failed"))
);
}
#[test]
fn rust_authoritative_service_builds_openai_content_stream_plan_from_direct_video_url() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
service.record_snapshot(LocalVideoTaskSnapshot::OpenAi(OpenAiVideoTaskSeed {
local_task_id: "task-local-123".to_string(),
upstream_task_id: "ext-video-task-123".to_string(),
created_at_unix_secs: 1712345678,
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: Some("sora-2".to_string()),
prompt: None,
size: None,
seconds: None,
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Completed,
progress_percent: 100,
completed_at_unix_secs: Some(1712345688),
expires_at_unix_secs: None,
error_code: None,
error_message: None,
video_url: Some("https://cdn.example.com/ext-video-task-123.mp4".to_string()),
persistence: sample_persistence("openai:video"),
transport: sample_transport("https://api.openai.example", "openai:video"),
}));
let action = service
.prepare_openai_content_stream_action(
"/v1/videos/task-local-123/content",
Some("variant=video"),
"trace-openai-content-123",
)
.expect("content action should exist");
let LocalVideoTaskContentAction::StreamPlan(plan) = action else {
panic!("content action should be stream plan");
};
assert_eq!(plan.method, "GET");
assert_eq!(plan.url, "https://cdn.example.com/ext-video-task-123.mp4");
assert!(plan.headers.is_empty());
}
#[test]
fn rust_authoritative_service_returns_processing_content_response_for_pending_openai_task() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
service.record_snapshot(LocalVideoTaskSnapshot::OpenAi(OpenAiVideoTaskSeed {
local_task_id: "task-local-123".to_string(),
upstream_task_id: "ext-video-task-123".to_string(),
created_at_unix_secs: 1712345678,
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: Some("sora-2".to_string()),
prompt: None,
size: None,
seconds: None,
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Processing,
progress_percent: 42,
completed_at_unix_secs: None,
expires_at_unix_secs: None,
error_code: None,
error_message: None,
video_url: None,
persistence: sample_persistence("openai:video"),
transport: sample_transport("https://api.openai.example", "openai:video"),
}));
let action = service
.prepare_openai_content_stream_action(
"/v1/videos/task-local-123/content",
Some("variant=video"),
"trace-openai-content-processing-123",
)
.expect("content action should exist");
let LocalVideoTaskContentAction::Immediate {
status_code,
body_json,
} = action
else {
panic!("content action should be immediate response");
};
assert_eq!(status_code, 202);
assert_eq!(
body_json,
json!({"detail": "Video is still processing (status: processing)"})
);
}
#[test]
fn rust_authoritative_service_projects_gemini_status_into_local_read_response() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
service.record_snapshot(LocalVideoTaskSnapshot::Gemini(GeminiVideoTaskSeed {
local_short_id: "localshort123".to_string(),
upstream_operation_name: "operations/ext-video-123".to_string(),
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: "veo-3".to_string(),
status: LocalVideoTaskStatus::Submitted,
progress_percent: 0,
error_code: None,
error_message: None,
metadata: json!({}),
persistence: sample_persistence("gemini:video"),
transport: sample_transport("https://generativelanguage.googleapis.com", "gemini:video"),
}));
assert!(service.project_gemini_task_response(
"localshort123",
json!({
"done": false,
"metadata": {
"state": "PROCESSING"
}
})
.as_object()
.expect("provider body should be object"),
));
let processing = service
.read_response(
Some("gemini"),
"/v1beta/models/veo-3/operations/localshort123",
)
.expect("processing read response should exist");
assert_eq!(processing.status_code, 200);
assert_eq!(processing.body_json.get("done"), Some(&json!(false)));
assert_eq!(
processing.body_json.get("metadata"),
Some(&json!({"state": "PROCESSING"}))
);
assert!(service.project_gemini_task_response(
"localshort123",
json!({
"done": true,
"response": {
"generateVideoResponse": {
"generatedSamples": [
{"video": {"uri": "https://example.invalid/video.mp4"}}
]
}
}
})
.as_object()
.expect("provider body should be object"),
));
let completed = service
.read_response(
Some("gemini"),
"/v1beta/models/veo-3/operations/localshort123",
)
.expect("completed read response should exist");
assert_eq!(completed.status_code, 200);
assert_eq!(completed.body_json.get("done"), Some(&json!(true)));
assert_eq!(
completed
.body_json
.get("response")
.and_then(|value| value.get("generateVideoResponse"))
.and_then(|value| value.get("generatedSamples"))
.and_then(Value::as_array)
.and_then(|value| value.first())
.and_then(|value| value.get("video"))
.and_then(|value| value.get("uri")),
Some(&json!("/v1beta/files/aev_localshort123:download?alt=media"))
);
}
@@ -0,0 +1,369 @@
use super::fixtures::*;
use super::*;
#[test]
fn openai_video_seed_preserves_existing_local_identity() {
let provider_body = json!({"id": "ext-video-task-123"});
let report_context = json!({
"local_task_id": "task-local-123",
"local_created_at": 1712345678u64,
"model": "sora-2",
"original_request_body": {
"prompt": "hello",
"size": "1024x1024",
"seconds": "8"
}
});
let seed = LocalVideoTaskSeed::from_sync_finalize(
"openai_video_create_sync_finalize",
provider_body
.as_object()
.expect("provider body should be object"),
report_context
.as_object()
.expect("report context should be object"),
&sample_plan("https://api.openai.example/v1/videos", "openai:video"),
)
.expect("seed should build");
let mut patched_context = report_context
.as_object()
.expect("report context should be object")
.clone();
seed.apply_to_report_context(&mut patched_context);
let client_body = seed.client_body_json();
assert_eq!(
seed.success_report_kind(),
"openai_video_create_sync_success"
);
assert_eq!(
patched_context.get("local_task_id").and_then(Value::as_str),
Some("task-local-123")
);
assert_eq!(
patched_context
.get("local_created_at")
.and_then(Value::as_u64),
Some(1712345678)
);
assert_eq!(
client_body.get("id").and_then(Value::as_str),
Some("task-local-123")
);
assert_eq!(
client_body.get("created_at").and_then(Value::as_u64),
Some(1712345678)
);
assert_eq!(
client_body.get("model").and_then(Value::as_str),
Some("sora-2")
);
assert_eq!(
client_body.get("prompt").and_then(Value::as_str),
Some("hello")
);
}
#[test]
fn openai_video_remix_seed_carries_source_task() {
let provider_body = json!({"id": "ext-remix-task-123"});
let report_context = json!({
"task_id": "task-source-123",
"original_request_body": {
"prompt": "remix it"
}
});
let seed = LocalVideoTaskSeed::from_sync_finalize(
"openai_video_remix_sync_finalize",
provider_body
.as_object()
.expect("provider body should be object"),
report_context
.as_object()
.expect("report context should be object"),
&sample_plan(
"https://api.openai.example/v1/videos/ext-source/remix",
"openai:video",
),
)
.expect("seed should build");
let client_body = seed.client_body_json();
assert_eq!(
seed.success_report_kind(),
"openai_video_remix_sync_success"
);
assert_eq!(
client_body
.get("remixed_from_video_id")
.and_then(Value::as_str),
Some("task-source-123")
);
}
#[test]
fn gemini_video_seed_generates_short_id_and_pending_operation_name() {
let provider_body = json!({"name": "operations/ext-video-task-123"});
let report_context = json!({
"model": "veo-3"
});
let seed = LocalVideoTaskSeed::from_sync_finalize(
"gemini_video_create_sync_finalize",
provider_body
.as_object()
.expect("provider body should be object"),
report_context
.as_object()
.expect("report context should be object"),
&sample_plan(
"https://generativelanguage.googleapis.com/v1beta/models/veo-3:predictLongRunning",
"gemini:video",
),
)
.expect("seed should build");
let mut patched_context = Map::new();
seed.apply_to_report_context(&mut patched_context);
let client_body = seed.client_body_json();
let local_short_id = patched_context
.get("local_short_id")
.and_then(Value::as_str)
.expect("local_short_id should exist");
assert_eq!(
seed.success_report_kind(),
"gemini_video_create_sync_success"
);
assert_eq!(local_short_id.len(), 12);
assert_eq!(
client_body.get("name").and_then(Value::as_str),
Some(format!("models/veo-3/operations/{local_short_id}").as_str())
);
assert_eq!(
client_body.get("done").and_then(Value::as_bool),
Some(false)
);
}
#[test]
fn python_backed_video_truth_source_requires_inline_sync_report() {
let provider_body = json!({"id": "ext-video-task-123"});
let report_context = json!({});
let plan = VideoTaskTruthSourceMode::PythonSyncReport
.prepare_sync_success(
"openai_video_create_sync_finalize",
provider_body
.as_object()
.expect("provider body should be object"),
report_context
.as_object()
.expect("report context should be object"),
&sample_plan("https://api.openai.example/v1/videos", "openai:video"),
)
.expect("plan should build");
assert_eq!(plan.report_mode(), VideoTaskSyncReportMode::InlineSync);
assert_eq!(
plan.success_report_kind(),
"openai_video_create_sync_success"
);
}
#[test]
fn rust_authoritative_video_truth_source_can_background_success_report() {
let provider_body = json!({"name": "operations/ext-video-task-123"});
let report_context = json!({
"model": "veo-3"
});
let plan = VideoTaskTruthSourceMode::RustAuthoritative
.prepare_sync_success(
"gemini_video_create_sync_finalize",
provider_body
.as_object()
.expect("provider body should be object"),
report_context
.as_object()
.expect("report context should be object"),
&sample_plan(
"https://generativelanguage.googleapis.com/v1beta/models/veo-3:predictLongRunning",
"gemini:video",
),
)
.expect("plan should build");
assert_eq!(plan.report_mode(), VideoTaskSyncReportMode::Background);
assert_eq!(
plan.success_report_kind(),
"gemini_video_create_sync_success"
);
let mut report_context = Map::new();
plan.apply_to_report_context(&mut report_context);
assert_eq!(
report_context.get("rust_video_task_persisted"),
Some(&Value::Bool(true))
);
}
#[test]
fn rust_authoritative_service_reads_openai_task_from_local_registry() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
let snapshot = LocalVideoTaskSnapshot::OpenAi(OpenAiVideoTaskSeed {
local_task_id: "task-local-123".to_string(),
upstream_task_id: "ext-video-task-123".to_string(),
created_at_unix_secs: 1712345678,
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: Some("sora-2".to_string()),
prompt: Some("hello".to_string()),
size: None,
seconds: None,
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Submitted,
progress_percent: 0,
completed_at_unix_secs: None,
expires_at_unix_secs: None,
error_code: None,
error_message: None,
video_url: None,
persistence: sample_persistence("openai:video"),
transport: sample_transport("https://api.openai.example", "openai:video"),
});
service.record_snapshot(snapshot);
let response = service
.read_response(Some("openai"), "/v1/videos/task-local-123")
.expect("read response should exist");
assert_eq!(response.status_code, 200);
assert_eq!(
response.body_json.get("id").and_then(Value::as_str),
Some("task-local-123")
);
assert_eq!(
response.body_json.get("status").and_then(Value::as_str),
Some("queued")
);
}
#[test]
fn rust_authoritative_service_applies_cancel_and_delete_mutations() {
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
service.record_snapshot(LocalVideoTaskSnapshot::OpenAi(OpenAiVideoTaskSeed {
local_task_id: "task-local-123".to_string(),
upstream_task_id: "ext-video-task-123".to_string(),
created_at_unix_secs: 1712345678,
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: Some("sora-2".to_string()),
prompt: None,
size: None,
seconds: None,
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Submitted,
progress_percent: 0,
completed_at_unix_secs: None,
expires_at_unix_secs: None,
error_code: None,
error_message: None,
video_url: None,
persistence: sample_persistence("openai:video"),
transport: sample_transport("https://api.openai.example", "openai:video"),
}));
service.record_snapshot(LocalVideoTaskSnapshot::Gemini(GeminiVideoTaskSeed {
local_short_id: "short12345678".to_string(),
upstream_operation_name: "operations/ext-video-task-123".to_string(),
user_id: Some("user-123".to_string()),
api_key_id: Some("key-123".to_string()),
model: "veo-3".to_string(),
status: LocalVideoTaskStatus::Submitted,
progress_percent: 0,
error_code: None,
error_message: None,
metadata: json!({}),
persistence: sample_persistence("gemini:video"),
transport: sample_transport("https://generativelanguage.googleapis.com", "gemini:video"),
}));
service.apply_finalize_mutation(
"/v1/videos/task-local-123/cancel",
"openai_video_cancel_sync_finalize",
);
let cancelled_openai = service
.read_response(Some("openai"), "/v1/videos/task-local-123")
.expect("openai read response should exist");
assert_eq!(cancelled_openai.status_code, 404);
assert_eq!(
cancelled_openai.body_json,
json!({"detail": "Video task was cancelled"})
);
service.apply_finalize_mutation(
"/v1beta/models/veo-3/operations/short12345678:cancel",
"gemini_video_cancel_sync_finalize",
);
let cancelled_gemini = service
.read_response(
Some("gemini"),
"/v1beta/models/veo-3/operations/short12345678",
)
.expect("gemini read response should exist");
assert_eq!(cancelled_gemini.status_code, 404);
assert_eq!(
cancelled_gemini.body_json,
json!({"detail": "Video task was cancelled"})
);
service.apply_finalize_mutation(
"/v1/videos/task-local-123",
"openai_video_delete_sync_finalize",
);
let deleted_openai = service
.read_response(Some("openai"), "/v1/videos/task-local-123")
.expect("deleted openai read response should exist");
assert_eq!(deleted_openai.status_code, 404);
assert_eq!(
deleted_openai.body_json,
json!({"detail": "Video task not found"})
);
}
#[test]
fn seed_captures_transport_metadata_from_execution_plan() {
let provider_body = json!({"id": "ext-video-task-123"});
let report_context = json!({});
let seed = LocalVideoTaskSeed::from_sync_finalize(
"openai_video_create_sync_finalize",
provider_body
.as_object()
.expect("provider body should be object"),
report_context
.as_object()
.expect("report context should be object"),
&sample_plan("https://api.openai.example/v1/videos", "openai:video"),
)
.expect("seed should build");
let LocalVideoTaskSeed::OpenAiCreate(seed) = seed else {
panic!("seed should be openai");
};
assert_eq!(
seed.transport.upstream_base_url,
"https://api.openai.example"
);
assert_eq!(seed.transport.provider_id, "provider-123");
assert_eq!(seed.transport.endpoint_id, "endpoint-123");
assert_eq!(seed.transport.key_id, "key-123");
assert_eq!(
seed.transport
.headers
.get("authorization")
.map(String::as_str),
Some("Bearer upstream-key")
);
}