From 2c40fead4b54a90f02ecde8fa1eb5f081aa42cc1 Mon Sep 17 00:00:00 2001 From: Matthew Tucciarone Date: Wed, 4 Nov 2026 12:55:57 -0500 Subject: [PATCH 1/4] chore: replace circleci with github actions --- .circleci/config.yml | 19 ------------------- .github/workflows/ci.yml | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 19 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/ci.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 8ed0802..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,19 +0,0 @@ -version: 2 -jobs: - build_and_test: - docker: - - image: circleci/python:3.9.5 - working_directory: ~/repo - steps: - - checkout - - run: - name: Install dependencies - command: pip install --user -r requirements.txt - - run: - name: Run tests - command: python3.9 tests/bootstrap.py TestUtils -workflows: - version: 2 - build_and_test: - jobs: - - build_and_test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9688ee6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: CI + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v7 + + - name: Set up Python + uses: actions/setup-python@v7 + with: + python-version: 3.9.5 + cache: pip + + - name: Install dependencies + run: python -m pip install --user -r requirements.txt + + - name: Run tests + run: python tests/bootstrap.py TestUtils From 5e3dde6c67fd68e86e0d532d8242d8bd90229d8b Mon Sep 17 00:00:00 2001 From: Matthew Tucciarone Date: Wed, 4 Nov 2026 13:04:14 -0500 Subject: [PATCH 2/4] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9688ee6..fb8be7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v7 with: - python-version: 3.9.5 + python-version: "3.9" cache: pip - name: Install dependencies From f50e3fbf7b3d8ea139dbea6a77dd23ee6e78723d Mon Sep 17 00:00:00 2001 From: Matthew Tucciarone Date: Wed, 4 Nov 2026 13:06:03 -0500 Subject: [PATCH 3/4] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb8be7e..641aa5c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v7 with: - python-version: "3.9" + python-version: "3.10" cache: pip - name: Install dependencies From e091043e5c04b04ed5a97a2c85b53839e7c0e9cf Mon Sep 17 00:00:00 2001 From: "shinymetilda.j" Date: Thu, 5 Nov 2026 10:36:28 +0530 Subject: [PATCH 4/4] fix: test cases --- .github/workflows/ci.yml | 2 +- tests/bootstrap.py | 11 +++++-- tests/unit/test_lang_format.py | 7 ++-- tests/unit/test_pull.py | 33 ++++++++++++++++++- tests/unit/test_push.py | 60 ++++++++++++++++++++++++++-------- 5 files changed, 91 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 641aa5c..e682915 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,4 +25,4 @@ jobs: run: python -m pip install --user -r requirements.txt - name: Run tests - run: python tests/bootstrap.py TestUtils + run: python tests/bootstrap.py diff --git a/tests/bootstrap.py b/tests/bootstrap.py index 3b884ce..05aed4e 100644 --- a/tests/bootstrap.py +++ b/tests/bootstrap.py @@ -1,19 +1,24 @@ #!/usr/bin/env python import unittest +import os from unit.test_utils import TestUtils from unit.test_push import TestPush from unit.test_get_url import TestGetUrl +from unit.test_lang_format import TestLanguageFormat from unit.test_pull import TestPull def main (): + os.chdir(os.path.dirname(__file__)) # Run the test suites util1= unittest.TestLoader().loadTestsFromTestCase(TestUtils) util2= unittest.TestLoader().loadTestsFromTestCase(TestPush) util3= unittest.TestLoader().loadTestsFromTestCase(TestGetUrl) util4= unittest.TestLoader().loadTestsFromTestCase(TestPull) - unittest.TestSuite([util1, util2, util3, util4]) - unittest.main() + util5= unittest.TestLoader().loadTestsFromTestCase(TestLanguageFormat) + suite = unittest.TestSuite([util1, util2, util3, util4, util5]) + result = unittest.TextTestRunner().run(suite) + return result.wasSuccessful() if __name__ == '__main__': - main() \ No newline at end of file + raise SystemExit(not main()) diff --git a/tests/unit/test_lang_format.py b/tests/unit/test_lang_format.py index 7d1aea6..012a4a3 100644 --- a/tests/unit/test_lang_format.py +++ b/tests/unit/test_lang_format.py @@ -7,11 +7,12 @@ class TestLanguageFormat (unittest.TestCase): def test_correct_language_format (self): - actual = check_and_return_lang_format('/unit/fr.json', 'push') + actual = check_and_return_lang_format('fr.json', 'push') expected = 'fr' self.assertEqual(actual, expected) def test_incorrect_language_format (self): - actual = check_and_return_lang_format('/unit/fr', 'push') + with self.assertRaises(SystemExit) as error: + check_and_return_lang_format('fr', 'push') expected = 'Wrong filename for' - self.assertTrue(expected in actual) \ No newline at end of file + self.assertIn(expected, str(error.exception)) diff --git a/tests/unit/test_pull.py b/tests/unit/test_pull.py index e570e0c..6a82248 100644 --- a/tests/unit/test_pull.py +++ b/tests/unit/test_pull.py @@ -1,13 +1,44 @@ import unittest +import json import sys import os from io import StringIO +from unittest.mock import patch import unit.test_config as test_config sys.path.append(os.path.join(os.path.dirname(__file__), '../../')) from localize.commands import * +class FakeResponse: + def __init__(self, status_code=200, message='', content=b'{}'): + self.status_code = status_code + self.text = json.dumps({'meta': {'error': {'message': message}}}) + self.content = content + + def iter_content(self, chunk_size=1024): + yield self.content + + class TestPull (unittest.TestCase): + def setUp(self): + self.requests_get = patch('localize.commands.requests.get', side_effect=self.fake_get) + self.requests_get.start() + + def tearDown(self): + self.requests_get.stop() + + @staticmethod + def fake_get(url, headers, verify, params, stream): + language = params['language'] + format = params['format'] + if language == 'ta': + return FakeResponse(400, 'Failed to pull ta. This language does not exist in your project') + if language == 'xyz': + return FakeResponse(400, 'language code you provided is not found') + if format == 'IOS_STRINGS': + return FakeResponse(400, "The format doesn't support export for web project") + return FakeResponse() + def test_pull_missing_pull_targets (self): config = { 'api': { @@ -235,4 +266,4 @@ def test_pull_unsupported_file_format (self): sys.stdout = sys.__stdout__ actual = capturedOutput.getvalue() expected = 'The format doesn\'t support export for web project' - self.assertTrue(expected in actual) \ No newline at end of file + self.assertTrue(expected in actual) diff --git a/tests/unit/test_push.py b/tests/unit/test_push.py index d2b9e31..7ca7bbe 100644 --- a/tests/unit/test_push.py +++ b/tests/unit/test_push.py @@ -1,14 +1,41 @@ import unittest +import json import sys import os from io import StringIO +from unittest.mock import patch import unit.test_config as test_config sys.path.append(os.path.join(os.path.dirname(__file__), '../../')) from localize.commands import * +class FakeResponse: + def __init__(self, status_code=200, message=''): + self.status_code = status_code + self.text = json.dumps({'meta': {'error': {'message': message}}}) + + class TestPush (unittest.TestCase): + def setUp(self): + self.requests_post = patch('localize.commands.requests.post', side_effect=self.fake_post) + self.requests_post.start() + + def tearDown(self): + self.requests_post.stop() + + @staticmethod + def fake_post(url, headers, verify, data, files): + language = data['language'] + format = data['format'] + if language == 'ta': + return FakeResponse(400, 'Target language is not supported by the project') + if language == 'wrong_name': + return FakeResponse(400, 'language code you provided is not found') + if format == 'STRINGS': + return FakeResponse(400, 'ValidationError: "format" must be one of') + return FakeResponse() + def test_push_missing_token (self): push_path = os.getcwd() + '/unit/test_files/fr.json' config = { @@ -62,10 +89,11 @@ def test_push_missing_file (self): 'type': 'phrase', } - with self.assertRaises(SystemExit) as SystemExitMessage: + capturedOutput = StringIO() + with patch('sys.stdout', capturedOutput): push(config, '') expected = 'Successfully pushed 0 file(s) to Localize' - self.assertTrue(expected in SystemExitMessage.exception.args[0]) + self.assertIn(expected, capturedOutput.getvalue()) def test_push_with_correct_data (self): push_path = os.getcwd() + '/unit/test_files/fr.json' @@ -86,10 +114,11 @@ def test_push_with_correct_data (self): 'type': 'phrase', } - with self.assertRaises(SystemExit) as SystemExitMessage: + capturedOutput = StringIO() + with patch('sys.stdout', capturedOutput): push(config, '') - expected = 'Successfully pushed 1 file(s) to Localize' - self.assertTrue(expected in SystemExitMessage.exception.args[0]) + expected = 'Successfully pushed 1 file(s) to Localize' + self.assertIn(expected, capturedOutput.getvalue()) def test_push_non_existent_file (self): pull_path = os.getcwd() + '/unit/test_files/xyz.csv' @@ -115,10 +144,11 @@ def test_push_non_existent_file (self): 'type': 'glossary', } - with self.assertRaises(SystemExit) as SystemExitMessage: + capturedOutput = StringIO() + with patch('sys.stdout', capturedOutput): push(config, 'my-phrase') - expected = 'Skipping import of xyz. No target file path in the localize cli config.yml' - self.assertTrue(expected in SystemExitMessage.exception.args[0]) + expected = 'Skipping import of xyz. No target file path in the localize cli config.yml' + self.assertIn(expected, capturedOutput.getvalue()) def test_push_with_custom_profile_test1 (self): push_path = os.getcwd() + '/unit/test_files/fr.json' @@ -144,10 +174,11 @@ def test_push_with_custom_profile_test1 (self): 'type': 'glossary', } - with self.assertRaises(SystemExit) as SystemExitMessage: + capturedOutput = StringIO() + with patch('sys.stdout', capturedOutput): push(config, 'my-glossary') - expected = 'Successfully pushed 1 file(s) to Localize' - self.assertTrue(expected in SystemExitMessage.exception.args[0]) + expected = 'Successfully pushed 1 file(s) to Localize' + self.assertIn(expected, capturedOutput.getvalue()) def test_push_with_custom_profile_test2 (self): pull_path = os.getcwd() + '/unit/test_files/fr.csv' @@ -173,10 +204,11 @@ def test_push_with_custom_profile_test2 (self): 'type': 'glossary', } - with self.assertRaises(SystemExit) as SystemExitMessage: + capturedOutput = StringIO() + with patch('sys.stdout', capturedOutput): push(config, 'my-phrase') - expected = 'Successfully pushed 1 file(s) to Localize' - self.assertTrue(expected in SystemExitMessage.exception.args[0]) + expected = 'Successfully pushed 1 file(s) to Localize' + self.assertIn(expected, capturedOutput.getvalue()) def test_push_incorrect_language_code (self): pull_path = os.getcwd() + '/unit/test_files/ta.csv'