Implement Sprint 1: Notebook Management CRUD
- Add NotebookService with full CRUD operations
- Add POST /api/v1/notebooks (create notebook)
- Add GET /api/v1/notebooks (list with pagination)
- Add GET /api/v1/notebooks/{id} (get by ID)
- Add PATCH /api/v1/notebooks/{id} (partial update)
- Add DELETE /api/v1/notebooks/{id} (delete)
- Add Pydantic models for requests/responses
- Add custom exceptions (ValidationError, NotFoundError, NotebookLMError)
- Add comprehensive unit tests (31 tests, 97% coverage)
- Add API integration tests (26 tests)
- Fix router prefix duplication
- Fix JSON serialization in error responses
BREAKING CHANGE: None
103 lines
3.3 KiB
Python
103 lines
3.3 KiB
Python
"""Tests for core configuration."""
|
|
|
|
import os
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from notebooklm_agent.core.config import Settings, get_settings
|
|
from notebooklm_agent.core.exceptions import NotebookLMAgentError, ValidationError
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestSettings:
|
|
"""Test suite for Settings configuration."""
|
|
|
|
def test_default_values(self):
|
|
"""Should create settings with default values."""
|
|
# Arrange & Act
|
|
settings = Settings()
|
|
|
|
# Assert
|
|
assert settings.port == 8000
|
|
assert settings.host == "0.0.0.0"
|
|
assert settings.log_level == "INFO"
|
|
assert settings.debug is False
|
|
assert settings.testing is False
|
|
|
|
def test_custom_values_from_env(self, monkeypatch):
|
|
"""Should load custom values from environment variables."""
|
|
# Arrange
|
|
monkeypatch.setenv("NOTEBOOKLM_AGENT_PORT", "9000")
|
|
monkeypatch.setenv("NOTEBOOKLM_AGENT_HOST", "127.0.0.1")
|
|
monkeypatch.setenv("LOG_LEVEL", "DEBUG")
|
|
monkeypatch.setenv("DEBUG", "true")
|
|
|
|
# Act
|
|
settings = Settings()
|
|
|
|
# Assert
|
|
assert settings.port == 9000
|
|
assert settings.host == "127.0.0.1"
|
|
assert settings.log_level == "DEBUG"
|
|
assert settings.debug is True
|
|
|
|
def test_cors_origins_parsing_from_string(self):
|
|
"""Should parse CORS origins from comma-separated string."""
|
|
# Arrange & Act
|
|
settings = Settings(cors_origins="http://localhost:3000, http://localhost:8080")
|
|
|
|
# Assert
|
|
assert settings.cors_origins == ["http://localhost:3000", "http://localhost:8080"]
|
|
|
|
def test_cors_origins_from_list(self):
|
|
"""Should accept CORS origins as list."""
|
|
# Arrange & Act
|
|
origins = ["http://localhost:3000", "http://localhost:8080"]
|
|
settings = Settings(cors_origins=origins)
|
|
|
|
# Assert
|
|
assert settings.cors_origins == origins
|
|
|
|
def test_log_level_validation_valid(self):
|
|
"""Should accept valid log levels."""
|
|
# Arrange & Act & Assert
|
|
for level in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]:
|
|
settings = Settings(log_level=level)
|
|
assert settings.log_level == level
|
|
|
|
def test_log_level_validation_invalid(self):
|
|
"""Should reject invalid log levels."""
|
|
# Arrange & Act & Assert
|
|
with pytest.raises(ValueError, match="log_level must be one of"):
|
|
Settings(log_level="INVALID")
|
|
|
|
def test_is_production_property(self):
|
|
"""Should correctly identify production mode."""
|
|
# Arrange & Act & Assert
|
|
assert Settings(debug=False, testing=False).is_production is True
|
|
assert Settings(debug=True, testing=False).is_production is False
|
|
assert Settings(debug=False, testing=True).is_production is False
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestGetSettings:
|
|
"""Test suite for get_settings function."""
|
|
|
|
def test_returns_settings_instance(self):
|
|
"""Should return a Settings instance."""
|
|
# Arrange & Act
|
|
settings = get_settings()
|
|
|
|
# Assert
|
|
assert isinstance(settings, Settings)
|
|
|
|
def test_caching(self):
|
|
"""Should cache settings instance."""
|
|
# Arrange & Act
|
|
settings1 = get_settings()
|
|
settings2 = get_settings()
|
|
|
|
# Assert - same instance due to lru_cache
|
|
assert settings1 is settings2
|