ArchiveBox/archivebox/tests/test_ui_admin_persona.py
2026-06-24 09:07:49 -07:00

232 lines
8.4 KiB
Python

import pytest
from django.urls import reverse
from archivebox.personas.importers import (
discover_persona_template_profiles,
import_persona_from_source,
resolve_browser_profile_source,
resolve_custom_import_source,
)
from archivebox.personas.models import Persona
from archivebox.tests.conftest import ADMIN_TEST_HOST
pytestmark = pytest.mark.django_db
def _make_profile_source(tmp_path):
user_data_dir = tmp_path / "Chrome User Data"
profile_dir = user_data_dir / "Default"
profile_dir.mkdir(parents=True)
(profile_dir / "Preferences").write_text("{}")
return resolve_browser_profile_source(
browser="chrome",
user_data_dir=user_data_dir,
profile_dir="Default",
browser_binary="/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
)
def _make_persona_template_source(name="TemplatePersona"):
from archivebox.config.constants import CONSTANTS
user_data_dir = CONSTANTS.PERSONAS_DIR / name / "chrome_profile"
profile_dir = user_data_dir / "Default"
profile_dir.mkdir(parents=True, exist_ok=True)
(profile_dir / "Preferences").write_text('{"profile":{"name":"Default"}}')
(profile_dir / "Bookmarks").write_text('{"roots":{}}')
(profile_dir / "Cache").mkdir(exist_ok=True)
(profile_dir / "Cache" / "volatile").write_text("skip")
return next(
profile for profile in discover_persona_template_profiles(personas_dir=CONSTANTS.PERSONAS_DIR) if profile.source_name == name
)
def test_resolve_custom_import_source_accepts_exact_profile_dir(tmp_path):
user_data_dir = tmp_path / "Brave User Data"
profile_dir = user_data_dir / "Profile 2"
profile_dir.mkdir(parents=True)
(profile_dir / "Preferences").write_text("{}")
source = resolve_custom_import_source(str(profile_dir))
assert source.kind == "browser-profile"
assert source.user_data_dir == user_data_dir.resolve()
assert source.profile_dir == "Profile 2"
def test_resolve_custom_import_source_accepts_cdp_url():
source = resolve_custom_import_source("ws://127.0.0.1:9222/devtools/browser/test-session")
assert source.kind == "cdp"
assert source.cdp_url == "ws://127.0.0.1:9222/devtools/browser/test-session"
def test_discover_persona_template_profiles_finds_chrome_profile_dirs(tmp_path):
personas_dir = tmp_path / "personas"
chrome_profile = personas_dir / "ExistingPersona" / "chrome_profile"
default_profile = chrome_profile / "Default"
default_profile.mkdir(parents=True)
(default_profile / "Preferences").write_text("{}")
discovered = discover_persona_template_profiles(personas_dir=personas_dir)
assert len(discovered) == 1
assert discovered[0].browser == "persona"
assert discovered[0].source_name == "ExistingPersona"
assert discovered[0].profile_dir == "Default"
assert discovered[0].user_data_dir == chrome_profile.resolve()
def test_discover_persona_template_profiles_finds_data_dir_personas():
from archivebox.config.constants import CONSTANTS
source = _make_persona_template_source("DataDirPersona")
discovered = discover_persona_template_profiles()
matching = [profile for profile in discovered if profile.source_name == "DataDirPersona"]
assert len(matching) == 1
assert matching[0].browser == "persona"
assert matching[0].profile_dir == "Default"
assert matching[0].user_data_dir == source.user_data_dir
assert matching[0].user_data_dir.parent.parent == CONSTANTS.PERSONAS_DIR.resolve()
def test_persona_admin_add_view_skips_unreadable_chrome_profile_entries(admin_client):
from archivebox.config.constants import CONSTANTS
chrome_profile = CONSTANTS.PERSONAS_DIR / "UnreadableTemplatePersona" / "chrome_profile"
default_profile = chrome_profile / "Default"
default_profile.mkdir(parents=True, exist_ok=True)
(default_profile / "Preferences").write_text('{"profile":{"name":"Default"}}')
unreadable_dir = chrome_profile / "ActorSafetyLists"
unreadable_dir.mkdir(parents=True, exist_ok=True)
(unreadable_dir / "Preferences").write_text("{}")
unreadable_dir.chmod(0)
try:
response = admin_client.get(reverse("admin:personas_persona_add"), HTTP_HOST=ADMIN_TEST_HOST)
finally:
unreadable_dir.chmod(0o700)
assert response.status_code == 200
assert b"UnreadableTemplatePersona" in response.content
assert b"Persona Template" in response.content
def test_persona_admin_add_view_renders_import_ui(admin_client):
source = _make_persona_template_source("RenderImportPersona")
response = admin_client.get(reverse("admin:personas_persona_add"), HTTP_HOST=ADMIN_TEST_HOST)
assert response.status_code == 200
assert source.source_name.encode() in response.content
assert b"Persona Template" in response.content
assert b"auth.json" in response.content
assert b"Plugin Config" in response.content
assert b'name="plugin_config__wget__WGET_TIMEOUT"' in response.content
def test_import_persona_from_source_copies_profile_without_browser_export(admin_user):
from archivebox.personas.models import Persona
source = _make_persona_template_source("ImporterTemplatePersona")
persona = Persona.objects.create(name="AgentPersona", created_by=admin_user)
result = import_persona_from_source(
persona,
source,
copy_profile=True,
import_cookies=False,
capture_storage=False,
)
copied_profile = persona.path / "chrome_profile" / "Default"
assert result.profile_copied is True
assert result.cookies_imported is False
assert result.storage_captured is False
assert result.user_agent_imported is False
assert (copied_profile / "Preferences").read_text() == '{"profile":{"name":"Default"}}'
assert not (copied_profile / "Cache").exists()
def test_persona_admin_add_post_runs_shared_importer(admin_client, admin_user):
from archivebox.personas.models import Persona
source = _make_persona_template_source("AdminPostTemplatePersona")
response = admin_client.post(
reverse("admin:personas_persona_add"),
{
"name": "ImportedPersona",
"created_by": str(admin_user.pk),
"permissions": "public",
"config": "{}",
"import_mode": "discovered",
"import_discovered_profile": source.choice_value,
"import_copy_profile": "on",
"_save": "Save",
},
HTTP_HOST=ADMIN_TEST_HOST,
)
assert response.status_code == 302
persona = Persona.objects.get(name="ImportedPersona")
assert (persona.path / "chrome_profile" / "Default" / "Preferences").exists()
assert not persona.COOKIES_FILE
assert not persona.AUTH_STORAGE_FILE
def test_persona_admin_saves_typed_plugin_config(admin_client, admin_user):
add_response = admin_client.get(reverse("admin:personas_persona_add"), HTTP_HOST=ADMIN_TEST_HOST)
add_form = add_response.context["adminform"].form
exposed_config_keys = {field["key"] for group in add_form.plugin_groups for card in group["plugins"] for field in card["config_fields"]}
assert {key for key in exposed_config_keys if key.endswith("_BINARY")}
assert (
not {
"ARCHIVE_DIR",
"USERS_DIR",
"PERSONAS_DIR",
"CUSTOM_TEMPLATES_DIR",
}
& exposed_config_keys
)
response = admin_client.post(
reverse("admin:personas_persona_add"),
{
"name": "PluginConfigPersona",
"created_by": str(admin_user.pk),
"permissions": "public",
"config": "{}",
"import_mode": "none",
"plugin_config__wget__WGET_TIMEOUT": "77",
"plugin_config__wget__WGET_WARC_ENABLED": "false",
"_save": "Save",
},
HTTP_HOST=ADMIN_TEST_HOST,
)
assert response.status_code == 302
persona = Persona.objects.get(name="PluginConfigPersona")
assert persona.config["WGET_TIMEOUT"] == 77
assert persona.config["WGET_WARC_ENABLED"] is False
def test_persona_config_save_heals_json_encoded_string_values(admin_user):
persona = Persona.objects.create(
name="QuotedConfigPersona",
created_by=admin_user,
config={
"EXTRA_CONTEXT": 'prefix "inner" suffix',
"USER_AGENT": '"ArchiveBox \\"Quoted\\" Agent"',
},
)
persona.refresh_from_db()
assert persona.config["EXTRA_CONTEXT"] == 'prefix "inner" suffix'
assert persona.config["USER_AGENT"] == 'ArchiveBox "Quoted" Agent'