diff --git a/.gitignore b/.gitignore index e44d1b6..240d5e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ #Eclipse .project - +venv +venv_win TestFiles test.txt diff --git a/Algorithmia/client.py b/Algorithmia/client.py index 82078d0..e72a8f6 100644 --- a/Algorithmia/client.py +++ b/Algorithmia/client.py @@ -41,6 +41,7 @@ def __init__(self, apiKey=None, apiAddress=None, caCert=None, bearerToken=None): self.apiAddress = Algorithmia.getApiAddress() if caCert == False: self.requestSession.verify = False + self.requestSession.trust_env = False config = Configuration(use_ssl=False) elif caCert is None and 'REQUESTS_CA_BUNDLE' in os.environ: caCert = os.environ.get('REQUESTS_CA_BUNDLE') diff --git a/Test/api/__init__.py b/Test/api/__init__.py index ead17d3..c5ff73e 100644 --- a/Test/api/__init__.py +++ b/Test/api/__init__.py @@ -1,348 +1,3 @@ -import importlib -from fastapi import FastAPI, Request -from typing import Optional -from fastapi.responses import Response -import json -import base64 -from multiprocessing import Process -import uvicorn +from .app import start_webserver_reg as start_webserver_reg +from .self_signed_app import start_webserver_self_signed as start_webserver_self_signed -app = FastAPI() - - -def start_webserver(): - def _start_webserver(): - uvicorn.run(app, host="127.0.0.1", port=8080, log_level="debug") - - p = Process(target=_start_webserver) - p.start() - return p - - -@app.post("/v1/algo/{username}/{algoname}") -async def process_algo_req(request: Request, username, algoname, output: Optional[str] = None): - metadata = {"request_id": "req-55c0480d-6af3-4a21-990a-5c51d29f5725", "duration": 0.000306774} - content_type = request.headers['Content-Type'] - auth = request.headers.get('Authorization', None) - if auth is None: - return {"error": {"message": "authorization required"}} - request = await request.body() - if output and output == "void": - return {"async": "abcd123", "request_id": "req-55c0480d-6af3-4a21-990a-5c51d29f5725"} - elif output and output == "raw": - return Response(request.decode(), status_code=200) - elif algoname == "500": - return Response("Internal Server Error", status_code=500) - elif algoname == "raise_exception": - return {"error": {"message": "This is an exception"}} - else: - if content_type != "application/octet-stream": - request = request.decode('utf-8') - if content_type == "text/plain": - metadata['content_type'] = "text" - elif content_type == "application/json": - request = json.loads(request) - metadata['content_type'] = "json" - else: - metadata['content_type'] = "binary" - request = base64.b64encode(request) - output = {"result": request, "metadata": metadata} - return output - -@app.post("/v1/algo/{username}/{algoname}/{githash}") -async def process_hello_world(request: Request, username, algoname, githash): - metadata = {"request_id": "req-55c0480d-6af3-4a21-990a-5c51d29f5725", "duration": 0.000306774, - 'content_type': "text"} - request = await request.body() - request = request.decode('utf-8') - return {"result": f"hello {request}", "metadata": metadata} - - -### Algorithm Routes -@app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}") -async def get_build_id(username, algoname, buildid): - return {"status": "succeeded", "build_id": buildid, "commit_sha": "bcdadj", - "started_at": "2021-09-27T22:54:20.786Z", "finished_at": "2021-09-27T22:54:40.898Z", - "version_info": {"semantic_version": "0.1.1"}} - - -@app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}/logs") -async def get_build_log(username, algoname, buildid): - return {"logs": "This is a log"} - - -@app.get("/v1/algorithms/{username}/{algoname}/scm/status") -async def get_scm_status(username, algoname): - return {"scm_connection_status": "active"} - - -@app.get("/v1/algorithms/{algo_id}/errors") -async def get_algo_errors(algo_id): - return {"error": {"message": "not found"}} - - -@app.post("/v1/algorithms/{username}") -async def create_algorithm(request: Request, username): - payload = await request.json() - return {"id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", "name": payload["name"], - "details": {"label": payload["details"]["label"]}, - "settings": {"algorithm_callability": "private", "source_visibility": "open", - "package_set": "tensorflow-gpu-2.3-python38", "license": "apl", "network_access": "isolated", - "pipeline_enabled": False, "insights_enabled": False, - "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a"}, - "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, - "resource_type": "algorithm"} - - -@app.post("/v1/algorithms/{username}/{algoname}/compile") -async def compile_algorithm(username, algoname): - return { - "id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", - "name": algoname, - "details": { - "summary": "Example Summary", - "label": "QA", - "tagline": "Example Tagline" - }, - "settings": { - "algorithm_callability": "private", - "source_visibility": "open", - "package_set": "tensorflow-gpu-2.3-python38", - "license": "apl", - "network_access": "isolated", - "pipeline_enabled": False, - "insights_enabled": False, - "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a" - }, - "version_info": { - "git_hash": "e85db9bca2fad519f540b445f30d12523e4dec9c", - "version_uuid": "1d9cb91d-11ca-49cb-a7f4-28f67f277654" - }, - "source": { - "scm": { - "id": "internal", - "provider": "internal", - "default": True, - "enabled": True - } - }, - "compilation": { - "successful": True, - "output": "" - }, - "self_link": f"http://localhost:8080/v1/algorithms/{username}/{algoname}/versions/e85db9bca2fad519f540b445f30d12523e4dec9c", - "resource_type": "algorithm" - } - - -@app.post("/v1/algorithms/{username}/{algoname}/versions") -async def publish_algorithm(request: Request, username, algoname): - return {"id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", "name": algoname, - "details": {"summary": "Example Summary", "label": "QA", "tagline": "Example Tagline"}, - "settings": {"algorithm_callability": "private", "source_visibility": "open", - "package_set": "tensorflow-gpu-2.3-python38", "license": "apl", "network_access": "isolated", - "pipeline_enabled": False, "insights_enabled": False, - "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a"}, - "version_info": {"semantic_version": "0.1.0", "git_hash": "e85db9bca2fad519f540b445f30d12523e4dec9c", - "release_notes": "created programmatically", "sample_input": "payload", - "version_uuid": "e85db9bca2fad519f540b445f30d12523e4dec9c"}, - "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, - "compilation": {"successful": True}, - "self_link": f"http://localhost:8080/v1/algorithms/{username}/{algoname}/versions/e85db9bca2fad519f540b445f30d12523e4dec9c", - "resource_type": "algorithm"} - - -@app.get("/v1/algorithms/{username}/{algoname}/versions/{algohash}") -async def get_algorithm_info(username, algoname, algohash): - return { - "id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", - "name": algoname, - "details": { - "summary": "Example Summary", - "label": "QA", - "tagline": "Example Tagline" - }, - "settings": { - "algorithm_callability": "private", - "source_visibility": "open", - "language": "python3", - "environment": "gpu", - "package_set": "tensorflow-gpu-2.3-python38", - "license": "apl", - "network_access": "isolated", - "pipeline_enabled": False, - "insights_enabled": False, - "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a" - }, - "version_info": { - "semantic_version": "0.1.0", - "git_hash": algohash, - "release_notes": "created programmatically", - "sample_input": "\"payload\"", - "sample_output": "Exception encountered while running sample input", - "version_uuid": "1d9cb91d-11ca-49cb-a7f4-28f67f277654" - }, - "source": { - "scm": { - "id": "internal", - "provider": "internal", - "default": True, - "enabled": True - } - }, - "compilation": { - "successful": True, - "output": "" - }, - "resource_type": "algorithm" - } - - -### Admin Routes -@app.post("/v1/users") -async def create_user(request: Request): - payload = await request.body() - data = json.loads(payload) - username = data['username'] - email = data['email'] - return { - "id": "1e5c89ab-3d5c-4bad-b8a3-6c8a294d4418", - "username": username, - "email": email, - "fullname": username, - "self_link": f"http://localhost:8080/v1/users/{username}", "resource_type": "user" - } - - -@app.get("/v1/users/{user_id}/errors") -async def get_user_errors(user_id): - return [] - - -@app.get("/v1/organization/types") -async def get_org_types(): - return [ - {"id": "d0c85ea6-ddfa-11ea-a0c8-12a811be4db3", "name": "basic"}, - {"id": "d0bff917-ddfa-11ea-a0c8-12a811be4db3", "name": "legacy"}, - {"id": "d0c9d825-ddfa-11ea-a0c8-12a811be4db3", "name": "pro"} - ] - - -@app.post("/v1/organizations") -async def create_org(request: Request): - payload = await request.body() - data = json.loads(payload) - org_name = data["org_name"] - org_email = data["org_email"] - return {"id": "55073c92-5f8e-4d7e-a14d-568f94924fd9", - "org_name": org_name, - "org_label": "some label", - "org_contact_name": "Some owner", - "org_email": org_email, - "org_created_at": "2021-10-22T16:41:32", - "org_url": None, - "type_id": "d0c85ea6-ddfa-11ea-a0c8-12a811be4db3", - "stripe_customer_id": None, - "external_admin_group": None, - "external_member_group": None, - "external_id": None, - "owner_ids": None, - "resource_type": "organization", - "self_link": "http://localhost:8080/v1/organizations/a_myOrg1542" - } - - -@app.put("/v1/organizations/{orgname}/members/{username}") -async def add_user_to_org(orgname, username): - return Response(status_code=200) - - -@app.get("/v1/organizations/{orgname}/errors") -async def org_errors(orgname): - return [] - - -@app.put("/v1/organizations/{org_name}") -async def edit_org(org_name): - return Response(status_code=204) - - -@app.get("/v1/organizations/{org_name}") -async def get_org_by_name(org_name): - return { - "id": "55073c92-5f8e-4d7e-a14d-568f94924fd9", - "org_name": org_name, - "org_label": "some label", - "org_contact_name": "Some owner", - "org_email": "a_myOrg1542@algo.com", - "org_created_at": "2021-10-22T16:41:32", - "org_url": None, - "type_id": "d0c85ea6-ddfa-11ea-a0c8-12a811be4db3", - "stripe_customer_id": None, - "external_admin_group": None, - "external_member_group": None, - "external_id": None, - "owner_ids": None, - "resource_type": "organization", - "self_link": "http://localhost:8080/v1/organizations/a_myOrg1542" - } - - -@app.get("/v1/algorithm-environments/edge/languages") -async def get_supported_langs(): - return [{"name": "anaconda3", "display_name": "Conda (Environments) - beta", - "configuration": "{\n \"display_name\": \"Conda (Environments) - beta\",\n \"req_files\": [\n \"environment.yml\"\n ],\n \"artifacts\": [\n {\"source\":\"/home/algo/.cache\", \"destination\":\"/home/algo/.cache/\"},\n {\"source\":\"/home/algo/anaconda_environment\", \"destination\": \"/home/algo/anaconda_environment/\"},\n {\"source\":\"/opt/algorithm\", \"destination\":\"/opt/algorithm/\"}\n ]\n}\n"}, - {"name": "csharp-dotnet-core2", "display_name": "C# .NET Core 2.x+ (Environments)", - "configuration": "{\n \"display_name\": \"C# .NET Core 2.x+ (Environments)\",\n \"artifacts\": [\n {\"source\":\"/opt/algorithm/bin/Release/*/*\", \"destination\":\"/opt/algorithm/\"},\n {\"source\":\"/opt/algorithm/resources\", \"destination\":\"/opt/algorithm/resources/\"},\n {\"source\":\"/home/algo/.nuget\", \"destination\":\"/home/algo/.nuget/\"}\n ]\n}\n"}, - {"name": "java11", "display_name": "Java OpenJDK 11.0 (Environments)", - "configuration": "{\n \"display_name\": \"Java OpenJDK 11.0 (Environments)\",\n \"artifacts\": [\n {\"source\":\"/opt/algorithm/target/*.jar\", \"destination\":\"/opt/algorithm/target/algorithm.jar\"},\n {\"source\":\"/opt/algorithm/target/lib\", \"destination\":\"/opt/algorithm/target/lib/\"}\n ]\n}\n"}, - {"name": "python2", "display_name": "Python 2.x (Environments)", - "configuration": "{\n \"display_name\": \"Python 2.x (Environments)\",\n \"req_files\": [\n \"requirements.txt\"\n ],\n \"artifacts\": [\n {\"source\":\"/home/algo/.local\", \"destination\":\"/home/algo/.local/\"},\n {\"source\":\"/opt/algorithm\", \"destination\":\"/opt/algorithm/\"}\n ]\n}\n"}, - {"name": "python3", "display_name": "Python 3.x (Environments)", - "configuration": "{\n \"display_name\": \"Python 3.x (Environments)\",\n \"req_files\": [\n \"requirements.txt\"\n ],\n \"artifacts\": [\n {\"source\":\"/home/algo/.local\", \"destination\":\"/home/algo/.local/\"},\n {\"source\":\"/opt/algorithm\", \"destination\":\"/opt/algorithm/\"}\n ]\n}\n"}, - {"name": "r36", "display_name": "R 3.6.x (Environments)", - "configuration": "{\n \"display_name\": \"R 3.6.x (Environments)\",\n \"req_files\": [\n \"packages.txt\"\n ],\n \"artifacts\": [\n {\"source\":\"/opt/algorithm\", \"destination\":\"/opt/algorithm/\"},\n {\"source\":\"/usr/local/lib/R/site-library\", \"destination\":\"/usr/local/lib/R/site-library/\"}\n ]\n}\n\n"}, - {"name": "scala-2", "display_name": "Scala 2.x & sbt 1.3.x (Environments)", - "configuration": "{\n \"display_name\": \"Scala 2.x & sbt 1.3.x (Environments)\",\n \"artifacts\": [\n {\"source\":\"/opt/algorithm/target/universal/stage\", \"destination\":\"/opt/algorithm/stage/\"}\n ]\n}\n\n"}] - - -@app.get("/v1/algorithm-environments/edge/languages/{language}/environments") -async def get_environments_by_lang(language): - return { - "environments": [ - { - "id": "717d36e0-222c-44a0-9aa8-06f4ebc1b82a", - "environment_specification_id": "f626effa-e519-431e-9d7a-0d3a7563ae1e", - "display_name": "Python 2.7", - "description": "Generic Python 2.7 installation", - "created_at": "2020-12-21T21:47:53.239", - "language": { - "name": language, - "display_name": "Python 2.x (Environments)", - "configuration": "{\n \"display_name\": \"Python 2.x (Environments)\",\n \"req_files\": [\n " - " \"requirements.txt\"\n ],\n \"artifacts\": [\n {" - "\"source\":\"/home/algo/.local\", \"destination\":\"/home/algo/.local/\"}," - "\n {\"source\":\"/opt/algorithm\", " - "\"destination\":\"/opt/algorithm/\"}\n ]\n}\n " - }, - "machine_type": "CPU" - }, - { - "id": "6f57e041-54e0-4e1a-8b2f-4589bb2c06f8", - "environment_specification_id": "faf81400-eb15-4f64-81c0-3d4ed7181e77", - "display_name": "Python 2.7 + GPU support", - "description": "Python2.7 installation with CUDA 9.0 and CUDNN7", - "created_at": "2020-08-14T07:22:32.955", - "language": { - "name": language, - "display_name": "Python 2.x (Environments)", - "configuration": "{\n \"display_name\": \"Python 2.x (Environments)\",\n \"req_files\": [\n " - " \"requirements.txt\"\n ],\n \"artifacts\": [\n {" - "\"source\":\"/home/algo/.local\", \"destination\":\"/home/algo/.local/\"}," - "\n {\"source\":\"/opt/algorithm\", " - "\"destination\":\"/opt/algorithm/\"}\n ]\n}\n " - }, - "machine_type": "GPU" - } - ] - } diff --git a/Test/api/app.py b/Test/api/app.py new file mode 100644 index 0000000..99c192b --- /dev/null +++ b/Test/api/app.py @@ -0,0 +1,355 @@ +from fastapi import FastAPI, Request +from typing import Optional +from fastapi.responses import Response +import json +import base64 +from multiprocessing import Process +import uvicorn + + + +regular_app = FastAPI() + + +def start_webserver_reg(): + def _start_webserver(): + uvicorn.run(regular_app, host="127.0.0.1", port=8080, log_level="debug") + + p = Process(target=_start_webserver) + p.start() + return p + +@regular_app.post("/v1/algo/{username}/{algoname}") +async def process_algo_req(request: Request, username, algoname, output: Optional[str] = None): + metadata = {"request_id": "req-55c0480d-6af3-4a21-990a-5c51d29f5725", "duration": 0.000306774} + content_type = request.headers['Content-Type'] + auth = request.headers.get('Authorization', None) + if auth is None: + return {"error": {"message": "authorization required"}} + request = await request.body() + if output and output == "void": + return {"async": "abcd123", "request_id": "req-55c0480d-6af3-4a21-990a-5c51d29f5725"} + elif output and output == "raw": + return Response(request.decode(), status_code=200) + elif algoname == "500": + return Response("Internal Server Error", status_code=500) + elif algoname == "raise_exception": + return {"error": {"message": "This is an exception"}} + else: + if content_type != "application/octet-stream": + request = request.decode('utf-8') + if content_type == "text/plain": + metadata['content_type'] = "text" + elif content_type == "application/json": + request = json.loads(request) + metadata['content_type'] = "json" + else: + metadata['content_type'] = "binary" + request = base64.b64encode(request) + output = {"result": request, "metadata": metadata} + return output + + +@regular_app.post("/v1/algo/{username}/{algoname}/{githash}") +async def process_hello_world(request: Request, username, algoname, githash): + metadata = {"request_id": "req-55c0480d-6af3-4a21-990a-5c51d29f5725", "duration": 0.000306774, + 'content_type': "text"} + request = await request.body() + request = request.decode('utf-8') + return {"result": f"hello {request}", "metadata": metadata} + + +### Algorithm Routes +@regular_app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}") +async def get_build_id(username, algoname, buildid): + return {"status": "succeeded", "build_id": buildid, "commit_sha": "bcdadj", + "started_at": "2021-09-27T22:54:20.786Z", "finished_at": "2021-09-27T22:54:40.898Z", + "version_info": {"semantic_version": "0.1.1"}} + + +@regular_app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}/logs") +async def get_build_log(username, algoname, buildid): + return {"logs": "This is a log"} + +@regular_app.get("/v1/algorithms/{username}/{algoname}/scm/status") +async def get_scm_status(username, algoname): + return {"scm_connection_status": "active"} + + +@regular_app.get("/v1/algorithms/{algo_id}/errors") +async def get_algo_errors(algo_id): + return {"error": {"message": "not found"}} + + +@regular_app.post("/v1/algorithms/{username}") +async def create_algorithm(request: Request, username): + payload = await request.json() + return {"id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", "name": payload["name"], + "details": {"label": payload["details"]["label"]}, + "settings": {"algorithm_callability": "private", "source_visibility": "open", + "package_set": "tensorflow-gpu-2.3-python38", "license": "apl", "network_access": "isolated", + "pipeline_enabled": False, "insights_enabled": False, + "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a"}, + "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, + "resource_type": "algorithm"} + + +@regular_app.post("/v1/algorithms/{username}/{algoname}/compile") +async def compile_algorithm(username, algoname): + return { + "id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", + "name": algoname, + "details": { + "summary": "Example Summary", + "label": "QA", + "tagline": "Example Tagline" + }, + "settings": { + "algorithm_callability": "private", + "source_visibility": "open", + "package_set": "tensorflow-gpu-2.3-python38", + "license": "apl", + "network_access": "isolated", + "pipeline_enabled": False, + "insights_enabled": False, + "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a" + }, + "version_info": { + "git_hash": "e85db9bca2fad519f540b445f30d12523e4dec9c", + "version_uuid": "1d9cb91d-11ca-49cb-a7f4-28f67f277654" + }, + "source": { + "scm": { + "id": "internal", + "provider": "internal", + "default": True, + "enabled": True + } + }, + "compilation": { + "successful": True, + "output": "" + }, + "self_link": f"http://localhost:8080/v1/algorithms/{username}/{algoname}/versions/e85db9bca2fad519f540b445f30d12523e4dec9c", + "resource_type": "algorithm" + } + + +@regular_app.post("/v1/algorithms/{username}/{algoname}/versions") +async def publish_algorithm(request: Request, username, algoname): + return {"id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", "name": algoname, + "details": {"summary": "Example Summary", "label": "QA", "tagline": "Example Tagline"}, + "settings": {"algorithm_callability": "private", "source_visibility": "open", + "package_set": "tensorflow-gpu-2.3-python38", "license": "apl", "network_access": "isolated", + "pipeline_enabled": False, "insights_enabled": False, + "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a"}, + "version_info": {"semantic_version": "0.1.0", "git_hash": "e85db9bca2fad519f540b445f30d12523e4dec9c", + "release_notes": "created programmatically", "sample_input": "payload", + "version_uuid": "e85db9bca2fad519f540b445f30d12523e4dec9c"}, + "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, + "compilation": {"successful": True}, + "self_link": f"http://localhost:8080/v1/algorithms/{username}/{algoname}/versions/e85db9bca2fad519f540b445f30d12523e4dec9c", + "resource_type": "algorithm"} + + +@regular_app.get("/v1/algorithms/{username}/{algoname}/versions/{algohash}") +async def get_algorithm_info(username, algoname, algohash): + return { + "id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", + "name": algoname, + "details": { + "summary": "Example Summary", + "label": "QA", + "tagline": "Example Tagline" + }, + "settings": { + "algorithm_callability": "private", + "source_visibility": "open", + "language": "python3", + "environment": "gpu", + "package_set": "tensorflow-gpu-2.3-python38", + "license": "apl", + "network_access": "isolated", + "pipeline_enabled": False, + "insights_enabled": False, + "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a" + }, + "version_info": { + "semantic_version": "0.1.0", + "git_hash": algohash, + "release_notes": "created programmatically", + "sample_input": "\"payload\"", + "sample_output": "Exception encountered while running sample input", + "version_uuid": "1d9cb91d-11ca-49cb-a7f4-28f67f277654" + }, + "source": { + "scm": { + "id": "internal", + "provider": "internal", + "default": True, + "enabled": True + } + }, + "compilation": { + "successful": True, + "output": "" + }, + "resource_type": "algorithm" + } + + +### Admin Routes +@regular_app.post("/v1/users") +async def create_user(request: Request): + payload = await request.body() + data = json.loads(payload) + username = data['username'] + email = data['email'] + return { + "id": "1e5c89ab-3d5c-4bad-b8a3-6c8a294d4418", + "username": username, + "email": email, + "fullname": username, + "self_link": f"http://localhost:8080/v1/users/{username}", "resource_type": "user" + } + + +@regular_app.get("/v1/users/{user_id}/errors") +async def get_user_errors(user_id): + return [] + + +@regular_app.get("/v1/organization/types") +async def get_org_types(): + return [ + {"id": "d0c85ea6-ddfa-11ea-a0c8-12a811be4db3", "name": "basic"}, + {"id": "d0bff917-ddfa-11ea-a0c8-12a811be4db3", "name": "legacy"}, + {"id": "d0c9d825-ddfa-11ea-a0c8-12a811be4db3", "name": "pro"} + ] + + +@regular_app.post("/v1/organizations") +async def create_org(request: Request): + payload = await request.body() + data = json.loads(payload) + org_name = data["org_name"] + org_email = data["org_email"] + return {"id": "55073c92-5f8e-4d7e-a14d-568f94924fd9", + "org_name": org_name, + "org_label": "some label", + "org_contact_name": "Some owner", + "org_email": org_email, + "org_created_at": "2021-10-22T16:41:32", + "org_url": None, + "type_id": "d0c85ea6-ddfa-11ea-a0c8-12a811be4db3", + "stripe_customer_id": None, + "external_admin_group": None, + "external_member_group": None, + "external_id": None, + "owner_ids": None, + "resource_type": "organization", + "self_link": "http://localhost:8080/v1/organizations/a_myOrg1542" + } + + +@regular_app.put("/v1/organizations/{orgname}/members/{username}") +async def add_user_to_org(orgname, username): + return Response(status_code=200) + + +@regular_app.get("/v1/organizations/{orgname}/errors") +async def org_errors(orgname): + return [] + + +@regular_app.put("/v1/organizations/{org_name}") +async def edit_org(org_name): + return Response(status_code=204) + + +@regular_app.get("/v1/organizations/{org_name}") +async def get_org_by_name(org_name): + return { + "id": "55073c92-5f8e-4d7e-a14d-568f94924fd9", + "org_name": org_name, + "org_label": "some label", + "org_contact_name": "Some owner", + "org_email": "a_myOrg1542@algo.com", + "org_created_at": "2021-10-22T16:41:32", + "org_url": None, + "type_id": "d0c85ea6-ddfa-11ea-a0c8-12a811be4db3", + "stripe_customer_id": None, + "external_admin_group": None, + "external_member_group": None, + "external_id": None, + "owner_ids": None, + "resource_type": "organization", + "self_link": "http://localhost:8080/v1/organizations/a_myOrg1542" + } + +@regular_app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}/logs") +async def get_build_log(username, algoname, buildid): + return {"logs": "This is a log"} + + +@regular_app.get("/v1/algorithm-environments/edge/languages") +async def get_supported_langs(): + return [{"name": "anaconda3", "display_name": "Conda (Environments) - beta", + "configuration": "{\n \"display_name\": \"Conda (Environments) - beta\",\n \"req_files\": [\n \"environment.yml\"\n ],\n \"artifacts\": [\n {\"source\":\"/home/algo/.cache\", \"destination\":\"/home/algo/.cache/\"},\n {\"source\":\"/home/algo/anaconda_environment\", \"destination\": \"/home/algo/anaconda_environment/\"},\n {\"source\":\"/opt/algorithm\", \"destination\":\"/opt/algorithm/\"}\n ]\n}\n"}, + {"name": "csharp-dotnet-core2", "display_name": "C# .NET Core 2.x+ (Environments)", + "configuration": "{\n \"display_name\": \"C# .NET Core 2.x+ (Environments)\",\n \"artifacts\": [\n {\"source\":\"/opt/algorithm/bin/Release/*/*\", \"destination\":\"/opt/algorithm/\"},\n {\"source\":\"/opt/algorithm/resources\", \"destination\":\"/opt/algorithm/resources/\"},\n {\"source\":\"/home/algo/.nuget\", \"destination\":\"/home/algo/.nuget/\"}\n ]\n}\n"}, + {"name": "java11", "display_name": "Java OpenJDK 11.0 (Environments)", + "configuration": "{\n \"display_name\": \"Java OpenJDK 11.0 (Environments)\",\n \"artifacts\": [\n {\"source\":\"/opt/algorithm/target/*.jar\", \"destination\":\"/opt/algorithm/target/algorithm.jar\"},\n {\"source\":\"/opt/algorithm/target/lib\", \"destination\":\"/opt/algorithm/target/lib/\"}\n ]\n}\n"}, + {"name": "python2", "display_name": "Python 2.x (Environments)", + "configuration": "{\n \"display_name\": \"Python 2.x (Environments)\",\n \"req_files\": [\n \"requirements.txt\"\n ],\n \"artifacts\": [\n {\"source\":\"/home/algo/.local\", \"destination\":\"/home/algo/.local/\"},\n {\"source\":\"/opt/algorithm\", \"destination\":\"/opt/algorithm/\"}\n ]\n}\n"}, + {"name": "python3", "display_name": "Python 3.x (Environments)", + "configuration": "{\n \"display_name\": \"Python 3.x (Environments)\",\n \"req_files\": [\n \"requirements.txt\"\n ],\n \"artifacts\": [\n {\"source\":\"/home/algo/.local\", \"destination\":\"/home/algo/.local/\"},\n {\"source\":\"/opt/algorithm\", \"destination\":\"/opt/algorithm/\"}\n ]\n}\n"}, + {"name": "r36", "display_name": "R 3.6.x (Environments)", + "configuration": "{\n \"display_name\": \"R 3.6.x (Environments)\",\n \"req_files\": [\n \"packages.txt\"\n ],\n \"artifacts\": [\n {\"source\":\"/opt/algorithm\", \"destination\":\"/opt/algorithm/\"},\n {\"source\":\"/usr/local/lib/R/site-library\", \"destination\":\"/usr/local/lib/R/site-library/\"}\n ]\n}\n\n"}, + {"name": "scala-2", "display_name": "Scala 2.x & sbt 1.3.x (Environments)", + "configuration": "{\n \"display_name\": \"Scala 2.x & sbt 1.3.x (Environments)\",\n \"artifacts\": [\n {\"source\":\"/opt/algorithm/target/universal/stage\", \"destination\":\"/opt/algorithm/stage/\"}\n ]\n}\n\n"}] + + +@regular_app.get("/v1/algorithm-environments/edge/languages/{language}/environments") +async def get_environments_by_lang(language): + return { + "environments": [ + { + "id": "717d36e0-222c-44a0-9aa8-06f4ebc1b82a", + "environment_specification_id": "f626effa-e519-431e-9d7a-0d3a7563ae1e", + "display_name": "Python 2.7", + "description": "Generic Python 2.7 installation", + "created_at": "2020-12-21T21:47:53.239", + "language": { + "name": language, + "display_name": "Python 2.x (Environments)", + "configuration": "{\n \"display_name\": \"Python 2.x (Environments)\",\n \"req_files\": [\n " + " \"requirements.txt\"\n ],\n \"artifacts\": [\n {" + "\"source\":\"/home/algo/.local\", \"destination\":\"/home/algo/.local/\"}," + "\n {\"source\":\"/opt/algorithm\", " + "\"destination\":\"/opt/algorithm/\"}\n ]\n}\n " + }, + "machine_type": "CPU" + }, + { + "id": "6f57e041-54e0-4e1a-8b2f-4589bb2c06f8", + "environment_specification_id": "faf81400-eb15-4f64-81c0-3d4ed7181e77", + "display_name": "Python 2.7 + GPU support", + "description": "Python2.7 installation with CUDA 9.0 and CUDNN7", + "created_at": "2020-08-14T07:22:32.955", + "language": { + "name": language, + "display_name": "Python 2.x (Environments)", + "configuration": "{\n \"display_name\": \"Python 2.x (Environments)\",\n \"req_files\": [\n " + " \"requirements.txt\"\n ],\n \"artifacts\": [\n {" + "\"source\":\"/home/algo/.local\", \"destination\":\"/home/algo/.local/\"}," + "\n {\"source\":\"/opt/algorithm\", " + "\"destination\":\"/opt/algorithm/\"}\n ]\n}\n " + }, + "machine_type": "GPU" + } + ] + } + + + \ No newline at end of file diff --git a/Test/api/self_signed_app.py b/Test/api/self_signed_app.py new file mode 100644 index 0000000..4d1423f --- /dev/null +++ b/Test/api/self_signed_app.py @@ -0,0 +1,351 @@ +from fastapi import FastAPI, Request +from typing import Optional +from fastapi.responses import Response +import json +import base64 +from multiprocessing import Process +import uvicorn + +self_signed_app = FastAPI() + + +def start_webserver_self_signed(): + def _start_webserver(): + uvicorn.run(self_signed_app, host="127.0.0.1", port=8090, log_level="debug", + ssl_certfile="Test/resources/cert.cert", ssl_keyfile="Test/resources/cert.key") + + p = Process(target=_start_webserver) + p.start() + return p + +@self_signed_app.post("/v1/algo/{username}/{algoname}") +async def process_algo_req(request: Request, username, algoname, output: Optional[str] = None): + metadata = {"request_id": "req-55c0480d-6af3-4a21-990a-5c51d29f5725", "duration": 0.000306774} + content_type = request.headers['Content-Type'] + auth = request.headers.get('Authorization', None) + if auth is None: + return {"error": {"message": "authorization required"}} + request = await request.body() + if output and output == "void": + return {"async": "abcd123", "request_id": "req-55c0480d-6af3-4a21-990a-5c51d29f5725"} + elif output and output == "raw": + return Response(request.decode(), status_code=200) + elif algoname == "500": + return Response("Internal Server Error", status_code=500) + elif algoname == "raise_exception": + return {"error": {"message": "This is an exception"}} + else: + if content_type != "application/octet-stream": + request = request.decode('utf-8') + if content_type == "text/plain": + metadata['content_type'] = "text" + elif content_type == "application/json": + request = json.loads(request) + metadata['content_type'] = "json" + else: + metadata['content_type'] = "binary" + request = base64.b64encode(request) + output = {"result": request, "metadata": metadata} + return output + + +@self_signed_app.post("/v1/algo/{username}/{algoname}/{githash}") +async def process_hello_world(request: Request, username, algoname, githash): + metadata = {"request_id": "req-55c0480d-6af3-4a21-990a-5c51d29f5725", "duration": 0.000306774, + 'content_type': "text"} + request = await request.body() + request = request.decode('utf-8') + return {"result": f"hello {request}", "metadata": metadata} + + +### Algorithm Routes +@self_signed_app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}") +async def get_build_id(username, algoname, buildid): + return {"status": "succeeded", "build_id": buildid, "commit_sha": "bcdadj", + "started_at": "2021-09-27T22:54:20.786Z", "finished_at": "2021-09-27T22:54:40.898Z", + "version_info": {"semantic_version": "0.1.1"}} + + +@self_signed_app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}/logs") +async def get_build_log(username, algoname, buildid): + return {"logs": "This is a log"} + +@self_signed_app.get("/v1/algorithms/{username}/{algoname}/scm/status") +async def get_scm_status(username, algoname): + return {"scm_connection_status": "active"} + + +@self_signed_app.get("/v1/algorithms/{algo_id}/errors") +async def get_algo_errors(algo_id): + return {"error": {"message": "not found"}} + + +@self_signed_app.post("/v1/algorithms/{username}") +async def create_algorithm(request: Request, username): + payload = await request.json() + return {"id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", "name": payload["name"], + "details": {"label": payload["details"]["label"]}, + "settings": {"algorithm_callability": "private", "source_visibility": "open", + "package_set": "tensorflow-gpu-2.3-python38", "license": "apl", "network_access": "isolated", + "pipeline_enabled": False, "insights_enabled": False, + "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a"}, + "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, + "resource_type": "algorithm"} + + +@self_signed_app.post("/v1/algorithms/{username}/{algoname}/compile") +async def compile_algorithm(username, algoname): + return { + "id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", + "name": algoname, + "details": { + "summary": "Example Summary", + "label": "QA", + "tagline": "Example Tagline" + }, + "settings": { + "algorithm_callability": "private", + "source_visibility": "open", + "package_set": "tensorflow-gpu-2.3-python38", + "license": "apl", + "network_access": "isolated", + "pipeline_enabled": False, + "insights_enabled": False, + "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a" + }, + "version_info": { + "git_hash": "e85db9bca2fad519f540b445f30d12523e4dec9c", + "version_uuid": "1d9cb91d-11ca-49cb-a7f4-28f67f277654" + }, + "source": { + "scm": { + "id": "internal", + "provider": "internal", + "default": True, + "enabled": True + } + }, + "compilation": { + "successful": True, + "output": "" + }, + "self_link": f"http://localhost:8080/v1/algorithms/{username}/{algoname}/versions/e85db9bca2fad519f540b445f30d12523e4dec9c", + "resource_type": "algorithm" + } + + +@self_signed_app.post("/v1/algorithms/{username}/{algoname}/versions") +async def publish_algorithm(request: Request, username, algoname): + return {"id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", "name": algoname, + "details": {"summary": "Example Summary", "label": "QA", "tagline": "Example Tagline"}, + "settings": {"algorithm_callability": "private", "source_visibility": "open", + "package_set": "tensorflow-gpu-2.3-python38", "license": "apl", "network_access": "isolated", + "pipeline_enabled": False, "insights_enabled": False, + "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a"}, + "version_info": {"semantic_version": "0.1.0", "git_hash": "e85db9bca2fad519f540b445f30d12523e4dec9c", + "release_notes": "created programmatically", "sample_input": "payload", + "version_uuid": "e85db9bca2fad519f540b445f30d12523e4dec9c"}, + "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, + "compilation": {"successful": True}, + "self_link": f"http://localhost:8080/v1/algorithms/{username}/{algoname}/versions/e85db9bca2fad519f540b445f30d12523e4dec9c", + "resource_type": "algorithm"} + + +@self_signed_app.get("/v1/algorithms/{username}/{algoname}/versions/{algohash}") +async def get_algorithm_info(username, algoname, algohash): + return { + "id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", + "name": algoname, + "details": { + "summary": "Example Summary", + "label": "QA", + "tagline": "Example Tagline" + }, + "settings": { + "algorithm_callability": "private", + "source_visibility": "open", + "language": "python3", + "environment": "gpu", + "package_set": "tensorflow-gpu-2.3-python38", + "license": "apl", + "network_access": "isolated", + "pipeline_enabled": False, + "insights_enabled": False, + "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a" + }, + "version_info": { + "semantic_version": "0.1.0", + "git_hash": algohash, + "release_notes": "created programmatically", + "sample_input": "\"payload\"", + "sample_output": "Exception encountered while running sample input", + "version_uuid": "1d9cb91d-11ca-49cb-a7f4-28f67f277654" + }, + "source": { + "scm": { + "id": "internal", + "provider": "internal", + "default": True, + "enabled": True + } + }, + "compilation": { + "successful": True, + "output": "" + }, + "resource_type": "algorithm" + } + + +### Admin Routes +@self_signed_app.post("/v1/users") +async def create_user(request: Request): + payload = await request.body() + data = json.loads(payload) + username = data['username'] + email = data['email'] + return { + "id": "1e5c89ab-3d5c-4bad-b8a3-6c8a294d4418", + "username": username, + "email": email, + "fullname": username, + "self_link": f"http://localhost:8080/v1/users/{username}", "resource_type": "user" + } + + +@self_signed_app.get("/v1/users/{user_id}/errors") +async def get_user_errors(user_id): + return [] + + +@self_signed_app.get("/v1/organization/types") +async def get_org_types(): + return [ + {"id": "d0c85ea6-ddfa-11ea-a0c8-12a811be4db3", "name": "basic"}, + {"id": "d0bff917-ddfa-11ea-a0c8-12a811be4db3", "name": "legacy"}, + {"id": "d0c9d825-ddfa-11ea-a0c8-12a811be4db3", "name": "pro"} + ] + + +@self_signed_app.post("/v1/organizations") +async def create_org(request: Request): + payload = await request.body() + data = json.loads(payload) + org_name = data["org_name"] + org_email = data["org_email"] + return {"id": "55073c92-5f8e-4d7e-a14d-568f94924fd9", + "org_name": org_name, + "org_label": "some label", + "org_contact_name": "Some owner", + "org_email": org_email, + "org_created_at": "2021-10-22T16:41:32", + "org_url": None, + "type_id": "d0c85ea6-ddfa-11ea-a0c8-12a811be4db3", + "stripe_customer_id": None, + "external_admin_group": None, + "external_member_group": None, + "external_id": None, + "owner_ids": None, + "resource_type": "organization", + "self_link": "http://localhost:8080/v1/organizations/a_myOrg1542" + } + + +@self_signed_app.put("/v1/organizations/{orgname}/members/{username}") +async def add_user_to_org(orgname, username): + return Response(status_code=200) + + +@self_signed_app.get("/v1/organizations/{orgname}/errors") +async def org_errors(orgname): + return [] + + +@self_signed_app.put("/v1/organizations/{org_name}") +async def edit_org(org_name): + return Response(status_code=204) + + +@self_signed_app.get("/v1/organizations/{org_name}") +async def get_org_by_name(org_name): + return { + "id": "55073c92-5f8e-4d7e-a14d-568f94924fd9", + "org_name": org_name, + "org_label": "some label", + "org_contact_name": "Some owner", + "org_email": "a_myOrg1542@algo.com", + "org_created_at": "2021-10-22T16:41:32", + "org_url": None, + "type_id": "d0c85ea6-ddfa-11ea-a0c8-12a811be4db3", + "stripe_customer_id": None, + "external_admin_group": None, + "external_member_group": None, + "external_id": None, + "owner_ids": None, + "resource_type": "organization", + "self_link": "http://localhost:8080/v1/organizations/a_myOrg1542" + } + +@self_signed_app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}/logs") +async def get_build_log(username, algoname, buildid): + return {"logs": "This is a log"} + + +@self_signed_app.get("/v1/algorithm-environments/edge/languages") +async def get_supported_langs(): + return [{"name": "anaconda3", "display_name": "Conda (Environments) - beta", + "configuration": "{\n \"display_name\": \"Conda (Environments) - beta\",\n \"req_files\": [\n \"environment.yml\"\n ],\n \"artifacts\": [\n {\"source\":\"/home/algo/.cache\", \"destination\":\"/home/algo/.cache/\"},\n {\"source\":\"/home/algo/anaconda_environment\", \"destination\": \"/home/algo/anaconda_environment/\"},\n {\"source\":\"/opt/algorithm\", \"destination\":\"/opt/algorithm/\"}\n ]\n}\n"}, + {"name": "csharp-dotnet-core2", "display_name": "C# .NET Core 2.x+ (Environments)", + "configuration": "{\n \"display_name\": \"C# .NET Core 2.x+ (Environments)\",\n \"artifacts\": [\n {\"source\":\"/opt/algorithm/bin/Release/*/*\", \"destination\":\"/opt/algorithm/\"},\n {\"source\":\"/opt/algorithm/resources\", \"destination\":\"/opt/algorithm/resources/\"},\n {\"source\":\"/home/algo/.nuget\", \"destination\":\"/home/algo/.nuget/\"}\n ]\n}\n"}, + {"name": "java11", "display_name": "Java OpenJDK 11.0 (Environments)", + "configuration": "{\n \"display_name\": \"Java OpenJDK 11.0 (Environments)\",\n \"artifacts\": [\n {\"source\":\"/opt/algorithm/target/*.jar\", \"destination\":\"/opt/algorithm/target/algorithm.jar\"},\n {\"source\":\"/opt/algorithm/target/lib\", \"destination\":\"/opt/algorithm/target/lib/\"}\n ]\n}\n"}, + {"name": "python2", "display_name": "Python 2.x (Environments)", + "configuration": "{\n \"display_name\": \"Python 2.x (Environments)\",\n \"req_files\": [\n \"requirements.txt\"\n ],\n \"artifacts\": [\n {\"source\":\"/home/algo/.local\", \"destination\":\"/home/algo/.local/\"},\n {\"source\":\"/opt/algorithm\", \"destination\":\"/opt/algorithm/\"}\n ]\n}\n"}, + {"name": "python3", "display_name": "Python 3.x (Environments)", + "configuration": "{\n \"display_name\": \"Python 3.x (Environments)\",\n \"req_files\": [\n \"requirements.txt\"\n ],\n \"artifacts\": [\n {\"source\":\"/home/algo/.local\", \"destination\":\"/home/algo/.local/\"},\n {\"source\":\"/opt/algorithm\", \"destination\":\"/opt/algorithm/\"}\n ]\n}\n"}, + {"name": "r36", "display_name": "R 3.6.x (Environments)", + "configuration": "{\n \"display_name\": \"R 3.6.x (Environments)\",\n \"req_files\": [\n \"packages.txt\"\n ],\n \"artifacts\": [\n {\"source\":\"/opt/algorithm\", \"destination\":\"/opt/algorithm/\"},\n {\"source\":\"/usr/local/lib/R/site-library\", \"destination\":\"/usr/local/lib/R/site-library/\"}\n ]\n}\n\n"}, + {"name": "scala-2", "display_name": "Scala 2.x & sbt 1.3.x (Environments)", + "configuration": "{\n \"display_name\": \"Scala 2.x & sbt 1.3.x (Environments)\",\n \"artifacts\": [\n {\"source\":\"/opt/algorithm/target/universal/stage\", \"destination\":\"/opt/algorithm/stage/\"}\n ]\n}\n\n"}] + + +@self_signed_app.get("/v1/algorithm-environments/edge/languages/{language}/environments") +async def get_environments_by_lang(language): + return { + "environments": [ + { + "id": "717d36e0-222c-44a0-9aa8-06f4ebc1b82a", + "environment_specification_id": "f626effa-e519-431e-9d7a-0d3a7563ae1e", + "display_name": "Python 2.7", + "description": "Generic Python 2.7 installation", + "created_at": "2020-12-21T21:47:53.239", + "language": { + "name": language, + "display_name": "Python 2.x (Environments)", + "configuration": "{\n \"display_name\": \"Python 2.x (Environments)\",\n \"req_files\": [\n " + " \"requirements.txt\"\n ],\n \"artifacts\": [\n {" + "\"source\":\"/home/algo/.local\", \"destination\":\"/home/algo/.local/\"}," + "\n {\"source\":\"/opt/algorithm\", " + "\"destination\":\"/opt/algorithm/\"}\n ]\n}\n " + }, + "machine_type": "CPU" + }, + { + "id": "6f57e041-54e0-4e1a-8b2f-4589bb2c06f8", + "environment_specification_id": "faf81400-eb15-4f64-81c0-3d4ed7181e77", + "display_name": "Python 2.7 + GPU support", + "description": "Python2.7 installation with CUDA 9.0 and CUDNN7", + "created_at": "2020-08-14T07:22:32.955", + "language": { + "name": language, + "display_name": "Python 2.x (Environments)", + "configuration": "{\n \"display_name\": \"Python 2.x (Environments)\",\n \"req_files\": [\n " + " \"requirements.txt\"\n ],\n \"artifacts\": [\n {" + "\"source\":\"/home/algo/.local\", \"destination\":\"/home/algo/.local/\"}," + "\n {\"source\":\"/opt/algorithm\", " + "\"destination\":\"/opt/algorithm/\"}\n ]\n}\n " + }, + "machine_type": "GPU" + } + ] + } diff --git a/Test/conftest.py b/Test/conftest.py index 71ca978..c758814 100644 --- a/Test/conftest.py +++ b/Test/conftest.py @@ -1,12 +1,15 @@ import sys from time import sleep +import os, signal if sys.version_info.major >= 3: - from Test.api import start_webserver + from Test.api import start_webserver_reg, start_webserver_self_signed import pytest @pytest.fixture(scope='package', autouse=True) def fastapi_start(): - p = start_webserver() + p_reg = start_webserver_reg() + p_self_signed = start_webserver_self_signed() sleep(2) - yield p - p.terminate() + yield p_reg, p_self_signed + os.kill(p_reg.pid, signal.SIGKILL) + os.kill(p_self_signed.pid, signal.SIGKILL) \ No newline at end of file diff --git a/Test/CLI_test.py b/Test/regular/CLI_test.py similarity index 97% rename from Test/CLI_test.py rename to Test/regular/CLI_test.py index 99f662e..93a2bb2 100644 --- a/Test/CLI_test.py +++ b/Test/regular/CLI_test.py @@ -93,8 +93,8 @@ def setUp(self): # create a directory to use in testing the cp command self.client = Algorithmia.client() CLI().mkdir("data://.my/moredata", self.client) - if not os.path.exists("./TestFiles/"): - os.mkdir("./TestFiles/") + if not os.path.exists("../TestFiles/"): + os.mkdir("../TestFiles/") def test_ls(self): parentDir = "data://.my/" @@ -132,7 +132,7 @@ def test_rmdir(self): def test_cat(self): file = "data://.my/moredata/test.txt" - localfile = "./TestFiles/test.txt" + localfile = "./../TestFiles/test.txt" fileContents = "some text in test file" CLI().rm(file, self.client) @@ -156,7 +156,7 @@ def test_get_build_logs(self): # local to remote def test_cp_L2R(self): - localfile = "./TestFiles/test.txt" + localfile = "./../TestFiles/test.txt" testfile = open(localfile, "w") testfile.write("some text") testfile.close() @@ -199,7 +199,7 @@ def test_auth(self): def test_auth_cert(self): - localfile = "./TestFiles/fakecert.pem" + localfile = "./../TestFiles/fakecert.pem" testfile = open(localfile, "w") testfile.write("") @@ -244,7 +244,7 @@ def test_list_languages(self): self.assertTrue(result is not None and "anaconda3" in result[1]) def test_rm(self): - localfile = "./TestFiles/testRM.txt" + localfile = "./../TestFiles/testRM.txt" testfile = open(localfile, "w") testfile.write("some text") @@ -263,7 +263,7 @@ def test_rm(self): self.assertTrue("testRM.txt" in result1 and "testRM.txt" not in result2) def test_get_template(self): - filename = "./temptest" + filename = "./../temptest" envid = "36fd467e-fbfe-4ea6-aa66-df3f403b7132" response = CLI().get_template(envid, filename, self.client) print(response) diff --git a/Test/regular/__init__.py b/Test/regular/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Test/acl_test.py b/Test/regular/acl_test.py similarity index 100% rename from Test/acl_test.py rename to Test/regular/acl_test.py diff --git a/Test/algo_failure_test.py b/Test/regular/algo_failure_test.py similarity index 97% rename from Test/algo_failure_test.py rename to Test/regular/algo_failure_test.py index 7defcc2..0804b4a 100644 --- a/Test/algo_failure_test.py +++ b/Test/regular/algo_failure_test.py @@ -11,8 +11,6 @@ # you will load the version installed on the computer. sys.path = ['../'] + sys.path from requests import Response - from Test.api import app - class AlgoTest(unittest.TestCase): error_500 = Response() diff --git a/Test/algo_test.py b/Test/regular/algo_test.py similarity index 100% rename from Test/algo_test.py rename to Test/regular/algo_test.py diff --git a/Test/client_test.py b/Test/regular/client_test.py similarity index 97% rename from Test/client_test.py rename to Test/regular/client_test.py index 3be87ad..8055e51 100644 --- a/Test/client_test.py +++ b/Test/regular/client_test.py @@ -20,7 +20,6 @@ class ClientDummyTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.client = Algorithmia.client(api_address="http://localhost:8080", api_key="simabcd123") - admin_username = "a_Mrtest" admin_org_name = "a_myOrg" environment_name = "Python 3.9" @@ -274,16 +273,6 @@ def test_get_build_logs(self): self.assertTrue(u'error' not in result) - def test_get_build_logs_no_ssl(self): - client = Algorithmia.client(api_address='https://api.algorithmia.com', - api_key=self.regular_api_key, ca_cert=False) - user = unicode(os.environ.get('ALGO_USER_NAME')) - algo = u'Echo' - result = client.algo(user + '/' + algo).build_logs() - if u'error' in result: - print(result) - self.assertTrue("error" not in result) - def test_edit_org(self): org_name = "a_myOrg84" diff --git a/Test/datadirectory_test.py b/Test/regular/datadirectory_test.py similarity index 100% rename from Test/datadirectory_test.py rename to Test/regular/datadirectory_test.py diff --git a/Test/datafile_test.py b/Test/regular/datafile_test.py similarity index 100% rename from Test/datafile_test.py rename to Test/regular/datafile_test.py diff --git a/Test/util_test.py b/Test/regular/util_test.py similarity index 100% rename from Test/util_test.py rename to Test/regular/util_test.py diff --git a/Test/resources/cert.cert b/Test/resources/cert.cert new file mode 100644 index 0000000..aeb562f --- /dev/null +++ b/Test/resources/cert.cert @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUTikiwxFBpLW4pC+5VfOis1xCYKcwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjA1MDMxNzE2MjZaFw0yMjA2 +MDIxNzE2MjZaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDBUMZkg/bCJurQIB9znskTjv8URtIK6qqvZpYGTbfI +AzY6HiI0o1gPxjINZW7cBky/9MeEV5zyJghC4WoK099cIUNq2TmAWAjlRgIE8iEy +9z7QVfbSMainuw0RTlD5/8FRWtRe5v8qwbqLICMn3qv/KsG6bRezyS7UVihwFJua +E4dki+y6KSha4RrCtC43inbPlncB4om7PfJQyt5nI7N4KxbY2L3BUa5/+x1ux/ni +C/3y808vLJVQ6nLYgTEg/6K6lFrig0mUIMnCuOiBsrms3NmBPuDdRri/z1ulFHJB +WVQVQ5DgWher0f/dMzHwyRj3ffC8bAPlhrvLHwPQtNeRAgMBAAGjUzBRMB0GA1Ud +DgQWBBRoC77Hql6kEzk7WC6BeaPBu82K/jAfBgNVHSMEGDAWgBRoC77Hql6kEzk7 +WC6BeaPBu82K/jAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCn +W9acM3+rxsiBBClTYEm2tOiukcXEkI7IzvW/4r7P24SmUiDD3vxVVbZ6nevVkg+P +4/QH+YYE3JUeXaN+xnHYjSy4NKxjd3EHT7BFxLMe0DQaodMj0klHqBtULNzojv8+ +/5tpQsjDLeeeDyOIJNz8r6CU9Gzh7j1EBF8BRdLA1z2UVmt6l6d4o3xOTYpOlZs3 +gI+ASxF9ODQzCCOeMYO2qiuMV3RD0oNdIEHUiMD+iHeC1jFGlxZzaWNeuUzP7Yj/ +MOwbBo8l6Hk2BUuUayLxZFLd0wN28IRkLEU5/SOh3mKz79nfPk6pD9rHUO1a53lI +Ua5xJ5tSwG6bMtNnHYYX +-----END CERTIFICATE----- diff --git a/Test/resources/cert.key b/Test/resources/cert.key new file mode 100644 index 0000000..1746297 --- /dev/null +++ b/Test/resources/cert.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDBUMZkg/bCJurQ +IB9znskTjv8URtIK6qqvZpYGTbfIAzY6HiI0o1gPxjINZW7cBky/9MeEV5zyJghC +4WoK099cIUNq2TmAWAjlRgIE8iEy9z7QVfbSMainuw0RTlD5/8FRWtRe5v8qwbqL +ICMn3qv/KsG6bRezyS7UVihwFJuaE4dki+y6KSha4RrCtC43inbPlncB4om7PfJQ +yt5nI7N4KxbY2L3BUa5/+x1ux/niC/3y808vLJVQ6nLYgTEg/6K6lFrig0mUIMnC +uOiBsrms3NmBPuDdRri/z1ulFHJBWVQVQ5DgWher0f/dMzHwyRj3ffC8bAPlhrvL +HwPQtNeRAgMBAAECggEAPr2OhhTmQ0EKOJ4UVxwTuotQci5CAVUELIUo78bNfNa+ +BMK+60KQVB5JJFvlTPemdS5miqc8wsJhMAOkvPripS4OiWES7nqj+HVuNli3OalQ +86DSyIlhaX6l0RYP5fOBtHu8LUjfS+swNfMqNchpHhmsYmsBpFIJJtUHrsihb7GR +4LpNOZ5go4+LG7FX9KaUE4FvAlS7hi6KLSMua10+3+NAlXggbcVikHr3Uq6eQIvk +z09cs+q2FHaESdTjXSIitmYOfJU5KK3QSfXAr/vaqakjnMvfp8MzQ5dHFsy03HRZ +Sy+LjRKOEOCMCT4DmGIPO4V89i3prbVH4JxixCOaeQKBgQDzuwERWE04JEtvfjxS +OAciQKLIxhfa4t2VB65d3115wxfDPIBYU5Mx5YV4aQyOddNxBwpmX/wYwstx2JDZ +2JM0OjOKLnSvlQfr5UmsY9jUO7CdmgC5HpgbHNhc8uJFw4pd+XypWSjytmVxBSdb +m0+in/iUUQuFNH/+BNLVVgWSiwKBgQDLDBCTEpKQvx2kAc8TEtwrWNhacZILab5D +StQBEL62VfGMdXYaA5dXreo5nqioHfBR3BfAmDecmq3iEFE8/yHJs6pLdcmj0Z1L +034UQedYLCmL9zuAgC6p4SKIMPubnYtMrNJOL3lq0ibogz3rfOhdN2B6S88IYoSL +M6asdoQN0wKBgCd1VPzr4MSAC75nH3joHS+Ma045087Z/6mK7s2/xbBax1QSTWz/ +Sss/L1aJG0FNDgg0bZiZXYTctHcf6oN6Loq8CXALiVSLuhaUrlK8b3QcncFGF2vg +6hspllWl9L/6okIIjAgWqSxyHwYnIXIRONlJMMNCQ60zDK2hNkjXflt1AoGAX0w3 +Tz/NSGBaogozTUlxymp1iOV63R5xLRYmsKVSTTPDHeBXYNhEpOM8ZnS/xb/fdhwt +jbgjib3TVKHB7zXzfr5zc91BmUCdaeRGbW2NDgYULdwIskP3IsZGtdL/lEb6BS+r +uQRxISCnIEPQwQCr8mw2PM/tyIqsmMTSOmmZiv8CgYBAfIC/cNwJyID6AVauZHQo +S3Bii9CPmPnuklBuS7ikX0bmZ93dzv537nqwGr0j9ksxabLWZRcoxx6MhgmXzXVT +dy48TWpqpHiMNorYskB9tcZSrBCl70bu5qKp2owqWHW0d4hqH3lkBNFhfwNWm+qC +54x3T/1fqyaqeapCiE5FGA== +-----END PRIVATE KEY----- diff --git a/Test/self_signed/__init__.py b/Test/self_signed/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Test/self_signed/acl_test.py b/Test/self_signed/acl_test.py new file mode 100644 index 0000000..2022c65 --- /dev/null +++ b/Test/self_signed/acl_test.py @@ -0,0 +1,38 @@ +import sys +# look in ../ BEFORE trying to import Algorithmia. If you append to the +# you will load the version installed on the computer. +sys.path = ['../'] + sys.path + +import unittest +import Algorithmia +from Algorithmia.acl import AclType, Acl, ReadAcl +from Algorithmia.datadirectory import DataDirectory + +class AclTypeTest(unittest.TestCase): + def test_types(self): + self.assertTrue(AclType.private.acl_string is None) + self.assertEquals(AclType.my_algos.acl_string, 'algo://.my/*') + self.assertEquals(AclType.public.acl_string, 'user://*') + self.assertEquals(AclType.default, AclType.my_algos) + + def test_from_acl_response(self): + self.assertEquals(AclType.from_acl_response([]), AclType.private) + self.assertEquals(AclType.from_acl_response(['algo://.my/*']), AclType.my_algos) + self.assertEquals(AclType.from_acl_response(['user://*']), AclType.public) + + def test_create_acl(self): + c = Algorithmia.client() + dd = DataDirectory(c, 'data://.my/privatePermissions') + if dd.exists(): + dd.delete(True) + dd.create(ReadAcl.private) + + dd_perms = DataDirectory(c, 'data://.my/privatePermissions').get_permissions() + self.assertEquals(dd_perms.read_acl, AclType.private) + + dd.update_permissions(ReadAcl.public) + dd_perms = DataDirectory(c, 'data://.my/privatePermissions').get_permissions() + self.assertEquals(dd_perms.read_acl, AclType.public) + +if __name__ == '__main__': + unittest.main() diff --git a/Test/self_signed/algo_failure_test.py b/Test/self_signed/algo_failure_test.py new file mode 100644 index 0000000..aed319d --- /dev/null +++ b/Test/self_signed/algo_failure_test.py @@ -0,0 +1,30 @@ +import sys + +if sys.version_info[0] >= 3: + import unittest + import Algorithmia + import uvicorn + import time + from multiprocessing import Process + + # look in ../ BEFORE trying to import Algorithmia. If you append to the + # you will load the version installed on the computer. + sys.path = ['../'] + sys.path + from requests import Response + + class AlgoTest(unittest.TestCase): + error_500 = Response() + error_500.status_code = 500 + error_message = "Non-Algorithm related Failure: " + str(error_500) + + @classmethod + def setUpClass(cls): + cls.client = Algorithmia.client(api_address="https://localhost:8090", api_key="simabcd123", ca_cert=False) + + def test_throw_500_error_HTTP_response_on_algo_request(self): + try: + result = self.client.algo('util/500').pipe(bytearray('foo', 'utf-8')) + except Exception as e: + result = e + pass + self.assertEqual(str(self.error_message), str(result)) diff --git a/Test/self_signed/algo_test.py b/Test/self_signed/algo_test.py new file mode 100644 index 0000000..a5e0964 --- /dev/null +++ b/Test/self_signed/algo_test.py @@ -0,0 +1,136 @@ +import sys +import os +from Algorithmia.errors import AlgorithmException +from Algorithmia.algorithm import OutputType +import Algorithmia +# look in ../ BEFORE trying to import Algorithmia. If you append to the +# you will load the version installed on the computer. +sys.path = ['../'] + sys.path + +import unittest + +if sys.version_info.major >= 3: + + + class AlgoDummyTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.client = Algorithmia.client(api_address="https://localhost:8090", api_key="simabcd123", ca_cert=False) + + def test_call_customCert(self): + result = self.client.algo('util/echo').pipe(bytearray('foo', 'utf-8')) + self.assertEquals('binary', result.metadata.content_type) + self.assertEquals(bytearray('foo', 'utf-8'), result.result) + + def test_normal_call(self): + result = self.client.algo('util/echo').pipe("foo") + self.assertEquals("text", result.metadata.content_type) + self.assertEquals("foo", result.result) + + def test_async_call(self): + result = self.client.algo('util/echo').set_options(output=OutputType.void).pipe("foo") + self.assertTrue(hasattr(result, "async_protocol")) + self.assertTrue(hasattr(result, "request_id")) + + def test_raw_call(self): + result = self.client.algo('util/echo').set_options(output=OutputType.raw).pipe("foo") + self.assertEquals("foo", result) + + def test_dict_call(self): + result = self.client.algo('util/echo').pipe({"foo": "bar"}) + self.assertEquals("json", result.metadata.content_type) + self.assertEquals({"foo": "bar"}, result.result) + + def test_text_unicode(self): + telephone = u"\u260E" + # Unicode input to pipe() + result1 = self.client.algo('util/Echo').pipe(telephone) + self.assertEquals('text', result1.metadata.content_type) + self.assertEquals(telephone, result1.result) + + # Unicode return in .result + result2 = self.client.algo('util/Echo').pipe(result1.result) + self.assertEquals('text', result2.metadata.content_type) + self.assertEquals(telephone, result2.result) + + def test_get_build_by_id(self): + result = self.client.algo("J_bragg/Echo").get_build("1a392e2c-b09f-4bae-a616-56c0830ac8e5") + self.assertTrue(result.build_id is not None) + + def test_get_build_logs(self): + result = self.client.algo("J_bragg/Echo").get_build_logs("1a392e2c-b09f-4bae-a616-56c0830ac8e5") + self.assertTrue(result.logs is not None) + + def test_get_scm_status(self): + result = self.client.algo("J_bragg/Echo").get_scm_status() + self.assertTrue(result.scm_connection_status is not None) + + def test_exception_ipa_algo(self): + try: + result = self.client.algo('zeryx/raise_exception').pipe("") + except AlgorithmException as e: + self.assertEqual(e.message, "This is an exception") + +else: + class AlgoTest(unittest.TestCase): + def setUp(self): + self.client = Algorithmia.client() + + def test_call_customCert(self): + open("./test.pem", 'w') + c = Algorithmia.client(ca_cert="./test.pem") + result = c.algo('util/Echo').pipe(bytearray('foo', 'utf-8')) + self.assertEquals('binary', result.metadata.content_type) + self.assertEquals(bytearray('foo', 'utf-8'), result.result) + try: + os.remove("./test.pem") + except OSError as e: + print(e) + + def test_call_binary(self): + result = self.client.algo('util/Echo').pipe(bytearray('foo', 'utf-8')) + self.assertEquals('binary', result.metadata.content_type) + self.assertEquals(bytearray('foo', 'utf-8'), result.result) + + def test_async_call(self): + result = self.client.algo('util/echo').set_options(output=OutputType.void).pipe("foo") + self.assertTrue(hasattr(result, "async_protocol")) + self.assertTrue(hasattr(result, "request_id")) + + def test_raw_call(self): + result = self.client.algo('util/echo').set_options(output=OutputType.raw).pipe("foo") + self.assertEquals("foo", result) + + def test_text_unicode(self): + telephone = u"\u260E" + + # Unicode input to pipe() + result1 = self.client.algo('util/Echo').pipe(telephone) + self.assertEquals('text', result1.metadata.content_type) + self.assertEquals(telephone, result1.result) + + # Unicode return in .result + result2 = self.client.algo('util/Echo').pipe(result1.result) + self.assertEquals('text', result2.metadata.content_type) + self.assertEquals(telephone, result2.result) + + def test_get_build_by_id(self): + result = self.client.algo("J_bragg/Echo").get_build("1a392e2c-b09f-4bae-a616-56c0830ac8e5") + self.assertTrue(result.build_id is not None) + + def test_get_build_logs(self): + result = self.client.algo("J_bragg/Echo").get_build_logs("1a392e2c-b09f-4bae-a616-56c0830ac8e5") + self.assertTrue(result.logs is not None) + + def test_get_scm_status(self): + result = self.client.algo("J_bragg/Echo").get_scm_status() + self.assertTrue(result.scm_connection_status is not None) + + def test_exception_ipa_algo(self): + try: + result = self.client.algo('zeryx/raise_exception').pipe("") + except AlgorithmException as e: + self.assertEqual(e.message, "This is an exception") + +if __name__ == '__main__': + unittest.main() diff --git a/Test/self_signed/client_test.py b/Test/self_signed/client_test.py new file mode 100644 index 0000000..fc36e5b --- /dev/null +++ b/Test/self_signed/client_test.py @@ -0,0 +1,413 @@ +import os +import shutil +import sys +from datetime import datetime +from random import random +from random import seed + +sys.path = ['../'] + sys.path + +import unittest +import Algorithmia +from Algorithmia.errors import AlgorithmException +from uuid import uuid4 + +if sys.version_info.major >= 3: + unicode = str + + + class ClientDummyTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.client = Algorithmia.client(api_address="https://localhost:8090", api_key="simabcd123", ca_cert=False) + + admin_username = "a_Mrtest" + admin_org_name = "a_myOrg" + environment_name = "Python 3.9" + + def setUp(self): + self.admin_username = self.admin_username + str(int(random() * 10000)) + self.admin_org_name = self.admin_org_name + str(int(random() * 10000)) + + self.environment_id = "abcd-123" + + def test_create_user(self): + response = self.client.create_user( + {"username": self.admin_username, "email": self.admin_username + "@algo.com", "passwordHash": "", + "shouldCreateHello": False}) + + if type(response) is dict: + self.assertEqual(self.admin_username, response['username']) + else: + self.assertIsNotNone(response) + + def test_get_org_types(self): + response = self.client.get_org_types() + self.assertTrue(len(response) > 0) + + def test_create_org(self): + response = self.client.create_org( + {"org_name": self.admin_org_name, "org_label": "some label", "org_contact_name": "Some owner", + "org_email": self.admin_org_name + "@algo.com", "type_id": "basic"}) + + self.assertEqual(self.admin_org_name, response[u'org_name']) + + def test_get_org(self): + response = self.client.get_org("a_myOrg84") + self.assertEqual("a_myOrg84", response['org_name']) + + def test_get_environment(self): + response = self.client.get_environment("python2") + + if u'error' not in response: + self.assertTrue(response is not None and u'environments' in response) + + def test_get_build_logs(self): + user = unicode(os.environ.get('ALGO_USER_NAME')) + algo = unicode('echo') + algo_path = u'%s/%s' % (user, algo) + result = self.client.algo(algo_path).build_logs() + + if u'error' in result: + print(result) + + self.assertTrue(u'error' not in result) + + def test_edit_org(self): + org_name = "a_myOrg84" + + obj = { + "id": "b85d8c4e-7f3c-40b9-9659-6adc2cb0e16f", + "org_name": "a_myOrg84", + "org_label": "some label", + "org_contact_name": "Some owner", + "org_email": "a_myOrg84@algo.com", + "org_created_at": "2020-11-30T23:51:40", + "org_url": "https://algorithmia.com", + "type_id": "basic", + "resource_type": "organization" + } + + response = self.client.edit_org(org_name, obj) + if type(response) is dict: + print(response) + else: + self.assertEqual(204, response.status_code) + + def test_get_supported_languages(self): + response = self.client.get_supported_languages() + self.assertTrue(response is not None) + + if type(response) is not list: + self.assertTrue(u'error' in response) + else: + language_found = any('anaconda3' in languages['name'] for languages in response) + self.assertTrue(response is not None and language_found) + + def test_invite_to_org(self): + response = self.client.invite_to_org("a_myOrg38", "a_Mrtest4") + if type(response) is dict: + self.assertTrue(u'error' in response) + else: + self.assertEqual(200, response.status_code) + + # This test will require updating after the /v1/organizations/{org_name}/errors endpoint has been + # deployed to the remote environment. + def test_get_organization_errors(self): + response = self.client.get_organization_errors(self.admin_org_name) + self.assertTrue(response is not None) + + if type(response) is list: + self.assertEqual(0, len(response), 'Received unexpected result, should have been 0.') + + def test_get_user_errors(self): + response = self.client.get_user_errors(self.admin_username) + + self.assertTrue(response is not None) + self.assertEqual(0, len(response)) + + + def test_get_algorithm_errors(self): + response = self.client.get_algorithm_errors('hello') + self.assertTrue(response is not None) + + if type(response) is dict: + self.assertTrue(u'error' in response) + else: + self.assertEqual(404, response.status_code) + + def test_algorithm_programmatic_create_process(self): + algorithm_name = "algo_e2d_test" + payload = "John" + expected_response = "hello John" + full_path = "a_Mrtest/" + algorithm_name + details = { + "summary": "Example Summary", + "label": "QA", + "tagline": "Example Tagline" + } + settings = { + "source_visibility": "open", + "algorithm_environment": self.environment_id, + "license": "apl", + "network_access": "isolated", + "pipeline_enabled": False + } + created_algo = self.client.algo(full_path) + response = created_algo.create(details=details, settings=settings) + self.assertEqual(response.name, algorithm_name, "algorithm creation failed") + + # --- Creation complete, compiling + + response = created_algo.compile() + git_hash = response.version_info.git_hash + algo_with_build = self.client.algo(full_path + "/" + git_hash) + self.assertEqual(response.name, created_algo.algoname) + + # --- compiling complete, now testing algorithm request + response = algo_with_build.pipe(payload).result + self.assertEqual(response, expected_response, "compiling failed") + + # --- testing complete, now publishing new release. + + pub_settings = {"algorithm_callability": "private"} + pub_version_info = { + "release_notes": "created programmatically", + "sample_input": payload, + "version_type": "minor" + } + pub_details = {"label": "testing123"} + + response = algo_with_build.publish( + details=pub_details, + settings=pub_settings, + version_info=pub_version_info + ) + self.assertEqual(response["version_info"]["semantic_version"], "0.1.0", + "Publishing failed, semantic version is not correct.") + + # --- publishing complete, getting additional information + + response = created_algo.info(git_hash) + + self.assertEqual(response.version_info.semantic_version, "0.1.0", "information is incorrect") + + def test_no_auth_client(self): + + key = os.environ.get('ALGORITHMIA_API_KEY', "") + if key != "": + del os.environ['ALGORITHMIA_API_KEY'] + + client = Algorithmia.client(api_address="http://localhost:8080") + error = None + try: + client.algo("demo/hello").pipe("world") + except Exception as e: + error = e + finally: + os.environ['ALGORITHMIA_API_KEY'] = key + self.assertEqual(str(error), str(AlgorithmException(message="authorization required", stack_trace=None, error_type=None))) + +else: + class ClientTest(unittest.TestCase): + seed(datetime.now().microsecond) + # due to legacy reasons, regular client tests are tested against api.algorithmia.com, whereas admin api tests + # are run against test.algorithmia.com. + admin_username = "a_Mrtest" + admin_org_name = "a_myOrg" + environment_name = "Python 3.9" + + def setUp(self): + self.admin_api_key = unicode(os.environ.get('ALGORITHMIA_A_KEY')) + self.regular_api_key = unicode(os.environ.get('ALGORITHMIA_API_KEY')) + + self.admin_username = self.admin_username + str(int(random() * 10000)) + self.admin_org_name = self.admin_org_name + str(int(random() * 10000)) + self.admin_client = Algorithmia.client(api_address="https://test.algorithmia.com", + api_key=self.admin_api_key) + self.regular_client = Algorithmia.client(api_address='https://api.algorithmia.com', + api_key=self.regular_api_key) + + environments = self.regular_client.get_environment("python3") + for environment in environments['environments']: + if environment['display_name'] == self.environment_name: + self.environment_id = environment['id'] + + def test_create_user(self): + response = self.admin_client.create_user( + {"username": self.admin_username, "email": self.admin_username + "@algo.com", "passwordHash": "", + "shouldCreateHello": False}) + + if type(response) is dict: + self.assertEqual(self.admin_username, response['username']) + else: + self.assertIsNotNone(response) + + def test_get_org_types(self): + response = self.admin_client.get_org_types() + self.assertTrue(len(response) > 0) + + def test_create_org(self): + response = self.admin_client.create_org( + {"org_name": self.admin_org_name, "org_label": "some label", "org_contact_name": "Some owner", + "org_email": self.admin_org_name + "@algo.com", "type_id": "basic"}) + + self.assertEqual(self.admin_org_name, response[u'org_name']) + + def test_get_org(self): + response = self.admin_client.get_org("a_myOrg84") + self.assertEqual("a_myOrg84", response['org_name']) + + def test_get_environment(self): + response = self.admin_client.get_environment("python2") + + if u'error' not in response: + self.assertTrue(response is not None and u'environments' in response) + + def test_get_build_logs(self): + user = unicode(os.environ.get('ALGO_USER_NAME')) + algo = unicode('echo') + algo_path = u'%s/%s' % (user, algo) + result = self.regular_client.algo(algo_path).build_logs() + + if u'error' in result: + print(result) + + self.assertTrue(u'error' not in result) + + def test_edit_org(self): + org_name = "a_myOrg84" + + obj = { + "id": "b85d8c4e-7f3c-40b9-9659-6adc2cb0e16f", + "org_name": "a_myOrg84", + "org_label": "some label", + "org_contact_name": "Some owner", + "org_email": "a_myOrg84@algo.com", + "org_created_at": "2020-11-30T23:51:40", + "org_url": "https://algorithmia.com", + "type_id": "basic", + "resource_type": "organization" + } + + response = self.admin_client.edit_org(org_name, obj) + if type(response) is dict: + print(response) + else: + self.assertEqual(204, response.status_code) + + def test_get_template(self): + filename = "./temptest" + response = self.admin_client.get_template("36fd467e-fbfe-4ea6-aa66-df3f403b7132", filename) + + if type(response) is dict: + self.assertTrue(u'error' in response or u'message' in response) + else: + self.assertTrue(response.ok) + try: + shutil.rmtree(filename) + except OSError as e: + print(e) + + def test_get_supported_languages(self): + response = self.admin_client.get_supported_languages() + self.assertTrue(response is not None) + + if type(response) is not list: + self.assertTrue(u'error' in response) + else: + language_found = any('anaconda3' in languages['name'] for languages in response) + self.assertTrue(response is not None and language_found) + + def test_invite_to_org(self): + response = self.admin_client.invite_to_org("a_myOrg38", "a_Mrtest4") + if type(response) is dict: + self.assertTrue(u'error' in response) + else: + self.assertEqual(200, response.status_code) + + # This test will require updating after the /v1/organizations/{org_name}/errors endpoint has been + # deployed to the remote environment. + def test_get_organization_errors(self): + response = self.admin_client.get_organization_errors(self.admin_org_name) + self.assertTrue(response is not None) + + if type(response) is list: + self.assertEqual(0, len(response), 'Received unexpected result, should have been 0.') + + def test_get_user_errors(self): + response = self.admin_client.get_user_errors(self.admin_username) + + self.assertTrue(response is not None) + self.assertEqual(0, len(response)) + + def test_get_algorithm_errors(self): + response = self.admin_client.get_algorithm_errors('hello') + self.assertTrue(response is not None) + + if type(response) is dict: + self.assertTrue(u'error' in response) + else: + self.assertEqual(404, response.status_code) + + def test_algorithm_programmatic_create_process(self): + algorithm_name = "algo_" + str(uuid4()).split("-")[-1] + payload = "John" + expected_response = "hello John" + full_path = self.regular_client.username() + "/" + algorithm_name + details = { + "summary": "Example Summary", + "label": "QA", + "tagline": "Example Tagline" + } + settings = { + "source_visibility": "open", + "algorithm_environment": self.environment_id, + "license": "apl", + "network_access": "isolated", + "pipeline_enabled": False + } + created_algo = self.regular_client.algo(full_path) + response = created_algo.create(details=details, settings=settings) + self.assertEqual(response.name, algorithm_name, "algorithm creation failed") + + # --- Creation complete, compiling + + response = created_algo.compile() + git_hash = response.version_info.git_hash + algo_with_build = self.regular_client.algo(full_path + "/" + git_hash) + self.assertEqual(response.name, created_algo.algoname) + + # --- compiling complete, now testing algorithm request + response = algo_with_build.pipe(payload).result + self.assertEqual(response, expected_response, "compiling failed") + + # --- testing complete, now publishing new release. + + pub_settings = {"algorithm_callability": "private"} + pub_version_info = { + "release_notes": "created programmatically", + "sample_input": payload, + "version_type": "minor" + } + pub_details = {"label": "testing123"} + + response = algo_with_build.publish( + details=pub_details, + settings=pub_settings, + version_info=pub_version_info + ) + self.assertEqual(response["version_info"]["semantic_version"], "0.1.0", + "Publishing failed, semantic version is not correct.") + + # --- publishing complete, getting additional information + + response = created_algo.info(git_hash) + + self.assertEqual(response.version_info.semantic_version, "0.1.0", "information is incorrect") + + def test_algo_freeze(self): + self.regular_client.freeze("Test/resources/manifests/example_manifest.json", "Test/resources/manifests") + +if __name__ == '__main__': + unittest.main()