test: add video job storage and retrieval tests in generate endpoints
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -458,6 +458,127 @@ async def test_generate_video_from_image_unauthenticated(client):
|
|||||||
assert resp.status_code == 401
|
assert resp.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Video job DB storage
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def test_generate_video_stored_in_db(client):
|
||||||
|
"""Submitting a video job inserts a row into generated_videos."""
|
||||||
|
token = await _user_token(client)
|
||||||
|
with patch("app.routers.generate.openrouter.generate_video", new_callable=AsyncMock, return_value=FAKE_VIDEO):
|
||||||
|
resp = await client.post(
|
||||||
|
"/generate/video",
|
||||||
|
json={"model": "stability/stable-video", "prompt": "Ocean waves"},
|
||||||
|
headers={"Authorization": f"Bearer {token}"},
|
||||||
|
)
|
||||||
|
assert resp.status_code == 200
|
||||||
|
|
||||||
|
row = db_module.get_conn().execute(
|
||||||
|
"SELECT job_id, model_id, prompt, status FROM generated_videos WHERE job_id = ?",
|
||||||
|
["gen-vid-1"],
|
||||||
|
).fetchone()
|
||||||
|
assert row is not None
|
||||||
|
assert row[0] == "gen-vid-1"
|
||||||
|
assert row[1] == "stability/stable-video"
|
||||||
|
assert row[2] == "Ocean waves"
|
||||||
|
assert row[3] == "queued"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_generate_video_from_image_stored_in_db(client):
|
||||||
|
"""Submitting a from-image job inserts a row into generated_videos."""
|
||||||
|
token = await _user_token(client)
|
||||||
|
with patch("app.routers.generate.openrouter.generate_video_from_image", new_callable=AsyncMock, return_value=FAKE_VIDEO_DONE):
|
||||||
|
resp = await client.post(
|
||||||
|
"/generate/video/from-image",
|
||||||
|
json={
|
||||||
|
"model": "runway/gen-3",
|
||||||
|
"image_url": "https://example.com/cat.jpg",
|
||||||
|
"prompt": "Cat runs",
|
||||||
|
},
|
||||||
|
headers={"Authorization": f"Bearer {token}"},
|
||||||
|
)
|
||||||
|
assert resp.status_code == 200
|
||||||
|
|
||||||
|
row = db_module.get_conn().execute(
|
||||||
|
"SELECT job_id, model_id, prompt, status FROM generated_videos WHERE job_id = ?",
|
||||||
|
["gen-vid-2"],
|
||||||
|
).fetchone()
|
||||||
|
assert row is not None
|
||||||
|
assert row[1] == "runway/gen-3"
|
||||||
|
assert row[2] == "Cat runs"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_poll_video_updates_db_on_completion(client):
|
||||||
|
"""Polling a completed job updates the row status and video_url."""
|
||||||
|
token = await _user_token(client)
|
||||||
|
# First submit a job
|
||||||
|
with patch("app.routers.generate.openrouter.generate_video", new_callable=AsyncMock, return_value=FAKE_VIDEO):
|
||||||
|
await client.post(
|
||||||
|
"/generate/video",
|
||||||
|
json={"model": "stability/stable-video", "prompt": "Test"},
|
||||||
|
headers={"Authorization": f"Bearer {token}"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Now poll and get completed status
|
||||||
|
mock_result = {
|
||||||
|
"id": "gen-vid-1",
|
||||||
|
"status": "completed",
|
||||||
|
"unsigned_urls": ["https://example.com/video.mp4"],
|
||||||
|
}
|
||||||
|
with patch("app.routers.generate.openrouter.poll_video_status", new_callable=AsyncMock, return_value=mock_result):
|
||||||
|
await client.get(
|
||||||
|
"/generate/video/status",
|
||||||
|
params={"polling_url": "https://openrouter.ai/api/v1/videos/gen-vid-1"},
|
||||||
|
headers={"Authorization": f"Bearer {token}"},
|
||||||
|
)
|
||||||
|
|
||||||
|
row = db_module.get_conn().execute(
|
||||||
|
"SELECT status, video_url FROM generated_videos WHERE job_id = ?",
|
||||||
|
["gen-vid-1"],
|
||||||
|
).fetchone()
|
||||||
|
assert row is not None
|
||||||
|
assert row[0] == "completed"
|
||||||
|
assert row[1] == "https://example.com/video.mp4"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_list_generated_videos_empty(client):
|
||||||
|
"""GET /generate/videos returns empty list initially."""
|
||||||
|
token = await _user_token(client)
|
||||||
|
resp = await client.get(
|
||||||
|
"/generate/videos",
|
||||||
|
headers={"Authorization": f"Bearer {token}"},
|
||||||
|
)
|
||||||
|
assert resp.status_code == 200
|
||||||
|
assert resp.json() == []
|
||||||
|
|
||||||
|
|
||||||
|
async def test_list_generated_videos_returns_own_jobs(client):
|
||||||
|
"""GET /generate/videos returns the current user's jobs only."""
|
||||||
|
token = await _user_token(client)
|
||||||
|
with patch("app.routers.generate.openrouter.generate_video", new_callable=AsyncMock, return_value=FAKE_VIDEO):
|
||||||
|
await client.post(
|
||||||
|
"/generate/video",
|
||||||
|
json={"model": "stability/stable-video", "prompt": "Waves"},
|
||||||
|
headers={"Authorization": f"Bearer {token}"},
|
||||||
|
)
|
||||||
|
|
||||||
|
resp = await client.get(
|
||||||
|
"/generate/videos",
|
||||||
|
headers={"Authorization": f"Bearer {token}"},
|
||||||
|
)
|
||||||
|
assert resp.status_code == 200
|
||||||
|
data = resp.json()
|
||||||
|
assert len(data) == 1
|
||||||
|
assert data[0]["job_id"] == "gen-vid-1"
|
||||||
|
assert data[0]["prompt"] == "Waves"
|
||||||
|
assert data[0]["status"] == "queued"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_list_generated_videos_unauthenticated(client):
|
||||||
|
resp = await client.get("/generate/videos")
|
||||||
|
assert resp.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
async def test_generate_video_from_image_upstream_error(client):
|
async def test_generate_video_from_image_upstream_error(client):
|
||||||
token = await _user_token(client)
|
token = await _user_token(client)
|
||||||
with patch("app.routers.generate.openrouter.generate_video_from_image", new_callable=AsyncMock, side_effect=Exception("error")):
|
with patch("app.routers.generate.openrouter.generate_video_from_image", new_callable=AsyncMock, side_effect=Exception("error")):
|
||||||
|
|||||||
@@ -152,7 +152,8 @@ def test_dashboard_renders_user_info(client):
|
|||||||
200, {"id": "1", "email": "u@example.com", "role": "user"})
|
200, {"id": "1", "email": "u@example.com", "role": "user"})
|
||||||
images_mock = _mock_response(200, [])
|
images_mock = _mock_response(200, [])
|
||||||
gen_images_mock = _mock_response(200, [])
|
gen_images_mock = _mock_response(200, [])
|
||||||
with patch("frontend.app.main.httpx.request", side_effect=[me_mock, images_mock, gen_images_mock]):
|
gen_videos_mock = _mock_response(200, [])
|
||||||
|
with patch("frontend.app.main.httpx.request", side_effect=[me_mock, images_mock, gen_images_mock, gen_videos_mock]):
|
||||||
resp = client.get("/dashboard")
|
resp = client.get("/dashboard")
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert b"u@example.com" in resp.data
|
assert b"u@example.com" in resp.data
|
||||||
@@ -533,7 +534,8 @@ def test_dashboard_shows_uploaded_images(client):
|
|||||||
"size_bytes": 1024, "created_at": "2026-04-29T10:00:00"},
|
"size_bytes": 1024, "created_at": "2026-04-29T10:00:00"},
|
||||||
])
|
])
|
||||||
gen_images_mock = _mock_response(200, [])
|
gen_images_mock = _mock_response(200, [])
|
||||||
with patch("frontend.app.main.httpx.request", side_effect=[me_mock, images_mock, gen_images_mock]):
|
gen_videos_mock = _mock_response(200, [])
|
||||||
|
with patch("frontend.app.main.httpx.request", side_effect=[me_mock, images_mock, gen_images_mock, gen_videos_mock]):
|
||||||
resp = client.get("/dashboard")
|
resp = client.get("/dashboard")
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert b"cat.png" in resp.data
|
assert b"cat.png" in resp.data
|
||||||
@@ -554,7 +556,8 @@ def test_dashboard_shows_generated_images(client):
|
|||||||
"created_at": "2026-04-29T10:00:00",
|
"created_at": "2026-04-29T10:00:00",
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
with patch("frontend.app.main.httpx.request", side_effect=[me_mock, images_mock, gen_images_mock]):
|
gen_videos_mock = _mock_response(200, [])
|
||||||
|
with patch("frontend.app.main.httpx.request", side_effect=[me_mock, images_mock, gen_images_mock, gen_videos_mock]):
|
||||||
resp = client.get("/dashboard")
|
resp = client.get("/dashboard")
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert b"Generated images" in resp.data
|
assert b"Generated images" in resp.data
|
||||||
@@ -568,7 +571,8 @@ def test_dashboard_no_images_section_when_empty(client):
|
|||||||
200, {"id": "1", "email": "u@example.com", "role": "user"})
|
200, {"id": "1", "email": "u@example.com", "role": "user"})
|
||||||
images_mock = _mock_response(200, [])
|
images_mock = _mock_response(200, [])
|
||||||
gen_images_mock = _mock_response(200, [])
|
gen_images_mock = _mock_response(200, [])
|
||||||
with patch("frontend.app.main.httpx.request", side_effect=[me_mock, images_mock, gen_images_mock]):
|
gen_videos_mock = _mock_response(200, [])
|
||||||
|
with patch("frontend.app.main.httpx.request", side_effect=[me_mock, images_mock, gen_images_mock, gen_videos_mock]):
|
||||||
resp = client.get("/dashboard")
|
resp = client.get("/dashboard")
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert b"Uploaded reference images" not in resp.data
|
assert b"Uploaded reference images" not in resp.data
|
||||||
|
|||||||
Reference in New Issue
Block a user