"""Tests for ``notebooklm status`` and ``notebooklm clear``. This file was extracted from the legacy ``test_session.py`` as part of D1 PR-3 (test-monkeypatch-policy migration). The shared fixtures and helpers live in ``_session_helpers.py``; the proxy-block-aware ``patch_session_login_dual`` lives in ``tests/_fixtures``. """ import json from unittest.mock import patch import notebooklm.cli.services.session_context as session_context_module from notebooklm.notebooklm_cli import cli class TestStatusCommand: def test_status_no_context(self, runner, mock_context_file): """Test status command when no notebook is selected.""" # Ensure context file doesn't exist if mock_context_file.exists(): mock_context_file.unlink() result = runner.invoke(cli, ["status"]) assert result.exit_code == 0 assert "No notebook selected" in result.output or "use" in result.output.lower() def test_status_with_context(self, runner, mock_context_file): """Test status command shows current notebook context.""" # Create context file with notebook info context_data = { "notebook_id": "nb_test_123", "title": "My Test Notebook", "is_owner": True, "created_at": "2024-01-15", } mock_context_file.write_text(json.dumps(context_data)) result = runner.invoke(cli, ["status"]) assert result.exit_code == 0 assert "nb_test_123" in result.output or "My Test Notebook" in result.output def test_status_with_conversation(self, runner, mock_context_file): """Test status command shows conversation ID when set.""" context_data = { "notebook_id": "nb_conv_test", "title": "Notebook with Conversation", "is_owner": True, "created_at": "2024-01-15", "conversation_id": "conv_abc123", } mock_context_file.write_text(json.dumps(context_data)) result = runner.invoke(cli, ["status"]) assert result.exit_code == 0 assert "conv_abc123" in result.output or "Conversation" in result.output def test_status_json_output_with_context(self, runner, mock_context_file): """Test status --json outputs valid JSON.""" context_data = { "notebook_id": "nb_json_test", "title": "JSON Test Notebook", "is_owner": True, "created_at": "2024-01-15", } mock_context_file.write_text(json.dumps(context_data)) result = runner.invoke(cli, ["status", "--json"]) assert result.exit_code == 0 # Should be valid JSON output_data = json.loads(result.output) assert output_data["has_context"] is True assert output_data["notebook"]["id"] == "nb_json_test" def test_status_json_output_no_context(self, runner, mock_context_file): """Test status --json outputs valid JSON when no context.""" if mock_context_file.exists(): mock_context_file.unlink() result = runner.invoke(cli, ["status", "--json"]) assert result.exit_code == 0 output_data = json.loads(result.output) assert output_data["has_context"] is False assert output_data["notebook"] is None def test_status_handles_corrupted_context_file(self, runner, mock_context_file): """Test status handles corrupted context file gracefully.""" # Write invalid JSON mock_context_file.write_text("{ invalid json }") result = runner.invoke(cli, ["status"]) # Should not crash, should show minimal info or no context assert result.exit_code == 0 # ============================================================================= # CLEAR COMMAND TESTS # ============================================================================= class TestClearCommand: def test_clear_removes_context(self, runner, mock_context_file): """Test clear command removes context file.""" # Create context file context_data = {"notebook_id": "nb_to_clear", "title": "Clear Me"} mock_context_file.write_text(json.dumps(context_data)) assert mock_context_file.exists(), "Precondition: context file should exist" result = runner.invoke(cli, ["clear"]) assert result.exit_code == 0 assert "cleared" in result.output.lower() or "Context" in result.output # Validate behaviour, not just exit/output: the context file must be # gone (or empty) after the command runs. Without this, a regression # that printed "cleared" but left the file in place would slip past. assert not mock_context_file.exists() or not mock_context_file.read_text().strip() def test_clear_when_no_context(self, runner, mock_context_file): """Test clear command when no context exists.""" if mock_context_file.exists(): mock_context_file.unlink() result = runner.invoke(cli, ["clear"]) # Should succeed even if no context exists assert result.exit_code == 0 # ============================================================================= # EDGE CASES # ============================================================================= class TestStatusPaths: """Tests for status --paths flag.""" def test_status_paths_flag_shows_table(self, runner, mock_context_file): """Test status --paths shows configuration paths table.""" with patch.object(session_context_module, "get_path_info") as mock_path_info: mock_path_info.return_value = { "home_dir": "/home/test/.notebooklm", "home_source": "default", "storage_path": "/home/test/.notebooklm/storage_state.json", "context_path": "/home/test/.notebooklm/context.json", "browser_profile_dir": "/home/test/.notebooklm/browser_profile", } result = runner.invoke(cli, ["status", "--paths"]) assert result.exit_code == 0 assert "Configuration Paths" in result.output assert "/home/test/.notebooklm" in result.output assert "storage_state.json" in result.output def test_status_paths_json_output(self, runner, mock_context_file): """Test status --paths --json outputs path info as JSON.""" with patch.object(session_context_module, "get_path_info") as mock_path_info: mock_path_info.return_value = { "home_dir": "/custom/path/.notebooklm", "home_source": "NOTEBOOKLM_HOME", "storage_path": "/custom/path/.notebooklm/storage_state.json", "context_path": "/custom/path/.notebooklm/context.json", "browser_profile_dir": "/custom/path/.notebooklm/browser_profile", } result = runner.invoke(cli, ["status", "--paths", "--json"]) assert result.exit_code == 0 output_data = json.loads(result.output) assert "paths" in output_data assert output_data["paths"]["home_dir"] == "/custom/path/.notebooklm" assert output_data["paths"]["home_source"] == "NOTEBOOKLM_HOME" def test_status_paths_shows_auth_json_note(self, runner, mock_context_file, monkeypatch): """Test status --paths shows note when NOTEBOOKLM_AUTH_JSON is set.""" monkeypatch.setenv("NOTEBOOKLM_AUTH_JSON", '{"cookies":[]}') with patch.object(session_context_module, "get_path_info") as mock_path_info: mock_path_info.return_value = { "home_dir": "/home/test/.notebooklm", "home_source": "default", "storage_path": "/home/test/.notebooklm/storage_state.json", "context_path": "/home/test/.notebooklm/context.json", "browser_profile_dir": "/home/test/.notebooklm/browser_profile", } result = runner.invoke(cli, ["status", "--paths"]) assert result.exit_code == 0 assert "NOTEBOOKLM_AUTH_JSON is set" in result.output # ============================================================================= # AUTH CHECK COMMAND TESTS # =============================================================================