- Add `tags` field to AddonInfo model with serialization support - Add `_load_repo_tags()` to CacheScraper to read tags from addon files - Populate 160 addon files with curated tags using 8 categories: ai, metadata, reader, notes, attachment, interface, integration, utility - Add test cases for tags field in AddonInfo model Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
214 lines
7.7 KiB
Python
214 lines
7.7 KiB
Python
"""Unit tests for data models."""
|
|
|
|
import pytest
|
|
|
|
from zotero_scraper.models.addon import (
|
|
AddonInfo,
|
|
AddonRelease,
|
|
Author,
|
|
XpiDownloadUrls,
|
|
)
|
|
|
|
|
|
class TestAuthor:
|
|
"""Tests for Author model."""
|
|
|
|
def test_to_dict_full(self):
|
|
"""Test to_dict with all fields."""
|
|
author = Author(
|
|
name="Test Author",
|
|
url="https://github.com/test",
|
|
avatar="https://avatars.githubusercontent.com/test",
|
|
)
|
|
result = author.to_dict()
|
|
assert result == {
|
|
"name": "Test Author",
|
|
"url": "https://github.com/test",
|
|
"avatar": "https://avatars.githubusercontent.com/test",
|
|
}
|
|
|
|
def test_to_dict_excludes_none(self):
|
|
"""Test to_dict excludes None values."""
|
|
author = Author(name="Test", url=None, avatar=None)
|
|
result = author.to_dict()
|
|
assert result == {"name": "Test"}
|
|
|
|
def test_from_dict(self):
|
|
"""Test from_dict factory method."""
|
|
data = {"name": "Test", "url": "https://example.com"}
|
|
author = Author.from_dict(data)
|
|
assert author.name == "Test"
|
|
assert author.url == "https://example.com"
|
|
assert author.avatar is None
|
|
|
|
def test_from_dict_none(self):
|
|
"""Test from_dict with None input."""
|
|
author = Author.from_dict(None)
|
|
assert author.name is None
|
|
assert author.url is None
|
|
assert author.avatar is None
|
|
|
|
|
|
class TestXpiDownloadUrls:
|
|
"""Tests for XpiDownloadUrls model."""
|
|
|
|
def test_to_dict_full(self):
|
|
"""Test to_dict with all fields."""
|
|
urls = XpiDownloadUrls(
|
|
github="https://github.com/example.xpi",
|
|
ghProxy="https://gh-proxy.com/example.xpi",
|
|
kgithub="https://kkgithub.com/example.xpi",
|
|
)
|
|
result = urls.to_dict()
|
|
assert result == {
|
|
"github": "https://github.com/example.xpi",
|
|
"ghProxy": "https://gh-proxy.com/example.xpi",
|
|
"kgithub": "https://kkgithub.com/example.xpi",
|
|
}
|
|
|
|
def test_to_dict_minimal(self):
|
|
"""Test to_dict with only required fields."""
|
|
urls = XpiDownloadUrls(github="https://github.com/example.xpi")
|
|
result = urls.to_dict()
|
|
assert result == {"github": "https://github.com/example.xpi"}
|
|
|
|
def test_from_dict_valid(self):
|
|
"""Test from_dict with valid data."""
|
|
data = {"github": "https://github.com/example.xpi"}
|
|
urls = XpiDownloadUrls.from_dict(data)
|
|
assert urls is not None
|
|
assert urls.github == "https://github.com/example.xpi"
|
|
|
|
def test_from_dict_missing_github(self):
|
|
"""Test from_dict returns None if github is missing."""
|
|
data = {"ghProxy": "https://gh-proxy.com/example.xpi"}
|
|
urls = XpiDownloadUrls.from_dict(data)
|
|
assert urls is None
|
|
|
|
|
|
class TestAddonRelease:
|
|
"""Tests for AddonRelease model."""
|
|
|
|
def test_zotero_check_version_7(self):
|
|
"""Test zotero_check_version for Zotero 7."""
|
|
release = AddonRelease(targetZoteroVersion="7", tagName="latest")
|
|
assert release.zotero_check_version == "7.*"
|
|
|
|
def test_zotero_check_version_6(self):
|
|
"""Test zotero_check_version for Zotero 6."""
|
|
release = AddonRelease(targetZoteroVersion="6", tagName="v1.0")
|
|
assert release.zotero_check_version == "6.*"
|
|
|
|
def test_zotero_check_version_invalid(self):
|
|
"""Test zotero_check_version raises for invalid version."""
|
|
release = AddonRelease(targetZoteroVersion="5", tagName="v1.0")
|
|
with pytest.raises(ValueError, match="Invalid targetZoteroVersion"):
|
|
_ = release.zotero_check_version
|
|
|
|
def test_to_dict_minimal(self):
|
|
"""Test to_dict with minimal fields."""
|
|
release = AddonRelease(targetZoteroVersion="7", tagName="latest")
|
|
result = release.to_dict()
|
|
assert result == {"targetZoteroVersion": "7", "tagName": "latest"}
|
|
|
|
def test_to_dict_full(self):
|
|
"""Test to_dict with all fields."""
|
|
release = AddonRelease(
|
|
targetZoteroVersion="7",
|
|
tagName="v1.0.0",
|
|
xpiDownloadUrl=XpiDownloadUrls(github="https://example.com"),
|
|
releaseDate="2024-01-01T00:00:00Z",
|
|
id="test@addon.com",
|
|
xpiVersion="1.0.0",
|
|
name="Test Addon",
|
|
description="Test description",
|
|
minZoteroVersion="7.0",
|
|
maxZoteroVersion="7.*",
|
|
)
|
|
result = release.to_dict()
|
|
assert result["targetZoteroVersion"] == "7"
|
|
assert result["tagName"] == "v1.0.0"
|
|
assert result["xpiDownloadUrl"] == {"github": "https://example.com"}
|
|
assert result["id"] == "test@addon.com"
|
|
|
|
def test_from_dict(self, sample_addon_config):
|
|
"""Test from_dict factory method."""
|
|
release_data = sample_addon_config["releases"][0]
|
|
release = AddonRelease.from_dict(release_data)
|
|
assert release.targetZoteroVersion == "7"
|
|
assert release.tagName == "latest"
|
|
|
|
|
|
class TestAddonInfo:
|
|
"""Tests for AddonInfo model."""
|
|
|
|
def test_owner_valid(self):
|
|
"""Test owner property with valid repo."""
|
|
addon = AddonInfo(repo="owner/repo", releases=[])
|
|
assert addon.owner == "owner"
|
|
|
|
def test_owner_invalid(self):
|
|
"""Test owner property with invalid repo."""
|
|
addon = AddonInfo(repo="invalid", releases=[])
|
|
assert addon.owner is None
|
|
|
|
def test_repository_valid(self):
|
|
"""Test repository property with valid repo."""
|
|
addon = AddonInfo(repo="owner/repo", releases=[])
|
|
assert addon.repository == "repo"
|
|
|
|
def test_repository_invalid(self):
|
|
"""Test repository property with invalid repo."""
|
|
addon = AddonInfo(repo="invalid", releases=[])
|
|
assert addon.repository is None
|
|
|
|
def test_to_dict_backward_compatible(self):
|
|
"""Test to_dict maintains backward compatibility."""
|
|
addon = AddonInfo(
|
|
repo="owner/repo",
|
|
releases=[AddonRelease(targetZoteroVersion="7", tagName="v1.0")],
|
|
name="Test",
|
|
stars=100,
|
|
author=Author(name="Author"),
|
|
)
|
|
result = addon.to_dict()
|
|
|
|
# Check backward compatibility: both "stars" and "star" should exist
|
|
assert result["stars"] == 100
|
|
assert result["star"] == 100
|
|
assert result["repo"] == "owner/repo"
|
|
assert result["name"] == "Test"
|
|
assert result["author"] == {"name": "Author"}
|
|
|
|
def test_from_dict(self, sample_addon_config):
|
|
"""Test from_dict factory method."""
|
|
addon = AddonInfo.from_dict(sample_addon_config)
|
|
assert addon.repo == "owner/repo"
|
|
assert len(addon.releases) == 2
|
|
assert addon.releases[0].targetZoteroVersion == "7"
|
|
assert addon.releases[1].tagName == "v1.0"
|
|
|
|
def test_to_dict_with_tags(self):
|
|
"""Test to_dict includes tags when present."""
|
|
addon = AddonInfo(repo="owner/repo", tags=["ai", "reader"])
|
|
result = addon.to_dict()
|
|
assert result["tags"] == ["ai", "reader"]
|
|
|
|
def test_to_dict_omits_tags_when_empty(self):
|
|
"""Test to_dict omits tags key when list is empty (backward compat)."""
|
|
addon = AddonInfo(repo="owner/repo")
|
|
result = addon.to_dict()
|
|
assert "tags" not in result
|
|
|
|
def test_from_dict_with_tags(self):
|
|
"""Test from_dict parses tags field."""
|
|
data = {"repo": "owner/repo", "tags": ["metadata", "integration"]}
|
|
addon = AddonInfo.from_dict(data)
|
|
assert addon.tags == ["metadata", "integration"]
|
|
|
|
def test_from_dict_missing_tags_defaults_to_empty(self):
|
|
"""Test from_dict defaults tags to empty list when key absent."""
|
|
data = {"repo": "owner/repo"}
|
|
addon = AddonInfo.from_dict(data)
|
|
assert addon.tags == []
|