diff --git a/python/compress-image/README.md b/python/compress-image/README.md
new file mode 100644
index 00000000..24267bd0
--- /dev/null
+++ b/python/compress-image/README.md
@@ -0,0 +1,81 @@
+# đŧī¸ Compress Image with TinyPNG and KrakenIO
+
+A Python Cloud Function for compressing images without losing quality using [Tinypng API](https://tinypng.com/) and [KrakenIO](https://kraken.io/).
+
+
+Example input with Tinypng:
+```json
+{
+ "provider":"tinypng",
+ "image":"iVBORw0KGgoAAAANSUhEUgAAAaQAAALiCAY...QoH9hbkTPQAAAABJRU5ErkJggg=="
+}
+```
+Example input with KrakenIO:
+```json
+{
+ "provider":"krakenio",
+ "image":"iVBORw0KGgoAAAANSUhEUgAAAaQAAALiCAY...QoH9hbkTPQAAAABJRU5ErkJggg=="
+}
+```
+
+Example output:
+```json
+{
+ "success":true,
+ "image":"iVBORw0KGgoAAAANSUhE...o6Ie+UAAAAASU5CYII="
+}
+```
+Example error output:
+```json
+{
+ "success":false,
+ "image":"iVBORw0KGgoAAAANSUhE...o6Ie+UAAAAASU5CYII="
+}
+```
+
+## đ Environment Variables
+
+List of environment variables used by this cloud function:
+- **TINYPNG_KEY** - Tinypng API Key
+- **KRAKENIO_KEY** - KrakenIO API Key
+- **KRAKENIO_SECRET_KEY** - KrakenIO Secret API Key
+
+
+âšī¸ _Create your TinyPNG API key at https://tinypng.com/developers_.
+âšī¸ _Create your KrakenIO API key at https://kraken.io/docs/getting-started_.
+
+
+## đ Deployment
+
+1. Clone this repository, and enter this function folder:
+
+```bash
+git clone https://github.com/open-runtimes/examples.git
+cd examples/python/compress-image
+```
+
+2. Enter this function folder and build the code:
+```bash
+docker run --rm --interactive --tty --volume $PWD:/usr/code openruntimes/python:v2-3.10 sh /usr/local/src/build.sh
+```
+As a result, a `code.tar.gz` file will be generated.
+
+3. Start the Open Runtime:
+```bash
+docker run -p 3000:3000 -e INTERNAL_RUNTIME_KEY=secret-key -e INTERNAL_RUNTIME_ENTRYPOINT=main.py --rm --interactive --tty --volume $PWD/code.tar.gz:/tmp/code.tar.gz:ro openruntimes/python:v2-3.10 sh /usr/local/src/start.sh
+```
+
+> Make sure to replace `YOUR_API_KEY` with your key.
+Your function is now listening on port `3000`, and you can execute it by sending `POST` request with appropriate authorization headers. To learn more about runtime, you can visit Python runtime [README](https://github.com/open-runtimes/open-runtimes/tree/main/openruntimes/python:v2-3.10).
+4. Run the cURL function to send request.
+>TinyPNG Curl Example (Supports only API_KEY in Environment Variables)
+```bash
+curl http://localhost:3000/ -H "X-Internal-Challenge: secret-key" -H "Content-Type: application/json" -d '{"payload": {"provider": "tinypng", "image": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj+L+U4T8ABu8CpCYJ1DQAAAAASUVORK5CYII="}, "variables": {"API_KEY": ""}}'
+```
+>KrakenIO Curl Example (Supports API_KEY and SECRET_API_KEY in Environment Variables)
+```bash
+curl http://localhost:3000/ -H "X-Internal-Challenge: secret-key" -H "Content-Type: application/json" -d '{"payload": {"provider": "krakenio", "image": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj+L+U4T8ABu8CpCYJ1DQAAAAASUVORK5CYII="}, "variables": {"API_KEY": "", "SECRET_API_KEY": ""}}'
+```
+## đ Notes
+- This function is designed for use with Appwrite Cloud Functions. You can learn more about it in [Appwrite docs](https://appwrite.io/docs/functions).
+- This example is compatible with Python 3.10. Other versions may work but are not guaranteed to work as they haven't been tested.
diff --git a/python/compress-image/main.py b/python/compress-image/main.py
new file mode 100644
index 00000000..d9b40555
--- /dev/null
+++ b/python/compress-image/main.py
@@ -0,0 +1,147 @@
+""" Compress image function using Tinypng and Krakenio API."""
+import base64
+import json
+import tinify
+import requests
+
+KRAKEN_API_ENDPOINT = "https://api.kraken.io/v1/upload"
+KRAKEN_USER_AGENT = (
+ "Mozilla/5.0 (Windows NT 6.1; Win64; x64)AppleWebKit/"
+ "537.36(KHTML, like Gecko)Chrome/40.0.2214.85 Safari/537.36"
+)
+
+
+def implement_krakenio(variables):
+ """
+ Implements image optimization using the Kraken.io API.
+
+ Input:
+ variables (dict): A dictionary containing the
+ required variables for optimization.
+ Returns:
+ optimized_image (bytes): decoded optimized image.
+ Raises:
+ raise_for_status (method): raise an HTTPError if the HTTP request
+ returned an unsuccessful status code.
+ """
+ # Headers for post request
+ headers = {"User-Agent": KRAKEN_USER_AGENT}
+ # Image that we will pass in
+ files = {"file": variables["decoded_image"]}
+ # Parameters for post request
+ params = {
+ "auth": {
+ "api_key": variables["api_key"],
+ "api_secret": variables["api_secret_key"]
+ },
+ "wait": True, # Optional: Wait for the optimization to complete.
+ "dev": False, # Optional: Set to false to enter user mode.
+ }
+ response = requests.post(
+ url=KRAKEN_API_ENDPOINT,
+ headers=headers,
+ files=files,
+ data={"data": json.dumps(params)},
+ timeout=10,
+ )
+ # Check status code of response
+ response.raise_for_status()
+ data = response.json()
+ # Response unsuccessful, raise error
+ if not data["success"]:
+ raise ValueError("KrakenIO was not able to compress image.")
+ # Response successful, parse the response
+ optimized_url = data["kraked_url"]
+ optimized_image = requests.get(optimized_url, timeout=10).content
+ return optimized_image
+
+
+def implement_tinypng(variables):
+ """
+ Implements image optimization using the Tinypng API.
+
+ Input:
+ variables (dict): A dictionary containing the required variables
+ for optimization. Includes api_key and decoded_image.
+ Returns:
+ tinify.from_buffer().tobuffer() (bytes): decoded optimized image.
+ Raises:
+ tinify.error (method): raised if tinify fails to compress image.
+ """
+ tinify.key = variables["api_key"]
+ return tinify.from_buffer(variables["decoded_image"]).to_buffer()
+
+
+def validate_request(req):
+ """
+ Validates the request and extracts the necessary information.
+
+ Input:
+ req (json): The request object containing the payload and variables.
+ Returns:
+ result (dict): Contains the validated request information.
+ Raises:
+ ValueError: If any required value is missing or invalid.
+ """
+ # Check if payload is empty
+ if not req.payload:
+ raise ValueError("Missing payload.")
+ # Accessing provider from payload
+ if not req.payload.get("provider"):
+ raise ValueError("Missing provider.")
+ # Check if payload is not empty
+ if not req.variables:
+ raise ValueError("Missing variables.")
+ # Accessing api_key from variables
+ if not req.variables.get("API_KEY"):
+ raise ValueError("Missing API_KEY.")
+ # Accessing encoded image from payload
+ if not req.payload.get("image"):
+ raise ValueError("Missing encoding image.")
+ result = {
+ "provider": req.payload.get("provider").lower(),
+ "api_key": req.variables.get("API_KEY"),
+ "decoded_image": base64.b64decode(req.payload.get("image")),
+ }
+ # Get secret key
+ if req.payload.get("provider") == "krakenio":
+ if not req.variables.get("SECRET_API_KEY"):
+ raise ValueError("Missing api secret key.")
+ result["api_secret_key"] = req.variables.get("SECRET_API_KEY")
+ return result
+
+
+def main(req, res):
+ """
+ The main function that runs validate_request and calls IMPLEMENTATIONS.
+
+ Input:
+ req (json): The request object.
+ res (json): The response object.
+
+ Returns:
+ res (json): A JSON response containing the optimization results.
+ """
+ try:
+ variables = validate_request(req)
+ except (ValueError) as value_error:
+ return res.json({
+ "success": False,
+ "error": f"{value_error}",
+ })
+ try:
+ if variables["provider"] == "tinypng":
+ optimized_image = implement_tinypng(variables)
+ elif variables["provider"] == "krakenio":
+ optimized_image = implement_krakenio(variables)
+ else:
+ raise ValueError("Invalid provider.")
+ except Exception as error:
+ return res.json({
+ "success": False,
+ "error": f"{type(error).__name__}: {error}",
+ })
+ return res.json({
+ "success": True,
+ "image": base64.b64encode(optimized_image).decode(),
+ })
diff --git a/python/compress-image/requirements.txt b/python/compress-image/requirements.txt
new file mode 100644
index 00000000..1fad875b
--- /dev/null
+++ b/python/compress-image/requirements.txt
@@ -0,0 +1,3 @@
+tinify==1.6.0
+requests==2.31.0
+parameterized==0.9.0
\ No newline at end of file
diff --git a/python/compress-image/secret.py b/python/compress-image/secret.py
new file mode 100644
index 00000000..7952e6bb
--- /dev/null
+++ b/python/compress-image/secret.py
@@ -0,0 +1,8 @@
+'''
+API Key for tinyPNG and KrakenIO are stored here
+
+You should be in python/compress-image directory to run test_main.py
+'''
+API_KEY_TINYPNG = None
+API_KEY_KRAKENIO = None
+SECRET_API_KEY_KRAKENIO = None
diff --git a/python/compress-image/test/1kb.png b/python/compress-image/test/1kb.png
new file mode 100644
index 00000000..5c9c29ba
Binary files /dev/null and b/python/compress-image/test/1kb.png differ
diff --git a/python/compress-image/test/1kb_result_encoded_krakenio.txt b/python/compress-image/test/1kb_result_encoded_krakenio.txt
new file mode 100644
index 00000000..263d0ef8
--- /dev/null
+++ b/python/compress-image/test/1kb_result_encoded_krakenio.txt
@@ -0,0 +1 @@
+iVBORw0KGgoAAAANSUhEUgAAABIAAAAUCAIAAAAP9fodAAABz0lEQVR42mP4TxZgoEjbtwMH3nV1vW1u/rh06d/PnyGCa8+8Kll1C4gWHX2GRdvLwsLHnp4PbWweWls/dnF5Ghr6+9kz155zhg0nVCqOApF+3XEgF0Xb1337gEofGBoioyN1faq5uyQLDyEjZDsZ3rS2PrK2RtO2xz9eN3sLmrbU+VcR2l7X1DyytETTdtA9RD9rE5q20GkXEdo+zp//yN4eTdvSmDSN3B3SERNVPdKdfHyT4ryq67IWrl6F0Pbn3bsnPj7Ievb52UT1hMlahqjomWqpKeioydoZyMQ5Sb9faPrjRCsiJH/dvftuwoSHyQlnA1yXhJkHdTjolFrIYYCV6VyfFxn+fnwIEW9AO0+e2Z6zINF5kpPhXBdlLy1MbZmuwh+nS/043Y2SSvY+OOK7Jl5/ngsQKdmpY2oLtRb7OEX4x+EqFG2nn19M2FoI0aYarIuprSZA8NNsxV+XZqNo+/Dj06QzcyHadDrt0PT4molfahb+tiv93+cn6En50ccncy8uz9hRHrohzbM5xCvc1c3eyMlMLc5N+Wi7wff9hX/eXseeA4B2Xn97++yLSxdfXjtx6dSp44eObl9y5cDSP0+P/v1wj0oZZ3BrAwARK/tUoZKzbgAAAABJRU5ErkJggg==
\ No newline at end of file
diff --git a/python/compress-image/test/1kb_result_encoded_tinypng.txt b/python/compress-image/test/1kb_result_encoded_tinypng.txt
new file mode 100644
index 00000000..0f9d1e73
--- /dev/null
+++ b/python/compress-image/test/1kb_result_encoded_tinypng.txt
@@ -0,0 +1 @@
+iVBORw0KGgoAAAANSUhEUgAAABIAAAAUCAMAAAC3SZ14AAABLFBMVEX///8eHh7gMTEZccIvnkTiOjrw+PL87u6ixeb+8uF0qtrM6NJFjM4wf8gkeMX85MT2v78mbbkuarPujY3thITkSkrjRETiPT1AMyFeRB8rJh7ymR398/OtzOr75ubi8eT63d3X7dv87dfR6dZln9VVltHI0sq94MTKx8LJzLehq6qd0afUwKXxpaXFt6Txn5+SzJ360psbWJHEfo74y4v4yIXrfHz4w3pouHf3v3FgtXHpcXF7fmr2umclSGdVsGalXGZsoGHhY2BNrF+8T19ajFblVVVJg1TBR1QdOVRCTE1iXkpKV0XNUEUxnURDkkJSiEAsiT6+TjwsdTikVjfvoTUkLjVGPzLzojHFhzAlUy0jSioiPidNNhdVOxbxlBPSgxNpRRN8UBGpZwp9EYrKAAAAs0lEQVQY063KRXYCQBAA0UkyBHd3d3d3d3eH+9+B1z0cgAW1/K/Ip8mUqrCJELNAwHtLUa3laDoWkVgiEYvYo/mFkvI/CD8VB6mlQHIBZRj1GLmBolqkmjxRGQxLASCuGmTR7m8MBp1+nQazKRvj1Wz5gz13Uvz89enkyOhuTBHI26T0xOhyzSIJC5QeGD2McaT/GKVzlPOtzCeYNZirdkdbnX6fdyDAZxc6Pb5ISArPN3sB8REVVO/DWBYAAAAASUVORK5CYII=
\ No newline at end of file
diff --git a/python/compress-image/test_main.py b/python/compress-image/test_main.py
new file mode 100644
index 00000000..0f25a284
--- /dev/null
+++ b/python/compress-image/test_main.py
@@ -0,0 +1,410 @@
+""" Unittests for Compress Image implementation in main.py."""
+# Standard library
+import base64
+import pathlib
+import unittest
+from unittest.mock import patch
+
+# Third party
+import requests
+import tinify
+from parameterized import parameterized
+
+# Local imports
+import main
+import secret
+
+# Path to 1kb image (png/jpg/jpeg)
+IMAGE = pathlib.Path("test/1kb.png").read_bytes()
+
+# Path to tinypng encoded result (str)
+RESULT_TINYPNG = (
+ pathlib.Path("test/1kb_result_encoded_tinypng.txt").
+ read_text(encoding="utf-8"))
+
+# Path to krakenio encoded result (str)
+RESULT_KRAKENIO = (
+ pathlib.Path("test/1kb_result_encoded_krakenio.txt").
+ read_text(encoding="utf-8"))
+
+
+class TestTinypng(unittest.TestCase):
+ """Class for testing the functionality of the "implement_tinypng" function."""
+ @unittest.skipUnless(secret.API_KEY_TINYPNG, "No Tinypng API Key set.")
+ def test_tinypng_happy_path(self):
+ """Test case optimizing 1kb image using "implement_tinypng" function."""
+ want = RESULT_TINYPNG
+ got = main.implement_tinypng(
+ {
+ "api_key": secret.API_KEY_TINYPNG,
+ "decoded_image": IMAGE,
+ }
+ )
+ self.assertEqual(base64.b64encode(got).decode(), want)
+
+ def test_tinypng_credential(self):
+ """Test case handling Account errors in the "implement_tinypng" function."""
+ # Incorrect Credential
+ self.assertRaises(
+ tinify.errors.AccountError,
+ main.implement_tinypng,
+ {
+ "api_key": "1NCORRECT4CREDENT1ALS",
+ "decoded_image": IMAGE,
+ }
+ )
+
+ @unittest.skipUnless(secret.API_KEY_TINYPNG, "No Tinypng API Key set.")
+ @parameterized.expand([
+ (b"",),
+ (b"ORw0KGgoAAAANSUhEUgAAABEAAAAOCAMAAAD+M",),
+ ])
+ def test_tinypng_client(self, image):
+ """Test case for Client errors in the "implement_tinypng" function."""
+ # Image is empty
+ data = {
+ "api_key": secret.API_KEY_TINYPNG,
+ "decoded_image": image,
+ }
+ self.assertRaises(tinify.errors.ClientError, main.implement_tinypng, data)
+
+ @unittest.skipUnless(secret.API_KEY_TINYPNG, "No Tinypng API Key set.")
+ @parameterized.expand([
+ (
+ {"a": secret.API_KEY_TINYPNG, "decoded_image": IMAGE},
+ ),
+ (
+ {"api_key": secret.API_KEY_TINYPNG, "code_image": IMAGE},
+ ),
+ (
+ {"": secret.API_KEY_TINYPNG, "code_image": IMAGE},
+ ),
+ (
+ {"api_key": secret.API_KEY_TINYPNG, "": IMAGE},
+ ),
+ ])
+ def test_tinypng_keys(self, data):
+ """Test case for handling KeyError in the "implement_tinypng" function."""
+ # Accessing wrong key
+ self.assertRaises(
+ KeyError,
+ main.implement_tinypng,
+ data,
+ )
+
+ @unittest.skipUnless(secret.API_KEY_TINYPNG, "No Tinypng API Key set.")
+ def test_tinypng_variables(self):
+ """Test case handling variables in the "implement_tinypng" function."""
+ # Empty variables
+ self.assertRaises(
+ KeyError,
+ main.implement_tinypng,
+ {},
+ )
+ # One key in variable
+ self.assertRaises(
+ KeyError,
+ main.implement_tinypng,
+ {
+ "api_key": secret.API_KEY_TINYPNG
+ },
+ )
+
+ @unittest.skipUnless(secret.API_KEY_TINYPNG, "No Tinypng API Key set.")
+ def test_implement_tinypng_basic_functionality_1kb(self):
+ """Basic functionality of "implement_tinypng" with a 1kb image."""
+ with patch.object(tinify, "from_buffer") as mock_from_buffer:
+ # Set up the mock return value as decoded result
+ mock_from_buffer.return_value.to_buffer.return_value = (
+ base64.b64decode(RESULT_TINYPNG))
+ # Assert the expected result
+ optimized_image = main.implement_tinypng(
+ {
+ "api_key": secret.API_KEY_TINYPNG,
+ "decoded_image": IMAGE,
+ },
+ )
+ # Check if the return type is a byte
+ self.assertIsInstance(optimized_image, bytes)
+ # Check if the optimized_image equals mock_from_buffer return value
+ self.assertEqual(
+ optimized_image,
+ mock_from_buffer.return_value.to_buffer.return_value)
+
+ @unittest.skipUnless(secret.API_KEY_TINYPNG, "No Tinypng API Key set.")
+ def test_implement_tinypng_unexpected_exception_account_error(self):
+ """Test case handling unexpected "AccountError" in implement_tinypng."""
+ with patch.object(tinify, "from_buffer") as mock_from_buffer:
+ # Set up the mock return value as account exception
+ mock_from_buffer.side_effect = (
+ tinify.errors.AccountError("API Key is wrong"))
+ # Check the raise for Account error
+ self.assertRaises(
+ tinify.errors.AccountError,
+ main.implement_tinypng,
+ {
+ "api_key": secret.API_KEY_TINYPNG,
+ "decoded_image": IMAGE,
+ },
+ )
+
+ @unittest.skipUnless(secret.API_KEY_TINYPNG, "No Tinypng API Key set.")
+ def test_implement_tinypng_unexpected_exception_client_error(self):
+ """Test case handling unexpected "ClientError" in implement_tinypng."""
+ with patch.object(tinify, "from_buffer") as mock_from_buffer:
+ # Set up the mock return value as client exception
+ mock_from_buffer.side_effect = (
+ tinify.errors.ClientError("Image is incorrect."))
+ # Check the raise for Client error
+ self.assertRaises(
+ tinify.errors.ClientError,
+ main.implement_tinypng,
+ {
+ "api_key": secret.API_KEY_TINYPNG,
+ "decoded_image": IMAGE
+ },
+ )
+
+
+class TestKrakenIO(unittest.TestCase):
+ """Class for testing the functionality of the "implement_krakenio" function."""
+ @unittest.skipUnless(
+ secret.API_KEY_KRAKENIO and secret.SECRET_API_KEY_KRAKENIO,
+ "No KrakenIO API Key or Secret Key.")
+ def test_krakenio_happy_path(self):
+ """Test case optimizing 1kb image using "implement_krakenio" function."""
+ want = RESULT_KRAKENIO
+ got = main.implement_krakenio(
+ {
+ "api_key": secret.API_KEY_KRAKENIO,
+ "api_secret_key": secret.SECRET_API_KEY_KRAKENIO,
+ "decoded_image": IMAGE
+ },
+ )
+ self.assertEqual(got, base64.b64decode(want))
+
+ @unittest.skipUnless(
+ secret.API_KEY_KRAKENIO and secret.SECRET_API_KEY_KRAKENIO,
+ "No KrakenIO API Key or Secret Key.")
+ def test_krakenio_time_out(self):
+ """Test case for KrakenIO Read Timeout."""
+ with patch.object(requests, "post") as mock_post:
+ mock_post.side_effect = requests.exceptions.ReadTimeout
+ self.assertRaises(
+ requests.exceptions.ReadTimeout,
+ main.implement_krakenio,
+ {
+ "api_key": secret.API_KEY_KRAKENIO,
+ "api_secret_key": secret.SECRET_API_KEY_KRAKENIO,
+ "decoded_image": IMAGE
+ },
+ )
+
+
+class MyRequest:
+ """Class for defining My Request structure."""
+ def __init__(self, data):
+ self.payload = data.get("payload", {})
+ self.variables = data.get("variables", {})
+
+
+class MyResponse:
+ """Class for defining My Response structure."""
+ def __init__(self):
+ self._json = None
+
+ def json(self, data=None):
+ """Create a response for json."""
+ if data is not None:
+ self._json = data
+ return self._json
+
+
+class TestValidateRequest(unittest.TestCase):
+ """Class for unittests testing validating requests."""
+ @parameterized.expand([
+ [
+ {
+ "payload": {
+ "provider": "tinypng",
+ "image": base64.b64encode(IMAGE).decode()
+ },
+ "variables": {"API_KEY": secret.API_KEY_TINYPNG},
+ },
+ {
+ "provider": "tinypng",
+ "api_key": secret.API_KEY_TINYPNG,
+ "decoded_image": IMAGE
+ }
+ ],
+ [
+ {
+ "payload": {
+ "provider": "krakenio",
+ "image": base64.b64encode(IMAGE).decode()
+ },
+ "variables": {
+ "API_KEY": secret.API_KEY_KRAKENIO,
+ "SECRET_API_KEY": secret.SECRET_API_KEY_KRAKENIO
+ }
+ },
+ {
+ "provider": "krakenio",
+ "api_key": secret.API_KEY_KRAKENIO,
+ "api_secret_key": secret.SECRET_API_KEY_KRAKENIO,
+ "decoded_image": IMAGE
+ }
+ ]
+ ])
+ @unittest.skipUnless(
+ secret.API_KEY_KRAKENIO and
+ secret.SECRET_API_KEY_KRAKENIO and
+ secret.API_KEY_TINYPNG,
+ "No Tinypng Key and KrakenIO Key.",
+ )
+ def test_validate_request(self, got, expected):
+ """Unittest for correct request."""
+ req = MyRequest(
+ {
+ "payload": got["payload"],
+ "variables": got["variables"],
+ },
+ )
+ self.assertEqual(main.validate_request(req), expected)
+
+ @parameterized.expand([
+ [
+ {
+ "payload": {},
+ "variables": {},
+ }
+ ],
+ [
+ {
+ "payload": {"provider": "IMNOTAPROVIDER", "image": ""},
+ "variables": {"API_KEY": "1234567"},
+ }
+ ],
+ [
+ {
+ "payload": {"provider": "krakenio", "image": ""},
+ "variables": {},
+ }
+ ],
+ [
+ {
+ "payload": {"provider": "tinypng", "image": "12345"},
+ "variables": {"API_KEY": ""},
+ }
+ ],
+ [
+ {
+ "payload": {"provider": "krakenio", "image": "12345"},
+ "variables": {"API_KEY": "", "SECRET_API_KEY": ""},
+ }
+ ],
+ [
+ {
+ "payload": {"provider": "krakenio", "image": "12345"},
+ "variables": {"API_KEY": "123", "SECRET_API_KEY": ""},
+ }
+ ],
+ [
+ {
+ "payload": {"WRONG_PROVIDER": "krakenio", "image": "12345"},
+ "variables": {"API_KEY": "123", "SECRET_API_KEY": ""},
+ }
+ ],
+ [
+ {
+ "payload": {"provider": "krakenio", "1Mage": "12345"},
+ "variables": {"API_KEY": "123", "SECRET_API_KEY": ""},
+ }
+ ],
+ [
+ {
+ "payload": {"provider": "krakenio", "1Mage": "12345"},
+ "variables": {"NOT AN API": "123", "SECRET_API_KEY": ""},
+ }
+ ],
+ [
+ {
+ "payload": {"provider": "krakenio", "1Mage": "12345"},
+ "variables": {"API": "123", "SecretKey": ""},
+ }
+ ],
+ ])
+ def test_validate_request_value_error(self, got):
+ """Unittest for testing value errors."""
+ req = MyRequest(
+ {
+ "payload": got["payload"],
+ "variables": got["variables"],
+ },
+ )
+ self.assertRaises(ValueError, main.validate_request, req)
+
+
+class TestMain(unittest.TestCase):
+ """Class test for main function."""
+ @unittest.skipUnless(secret.API_KEY_TINYPNG, "No Tinypng API Key set.")
+ def test_main_success(self):
+ """Unittest for main function success json response."""
+ want = {
+ "success": True,
+ "image": RESULT_TINYPNG
+ }
+ # Create a request
+ req = MyRequest({
+ "payload": {
+ "provider": "tinypng",
+ "image": base64.b64encode(IMAGE).decode()
+ },
+ "variables": {
+ "API_KEY": secret.API_KEY_TINYPNG
+ }
+ })
+ # Create a response object
+ res = MyResponse()
+ main.main(req, res)
+ # Check the response
+ got = res.json()
+ self.assertEqual(got, want)
+
+ def test_main_value_error(self):
+ """Unittest for main function when a value error is raised."""
+ want = {"success": False, "error": "Missing payload."}
+ # Create a request
+ req = MyRequest({"payload": {}, "variables": {}})
+ # Create a response object
+ res = MyResponse()
+ main.main(req, res)
+
+ # Check the response
+ got = res.json()
+ self.assertEqual(got, want)
+
+ def test_main_exception(self):
+ """Unittest case for main function when exception is raised."""
+ # Create a request
+ req = MyRequest({
+ "payload": {
+ "provider": "tinypng",
+ "image": base64.b64encode(IMAGE).decode()
+ },
+ "variables": {
+ "API_KEY": "wrong_api_key"
+ }
+ })
+ # Create a response object
+ res = MyResponse() # Create a response object
+ main.main(req, res)
+
+ # Check the response
+ got = res.json()
+ self.assertFalse(got["success"])
+ self.assertIn("AccountError", got["error"])
+
+
+if __name__ == "__main__":
+ unittest.main()