Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: parse release-candidate hathor-core versions #40

Merged
merged 2 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
with:
python-version: ${{ matrix.python }}
- name: Install Poetry
run: pipx install poetry
run: pip install poetry
- name: Install Poetry dependencies
run: poetry install -n --no-root -E client
- name: Run linters
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ check: flake8 isort-check mypy

# formatting:

.PHONY: fmt
fmt: isort

.PHONY: isort
isort: $(py_sources) $(py_tests)
isort -ac $^
Expand Down
32 changes: 29 additions & 3 deletions hathorlib/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import re
from typing import Any, Dict, List, NamedTuple, Optional, cast
from urllib.parse import urljoin

Expand All @@ -21,6 +22,15 @@
logger = get_logger()


# This regex was copied from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
semver_pattern = (
r"(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)"
r"(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?"
r"(?:\+(?P<metadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?"
)
semver_re = re.compile(semver_pattern)


class BlockTemplate(NamedTuple):
"""Block template."""

Expand All @@ -41,6 +51,8 @@ class HathorVersion(NamedTuple):
major: int
minor: int
patch: int
prerelease: Optional[str] = None
metadata: Optional[str] = None


class HathorClient:
Expand Down Expand Up @@ -73,11 +85,25 @@ def _get_url(self, url: str) -> str:
async def version(self) -> HathorVersion:
"""Return the version of the backend."""
assert self._session is not None

async with self._session.get(self._get_url('version')) as resp:
data = await resp.json()
ver = data['version']
major, minor, patch = ver.split('.')
return HathorVersion(int(major), int(minor), int(patch))
version = data['version']

match = semver_re.match(version)

if match:
result = match.groupdict()

return HathorVersion(
major=int(result['major']),
minor=int(result['minor']),
patch=int(result['patch']),
prerelease=result.get('prerelease'),
metadata=result.get('metadata'),
)
else:
raise RuntimeError(f'Cannot parse version {version}')

async def get_block_template(self, address: Optional[str] = None) -> BlockTemplate:
"""Return a block template."""
Expand Down
48 changes: 48 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
LICENSE file in the root directory of this source tree.
"""

from contextlib import asynccontextmanager
from typing import AsyncIterator
from unittest import IsolatedAsyncioTestCase
from unittest.mock import Mock

Expand Down Expand Up @@ -133,3 +135,49 @@ async def json(self):
'v1a/get_block_template',
params=dict(address='my_address')
)

async def test_version(self) -> None:
# Preparation
versions = [
"1.2.3",
"1.2.3-rc.2",
"1.2.3-rc.2+build.2",
"1.2.3+build.2",
]

class MockResponse:
def __init__(self):
self.status = 200
self.version = None

async def json(self):
return {"version": self.version}

mock_response = MockResponse()
self.client._session = Mock()

@asynccontextmanager
async def get_mock(url: str) -> AsyncIterator[MockResponse]:
yield mock_response

self.client._session.get = get_mock

# Execution
for version in versions:
mock_response.version = version
result = await self.client.version()

# Assertion
self.assertEqual(result.major, 1)
self.assertEqual(result.minor, 2)
self.assertEqual(result.patch, 3)

if version.endswith('-rc.2+build.2'):
self.assertEqual(result.metadata, 'build.2')
self.assertEqual(result.prerelease, 'rc.2')
elif version.endswith('+build.2'):
self.assertEqual(result.metadata, 'build.2')
self.assertIsNone(result.prerelease)
elif version.endswith('-rc.2'):
self.assertIsNone(result.metadata)
self.assertEqual(result.prerelease, 'rc.2')