diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f73301c..9ab101a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,46 @@ concurrency: cancel-in-progress: true jobs: + pytest: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.12 + + - name: Install Python Dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + python -m pip install --upgrade -r requirements.txt + python -m pip install --upgrade -r requirements-dev.txt + + - name: Test with pytest + id: test + shell: bash + run: | + python -m pytest \ + -rxXs \ + --tb=native \ + --verbose \ + --color=yes \ + --cov=action \ + tests + + - name: Upload coverage + # any except canceled or skipped + if: >- + always() && + (steps.test.outcome == 'success' || steps.test.outcome == 'failure') && + startsWith(github.repository, 'LizardByte/') + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} + action: runs-on: ubuntu-latest steps: @@ -33,7 +73,7 @@ jobs: release: if: github.event_name == 'push' && github.ref == 'refs/heads/master' needs: - # - pytest + - pytest - action runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index 7617014..b619960 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # facebook-post-action +[![GitHub Workflow Status (CI)](https://img.shields.io/github/actions/workflow/status/lizardbyte/facebook-post-action/ci.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge)](https://github.com/LizardByte/facebook-post-action/actions/workflows/ci.yml?query=branch%3Amaster) +[![Codecov](https://img.shields.io/codecov/c/gh/LizardByte/facebook-post-action.svg?token=XX&style=for-the-badge&logo=codecov&label=codecov)](https://app.codecov.io/gh/LizardByte/facebook-post-action) + GitHub Action for posting to a facebook page or group. ## 🎒 Prep Work diff --git a/action/main.py b/action/main.py index 276b627..9b3c049 100644 --- a/action/main.py +++ b/action/main.py @@ -8,26 +8,24 @@ load_dotenv() -# inputs -ACCESS_TOKEN = os.environ['INPUT_ACCESS_TOKEN'] -MESSAGE = os.environ['INPUT_MESSAGE'] -PAGE_ID = os.environ['INPUT_PAGE_ID'] -URL = os.getenv('INPUT_URL', None) -FAIL_ON_ERROR = os.getenv('INPUT_FAIL_ON_ERROR', 'true') -# constants -FACEBOOK_API_END = f'https://graph.facebook.com/{PAGE_ID}/feed' +def facebook_post( + access_token: str, + message: str, + page_id: str, + url: str = None, + fail_on_error: str = 'true' +): + facebook_api_end = f'https://graph.facebook.com/{page_id}/feed' - -def main(): facebook_api_data = { - 'message': MESSAGE, - 'access_token': ACCESS_TOKEN, + 'message': message, + 'access_token': access_token, } - if URL: - facebook_api_data['link'] = URL + if url: + facebook_api_data['link'] = url - r = requests.post(url=FACEBOOK_API_END, json=facebook_api_data) + r = requests.post(url=facebook_api_end, json=facebook_api_data) result = r.json() @@ -36,10 +34,16 @@ def main(): else: print('Post error:') print(result) - if FAIL_ON_ERROR.lower() == 'true': + if fail_on_error.lower() == 'true': print('Failing the workflow') sys.exit(1) if __name__ == '__main__': - main() # pragma: no cover + facebook_post( + access_token=os.environ['INPUT_ACCESS_TOKEN'], + message=os.environ['INPUT_MESSAGE'], + page_id=os.environ['INPUT_PAGE_ID'], + url=os.getenv('INPUT_URL', None), + fail_on_error=os.getenv('INPUT_FAIL_ON_ERROR', 'true') + ) # pragma: no cover diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..c9d3a1a --- /dev/null +++ b/codecov.yml @@ -0,0 +1,15 @@ +--- +codecov: + branch: master + +coverage: + status: + project: + default: + target: auto + threshold: 10% + +comment: + layout: "diff, flags, files" + behavior: default + require_changes: false # if true: only post the comment if coverage changes diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..1fe7b5e --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,2 @@ +pytest==8.3.3 +pytest-cov==5.0.0 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py new file mode 100644 index 0000000..f0226bd --- /dev/null +++ b/tests/unit/test_main.py @@ -0,0 +1,68 @@ +from unittest.mock import patch +from action.main import facebook_post + + +def test_facebook_post_success(): + with patch('requests.post') as mock_post: + mock_post.return_value.json.return_value = {} + + facebook_post( + access_token='test_access_token', + message='test_message', + page_id='test_page_id', + url='http://test.url', + fail_on_error='true' + ) + + mock_post.assert_called_once_with( + url='https://graph.facebook.com/test_page_id/feed', + json={ + 'message': 'test_message', + 'access_token': 'test_access_token', + 'link': 'http://test.url' + } + ) + + +def test_facebook_post_error(): + with patch('requests.post') as mock_post, patch('sys.exit') as mock_exit: + mock_post.return_value.json.return_value = {'error': 'test_error'} + + facebook_post( + access_token='test_access_token', + message='test_message', + page_id='test_page_id', + url='http://test.url', + fail_on_error='true' + ) + + mock_post.assert_called_once_with( + url='https://graph.facebook.com/test_page_id/feed', + json={ + 'message': 'test_message', + 'access_token': 'test_access_token', + 'link': 'http://test.url' + } + ) + mock_exit.assert_called_once_with(1) + + +def test_facebook_post_error_no_fail(): + with patch('requests.post') as mock_post, patch('sys.exit') as mock_exit: + mock_post.return_value.json.return_value = {'error': 'test_error'} + + facebook_post( + access_token='test_access_token', + message='test_message', + page_id='test_page_id', + url='http://test.url', + fail_on_error='false' + ) + mock_post.assert_called_once_with( + url='https://graph.facebook.com/test_page_id/feed', + json={ + 'message': 'test_message', + 'access_token': 'test_access_token', + 'link': 'http://test.url' + } + )