Skip to content

Commit

Permalink
[core] add testserver (Azure#19153)
Browse files Browse the repository at this point in the history
  • Loading branch information
iscai-msft authored and rakshith91 committed Jul 16, 2021
1 parent 3e3b438 commit aae243e
Show file tree
Hide file tree
Showing 17 changed files with 672 additions and 0 deletions.
1 change: 1 addition & 0 deletions eng/.docsettings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ known_content_issues:
- ['sdk/containerregistry/azure-containerregistry/swagger/README.md', '#4554']
- ['sdk/appconfiguration/azure-appconfiguration/swagger/README.md', '#4554']
- ['sdk/attestation/azure-security-attestation/swagger/README.md', '#4554']
- ['sdk/core/azure-core/tests/testserver_tests/coretestserver/README.rst', '#4554']

# common.
- ['sdk/appconfiguration/azure-appconfiguration/README.md', 'common']
Expand Down
1 change: 1 addition & 0 deletions sdk/core/azure-core/dev_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ opencensus-ext-threading
mock; python_version < '3.3'
-e ../../../tools/azure-sdk-tools
-e ../../../tools/azure-devtools
-e tests/testserver_tests/coretestserver
88 changes: 88 additions & 0 deletions sdk/core/azure-core/tests/testserver_tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# --------------------------------------------------------------------------
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# The MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the ""Software""), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# --------------------------------------------------------------------------
import time
import pytest
import signal
import os
import subprocess
import sys
import random
from six.moves import urllib

def is_port_available(port_num):
req = urllib.request.Request("http://localhost:{}/health".format(port_num))
try:
return urllib.request.urlopen(req).code != 200
except Exception as e:
return True

def get_port():
count = 3
for _ in range(count):
port_num = random.randrange(3000, 5000)
if is_port_available(port_num):
return port_num
raise TypeError("Tried {} times, can't find an open port".format(count))

@pytest.fixture
def port():
return os.environ["FLASK_PORT"]

def start_testserver():
port = get_port()
os.environ["FLASK_APP"] = "coretestserver"
os.environ["FLASK_PORT"] = str(port)
cmd = "flask run -p {}".format(port)
if os.name == 'nt': #On windows, subprocess creation works without being in the shell
child_process = subprocess.Popen(cmd, env=dict(os.environ))
else:
#On linux, have to set shell=True
child_process = subprocess.Popen(cmd, shell=True, preexec_fn=os.setsid, env=dict(os.environ))
count = 5
for _ in range(count):
if not is_port_available(port):
return child_process
time.sleep(1)
raise ValueError("Didn't start!")

def terminate_testserver(process):
if os.name == 'nt':
process.kill()
else:
os.killpg(os.getpgid(process.pid), signal.SIGTERM) # Send the signal to all the process groups

@pytest.fixture(autouse=True, scope="package")
def testserver():
"""Start the Autorest testserver."""
server = start_testserver()
yield
terminate_testserver(server)


# Ignore collection of async tests for Python 2
collect_ignore_glob = []
if sys.version_info < (3, 5):
collect_ignore_glob.append("*_async.py")
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Testserver for Python Azure Core
==============================================

This package contains a testserver to aid in testing of Python's Azure Core package

It has the following component:

coretestserver
--------------

A testing server for Azure Core tests to use

Contributing
============

This project has adopted the
`Microsoft Open Source Code of Conduct <https://opensource.microsoft.com/codeofconduct/>`__.
For more information see the
`Code of Conduct FAQ <https://opensource.microsoft.com/codeofconduct/faq/>`__
or contact
`[email protected] <mailto:[email protected]>`__
with any additional questions or comments.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# coding: utf-8
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for
# license information.
# -------------------------------------------------------------------------

from flask import Flask, Response
from .test_routes import (
basic_api,
encoding_api,
errors_api,
streams_api,
urlencoded_api,
multipart_api,
xml_api
)

app = Flask(__name__)
app.register_blueprint(basic_api, url_prefix="/basic")
app.register_blueprint(encoding_api, url_prefix="/encoding")
app.register_blueprint(errors_api, url_prefix="/errors")
app.register_blueprint(streams_api, url_prefix="/streams")
app.register_blueprint(urlencoded_api, url_prefix="/urlencoded")
app.register_blueprint(multipart_api, url_prefix="/multipart")
app.register_blueprint(xml_api, url_prefix="/xml")

@app.route('/health', methods=['GET'])
def latin_1_charset_utf8():
return Response(status=200)

if __name__ == "__main__":
app.run(debug=True)
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# coding: utf-8
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for
# license information.
# -------------------------------------------------------------------------

from .basic import basic_api
from .encoding import encoding_api
from .errors import errors_api
from .multipart import multipart_api
from .streams import streams_api
from .urlencoded import urlencoded_api
from .xml_route import xml_api

__all__ = [
"basic_api",
"encoding_api",
"errors_api",
"multipart_api",
"streams_api",
"urlencoded_api",
"xml_api",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# coding: utf-8
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for
# license information.
# -------------------------------------------------------------------------

from flask import (
Response,
Blueprint,
request
)

basic_api = Blueprint('basic_api', __name__)

@basic_api.route('/string', methods=['GET'])
def string():
return Response(
"Hello, world!", status=200, mimetype="text/plain"
)

@basic_api.route('/lines', methods=['GET'])
def lines():
return Response(
"Hello,\nworld!", status=200, mimetype="text/plain"
)

@basic_api.route("/bytes", methods=['GET'])
def bytes():
return Response(
"Hello, world!".encode(), status=200, mimetype="text/plain"
)

@basic_api.route("/html", methods=['GET'])
def html():
return Response(
"<html><body>Hello, world!</html></body>", status=200, mimetype="text/html"
)

@basic_api.route("/json", methods=['GET'])
def json():
return Response(
'{"greeting": "hello", "recipient": "world"}', status=200, mimetype="application/json"
)

@basic_api.route("/complicated-json", methods=['POST'])
def complicated_json():
# thanks to Sean Kane for this test!
assert request.json['EmptyByte'] == ''
assert request.json['EmptyUnicode'] == ''
assert request.json['SpacesOnlyByte'] == ' '
assert request.json['SpacesOnlyUnicode'] == ' '
assert request.json['SpacesBeforeByte'] == ' Text'
assert request.json['SpacesBeforeUnicode'] == ' Text'
assert request.json['SpacesAfterByte'] == 'Text '
assert request.json['SpacesAfterUnicode'] == 'Text '
assert request.json['SpacesBeforeAndAfterByte'] == ' Text '
assert request.json['SpacesBeforeAndAfterUnicode'] == ' Text '
assert request.json['啊齄丂狛'] == 'ꀕ'
assert request.json['RowKey'] == 'test2'
assert request.json['啊齄丂狛狜'] == 'hello'
assert request.json["singlequote"] == "a''''b"
assert request.json["doublequote"] == 'a""""b'
assert request.json["None"] == None

return Response(status=200)
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# coding: utf-8
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for
# license information.
# -------------------------------------------------------------------------
from json import dumps
from flask import (
Response,
Blueprint,
)

encoding_api = Blueprint('encoding_api', __name__)

@encoding_api.route('/latin-1', methods=['GET'])
def latin_1():
r = Response(
"Latin 1: ÿ".encode("latin-1"), status=200
)
r.headers["Content-Type"] = "text/plain; charset=latin-1"
return r

@encoding_api.route('/latin-1-with-utf-8', methods=['GET'])
def latin_1_charset_utf8():
r = Response(
"Latin 1: ÿ".encode("latin-1"), status=200
)
r.headers["Content-Type"] = "text/plain; charset=utf-8"
return r

@encoding_api.route('/no-charset', methods=['GET'])
def latin_1_no_charset():
r = Response(
"Hello, world!", status=200
)
r.headers["Content-Type"] = "text/plain"
return r

@encoding_api.route('/iso-8859-1', methods=['GET'])
def iso_8859_1():
r = Response(
"Accented: Österreich".encode("iso-8859-1"), status=200
)
r.headers["Content-Type"] = "text/plain"
return r

@encoding_api.route('/emoji', methods=['GET'])
def emoji():
r = Response(
"👩", status=200
)
return r

@encoding_api.route('/emoji-family-skin-tone-modifier', methods=['GET'])
def emoji_family_skin_tone_modifier():
r = Response(
"👩🏻‍👩🏽‍👧🏾‍👦🏿 SSN: 859-98-0987", status=200
)
return r

@encoding_api.route('/korean', methods=['GET'])
def korean():
r = Response(
"아가", status=200
)
return r

@encoding_api.route('/json', methods=['GET'])
def json():
data = {"greeting": "hello", "recipient": "world"}
content = dumps(data).encode("utf-16")
r = Response(
content, status=200
)
r.headers["Content-Type"] = "application/json; charset=utf-16"
return r

@encoding_api.route('/invalid-codec-name', methods=['GET'])
def invalid_codec_name():
r = Response(
"おはようございます。".encode("utf-8"), status=200
)
r.headers["Content-Type"] = "text/plain; charset=invalid-codec-name"
return r

@encoding_api.route('/no-charset', methods=['GET'])
def no_charset():
r = Response(
"Hello, world!", status=200
)
r.headers["Content-Type"] = "text/plain"
return r
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# coding: utf-8
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for
# license information.
# -------------------------------------------------------------------------
from flask import (
Response,
Blueprint,
)

errors_api = Blueprint('errors_api', __name__)

@errors_api.route('/403', methods=['GET'])
def get_403():
return Response(status=403)

@errors_api.route('/500', methods=['GET'])
def get_500():
return Response(status=500)

@errors_api.route('/stream', methods=['GET'])
def get_stream():
class StreamingBody:
def __iter__(self):
yield b"Hello, "
yield b"world!"
return Response(StreamingBody(), status=500)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for
# license information.
# -------------------------------------------------------------------------

def assert_with_message(param_name, expected_value, actual_value):
assert expected_value == actual_value, "Expected '{}' to be '{}', got '{}'".format(
param_name, expected_value, actual_value
)

__all__ = ["assert_with_message"]
Loading

0 comments on commit aae243e

Please sign in to comment.