Skip to content

Commit

Permalink
Merge pull request #11 from cuchi/add-unit-tests
Browse files Browse the repository at this point in the history
Refactor & add unit tests
  • Loading branch information
cuchi authored Oct 27, 2020
2 parents 6cef64b + fe228f6 commit 55f4190
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 36 deletions.
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
test/
.github
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ jobs:
- name: Checkout VCS
uses: actions/checkout@v1

- name: 'Unit tests'
run: |
docker build --tag tests .
docker run --entrypoint python tests unit-tests.py
- name: '[run] simple'
uses: ./
with:
Expand Down
146 changes: 146 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@

### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
*.pyc




# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
pytestdebug.log

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/
doc/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
pythonenv*

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# profiling data
.prof

# End of https://www.toptal.com/developers/gitignore/api/python
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ RUN poetry config virtualenvs.create false
COPY poetry.lock pyproject.toml ./
RUN poetry install

COPY entrypoint.py ./entrypoint.py
COPY . ./

ENTRYPOINT ["./entrypoint.py"]
41 changes: 7 additions & 34 deletions entrypoint.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,12 @@
#!/usr/bin/env python3

import os
from jinja2 import Template, StrictUndefined
from j2cli.context import read_context_data

def guess_format(file_name):
_, extension = os.path.splitext(file_name)
print(extension)
formats = {
'.yaml': 'yaml',
'.yml': 'yaml',
'.json': 'json',
'.ini': 'ini',
'.env': 'env',
}
return formats.get(extension, 'env')
from main import Context

variables = {'env': os.environ}
for variable in os.environ.get('INPUT_VARIABLES', '').split('\n'):
clean_variable = bytes(variable.strip(), 'utf-8').decode('unicode_escape')
if clean_variable != '':
name, value = clean_variable.split('=', 1)
variables.update({name: value})

data_file = os.environ.get('INPUT_DATA_FILE')
if data_file:
format = os.environ.get('INPUT_DATA_FORMAT', guess_format(data_file))
with open(data_file, 'r') as file:
variables.update(read_context_data(format, file, None))

with open(os.environ['INPUT_TEMPLATE'], 'r') as file:
template_kwargs = {}
if os.environ.get('INPUT_STRICT') == 'true':
template_kwargs.update({'undefined': StrictUndefined})
template = Template(str(file.read()), **template_kwargs)

with open(os.environ['INPUT_OUTPUT_FILE'], 'w') as file:
file.write(template.render(**variables) + '\n')
if __name__ == '__main__':
context = Context(os.environ)
context.load_from_env()
context.load_from_input()
context.load_from_data_file()
context.render_template()
13 changes: 13 additions & 0 deletions enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from enum import Enum


class GitHubActionsInput(str, Enum):
DATA_FILE = 'INPUT_DATA_FILE'
DATA_FORMAT = 'INPUT_DATA_FORMAT'
OUTPUT_FILE = 'INPUT_OUTPUT_FILE'
STRICT = 'INPUT_STRICT'
TEMPLATE = 'INPUT_TEMPLATE'
VARIABLES = 'INPUT_VARIABLES'

def __str__(self):
return self.value
53 changes: 53 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import os
from j2cli.context import read_context_data
from jinja2 import Template, StrictUndefined
from enums import GitHubActionsInput

class Context:
def __init__(self, environ):
self._variables = {}
self._environ = environ

def load_from_env(self):
self._variables.update({'env': self._environ})

def load_from_input(self):
for variable in self._environ.get(GitHubActionsInput.VARIABLES, '').split('\n'):
clean_variable = bytes(variable.strip(), 'utf-8').decode('unicode_escape')
if clean_variable != '':
name, value = clean_variable.split('=', 1)
self._variables.update({name: value})

def load_from_data_file(self):
data_file = self._environ.get(GitHubActionsInput.DATA_FILE)
if data_file:
format = self._environ.get(
GitHubActionsInput.DATA_FORMAT,
self._guess_format(data_file),
)
with open(data_file, 'r') as file:
self._variables.update(read_context_data(format, file, None))


def render_template(self):
with open(self._environ[GitHubActionsInput.TEMPLATE], 'r') as file:
template_kwargs = {}
if self._environ.get(GitHubActionsInput.STRICT) == 'true':
template_kwargs.update({'undefined': StrictUndefined})
template = Template(str(file.read()), **template_kwargs)

with open(self._environ[GitHubActionsInput.OUTPUT_FILE], 'w') as file:
file.write(template.render(**self._variables) + '\n')


def _guess_format(self, file_name):
_, extension = os.path.splitext(file_name)
formats = {
'.yaml': 'yaml',
'.yml': 'yaml',
'.json': 'json',
'.ini': 'ini',
'.env': 'env',
}
return formats.get(extension, 'env')

69 changes: 69 additions & 0 deletions unit-tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import os
import unittest

from enums import GitHubActionsInput
from main import Context


class TestContext(unittest.TestCase):
TEST_FILE = 'test_output'

def test_load_from_env(self):
env_vars = {
'foo': 'bar',
GitHubActionsInput.VARIABLES.value: 'name=John\nsurname=Doe'
}

context = Context(env_vars)
context.load_from_env()
expected = {'env': env_vars}

self.assertEqual(context._variables, expected)

def test_load_from_input(self):
context = Context({GitHubActionsInput.VARIABLES.value: 'name=John\nsurname=Doe'})
context.load_from_input()

expected = {
'name': 'John',
'surname': 'Doe',
}
self.assertEqual(context._variables, expected)


def test_load_from_data_file(self):
context = Context({
GitHubActionsInput.DATA_FILE.value: 'test/data-files/data.yml'
})

context.load_from_data_file()

expected = {
'foo': 'bar',
'baz': 'cux',
}
self.assertEqual(context._variables, expected)

def test_render_template(self):
context = Context({
GitHubActionsInput.DATA_FILE.value: 'test/data-files/data.json',
GitHubActionsInput.OUTPUT_FILE.value: self.TEST_FILE,
GitHubActionsInput.TEMPLATE.value: 'test/many-variables/template',
})

context.load_from_data_file()
context.render_template()

expected = "\nbar\ncux\n"

with open('test_output', 'r') as file:
result = file.read()
self.assertEqual(result, expected)

@classmethod
def tearDownClass(cls):
if os.path.exists(cls.TEST_FILE):
os.remove(cls.TEST_FILE)

if __name__ == '__main__':
unittest.main()

0 comments on commit 55f4190

Please sign in to comment.