Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 53 additions & 90 deletions ext/opentelemetry-ext-flask/tests/test_flask_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
# limitations under the License.

import unittest
from unittest import mock

from flask import Flask
from werkzeug.test import Client
Expand All @@ -28,14 +27,6 @@ class TestFlaskIntegration(WsgiTestBase):
def setUp(self):
super().setUp()

self.span_attrs = {}

def setspanattr(key, value):
self.assertIsInstance(key, str)
self.span_attrs[key] = value

self.span.set_attribute = setspanattr

self.app = Flask(__name__)

def hello_endpoint(helloid):
Expand All @@ -49,100 +40,72 @@ def hello_endpoint(helloid):
self.client = Client(self.app, BaseResponse)

def test_simple(self):
expected_attrs = {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some of these expected_attrs looks pretty common. could they be pooled into some common constructor or factory?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to use a method with a dict argument for anything that the test needs to override

"component": "http",
"http.method": "GET",
"http.server_name": "localhost",
"http.scheme": "http",
"host.port": 80,
"http.host": "localhost",
"http.target": "/hello/123",
"http.flavor": "1.1",
"http.route": "/hello/<int:helloid>",
"http.status_text": "OK",
"http.status_code": 200,
}
resp = self.client.get("/hello/123")
self.assertEqual(200, resp.status_code)
self.assertEqual([b"Hello: 123"], list(resp.response))

self.start_span.assert_called_with(
"hello_endpoint",
trace_api.INVALID_SPAN_CONTEXT,
kind=trace_api.SpanKind.SERVER,
attributes={
"component": "http",
"http.method": "GET",
"http.server_name": "localhost",
"http.scheme": "http",
"host.port": 80,
"http.host": "localhost",
"http.target": "/hello/123",
"http.flavor": "1.1",
"http.route": "/hello/<int:helloid>",
},
start_time=mock.ANY,
)

# TODO: Change this test to use the SDK, as mocking becomes painful

self.assertEqual(
self.span_attrs,
{"http.status_code": 200, "http.status_text": "OK"},
)
span_list = self.memory_exporter.get_finished_spans()
self.assertEqual(len(span_list), 1)
self.assertEqual(span_list[0].name, "hello_endpoint")
self.assertEqual(span_list[0].kind, trace_api.SpanKind.SERVER)
self.assertEqual(span_list[0].attributes, expected_attrs)

def test_404(self):
expected_attrs = {
"component": "http",
"http.method": "POST",
"http.server_name": "localhost",
"http.scheme": "http",
"host.port": 80,
"http.host": "localhost",
"http.target": "/bye",
"http.flavor": "1.1",
"http.status_text": "NOT FOUND",
"http.status_code": 404,
}
resp = self.client.post("/bye")
self.assertEqual(404, resp.status_code)
resp.close()

self.start_span.assert_called_with(
"/bye",
trace_api.INVALID_SPAN_CONTEXT,
kind=trace_api.SpanKind.SERVER,
attributes={
"component": "http",
"http.method": "POST",
"http.server_name": "localhost",
"http.scheme": "http",
"host.port": 80,
"http.host": "localhost",
"http.target": "/bye",
"http.flavor": "1.1",
},
start_time=mock.ANY,
)

# Nope, this uses Tracer.use_span(end_on_exit)
# self.assertEqual(1, self.span.end.call_count)
# TODO: Change this test to use the SDK, as mocking becomes painful

self.assertEqual(
self.span_attrs,
{"http.status_code": 404, "http.status_text": "NOT FOUND"},
)
span_list = self.memory_exporter.get_finished_spans()
self.assertEqual(len(span_list), 1)
self.assertEqual(span_list[0].name, "/bye")
self.assertEqual(span_list[0].kind, trace_api.SpanKind.SERVER)
self.assertEqual(span_list[0].attributes, expected_attrs)

def test_internal_error(self):
expected_attrs = {
"component": "http",
"http.method": "GET",
"http.server_name": "localhost",
"http.scheme": "http",
"host.port": 80,
"http.host": "localhost",
"http.target": "/hello/500",
"http.flavor": "1.1",
"http.route": "/hello/<int:helloid>",
"http.status_text": "INTERNAL SERVER ERROR",
"http.status_code": 500,
}
resp = self.client.get("/hello/500")
self.assertEqual(500, resp.status_code)
resp.close()

self.start_span.assert_called_with(
"hello_endpoint",
trace_api.INVALID_SPAN_CONTEXT,
kind=trace_api.SpanKind.SERVER,
attributes={
"component": "http",
"http.method": "GET",
"http.server_name": "localhost",
"http.scheme": "http",
"host.port": 80,
"http.host": "localhost",
"http.target": "/hello/500",
"http.flavor": "1.1",
"http.route": "/hello/<int:helloid>",
},
start_time=mock.ANY,
)

