Update package version to 0.0.1-beta, add new dependencies including ExcelJS, and refactor export utilities to utilize ExcelJS for Excel file generation. Enhance component JSON files with vendor information for improved asset management.
This commit is contained in:
1
tests/__init__.py
Normal file
1
tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Tests package
|
||||
240
tests/test_check_updates.py
Normal file
240
tests/test_check_updates.py
Normal file
@@ -0,0 +1,240 @@
|
||||
#!/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'])
|
||||
317
tests/test_vendor_update.py
Normal file
317
tests/test_vendor_update.py
Normal file
@@ -0,0 +1,317 @@
|
||||
#!/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'])
|
||||
Reference in New Issue
Block a user