#!/usr/bin/env python3 """ Tests for check_updates.py """ import json import sys import tempfile from pathlib import Path from unittest.mock import patch import pytest import responses # Import the module sys.path.insert(0, str(Path(__file__).parent.parent / 'scripts')) from check_updates import GitHubAPI, check_entry @pytest.fixture def github_api(): """Create a GitHubAPI instance for testing.""" return GitHubAPI(token='test-token') @pytest.fixture def sample_manifest_entry(): """Sample manifest entry for testing.""" return { 'id': 'test-entry', 'source_repo': 'owner/repo', 'source_path': 'path/to/file.stl', 'source_ref': 'main', 'pinned_sha': 'pinned-sha-123', 'pinned_raw_url': 'https://raw.githubusercontent.com/owner/repo/pinned-sha-123/path/to/file.stl', 'local_path': 'vendor/owner-repo/path/to/file.stl', 'checksum_sha256': 'abc123', 'last_checked': '2024-01-01T00:00:00Z', 'upstream_latest_sha': None, 'status': 'unknown', 'license': None } @responses.activate def test_check_entry_up_to_date(github_api, sample_manifest_entry): """Test checking an entry that is up-to-date.""" owner = 'owner' repo = 'repo' path = 'path/to/file.stl' ref = 'main' pinned_sha = 'pinned-sha-123' # Mock commits API - return same SHA as pinned responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/commits', json=[{'sha': pinned_sha}], match=[responses.matchers.query_param_matcher({ 'path': path, 'sha': ref, 'per_page': 1 })] ) updated_entry = check_entry(sample_manifest_entry, github_api) assert updated_entry['status'] == 'up-to-date' assert updated_entry['upstream_latest_sha'] == pinned_sha assert updated_entry['last_checked'] is not None @responses.activate def test_check_entry_out_of_date(github_api, sample_manifest_entry): """Test checking an entry that is out-of-date.""" owner = 'owner' repo = 'repo' path = 'path/to/file.stl' ref = 'main' pinned_sha = 'pinned-sha-123' latest_sha = 'latest-sha-456' # Mock commits API - return different SHA responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/commits', json=[{'sha': latest_sha}], match=[responses.matchers.query_param_matcher({ 'path': path, 'sha': ref, 'per_page': 1 })] ) updated_entry = check_entry(sample_manifest_entry, github_api) assert updated_entry['status'] == 'out-of-date' assert updated_entry['upstream_latest_sha'] == latest_sha assert updated_entry['pinned_sha'] == pinned_sha # Pinned SHA unchanged assert updated_entry['last_checked'] is not None @responses.activate def test_check_entry_no_pinned_sha(github_api): """Test checking an entry with no pinned SHA.""" entry = { 'id': 'test-entry', 'source_repo': 'owner/repo', 'source_path': 'path/to/file.stl', 'source_ref': 'main', 'pinned_sha': None, 'status': 'unknown' } owner = 'owner' repo = 'repo' path = 'path/to/file.stl' ref = 'main' latest_sha = 'latest-sha-456' responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/commits', json=[{'sha': latest_sha}], match=[responses.matchers.query_param_matcher({ 'path': path, 'sha': ref, 'per_page': 1 })] ) updated_entry = check_entry(entry, github_api) assert updated_entry['status'] == 'unknown' assert updated_entry['upstream_latest_sha'] == latest_sha @responses.activate def test_check_entry_api_error(github_api, sample_manifest_entry): """Test handling of API errors.""" owner = 'owner' repo = 'repo' path = 'path/to/file.stl' ref = 'main' # Mock API error responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/commits', status=500 ) updated_entry = check_entry(sample_manifest_entry, github_api) assert updated_entry['status'] == 'unknown' assert updated_entry['upstream_latest_sha'] is None @responses.activate def test_check_entry_file_not_found(github_api, sample_manifest_entry): """Test handling when file doesn't exist at ref.""" owner = 'owner' repo = 'repo' path = 'path/to/file.stl' ref = 'main' # Mock empty commits response (file doesn't exist) responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/commits', json=[], match=[responses.matchers.query_param_matcher({ 'path': path, 'sha': ref, 'per_page': 1 })] ) updated_entry = check_entry(sample_manifest_entry, github_api) # Should still update last_checked but status might be unknown assert updated_entry['last_checked'] is not None def test_github_api_get_latest_commit_sha(github_api): """Test getting latest commit SHA.""" owner = 'owner' repo = 'repo' path = 'file.stl' ref = 'main' expected_sha = 'commit-sha-789' with responses.RequestsMock() as rsps: rsps.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/commits', json=[{'sha': expected_sha}], match=[responses.matchers.query_param_matcher({ 'path': path, 'sha': ref, 'per_page': 1 })] ) sha = github_api.get_latest_commit_sha(owner, repo, path, ref) assert sha == expected_sha def test_github_api_get_latest_commit_sha_ref_is_sha(github_api): """Test when ref is already a SHA.""" owner = 'owner' repo = 'repo' path = 'file.stl' ref = 'a' * 40 # Valid SHA format # Should return the ref as-is if it's already a SHA sha = github_api.get_latest_commit_sha(owner, repo, path, ref) # Actually, the function tries to get commits first, so it will make an API call # But if ref is a SHA, it should work with responses.RequestsMock() as rsps: rsps.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/commits', json=[{'sha': ref}], match=[responses.matchers.query_param_matcher({ 'path': path, 'sha': ref, 'per_page': 1 })] ) sha = github_api.get_latest_commit_sha(owner, repo, path, ref) assert sha == ref if __name__ == '__main__': pytest.main([__file__, '-v'])