#!/usr/bin/env python3 """ Tests for vendor_update.py """ import hashlib import json import tempfile from pathlib import Path from unittest.mock import Mock, patch, mock_open import pytest import responses # Import the module (adjust path as needed) import sys sys.path.insert(0, str(Path(__file__).parent.parent / 'scripts')) from vendor_update import GitHubAPI, compute_sha256, download_file, update_manifest_entry @pytest.fixture def temp_dir(): """Create a temporary directory for tests.""" with tempfile.TemporaryDirectory() as tmpdir: yield Path(tmpdir) @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': None, 'pinned_raw_url': None, 'local_path': 'vendor/owner-repo/path/to/file.stl', 'checksum_sha256': None, 'last_checked': None, 'upstream_latest_sha': None, 'status': 'unknown', 'license': None } @pytest.fixture def github_api(): """Create a GitHubAPI instance for testing.""" return GitHubAPI(token='test-token') def test_compute_sha256(temp_dir): """Test SHA256 computation.""" test_file = temp_dir / 'test.txt' test_file.write_text('test content') checksum = compute_sha256(test_file) # Verify it's a valid SHA256 hex string assert len(checksum) == 64 assert all(c in '0123456789abcdef' for c in checksum.lower()) # Verify it matches expected hash expected = hashlib.sha256(b'test content').hexdigest() assert checksum == expected @responses.activate def test_download_file_success(temp_dir): """Test successful file download.""" test_url = 'https://example.com/file.stl' test_content = b'STL file content' dest_path = temp_dir / 'downloaded.stl' responses.add( responses.GET, test_url, body=test_content, status=200 ) result = download_file(test_url, dest_path) assert result is True assert dest_path.exists() assert dest_path.read_bytes() == test_content @responses.activate def test_download_file_failure(): """Test file download failure.""" test_url = 'https://example.com/missing.stl' dest_path = Path('/tmp/test.stl') responses.add( responses.GET, test_url, status=404 ) result = download_file(test_url, dest_path) assert result is False @responses.activate def test_github_api_get_file_sha(github_api): """Test getting file SHA from GitHub API.""" owner = 'test-owner' repo = 'test-repo' path = 'file.stl' ref = 'main' # Mock Contents API response responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/contents/{path}', json={'sha': 'blob-sha-123'}, match=[responses.matchers.query_param_matcher({'ref': ref})] ) # Mock Commits API response responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/commits', json=[{'sha': 'commit-sha-456'}], match=[responses.matchers.query_param_matcher({ 'path': path, 'sha': ref, 'per_page': 1 })] ) sha = github_api.get_file_sha(owner, repo, path, ref) assert sha == 'commit-sha-456' @responses.activate def test_github_api_get_license(github_api): """Test getting license information.""" owner = 'test-owner' repo = 'test-repo' sha = 'abc123' # Mock LICENSE file found responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/contents/LICENSE', json={'type': 'file'}, match=[responses.matchers.query_param_matcher({'ref': sha})] ) license_url = github_api.get_license(owner, repo, sha) assert license_url == f'https://raw.githubusercontent.com/{owner}/{repo}/{sha}/LICENSE' @responses.activate def test_update_manifest_entry_dry_run(temp_dir, sample_manifest_entry): """Test updating manifest entry in dry-run mode.""" owner = 'owner' repo = 'repo' path = 'path/to/file.stl' ref = 'main' # Mock API responses responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/contents/{path}', json={'sha': 'blob-sha'}, match=[responses.matchers.query_param_matcher({'ref': ref})] ) responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/commits', json=[{'sha': 'commit-sha-123'}], match=[responses.matchers.query_param_matcher({ 'path': path, 'sha': ref, 'per_page': 1 })] ) api = GitHubAPI(token='test-token') updated_entry = update_manifest_entry( sample_manifest_entry, api, temp_dir, dry_run=True ) assert updated_entry['pinned_sha'] == 'commit-sha-123' assert updated_entry['pinned_raw_url'] == f'https://raw.githubusercontent.com/{owner}/{repo}/commit-sha-123/{path}' assert updated_entry['status'] == 'up-to-date' assert updated_entry['last_checked'] is not None assert updated_entry['upstream_latest_sha'] == 'commit-sha-123' # In dry-run, file should not be downloaded local_path = temp_dir / updated_entry['local_path'] assert not local_path.exists() @responses.activate def test_update_manifest_entry_with_download(temp_dir, sample_manifest_entry): """Test updating manifest entry with actual download.""" owner = 'owner' repo = 'repo' path = 'path/to/file.stl' ref = 'main' commit_sha = 'commit-sha-123' file_content = b'STL file content here' # Mock API responses responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/contents/{path}', json={'sha': 'blob-sha'}, match=[responses.matchers.query_param_matcher({'ref': ref})] ) responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/commits', json=[{'sha': commit_sha}], match=[responses.matchers.query_param_matcher({ 'path': path, 'sha': ref, 'per_page': 1 })] ) # Mock file download pinned_url = f'https://raw.githubusercontent.com/{owner}/{repo}/{commit_sha}/{path}' responses.add( responses.GET, pinned_url, body=file_content, status=200 ) api = GitHubAPI(token='test-token') updated_entry = update_manifest_entry( sample_manifest_entry, api, temp_dir, dry_run=False ) assert updated_entry['pinned_sha'] == commit_sha assert updated_entry['checksum_sha256'] is not None assert updated_entry['status'] == 'up-to-date' # Verify file was downloaded local_path = temp_dir / updated_entry['local_path'] assert local_path.exists() assert local_path.read_bytes() == file_content # Verify checksum expected_checksum = hashlib.sha256(file_content).hexdigest() assert updated_entry['checksum_sha256'] == expected_checksum @responses.activate def test_update_manifest_entry_download_failure(temp_dir, sample_manifest_entry): """Test handling of download failure.""" owner = 'owner' repo = 'repo' path = 'path/to/file.stl' ref = 'main' commit_sha = 'commit-sha-123' # Mock API responses responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/contents/{path}', json={'sha': 'blob-sha'}, match=[responses.matchers.query_param_matcher({'ref': ref})] ) responses.add( responses.GET, f'https://api.github.com/repos/{owner}/{repo}/commits', json=[{'sha': commit_sha}], match=[responses.matchers.query_param_matcher({ 'path': path, 'sha': ref, 'per_page': 1 })] ) # Mock file download failure pinned_url = f'https://raw.githubusercontent.com/{owner}/{repo}/{commit_sha}/{path}' responses.add( responses.GET, pinned_url, status=404 ) api = GitHubAPI(token='test-token') updated_entry = update_manifest_entry( sample_manifest_entry, api, temp_dir, dry_run=False ) assert updated_entry['status'] == 'error' assert updated_entry['pinned_sha'] == commit_sha # SHA was resolved assert updated_entry['checksum_sha256'] is None # File not downloaded if __name__ == '__main__': pytest.main([__file__, '-v'])