09e9f3545f
CodeQL / Analyze (push) Waiting to run
dependency-audit / pip-audit (push) Waiting to run
Test / Code Quality (push) Has been cancelled
Test / Test (macos-latest, Python 3.10) (push) Has been cancelled
Test / Test (macos-latest, Python 3.11) (push) Has been cancelled
Test / Test (macos-latest, Python 3.12) (push) Has been cancelled
Test / Test (macos-latest, Python 3.13) (push) Has been cancelled
Test / Test (macos-latest, Python 3.14) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.10) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.11) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.12) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.13) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.14) (push) Has been cancelled
Test / Test (windows-latest, Python 3.10) (push) Has been cancelled
Test / Test (windows-latest, Python 3.11) (push) Has been cancelled
Test / Test (windows-latest, Python 3.12) (push) Has been cancelled
Test / Test (windows-latest, Python 3.13) (push) Has been cancelled
Test / Test (windows-latest, Python 3.14) (push) Has been cancelled
144 lines
4.2 KiB
Python
144 lines
4.2 KiB
Python
"""Integration tests for source delete CLI flows."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
import pytest
|
|
from click.testing import CliRunner
|
|
from pytest_httpx import HTTPXMock
|
|
|
|
import notebooklm.auth as auth_module
|
|
import notebooklm.cli.context as context_module
|
|
import notebooklm.cli.helpers as helpers_module
|
|
import notebooklm.cli.resolve as resolve_module
|
|
from notebooklm.notebooklm_cli import cli
|
|
from notebooklm.rpc import RPCMethod
|
|
|
|
|
|
@pytest.fixture
|
|
def runner() -> CliRunner:
|
|
"""Create a Click test runner."""
|
|
return CliRunner()
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_auth():
|
|
"""Mock authentication for CLI integration tests."""
|
|
import httpx
|
|
|
|
mock_jar = httpx.Cookies()
|
|
mock_jar.set("SID", "test", domain=".google.com")
|
|
|
|
with (
|
|
patch.object(
|
|
helpers_module,
|
|
"load_auth_from_storage",
|
|
return_value={
|
|
"SID": "test",
|
|
"HSID": "test",
|
|
"SSID": "test",
|
|
"APISID": "test",
|
|
"SAPISID": "test",
|
|
},
|
|
),
|
|
patch.object(
|
|
auth_module,
|
|
"fetch_tokens_with_domains",
|
|
new_callable=AsyncMock,
|
|
) as mock_fetch,
|
|
patch.object(
|
|
helpers_module,
|
|
"build_cookie_jar",
|
|
return_value=mock_jar,
|
|
),
|
|
):
|
|
mock_fetch.return_value = ("csrf_token", "session_id")
|
|
yield
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_context(tmp_path: Path):
|
|
"""Provide a canonical notebook UUID so CLI skips notebook-list resolution."""
|
|
context_file = tmp_path / "context.json"
|
|
context_file.write_text(
|
|
json.dumps({"notebook_id": "06f0c5bd-108f-4c8b-8911-34b2acc656de"}),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
with (
|
|
patch.object(helpers_module, "get_context_path", return_value=context_file),
|
|
patch.object(context_module, "get_context_path", return_value=context_file),
|
|
patch.object(resolve_module, "get_context_path", return_value=context_file),
|
|
):
|
|
yield context_file
|
|
|
|
|
|
def _build_source_list_response(build_rpc_response, source_id: str, title: str) -> str:
|
|
"""Build a GET_NOTEBOOK response containing a single source."""
|
|
return build_rpc_response(
|
|
RPCMethod.GET_NOTEBOOK,
|
|
[
|
|
[
|
|
"Test Notebook",
|
|
[
|
|
[
|
|
[source_id],
|
|
title,
|
|
[None, 11, [1704067200, 0], None, 5, None, None, None],
|
|
[None, 2],
|
|
]
|
|
],
|
|
"06f0c5bd-108f-4c8b-8911-34b2acc656de",
|
|
"📘",
|
|
None,
|
|
[None, None, None, None, None, [1704067200, 0]],
|
|
]
|
|
],
|
|
)
|
|
|
|
|
|
class TestCliSourceDeleteIntegration:
|
|
"""Integration coverage for CLI source delete flows."""
|
|
|
|
def test_source_delete_by_title(
|
|
self, runner, mock_auth, mock_context, httpx_mock: HTTPXMock, build_rpc_response
|
|
):
|
|
httpx_mock.add_response(
|
|
content=_build_source_list_response(
|
|
build_rpc_response,
|
|
"ff503bfa-5e39-4281-a1d8-2a66c7b86724",
|
|
"VCR Delete Test Source",
|
|
).encode()
|
|
)
|
|
httpx_mock.add_response(
|
|
content=build_rpc_response(RPCMethod.DELETE_SOURCE, [True]).encode()
|
|
)
|
|
|
|
result = runner.invoke(
|
|
cli,
|
|
["source", "delete-by-title", "VCR Delete Test Source", "-y"],
|
|
)
|
|
|
|
assert result.exit_code == 0
|
|
assert "Deleted source" in result.output
|
|
|
|
def test_source_delete_title_suggests_delete_by_title(
|
|
self, runner, mock_auth, mock_context, httpx_mock: HTTPXMock, build_rpc_response
|
|
):
|
|
httpx_mock.add_response(
|
|
content=_build_source_list_response(
|
|
build_rpc_response,
|
|
"ff503bfa-5e39-4281-a1d8-2a66c7b86724",
|
|
"VCR Delete Test Source",
|
|
).encode()
|
|
)
|
|
|
|
result = runner.invoke(
|
|
cli,
|
|
["source", "delete", "VCR Delete Test Source", "-y"],
|
|
)
|
|
|
|
assert result.exit_code == 1
|
|
assert "delete-by-title" in result.output
|