diff --git a/Algorithmia/algorithm.py b/Algorithmia/algorithm.py index 2fe5820..85a6f85 100644 --- a/Algorithmia/algorithm.py +++ b/Algorithmia/algorithm.py @@ -69,11 +69,6 @@ def update(self, details={}, settings={}, version_info={}): # Publish an algorithm def publish(self, details={}, settings={}, version_info={}): - # detailsObj = Details(**details) - # settingsObj = SettingsPublish(**settings) - # versionRequestObj = VersionInfoPublish(**version_info) - # publish_parameters = {"details": detailsObj, "settings": settingsObj, "version_info": versionRequestObj} - # version_request = VersionRequest(**publish_parameters) # VersionRequest | Publish Version Request publish_parameters = {"details": details, "settings": settings, "version_info": version_info} url = "/v1/algorithms/"+self.username+"/"+self.algoname + "/versions" print(publish_parameters) diff --git a/Test/CLI_test.py b/Test/CLI_test.py index 35ca486..ae1d546 100644 --- a/Test/CLI_test.py +++ b/Test/CLI_test.py @@ -1,4 +1,5 @@ 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 @@ -10,225 +11,233 @@ from Algorithmia.CLI import CLI import argparse import shutil -import toml - -class CLITest(unittest.TestCase): - 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/") - - def test_ls(self): - parentDir = "data://.my/" - newDir = "test" - - CLI().mkdir(parentDir+newDir, self.client) - result = CLI().ls(parentDir, self.client) - self.assertTrue(result is not None and "moredata" in result and newDir in result) - - CLI().rmdir(parentDir+newDir, self.client) - - - def test_mkdir(self): - - parentDir = "data://.my/" - newDir = "test" - - CLI().mkdir(parentDir+newDir, self.client) - result = CLI().ls(parentDir, self.client) - self.assertTrue(newDir in result) - - CLI().rmdir(parentDir+newDir, self.client) - - def test_rmdir(self): - parentDir = "data://.my/" - newDir = "testRmdir" - - CLI().mkdir(parentDir+newDir, self.client) - result = CLI().ls(parentDir, self.client) - self.assertTrue(newDir in result) - - CLI().rmdir(parentDir+newDir, self.client) - - result = CLI().ls(parentDir, self.client) - self.assertTrue(newDir not in result) - - def test_cat(self): - file = "data://.my/moredata/test.txt" - localfile = "./TestFiles/test.txt" - fileContents = "some text in test file" - - CLI().rm(file, self.client) - testfile = open(localfile, "w") - testfile.write(fileContents) - testfile.close() - - CLI().cp([localfile],file,self.client) - - result = CLI().cat([file],self.client) - self.assertEqual(result, fileContents) - - def test_get_build_logs(self): - user=os.environ.get('ALGO_USER_NAME') - algo="Echo" - - result = json.loads(CLI().getBuildLogs(user,algo,self.client)) - if "error" in result: - print(result) - self.assertTrue("error" not in result) - - -#local to remote - def test_cp_L2R(self): - localfile = "./TestFiles/test.txt" - testfile = open(localfile, "w") - testfile.write("some text") - testfile.close() - - src = [localfile] - dest = "data://.my/moredata/test.txt" - CLI().cp(src,dest,self.client) - - result = CLI().ls("data://.my/moredata/",self.client) - self.assertTrue("test.txt" in result) - -#remote to remote - def test_cp_R2R(self): - - src = ["data://.my/moredata/test.txt"] - dest = "data://.my/moredata/test2.txt" - CLI().cp(src,dest,self.client) - - result = CLI().ls("data://.my/moredata/",self.client) - self.assertTrue("test2.txt" in result) - -#remote to local - def test_cp_R2L(self): - src = ["data://.my/moredata/test.txt"] - dest = "./test.txt" - - CLI().cp(src,dest,self.client) - self.assertTrue(os.path.isfile(dest)) - - def test_run(self): - name = "util/Echo" - inputs = "test" - - parser = argparse.ArgumentParser('CLI for interacting with Algorithmia') - - subparsers = parser.add_subparsers(help = 'sub cmd',dest = 'subparser_name') - parser_run = subparsers.add_parser('run', help = 'algo run [input options] [output options]') - - parser_run.add_argument('algo') - parser_run.add_argument('-d','--data', action = 'store', help = 'detect input type', default = None) - parser_run.add_argument('-t','--text', action = 'store', help = 'treat input as text', default = None) - parser_run.add_argument('-j','--json', action = 'store', help = 'treat input as json data', default = None) - parser_run.add_argument('-b','--binary', action = 'store', help = 'treat input as binary data', default = None) - parser_run.add_argument('-D','--data-file', action = 'store', help = 'specify a path to an input file', default = None) - parser_run.add_argument('-T','--text-file', action = 'store', help = 'specify a path to a text file', default = None) - parser_run.add_argument('-J','--json-file', action = 'store', help = 'specify a path to a json file', default = None) - parser_run.add_argument('-B','--binary-file', action = 'store', help = 'specify a path to a binary file', default = None) - parser_run.add_argument('--timeout', action = 'store',type = int, default = 300, help = 'specify a timeout (seconds)') - parser_run.add_argument('--debug', action = 'store_true', help = 'print the stdout from the algo ') - parser_run.add_argument('--profile', action = 'store', type = str, default = 'default') - parser_run.add_argument('-o', '--output', action = 'store', default = None, type = str) - - args = parser.parse_args(['run',name,'-d',inputs]) - - result = CLI().runalgo(args, self.client) - self.assertEqual(result, inputs) - - def test_auth(self): - #key for test account - key = os.getenv('ALGORITHMIA_API_KEY') - address = 'https://api.algorithmia.com' - profile = 'default' - CLI().auth(key,address,profile=profile) - resultK = CLI().getAPIkey(profile) - resultA = CLI().getAPIaddress(profile) - self.assertEqual(resultK, key) - self.assertEqual(resultA, address) - - def test_auth_cert(self): - - localfile = "./TestFiles/fakecert.pem" - - testfile = open(localfile, "w") - testfile.write("") - testfile.close() - - #key for test account - key = os.getenv('ALGORITHMIA_API_KEY') - address = 'https://api.algorithmia.com' - cacert = localfile - profile = 'test' - - CLI().auth(key,address,cacert,profile) - resultK = CLI().getAPIkey(profile) - resultA = CLI().getAPIaddress(profile) - resultC = CLI().getCert(profile) - self.assertEqual(resultK, key) - self.assertEqual(resultA, address) - self.assertEqual(resultC, cacert) - - def test_get_environment(self): - result = CLI().get_environment_by_language("python2",self.client) - print(result) - if("error" in result): - print(result) - self.assertTrue(result is not None and "display_name" in result) - - def test_list_languages(self): - result = CLI().list_languages(self.client) - if("error" in result[0]): - print(result) - self.assertTrue(result is not None and "anaconda3" in result[1]) - - - def test_rm(self): - localfile = "./TestFiles/testRM.txt" - - testfile = open(localfile, "w") - testfile.write("some text") - testfile.close() - - src = [localfile] - dest = "data://.my/moredata/" - CLI().cp(src,dest,self.client) - - result1 = CLI().ls(dest,self.client) - - CLI().rm("data://.my/moredata/testRM.txt",self.client) - - result2 = CLI().ls(dest,self.client) - - self.assertTrue("testRM.txt" in result1 and "testRM.txt" not in result2) - - def test_get_template(self): - filename = "./temptest" - envid = "36fd467e-fbfe-4ea6-aa66-df3f403b7132" - response = CLI().get_template(envid,filename,self.client) - print(response) - self.assertTrue(response.ok) - try: - shutil.rmtree(filename) - except OSError as e: - print(e) - - def test_api_address_auth(self): - api_key = os.getenv('ALGORITHMIA_TEST_API_KEY') - api_address = "https://api.test.algorithmia.com" - CLI().auth(api_key, api_address) - profile = "default" - - client = Algorithmia.client(CLI().getAPIkey(profile), CLI().getAPIaddress(profile), CLI().getCert(profile)) - result2 = CLI().ls("data://.my", client) - print(result2) - self.assertTrue(result2 != "") - + +if sys.version_info.major >= 3: + class CLIDummyTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.client = Algorithmia.client(api_address="http://localhost:8080", api_key="simabcd123") + + def test_run(self): + name = "util/Echo" + inputs = "test" + + parser = argparse.ArgumentParser('CLI for interacting with Algorithmia') + + subparsers = parser.add_subparsers(help='sub cmd', dest='subparser_name') + parser_run = subparsers.add_parser('run', help='algo run [input options] [output options]') + + parser_run.add_argument('algo') + parser_run.add_argument('-d', '--data', action='store', help='detect input type', default=None) + parser_run.add_argument('-t', '--text', action='store', help='treat input as text', default=None) + parser_run.add_argument('-j', '--json', action='store', help='treat input as json data', default=None) + parser_run.add_argument('-b', '--binary', action='store', help='treat input as binary data', default=None) + parser_run.add_argument('-D', '--data-file', action='store', help='specify a path to an input file', + default=None) + parser_run.add_argument('-T', '--text-file', action='store', help='specify a path to a text file', + default=None) + parser_run.add_argument('-J', '--json-file', action='store', help='specify a path to a json file', + default=None) + parser_run.add_argument('-B', '--binary-file', action='store', help='specify a path to a binary file', + default=None) + parser_run.add_argument('--timeout', action='store', type=int, default=300, + help='specify a timeout (seconds)') + parser_run.add_argument('--debug', action='store_true', + help='print the stdout from the algo ') + parser_run.add_argument('--profile', action='store', type=str, default='default') + parser_run.add_argument('-o', '--output', action='store', default=None, type=str) + + args = parser.parse_args(['run', name, '-d', inputs]) + + result = CLI().runalgo(args, self.client) + self.assertEqual(result, inputs) + + +class CLIMainTest(unittest.TestCase): + 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/") + + def test_ls(self): + parentDir = "data://.my/" + newDir = "test" + + CLI().mkdir(parentDir + newDir, self.client) + result = CLI().ls(parentDir, self.client) + self.assertTrue(result is not None and "moredata" in result and newDir in result) + + CLI().rmdir(parentDir + newDir, self.client) + + def test_mkdir(self): + + parentDir = "data://.my/" + newDir = "test" + + CLI().mkdir(parentDir + newDir, self.client) + result = CLI().ls(parentDir, self.client) + self.assertTrue(newDir in result) + + CLI().rmdir(parentDir + newDir, self.client) + + def test_rmdir(self): + parentDir = "data://.my/" + newDir = "testRmdir" + + CLI().mkdir(parentDir + newDir, self.client) + result = CLI().ls(parentDir, self.client) + self.assertTrue(newDir in result) + + CLI().rmdir(parentDir + newDir, self.client) + + result = CLI().ls(parentDir, self.client) + self.assertTrue(newDir not in result) + + def test_cat(self): + file = "data://.my/moredata/test.txt" + localfile = "./TestFiles/test.txt" + fileContents = "some text in test file" + + CLI().rm(file, self.client) + testfile = open(localfile, "w") + testfile.write(fileContents) + testfile.close() + + CLI().cp([localfile], file, self.client) + + result = CLI().cat([file], self.client) + self.assertEqual(result, fileContents) + + def test_get_build_logs(self): + user = os.environ.get('ALGO_USER_NAME') + algo = "Echo" + + result = json.loads(CLI().getBuildLogs(user, algo, self.client)) + if "error" in result: + print(result) + self.assertTrue("error" not in result) + + # local to remote + def test_cp_L2R(self): + localfile = "./TestFiles/test.txt" + testfile = open(localfile, "w") + testfile.write("some text") + testfile.close() + + src = [localfile] + dest = "data://.my/moredata/test.txt" + CLI().cp(src, dest, self.client) + + result = CLI().ls("data://.my/moredata/", self.client) + self.assertTrue("test.txt" in result) + + # remote to remote + def test_cp_R2R(self): + + src = ["data://.my/moredata/test.txt"] + dest = "data://.my/moredata/test2.txt" + CLI().cp(src, dest, self.client) + + result = CLI().ls("data://.my/moredata/", self.client) + self.assertTrue("test2.txt" in result) + + # remote to local + def test_cp_R2L(self): + src = ["data://.my/moredata/test.txt"] + dest = "./test.txt" + + CLI().cp(src, dest, self.client) + self.assertTrue(os.path.isfile(dest)) + + def test_auth(self): + # key for test account + key = os.getenv('ALGORITHMIA_API_KEY') + address = 'https://api.algorithmia.com' + profile = 'default' + CLI().auth(key, address, profile=profile) + resultK = CLI().getAPIkey(profile) + resultA = CLI().getAPIaddress(profile) + self.assertEqual(resultK, key) + self.assertEqual(resultA, address) + + def test_auth_cert(self): + + localfile = "./TestFiles/fakecert.pem" + + testfile = open(localfile, "w") + testfile.write("") + testfile.close() + + # key for test account + key = os.getenv('ALGORITHMIA_API_KEY') + address = 'https://api.algorithmia.com' + cacert = localfile + profile = 'test' + + CLI().auth(key, address, cacert, profile) + resultK = CLI().getAPIkey(profile) + resultA = CLI().getAPIaddress(profile) + resultC = CLI().getCert(profile) + self.assertEqual(resultK, key) + self.assertEqual(resultA, address) + self.assertEqual(resultC, cacert) + + def test_get_environment(self): + result = CLI().get_environment_by_language("python2", self.client) + print(result) + if ("error" in result): + print(result) + self.assertTrue(result is not None and "display_name" in result) + + def test_list_languages(self): + result = CLI().list_languages(self.client) + if ("error" in result[0]): + print(result) + self.assertTrue(result is not None and "anaconda3" in result[1]) + + def test_rm(self): + localfile = "./TestFiles/testRM.txt" + + testfile = open(localfile, "w") + testfile.write("some text") + testfile.close() + + src = [localfile] + dest = "data://.my/moredata/" + CLI().cp(src, dest, self.client) + + result1 = CLI().ls(dest, self.client) + + CLI().rm("data://.my/moredata/testRM.txt", self.client) + + result2 = CLI().ls(dest, self.client) + + self.assertTrue("testRM.txt" in result1 and "testRM.txt" not in result2) + + def test_get_template(self): + filename = "./temptest" + envid = "36fd467e-fbfe-4ea6-aa66-df3f403b7132" + response = CLI().get_template(envid, filename, self.client) + print(response) + self.assertTrue(response.ok) + try: + shutil.rmtree(filename) + except OSError as e: + print(e) + + def test_api_address_auth(self): + api_key = os.getenv('ALGORITHMIA_TEST_API_KEY') + api_address = "https://api.test.algorithmia.com" + CLI().auth(api_key, api_address) + profile = "default" + + client = Algorithmia.client(CLI().getAPIkey(profile), CLI().getAPIaddress(profile), CLI().getCert(profile)) + result2 = CLI().ls("data://.my", client) + print(result2) + self.assertTrue(result2 != "") if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/Test/algo_failure_test.py b/Test/algo_failure_test.py index 236857a..1e65234 100644 --- a/Test/algo_failure_test.py +++ b/Test/algo_failure_test.py @@ -6,15 +6,18 @@ 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 from Test.api import app + def start_webserver(): uvicorn.run(app, host="127.0.0.1", port=8080, log_level="debug") + class AlgoTest(unittest.TestCase): error_500 = Response() error_500.status_code = 500 @@ -24,11 +27,13 @@ def setUp(self): self.uvi_p = Process(target=start_webserver) self.uvi_p.start() time.sleep(1) + def tearDown(self): self.uvi_p.terminate() + def test_throw_500_error_HTTP_response_on_algo_request(self): try: - result = self.client.algo('util/Echo').pipe(bytearray('foo','utf-8')) + result = self.client.algo('util/500').pipe(bytearray('foo', 'utf-8')) except Exception as e: result = e pass diff --git a/Test/algo_test.py b/Test/algo_test.py index b14e99b..b22988e 100644 --- a/Test/algo_test.py +++ b/Test/algo_test.py @@ -1,77 +1,117 @@ import sys import os from Algorithmia.errors import AlgorithmException +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 -import Algorithmia +if sys.version_info.major >= 3: + + + class AlgoDummyTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.client = Algorithmia.client(api_address="http://localhost:8080", api_key="simabcd123") + + 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_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_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) -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_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") - - # def test_json_unicode(self): - # telephone = [u"\u260E"] - # - # #Unicode input to pipe() - # result1 = self.client.algo('util/Echo').pipe(telephone) - # self.assertEquals('json', result1.metadata.content_type) - # self.assertEquals(telephone, result1.result) - # - # #Unicode return in .result - # result2 = self.client.algo('util/Echo').pipe(result1.result) - # self.assertEquals('json', result2.metadata.content_type) - # self.assertEquals(telephone, result2.result) + 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/api/__init__.py b/Test/api/__init__.py index 5ca3185..99e7e68 100644 --- a/Test/api/__init__.py +++ b/Test/api/__init__.py @@ -1,16 +1,341 @@ import importlib -from fastapi import FastAPI, Response +from fastapi import FastAPI, Request +from fastapi.responses import Response +import json +import base64 +from multiprocessing import Process +import uvicorn app = FastAPI() -@app.post("/v1/{username}/{algoname}/{version}") -async def throw_error(username, algoname, version): - return Response("Internal Server Error", status_code=500) +def start_webserver(): + def _start_webserver(): + uvicorn.run(app, host="127.0.0.1", port=8080, log_level="debug") -def create_endpoint(algoname): - module = importlib.import_module(algoname) - @app.get("/invocations") - def invocations(data): - return module.apply(data) + p = Process(target=_start_webserver) + p.start() + return p + +@app.post("/v1/algo/{username}/{algoname}") +async def process_algo_req(request: Request, username, algoname): + metadata = {"request_id": "req-55c0480d-6af3-4a21-990a-5c51d29f5725", "duration": 0.000306774} + content_type = request.headers['Content-Type'] + request = await request.body() + if 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/client_test.py b/Test/client_test.py index e7d7e8f..a254c45 100644 --- a/Test/client_test.py +++ b/Test/client_test.py @@ -11,215 +11,391 @@ import Algorithmia from uuid import uuid4 -if sys.version_info.major == 3: +if sys.version_info.major >= 3: unicode = str -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_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" - - 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") + 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" + + 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") +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_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" + + 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") if __name__ == '__main__': diff --git a/Test/conftest.py b/Test/conftest.py new file mode 100644 index 0000000..71ca978 --- /dev/null +++ b/Test/conftest.py @@ -0,0 +1,12 @@ +import sys +from time import sleep +if sys.version_info.major >= 3: + from Test.api import start_webserver + import pytest + + @pytest.fixture(scope='package', autouse=True) + def fastapi_start(): + p = start_webserver() + sleep(2) + yield p + p.terminate()