Skip to content

Commit

Permalink
Added test cases (#13)
Browse files Browse the repository at this point in the history
* output to csv and json

* added gitignore file

* Written test files

* Run CI action with secrets
  • Loading branch information
jimmychu0807 authored Sep 27, 2024
1 parent 8a51481 commit 49c73f5
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ jobs:
pdm sync -d
- name: Run Tests
run: |
pdm run all
CRYPTOCOMPARE_APIKEY=${{secrets.CRYPTOCOMPARE_APIKEY}} pdm run all
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# project specific file
tests/fixtures/config.json
4 changes: 2 additions & 2 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ requires = ["pdm-backend"]
build-backend = "pdm.backend"

[project]
name = "airdrop-scripts"
name = "dhkdao_airdrop_scripts"
version = "0.1.0"
authors = [
{ name = "Jimmy Chu", email = "[email protected]" },
Expand All @@ -28,6 +28,7 @@ lint = [
]
test = [
"pytest>=8.3.3",
"python-dotenv>=1.0.1"
]

[project.urls]
Expand All @@ -38,8 +39,9 @@ Issues = "https://github.com/dhkdao/airdrop-scripts/issues"
distribution = true

[tool.pdm.scripts]
test = "pytest tests"
test = "pytest --capture=no tests"
"test:ci" = "pytest"
lint = "flake8 src tests"
"lint:write" = "black src tests"
exe = "pdm run src/scripts/main.py"
all = { composite = ["lint", "test"]}
all = { composite = ["lint", "test:ci"]}
5 changes: 3 additions & 2 deletions src/scripts/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .airdrop import airdrop_monthly_alloc
from .airdrop import Airdrop
from .utils import is_number, round_output

__all__ = ["airdrop_monthly_alloc"]
__all__ = ["Airdrop", "is_number", "round_output"]
28 changes: 19 additions & 9 deletions src/scripts/airdrop.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from rich import print
from typing import Optional

from scripts.utils import is_number


class Airdrop:
@classmethod
Expand All @@ -24,8 +26,7 @@ def check_config(cls, config):

return True

def __init__(self, config_file):
config = json.loads(config_file.read())
def __init__(self, config):
if Airdrop.check_config(config):
self.config = config

Expand Down Expand Up @@ -127,17 +128,22 @@ def monthly_alloc(self):
token, staking_amt = t["token"], t["qty"]
token_price = t["price"] if "price" in t else self.fetch_price(token)

staking_val = token_price * staking_amt
staking_apr = 0
if "staking_apr" in t:
staking_apr = t["staking_apr"]
staking_val = (
token_price * staking_amt
if is_number(token_price) and is_number(staking_amt)
else None
)

staking_apr = None
if "staking-apr" in t:
staking_apr = t["staking-apr"]
elif "network" in t:
staking_apr = self.fetch_staking_apr(t["network"])

# NX> handles TypeError: unsupported operand type(s) for *: 'NoneType' and 'float'
reward = (
staking_apr * staking_val
if isinstance(staking_apr, float) and isinstance(staking_val, float)
if is_number(staking_apr) and is_number(staking_val)
else None
)

Expand Down Expand Up @@ -167,9 +173,13 @@ def monthly_alloc(self):

# Calculate DHK distribution and distribution percent
for idx, row in df.iterrows():
df.at[idx, "dhk-distribution-pc"] = (row["reward"] / ttl_reward) * 100
df.at[idx, "dhk-distribution-pc"] = (
(row["reward"] / ttl_reward) * 100 if is_number(row["reward"]) else None
)
df.at[idx, "dhk-distribution"] = (
row["reward"] / ttl_reward * self.config["dhk_distribution"]
(row["reward"] / ttl_reward) * self.config["dhk_distribution"]
if is_number(row["reward"])
else None
)

return df
12 changes: 6 additions & 6 deletions src/scripts/main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import json
import typer
from typing_extensions import Annotated
from enum import Enum
from rich import print

import utils
from airdrop import Airdrop
from scripts import round_output, Airdrop


class OutputType(str, Enum):
Expand Down Expand Up @@ -34,16 +34,16 @@ def monthly_alloc(
"""
Compute the DHK dao monthly airdrop allocation based on staked value on various blockchains.
"""
airdrop = Airdrop(config)
result = utils.round_output(airdrop.monthly_alloc())
airdrop = Airdrop(json.loads(config.read()))
result = round_output(airdrop.monthly_alloc())
result_output = None

# Transform the result suitable for output
match type:
case OutputType.json:
pass
result_output = result.to_json(orient="records")
case OutputType.csv:
pass
result_output = result.to_csv()
case OutputType.table:
result_output = result
case _: # Unexpected output type
Expand Down
4 changes: 4 additions & 0 deletions src/scripts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ def round_output(df):
for label, col_data in df.items():
df[label] = col_data.apply(round_number)
return df


def is_number(i):
return isinstance(i, (int, float))
73 changes: 70 additions & 3 deletions tests/scripts/test_airdrop.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,70 @@
class TestClass:
def test(self):
assert True
import json
import os
import pytest
import typer
from dotenv import load_dotenv
from scripts import Airdrop


load_dotenv()

UNKNOWN_TOKEN = "HASH"
KNOWN_TOKEN = "ATOM"
INVALID_CONFIG_FILEPATH = os.path.join(
os.path.dirname(__file__), "../fixtures/config-invalid-1.json"
)

print("INVALID_CONFIG_FILEPATH", INVALID_CONFIG_FILEPATH)


def get_config():
return json.loads(
"""{
"dhk_distribution": 100000,
"reference_date": "2024-08-27",
"tokens": [
{ "token": "AKT", "network": "akash", "qty": 188787 },
{ "token": "ATOM", "network": "cosmos", "qty": 592015 }
],
"apis": {
"cryptocompare": {
"endpoint": "https://min-api.cryptocompare.com/data/pricehistorical"
},
"mintscan": {
"endpoint": "https://apis.mintscan.io/v1/:network/apr"
}
}
}"""
)


def get_config_with_cryptocompare_apikey_env():
config = get_config()
config["apis"]["cryptocompare"]["apikey"] = os.getenv("CRYPTOCOMPARE_APIKEY")
return config


class TestAirdropClass:
def test_fetch_price_on_unknown_token_should_return_none(self):
config = get_config_with_cryptocompare_apikey_env()
airdrop = Airdrop(config)
price = airdrop.fetch_price(UNKNOWN_TOKEN)
assert price is None

def test_fetch_price_on_known_token_should_return_value(self):
config = get_config_with_cryptocompare_apikey_env()
airdrop = Airdrop(config)
price = airdrop.fetch_price(KNOWN_TOKEN)
assert isinstance(price, float)

def test_on_config_missing_key_should_raise_exception(self):
with open(INVALID_CONFIG_FILEPATH, "r") as f:
config = json.loads(f.read())

with pytest.raises(typer.Exit) as excinfo:
Airdrop(config)

assert excinfo.type is typer.Exit

def test_fetch_staking_apr(self):
pass

0 comments on commit 49c73f5

Please sign in to comment.