# Nope, this uses Tracer.use_span(end_on_exit)
# self.assertEqual(1, self.span.end.call_count)
# TODO: Change this test to use the SDK, as mocking becomes painful

self.assertEqual(
self.span_attrs,
{
"http.status_code": 500,
"http.status_text": "INTERNAL SERVER ERROR",
},
)
span_list = self.memory_exporter.get_finished_spans()
self.assertEqual(len(span_list), 1)
self.assertEqual(span_list[0].name, "hello_endpoint")
self.assertEqual(span_list[0].kind, trace_api.SpanKind.SERVER)
self.assertEqual(span_list[0].attributes, expected_attrs)


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
import io
import unittest
import unittest.mock as mock
import wsgiref.util as wsgiref_util

from opentelemetry import trace as trace_api
from opentelemetry.sdk.trace import TracerSource, export
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
InMemorySpanExporter,
)


class WsgiTestBase(unittest.TestCase):
def setUp(self):
self.span = mock.create_autospec(trace_api.Span, spec_set=True)
tracer = trace_api.Tracer()
self.get_tracer_patcher = mock.patch.object(
trace_api.TracerSource,
"get_tracer",
autospec=True,
spec_set=True,
return_value=tracer,
)
self.get_tracer_patcher.start()

self.start_span_patcher = mock.patch.object(
tracer,
"start_span",
autospec=True,
spec_set=True,
return_value=self.span,
@classmethod
def setUpClass(cls):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to add a tearDownClass that undoes this (probably using importlib.reload). Otherwise, as soon as we have a second unit test that uses the same pattern, we'll get problems. See also the loader tests in the API package.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pointer, I did not know about the reload method, will updated the test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

trace_api.set_preferred_tracer_source_implementation(
lambda T: TracerSource()
)
self.start_span = self.start_span_patcher.start()

def setUp(self):
tracer_source = trace_api.tracer_source()

self.memory_exporter = InMemorySpanExporter()
span_processor = export.SimpleExportSpanProcessor(self.memory_exporter)
tracer_source.add_span_processor(span_processor)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tracer_source would be shared by all test cases so you will be adding new span processor for every test case executed, maybe using a single InMemorySpanExporter and just call clear method during setup?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the test to set a single in-memory span exporter on class setup and clear it each time.


self.write_buffer = io.BytesIO()
self.write = self.write_buffer.write

Expand All @@ -37,10 +33,6 @@ def setUp(self):
self.response_headers = None
self.exc_info = None

def tearDown(self):
self.get_tracer_patcher.stop()
self.start_span_patcher.stop()

def start_response(self, status, response_headers, exc_info=None):
self.status = status
self.response_headers = response_headers
Expand Down
17 changes: 9 additions & 8 deletions ext/opentelemetry-ext-wsgi/tests/test_wsgi_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,8 @@ def validate_response(self, response, error=None):
while True:
try:
value = next(response)
self.assertEqual(0, self.span.end.call_count)
self.assertEqual(value, b"*")
except StopIteration:
self.span.end.assert_called_once_with()
break

self.assertEqual(self.status, "200 OK")
Expand All @@ -95,12 +93,13 @@ def validate_response(self, response, error=None):
else:
self.assertIsNone(self.exc_info)

# Verify that start_span has been called
self.start_span.assert_called_with(
"/",
trace_api.INVALID_SPAN_CONTEXT,
kind=trace_api.SpanKind.SERVER,
attributes={
span_list = self.memory_exporter.get_finished_spans()
self.assertEqual(len(span_list), 1)
self.assertEqual(span_list[0].name, "/")
self.assertEqual(span_list[0].kind, trace_api.SpanKind.SERVER)
self.assertEqual(
span_list[0].attributes,
{
"component": "http",
"http.method": "GET",
"http.server_name": "127.0.0.1",
Expand All @@ -109,6 +108,8 @@ def validate_response(self, response, error=None):
"http.host": "127.0.0.1",
"http.flavor": "1.0",
"http.url": "http://127.0.0.1/",
"http.status_text": "OK",
"http.status_code": 200,
},
)

Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ commands_pre =
ext: pip install {toxinidir}/opentelemetry-api
wsgi,flask: pip install {toxinidir}/ext/opentelemetry-ext-testutil
wsgi,flask: pip install {toxinidir}/ext/opentelemetry-ext-wsgi
wsgi,flask: pip install {toxinidir}/opentelemetry-sdk
flask: pip install {toxinidir}/ext/opentelemetry-ext-flask[test]
pymongo: pip install {toxinidir}/ext/opentelemetry-ext-pymongo
http-requests: pip install {toxinidir}/ext/opentelemetry-ext-http-requests
Expand Down