mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-10-14 13:30:55 +02:00
408 lines
12 KiB
Python
408 lines
12 KiB
Python
"""Contract tests for isolated Playwright provider-oracle automation."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import TYPE_CHECKING
|
|
|
|
import provider_oracle_web as web
|
|
import provider_oracles as oracles
|
|
import pytest
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
|
|
def _thordata_slot() -> oracles.ExecutionSlot:
|
|
return {
|
|
"name": "openai-thordata-us",
|
|
"surface": "openai-web",
|
|
"account_label": None,
|
|
"browser_profile": None,
|
|
"google_account_index": None,
|
|
"network_label": "thordata-us-residential",
|
|
"proxy_url_env": "THORDATA_ROUTE_US",
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("surface", "page_text", "watermark", "provenance"),
|
|
[
|
|
(
|
|
"openai-web",
|
|
"Generated with OpenAI tools\nSynthID detected\nContent Credentials not detected",
|
|
"detected",
|
|
"absent",
|
|
),
|
|
(
|
|
"openai-web",
|
|
"No OpenAI signals detected",
|
|
"not_detected",
|
|
"unavailable",
|
|
),
|
|
(
|
|
"microsoft-web",
|
|
"Microsoft AI detected\nWatermark detected\nC2PA detected",
|
|
"detected",
|
|
"present",
|
|
),
|
|
(
|
|
"microsoft-web",
|
|
"Inconclusive",
|
|
"not_detected",
|
|
"unavailable",
|
|
),
|
|
(
|
|
"microsoft-web",
|
|
"No watermark detected\nC2PA detected",
|
|
"not_detected",
|
|
"present",
|
|
),
|
|
(
|
|
"meta-web",
|
|
"AI signature from Meta was found\nUpload another file",
|
|
"detected",
|
|
"unavailable",
|
|
),
|
|
(
|
|
"meta-web",
|
|
"AI signatures were found\nUpload another file",
|
|
"detected",
|
|
"unavailable",
|
|
),
|
|
(
|
|
"meta-web",
|
|
"No AI signatures from Meta were found\nUpload another file",
|
|
"not_detected",
|
|
"unavailable",
|
|
),
|
|
],
|
|
)
|
|
def test_parse_provider_result(
|
|
surface: str,
|
|
page_text: str,
|
|
watermark: str,
|
|
provenance: str,
|
|
) -> None:
|
|
verdict = web.parse_provider_result(surface, page_text)
|
|
|
|
assert verdict.watermark_result == watermark
|
|
assert verdict.provenance_result == provenance
|
|
assert verdict.raw_response == page_text
|
|
|
|
|
|
def test_parse_provider_result_keeps_throttle_separate_from_clean() -> None:
|
|
verdict = web.parse_provider_result(
|
|
"meta-web",
|
|
"You\u2019ve reached the daily limit for identifications. Try again tomorrow",
|
|
)
|
|
|
|
assert verdict.watermark_result == "refused"
|
|
assert verdict.provenance_result == "unavailable"
|
|
|
|
|
|
def test_openai_static_explanation_is_not_a_settled_result() -> None:
|
|
page_text = (
|
|
"Verify OpenAI-generated content\n"
|
|
"Upload an image or audio file to check for signals that it was generated with OpenAI tools.\n"
|
|
"Upload a file\n"
|
|
"This tool checks whether an uploaded file contains provenance signals associated with OpenAI tools."
|
|
)
|
|
|
|
assert not web._settled("openai-web", page_text)
|
|
assert web.parse_provider_result("openai-web", page_text).watermark_result == "indeterminate"
|
|
|
|
|
|
def test_openai_provider_error_is_a_settled_indeterminate_result() -> None:
|
|
page_text = "Something went wrong. Please try again. If the issue persists, contact support."
|
|
|
|
assert web._settled("openai-web", page_text)
|
|
assert web.parse_provider_result("openai-web", page_text).watermark_result == "indeterminate"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("surface", "ready_state"),
|
|
[("meta-web", "networkidle"), ("microsoft-web", "networkidle"), ("openai-web", "load")],
|
|
)
|
|
def test_react_uploads_wait_for_surface_readiness_before_assigning_the_file(
|
|
tmp_path: Path,
|
|
surface: str,
|
|
ready_state: str,
|
|
) -> None:
|
|
events: list[str] = []
|
|
|
|
class FakeInput:
|
|
def wait_for(self, *, state: str, timeout: int) -> None:
|
|
assert state == "attached"
|
|
assert timeout == 5_000
|
|
events.append("input-attached")
|
|
|
|
def set_input_files(self, path: str, *, timeout: int) -> None:
|
|
assert path == str(tmp_path / "upload.webp")
|
|
assert timeout == 5_000
|
|
events.append("file-assigned")
|
|
|
|
class FakeLocator:
|
|
@property
|
|
def first(self) -> FakeInput:
|
|
return FakeInput()
|
|
|
|
class FakePage:
|
|
def wait_for_load_state(self, state: str, *, timeout: int) -> None:
|
|
assert state == ready_state
|
|
assert timeout == 5_000
|
|
events.append(f"ready:{state}")
|
|
|
|
def locator(self, selector: str) -> FakeLocator:
|
|
assert selector == 'input[type="file"]'
|
|
events.append("input-located")
|
|
return FakeLocator()
|
|
|
|
web._set_upload_file(FakePage(), surface, tmp_path / "upload.webp", 5.0) # type: ignore[arg-type]
|
|
|
|
assert events == [f"ready:{ready_state}", "input-located", "input-attached", "file-assigned"]
|
|
|
|
|
|
def test_proxy_settings_are_loaded_from_an_environment_reference() -> None:
|
|
slot: oracles.ExecutionSlot = {
|
|
"name": "openai-west",
|
|
"surface": "openai-web",
|
|
"account_label": None,
|
|
"browser_profile": None,
|
|
"google_account_index": None,
|
|
"network_label": "west-egress",
|
|
"proxy_url_env": "OPENAI_WEST_PROXY",
|
|
}
|
|
|
|
proxy = web.proxy_settings(slot, {"OPENAI_WEST_PROXY": "http://alice:secret@proxy.example:8080"})
|
|
|
|
assert proxy == {
|
|
"server": "http://proxy.example:8080",
|
|
"username": "alice",
|
|
"password": "secret",
|
|
}
|
|
|
|
|
|
def test_thordata_proxy_settings_pin_one_exit_for_the_browser_session() -> None:
|
|
proxy = web.proxy_settings(
|
|
_thordata_slot(),
|
|
{"THORDATA_ROUTE_US": "http://route:secret@gateway.pr.thordata.net:9999"},
|
|
sticky_session_id="oracle0123456789abcdef",
|
|
)
|
|
|
|
assert proxy == {
|
|
"server": "http://gateway.pr.thordata.net:9999",
|
|
"username": "route-sessid-oracle0123456789abcdef-sesstime-90",
|
|
"password": "secret",
|
|
}
|
|
|
|
|
|
def test_thordata_proxy_settings_preserve_an_explicit_session() -> None:
|
|
proxy = web.proxy_settings(
|
|
_thordata_slot(),
|
|
{"THORDATA_ROUTE_US": ("http://route-sessid-manual-sesstime-15:secret@gateway.pr.thordata.net:9999")},
|
|
sticky_session_id="oracle0123456789abcdef",
|
|
)
|
|
|
|
assert proxy is not None
|
|
assert proxy["username"] == "route-sessid-manual-sesstime-15"
|
|
|
|
|
|
def test_proxy_settings_do_not_treat_a_missing_secret_as_direct_access() -> None:
|
|
slot: oracles.ExecutionSlot = {
|
|
"name": "openai-west",
|
|
"surface": "openai-web",
|
|
"account_label": None,
|
|
"browser_profile": None,
|
|
"google_account_index": None,
|
|
"network_label": "west-egress",
|
|
"proxy_url_env": "OPENAI_WEST_PROXY",
|
|
}
|
|
|
|
with pytest.raises(ValueError, match="OPENAI_WEST_PROXY is not set"):
|
|
web.proxy_settings(slot, {})
|
|
|
|
|
|
def test_proxy_context_accepts_proxy_certificate() -> None:
|
|
calls: list[dict[str, object]] = []
|
|
|
|
class FakeBrowser:
|
|
def new_context(self, **kwargs: object) -> object:
|
|
calls.append(kwargs)
|
|
return object()
|
|
|
|
proxy = {"server": "http://proxy.example:9999", "username": "route", "password": "secret"}
|
|
|
|
web._new_context(FakeBrowser(), proxy) # type: ignore[arg-type]
|
|
|
|
assert calls == [{"proxy": proxy, "ignore_https_errors": True}]
|
|
|
|
|
|
def test_proxy_browser_accepts_proxy_certificate() -> None:
|
|
calls: list[dict[str, object]] = []
|
|
|
|
class FakeChromium:
|
|
def launch(self, **kwargs: object) -> object:
|
|
calls.append(kwargs)
|
|
return object()
|
|
|
|
proxy = {"server": "http://proxy.example:9999", "username": "route", "password": "secret"}
|
|
|
|
web._launch_browser(FakeChromium(), proxy, headed=False) # type: ignore[arg-type]
|
|
|
|
assert calls == [{"headless": True, "args": ["--ignore-certificate-errors"]}]
|
|
|
|
|
|
def test_direct_browser_keeps_normal_certificate_checks() -> None:
|
|
calls: list[dict[str, object]] = []
|
|
|
|
class FakeChromium:
|
|
def launch(self, **kwargs: object) -> object:
|
|
calls.append(kwargs)
|
|
return object()
|
|
|
|
web._launch_browser(FakeChromium(), None, headed=True) # type: ignore[arg-type]
|
|
|
|
assert calls == [{"headless": False}]
|
|
|
|
|
|
def test_run_web_batch_records_playwright_result(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path: Path,
|
|
tmp_clean_png: Path,
|
|
) -> None:
|
|
slot: oracles.ExecutionSlot = {
|
|
"name": "meta-direct",
|
|
"surface": "meta-web",
|
|
"account_label": None,
|
|
"browser_profile": None,
|
|
"google_account_index": None,
|
|
"network_label": "home",
|
|
"proxy_url_env": None,
|
|
}
|
|
manifest_path = oracles.prepare_batch(
|
|
"meta-web",
|
|
[tmp_clean_png],
|
|
output_dir=tmp_path / "oracle-batch",
|
|
repository_root=oracles.REPOSITORY_ROOT,
|
|
slot=slot,
|
|
)
|
|
calls: list[tuple[str, list[Path], dict[str, str] | None, bool, float]] = []
|
|
|
|
def submit(
|
|
surface: str,
|
|
uploads: list[Path],
|
|
proxy: dict[str, str] | None,
|
|
*,
|
|
headed: bool,
|
|
timeout_seconds: float,
|
|
) -> list[web.WebVerdict]:
|
|
calls.append((surface, uploads, proxy, headed, timeout_seconds))
|
|
return [
|
|
web.WebVerdict(
|
|
watermark_result="detected",
|
|
provenance_result="unavailable",
|
|
raw_response="AI signature from Meta was found\nUpload another file",
|
|
)
|
|
]
|
|
|
|
monkeypatch.setattr(web, "_submit_batch", submit)
|
|
report = web.run_web_batch(
|
|
manifest_path,
|
|
acknowledge_uploads=True,
|
|
headed=False,
|
|
timeout_seconds=30,
|
|
environ={},
|
|
)
|
|
|
|
assert report["complete"] is True
|
|
assert calls[0][0] == "meta-web"
|
|
assert calls[0][2] is None
|
|
results = json.loads((manifest_path.parent / "results.json").read_text(encoding="utf-8"))
|
|
assert results["rows"][0]["watermark_result"] == "detected"
|
|
|
|
|
|
def test_run_web_batch_loads_the_selected_proxy_from_dotenv(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path: Path,
|
|
tmp_clean_png: Path,
|
|
) -> None:
|
|
manifest_path = oracles.prepare_batch(
|
|
"openai-web",
|
|
[tmp_clean_png],
|
|
output_dir=tmp_path / "oracle-batch",
|
|
repository_root=oracles.REPOSITORY_ROOT,
|
|
slot=_thordata_slot(),
|
|
)
|
|
env_path = tmp_path / ".env"
|
|
env_path.write_text(
|
|
"THORDATA_PASSWORD=test-thordata-password\n"
|
|
"THORDATA_ROUTE_US=http://td-customer-user-country-US:${THORDATA_PASSWORD}"
|
|
"@proxy.pr.thordata.net:9999\n",
|
|
encoding="utf-8",
|
|
)
|
|
proxies: list[dict[str, str] | None] = []
|
|
|
|
def submit(
|
|
_surface: str,
|
|
_uploads: list[Path],
|
|
proxy: dict[str, str] | None,
|
|
*,
|
|
headed: bool,
|
|
timeout_seconds: float,
|
|
) -> list[web.WebVerdict]:
|
|
assert headed is False
|
|
assert timeout_seconds == 30
|
|
proxies.append(proxy)
|
|
return [web.WebVerdict("not_detected", "unavailable", "No OpenAI signals detected")]
|
|
|
|
monkeypatch.setattr(web, "_submit_batch", submit)
|
|
report = web.run_web_batch(
|
|
manifest_path,
|
|
acknowledge_uploads=True,
|
|
headed=False,
|
|
timeout_seconds=30,
|
|
env_file=env_path,
|
|
environ={},
|
|
)
|
|
|
|
assert report["watermark_results"] == {"not_detected": 1}
|
|
manifest_sha256 = json.loads((manifest_path.parent / "results.json").read_text(encoding="utf-8"))["manifest_sha256"]
|
|
assert proxies == [
|
|
{
|
|
"server": "http://proxy.pr.thordata.net:9999",
|
|
"username": (f"td-customer-user-country-US-sessid-oracle{manifest_sha256[:16]}-sesstime-90"),
|
|
"password": "test-thordata-password",
|
|
}
|
|
]
|
|
|
|
|
|
def test_run_web_batch_requires_explicit_upload_acknowledgement(
|
|
tmp_path: Path,
|
|
tmp_clean_png: Path,
|
|
) -> None:
|
|
manifest_path = oracles.prepare_batch(
|
|
"meta-web",
|
|
[tmp_clean_png],
|
|
output_dir=tmp_path / "oracle-batch",
|
|
repository_root=oracles.REPOSITORY_ROOT,
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="acknowledge_uploads"):
|
|
web.run_web_batch(manifest_path, acknowledge_uploads=False)
|
|
|
|
|
|
def test_run_web_batch_refuses_google_real_browser_workflow(
|
|
tmp_path: Path,
|
|
tmp_clean_png: Path,
|
|
) -> None:
|
|
manifest_path = oracles.prepare_batch(
|
|
"gemini-web",
|
|
[tmp_clean_png],
|
|
output_dir=tmp_path / "oracle-batch",
|
|
repository_root=oracles.REPOSITORY_ROOT,
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="real Chrome"):
|
|
web.run_web_batch(manifest_path, acknowledge_uploads=True)
|