mirror of
https://github.com/facefusion/facefusion.git
synced 2026-11-03 09:27:16 +01:00
* allow refresh after the access token expired The session lives 10 minutes and the access token is valid for the first half. The session guard checks the access window, while refresh, teardown, session count and fork validation keep checking the session window. A request declined with 401 can now refresh and resend. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cd6JuupGDM8y2cpHrkJq6D * minor change --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
402 lines
13 KiB
Python
402 lines
13 KiB
Python
import asyncio
|
|
import os
|
|
import tempfile
|
|
from datetime import timedelta
|
|
from typing import Iterator
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from starlette.testclient import TestClient
|
|
|
|
from facefusion import content_store, face_store, inference_manager, metadata, process_manager, rtc_manager, session_context, session_manager, state_manager, store_creator, store_manager, video_manager
|
|
from facefusion.apis import asset_store, http_manager, websocket_manager
|
|
from facefusion.apis.core import create_api
|
|
from facefusion.download import conditional_download
|
|
from facefusion.libraries import datachannel as datachannel_module
|
|
from facefusion.types import ApiSession
|
|
from tests.assert_helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory
|
|
|
|
|
|
@pytest.fixture(scope = 'module', autouse = True)
|
|
def before_all() -> None:
|
|
state_manager.init()
|
|
|
|
datachannel_module.pre_check()
|
|
|
|
process_manager.start()
|
|
conditional_download(get_test_examples_directory(),
|
|
[
|
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg'
|
|
])
|
|
|
|
|
|
@pytest.fixture(scope = 'function', autouse = True)
|
|
def before_each() -> Iterator[None]:
|
|
local_id = session_context.resolve_local_id()
|
|
|
|
session_context.set_session_id(local_id)
|
|
state_manager.init_item('temp_path', tempfile.gettempdir())
|
|
state_manager.init_item('jobs_path', get_test_jobs_directory())
|
|
state_manager.init_item('api_session_limit', 10)
|
|
session_manager.API_SESSIONS.clear()
|
|
asset_store.delete_assets()
|
|
|
|
yield
|
|
|
|
session_context.set_session_id(local_id)
|
|
|
|
|
|
@pytest.fixture(scope = 'module')
|
|
def test_client() -> Iterator[TestClient]:
|
|
with TestClient(create_api()) as test_client:
|
|
yield test_client
|
|
|
|
|
|
def test_create_session(test_client : TestClient) -> None:
|
|
create_session_response = test_client.post('/session', json =
|
|
{
|
|
'client_version': metadata.get('version')
|
|
})
|
|
create_session_body = create_session_response.json()
|
|
|
|
assert create_session_body.get('access_token')
|
|
assert create_session_body.get('refresh_token')
|
|
assert create_session_response.status_code == 201
|
|
|
|
create_session_response = test_client.post('/session', json =
|
|
{
|
|
'api_key': 'TEST',
|
|
'client_version': metadata.get('version')
|
|
})
|
|
|
|
assert create_session_response.status_code == 401
|
|
|
|
os.environ['FACEFUSION_API_KEY'] = 'TEST'
|
|
create_session_response = test_client.post('/session', json =
|
|
{
|
|
'client_version': metadata.get('version')
|
|
})
|
|
|
|
assert create_session_response.status_code == 401
|
|
|
|
os.environ['FACEFUSION_API_KEY'] = 'TEST'
|
|
create_session_response = test_client.post('/session', json =
|
|
{
|
|
'api_key': 'INVALID',
|
|
'client_version': metadata.get('version')
|
|
})
|
|
|
|
assert create_session_response.status_code == 401
|
|
|
|
os.environ['FACEFUSION_API_KEY'] = 'TEST'
|
|
create_session_response = test_client.post('/session', json =
|
|
{
|
|
'api_key': 'TEST',
|
|
'client_version': metadata.get('version')
|
|
})
|
|
|
|
assert create_session_response.status_code == 201
|
|
|
|
del os.environ['FACEFUSION_API_KEY']
|
|
|
|
assert test_client.post('/session', content = 'invalid').status_code == 400
|
|
|
|
|
|
def test_get_session(test_client : TestClient) -> None:
|
|
get_session_response = test_client.get('/session')
|
|
|
|
assert get_session_response.status_code == 401
|
|
|
|
create_session_response = test_client.post('/session', json =
|
|
{
|
|
'client_version': metadata.get('version')
|
|
})
|
|
create_session_body = create_session_response.json()
|
|
|
|
get_session_response = test_client.get('/session', headers =
|
|
{
|
|
'Authorization': 'Bearer ' + create_session_body.get('access_token')
|
|
})
|
|
get_session_body = get_session_response.json()
|
|
|
|
assert get_session_body.get('refresh_token') is None
|
|
assert get_session_response.status_code == 200
|
|
|
|
session_id = session_manager.find_api_session_id(create_session_body.get('access_token'))
|
|
session : ApiSession = session_manager.get_api_session(session_id)
|
|
session_manager.set_api_session(session_id,
|
|
{
|
|
'access_token': session.get('access_token'),
|
|
'refresh_token': session.get('refresh_token'),
|
|
'created_at': session.get('created_at') - timedelta(minutes = 6),
|
|
'expires_at': session.get('expires_at') - timedelta(minutes = 6)
|
|
})
|
|
|
|
get_session_response = test_client.get('/session', headers =
|
|
{
|
|
'Authorization': 'Bearer ' + create_session_body.get('access_token')
|
|
})
|
|
get_session_body = get_session_response.json()
|
|
|
|
assert get_session_body.get('message') == 'invalid access token'
|
|
assert get_session_response.status_code == 401
|
|
|
|
|
|
def test_refresh_session(test_client : TestClient) -> None:
|
|
create_session_response = test_client.post('/session', json =
|
|
{
|
|
'client_version': metadata.get('version')
|
|
})
|
|
create_session_body = create_session_response.json()
|
|
|
|
refresh_session_response = test_client.put('/session', json =
|
|
{
|
|
'refresh_token': 'INVALID'
|
|
})
|
|
|
|
assert refresh_session_response.status_code == 401
|
|
|
|
access_token = create_session_body.get('access_token')
|
|
session_id = session_manager.find_api_session_id(access_token)
|
|
session_context.set_session_id(session_id)
|
|
session_manager.fork_session()
|
|
|
|
refresh_session_response = test_client.put('/session', json = {})
|
|
|
|
assert refresh_session_response.status_code == 401
|
|
|
|
session_manager.join_session()
|
|
|
|
refresh_session_response = test_client.put('/session', json =
|
|
{
|
|
'refresh_token': create_session_body.get('refresh_token')
|
|
})
|
|
refresh_session_body = refresh_session_response.json()
|
|
|
|
assert refresh_session_body.get('access_token')
|
|
assert refresh_session_body.get('refresh_token')
|
|
assert session_manager.find_api_session_id(access_token) is None
|
|
assert refresh_session_response.status_code == 200
|
|
|
|
refresh_session_response = test_client.put('/session', json =
|
|
{
|
|
'refresh_token': create_session_body.get('refresh_token')
|
|
})
|
|
|
|
assert refresh_session_response.status_code == 401
|
|
|
|
session_id = session_manager.find_api_session_id(refresh_session_body.get('access_token'))
|
|
session : ApiSession = session_manager.get_api_session(session_id)
|
|
session_manager.set_api_session(session_id,
|
|
{
|
|
'access_token': session.get('access_token'),
|
|
'refresh_token': session.get('refresh_token'),
|
|
'created_at': session.get('created_at') - timedelta(minutes = 6),
|
|
'expires_at': session.get('expires_at') - timedelta(minutes = 6)
|
|
})
|
|
|
|
get_session_response = test_client.get('/session', headers =
|
|
{
|
|
'Authorization': 'Bearer ' + refresh_session_body.get('access_token')
|
|
})
|
|
|
|
assert get_session_response.status_code == 401
|
|
|
|
refresh_session_response = test_client.put('/session', json =
|
|
{
|
|
'refresh_token': refresh_session_body.get('refresh_token')
|
|
})
|
|
refresh_session_body = refresh_session_response.json()
|
|
|
|
assert refresh_session_response.status_code == 200
|
|
|
|
get_session_response = test_client.get('/session', headers =
|
|
{
|
|
'Authorization': 'Bearer ' + refresh_session_body.get('access_token')
|
|
})
|
|
|
|
assert get_session_response.status_code == 200
|
|
|
|
create_session_response = test_client.post('/session', json =
|
|
{
|
|
'client_version': metadata.get('version')
|
|
})
|
|
create_session_body = create_session_response.json()
|
|
|
|
session_id = session_manager.find_api_session_id(create_session_body.get('access_token'))
|
|
session = session_manager.get_api_session(session_id)
|
|
session_manager.set_api_session(session_id,
|
|
{
|
|
'access_token': session.get('access_token'),
|
|
'refresh_token': session.get('refresh_token'),
|
|
'created_at': session.get('created_at'),
|
|
'expires_at': session.get('expires_at') - timedelta(hours = 1)
|
|
})
|
|
|
|
refresh_session_response = test_client.put('/session', json =
|
|
{
|
|
'refresh_token': create_session_body.get('refresh_token')
|
|
})
|
|
|
|
assert refresh_session_response.status_code == 401
|
|
|
|
assert test_client.put('/session', content = 'invalid').status_code == 400
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_destroy_session(test_client : TestClient) -> None:
|
|
local_id = session_context.resolve_local_id()
|
|
create_session_response = test_client.post('/session', json =
|
|
{
|
|
'client_version': metadata.get('version')
|
|
})
|
|
access_token = create_session_response.json().get('access_token')
|
|
session_id = session_manager.find_api_session_id(access_token)
|
|
jobs_path = os.path.join(get_test_jobs_directory(), session_id)
|
|
|
|
delete_session_response = test_client.delete('/session', headers =
|
|
{
|
|
'Authorization': 'Bearer INVALID'
|
|
})
|
|
|
|
assert os.path.isdir(jobs_path) is True
|
|
assert delete_session_response.status_code == 401
|
|
|
|
with patch('facefusion.store_manager.is_busy', return_value = True):
|
|
delete_session_response = test_client.delete('/session', headers =
|
|
{
|
|
'Authorization': 'Bearer ' + access_token
|
|
})
|
|
|
|
assert delete_session_response.json().get('message') == 'session not destroyed'
|
|
assert session_manager.find_api_session_id(access_token) == session_id
|
|
assert delete_session_response.status_code == 409
|
|
|
|
session_context.set_session_id(session_id)
|
|
http_task = asyncio.current_task()
|
|
http_manager.start_request('upload', http_task)
|
|
delete_session_response = test_client.delete('/session', headers =
|
|
{
|
|
'Authorization': 'Bearer ' + access_token
|
|
})
|
|
|
|
assert delete_session_response.json().get('message') == 'session not destroyed'
|
|
assert session_manager.find_api_session_id(access_token) == session_id
|
|
assert delete_session_response.status_code == 409
|
|
|
|
http_manager.end_request('upload', http_task)
|
|
session_context.set_session_id(local_id)
|
|
|
|
delete_session_response = test_client.delete('/session', headers =
|
|
{
|
|
'Authorization': 'Bearer ' + access_token
|
|
})
|
|
|
|
assert os.path.isdir(jobs_path) is False
|
|
assert session_manager.find_api_session_id(access_token) is None
|
|
assert delete_session_response.status_code == 200
|
|
|
|
create_session_response = test_client.post('/session', json =
|
|
{
|
|
'client_version': metadata.get('version')
|
|
})
|
|
access_token = create_session_response.json().get('access_token')
|
|
session_id = session_manager.find_api_session_id(access_token)
|
|
session_context.set_session_id(session_id)
|
|
source_path = get_test_example_file('source.jpg')
|
|
|
|
with open(source_path, 'rb') as source_file:
|
|
test_client.post('/assets?type=source', headers =
|
|
{
|
|
'Authorization': 'Bearer ' + access_token
|
|
}, files =
|
|
[
|
|
('file', ('source.jpg', source_file.read(), 'image/jpeg'))
|
|
])
|
|
|
|
asset_paths = []
|
|
|
|
for asset in asset_store.get_assets().values():
|
|
asset_paths.append(asset.get('path'))
|
|
|
|
with patch('facefusion.apis.endpoints.session.remove_directory', return_value = False):
|
|
delete_session_response = test_client.delete('/session', headers =
|
|
{
|
|
'Authorization': 'Bearer ' + access_token
|
|
})
|
|
|
|
for asset_path in asset_paths:
|
|
assert os.path.exists(asset_path) is True
|
|
|
|
assert delete_session_response.json().get('message') == 'directory not removed'
|
|
assert session_manager.find_api_session_id(access_token) is None
|
|
assert store_creator.has_content(asset_store.ASSET_STORE, session_id) is False
|
|
assert delete_session_response.status_code == 206
|
|
|
|
|
|
def test_destroy_session_content(test_client : TestClient) -> None:
|
|
create_session_response = test_client.post('/session', json =
|
|
{
|
|
'client_version': metadata.get('version')
|
|
})
|
|
access_token = create_session_response.json().get('access_token')
|
|
session_id = session_manager.find_api_session_id(access_token)
|
|
session : ApiSession = session_manager.get_api_session(session_id)
|
|
local_id = session_context.resolve_local_id()
|
|
|
|
stores =\
|
|
[
|
|
(state_manager, state_manager.STATE_SET),
|
|
(asset_store, asset_store.ASSET_STORE),
|
|
(content_store, content_store.CONTENT_STORE),
|
|
(face_store, face_store.FACE_STORE),
|
|
(inference_manager, inference_manager.INFERENCE_POOL_STORE),
|
|
(process_manager, process_manager.PROCESS_STORE),
|
|
(rtc_manager, rtc_manager.RTC_STORE),
|
|
(video_manager, video_manager.VIDEO_POOL_STORE),
|
|
(http_manager, http_manager.HTTP_STORE),
|
|
(websocket_manager, websocket_manager.WEBSOCKET_STORE)
|
|
]
|
|
|
|
session_manager.set_api_session(session_id,
|
|
{
|
|
'access_token': session.get('access_token'),
|
|
'refresh_token': session.get('refresh_token'),
|
|
'created_at': session.get('created_at') - timedelta(minutes = 6),
|
|
'expires_at': session.get('expires_at') - timedelta(minutes = 6)
|
|
})
|
|
|
|
with patch('facefusion.session_manager.sleep', side_effect = InterruptedError):
|
|
with pytest.raises(InterruptedError):
|
|
session_manager.conditional_clear_api_session(session_id)
|
|
|
|
with patch('facefusion.store_manager.sleep', side_effect = InterruptedError):
|
|
with pytest.raises(InterruptedError):
|
|
store_manager.conditional_api_destroy(session_id)
|
|
|
|
assert session_manager.get_api_session(session_id).get('access_token') == access_token
|
|
|
|
session_manager.set_api_session(session_id,
|
|
{
|
|
'access_token': session.get('access_token'),
|
|
'refresh_token': session.get('refresh_token'),
|
|
'created_at': session.get('created_at'),
|
|
'expires_at': session.get('expires_at') - timedelta(hours = 1)
|
|
})
|
|
|
|
for session_module, store in stores:
|
|
assert store_creator.has_content(store, session_id) is True
|
|
|
|
store_manager.destroy(session_id)
|
|
session_manager.conditional_clear_api_session(session_id)
|
|
|
|
for session_module, store in stores:
|
|
assert store_creator.has_content(store, session_id) is False
|
|
|
|
session_module.destroy(session_id)
|
|
|
|
assert store_creator.has_content(store, session_id) is False
|
|
|
|
assert store_creator.has_content(state_manager.STATE_SET, local_id) is True
|
|
assert session_manager.get_api_session(session_id) is None
|