specmatic 0.4.1__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of specmatic might be problematic. Click here for more details.

@@ -1,15 +1,16 @@
1
- from flask import Flask
1
+ import os
2
+
2
3
  from specmatic.server.wsgi_server import WSGIServer
3
4
  from specmatic.core.specmatic import Specmatic
4
5
 
5
6
 
6
- def specmatic_stub(project_root: str, host: str, port: int, expectation_json_files=None, contract_file='', specmatic_json_file: str = ''):
7
+ def specmatic_stub(project_root: str, host: str = '127.0.0.1', port: int = 0, expectations=None, contract_file='',
8
+ specmatic_json_file: str = ''):
7
9
  def decorator(cls):
8
10
  try:
9
- stub = Specmatic.create_stub(project_root, host, port, specmatic_json_file, contract_file)
10
- stub.start()
11
+ stub = Specmatic.start_stub(project_root, host, port, specmatic_json_file, contract_file)
11
12
  cls.stub = stub
12
- stub.set_expectations(expectation_json_files)
13
+ stub.set_expectations(expectations)
13
14
  except Exception as e:
14
15
  if hasattr(cls, 'stub'):
15
16
  cls.stub.stop()
@@ -22,10 +23,19 @@ def specmatic_stub(project_root: str, host: str, port: int, expectation_json_fil
22
23
  return decorator
23
24
 
24
25
 
25
- def specmatic_contract_test(project_root: str, host: str, port: int, contract_file='', specmatic_json_file: str = ''):
26
+ def specmatic_contract_test(project_root: str, host: str = '127.0.0,1', port: int = 0, contract_file='',
27
+ specmatic_json_file: str = ''):
26
28
  def decorator(cls):
27
29
  try:
28
- Specmatic.run_tests(project_root, cls, host, port, specmatic_json_file, contract_file)
30
+ test_host = host
31
+ test_port = port
32
+ if test_port == 0:
33
+ if hasattr(cls, 'app'):
34
+ app = cls.app
35
+ test_host = app.host
36
+ test_port = app.port
37
+
38
+ Specmatic.test(project_root, cls, test_host, test_port, specmatic_json_file, contract_file)
29
39
  return cls
30
40
  except Exception as e:
31
41
  if hasattr(cls, 'stub'):
@@ -43,7 +53,7 @@ def specmatic_contract_test(project_root: str, host: str, port: int, contract_fi
43
53
  return decorator
44
54
 
45
55
 
46
- def start_app(app, host: str, port: int):
56
+ def start_app(app, host: str = '127.0.0.1', port: int = 0):
47
57
  def decorator(cls):
48
58
  try:
49
59
  wsgi_app = WSGIServer(app, host, port)
@@ -8,18 +8,20 @@ from specmatic.utils import get_junit_report_file_path
8
8
  class Specmatic:
9
9
 
10
10
  @classmethod
11
- def create_stub(cls, project_root: str, host: str, port: int, specmatic_json_file_path: str = '',
12
- contract_file_path: str = ''):
13
- return SpecmaticStub(project_root, host, port, specmatic_json_file_path, contract_file_path)
11
+ def start_stub(cls, project_root: str, host: str = '127.0.0.1', port: int = 0, specmatic_json_file_path: str = '',
12
+ contract_file_path: str = ''):
13
+ stub = None
14
+ try:
15
+ stub = SpecmaticStub(project_root, host, port, specmatic_json_file_path, contract_file_path)
16
+ return stub
17
+ except Exception as e:
18
+ stub.stop()
19
+ print(f"Error: {e}")
20
+ raise e
14
21
 
15
22
  @classmethod
16
- def run_tests(cls, project_root: str, test_class, host: str, port: int, specmatic_json_file_path: str = '',
17
- contract_file_path: str = ''):
23
+ def test(cls, project_root: str, test_class, host: str, port: int, specmatic_json_file_path: str = '',
24
+ contract_file_path: str = ''):
18
25
  SpecmaticTest(project_root, host, port, contract_file_path, specmatic_json_file_path).run()
19
26
  PyTestGenerator(test_class, get_junit_report_file_path()).generate()
20
27
 
21
- @classmethod
22
- def run_unit_tests(cls, project_root: str, test_class, host: str, port: int, specmatic_json_file_path: str = '',
23
- contract_file_path: str = ''):
24
- SpecmaticTest(project_root, host, port, contract_file_path, specmatic_json_file_path).run()
25
- UnitTestGenerator(test_class, get_junit_report_file_path()).generate()
@@ -1,16 +1,19 @@
1
1
  import json
2
2
  import os
3
+ import re
3
4
  import subprocess
4
5
  import threading
5
6
  import traceback
6
7
  from queue import Queue
7
8
 
8
9
  import requests
10
+ from urllib.parse import urlparse
9
11
 
10
12
 
11
13
  class SpecmaticStub:
12
14
 
13
- def __init__(self, project_root:str, host: str, port: int, specmatic_json_file_path: str, contract_file_path: str):
15
+ def __init__(self, project_root: str, host: str = '127.0.0.1', port: int = 0, specmatic_json_file_path: str = '',
16
+ contract_file_path: str = ''):
14
17
  self.__stub_started_event = None
15
18
  self.__process = None
16
19
  self.project_root = project_root
@@ -18,11 +21,11 @@ class SpecmaticStub:
18
21
  self.port = port
19
22
  self.specmatic_json_file_path = specmatic_json_file_path
20
23
  self.contract_file_path = contract_file_path
21
- self.__expectation_api = f'http://{self.host}:{self.port}/_specmatic/expectations'
22
- self.__stub_running_success_message = f'Stub server is running on http://{self.host}:{self.port}'
24
+ self.__stub_running_success_message = 'Stub server is running on '
23
25
  self.__error_queue = Queue()
26
+ self.__start()
24
27
 
25
- def start(self):
28
+ def __start(self):
26
29
  try:
27
30
  self.__stub_started_event = threading.Event()
28
31
  self.__start_specmatic_stub_in_subprocess()
@@ -47,7 +50,7 @@ class SpecmaticStub:
47
50
  headers = {
48
51
  "Content-Type": "application/json"
49
52
  }
50
- response = requests.post(self.__expectation_api, json=json_string, headers=headers)
53
+ response = requests.post(self.__get_expectations_api_url(), json=json_string, headers=headers)
51
54
  if response.status_code != 200:
52
55
  self.stop()
53
56
  raise Exception(f"{response.content} received for expectation json file: {json_string}")
@@ -57,9 +60,13 @@ class SpecmaticStub:
57
60
  print(f"Error: {e}")
58
61
  raise e
59
62
 
63
+ def __get_expectations_api_url(self):
64
+ return f'http://{self.host}:{self.port}/_specmatic/expectations'
65
+
60
66
  def __start_specmatic_stub_in_subprocess(self):
61
67
  stub_command = self.__create_stub_process_command()
62
- print(f"\n Starting specmatic stub server on {self.host}:{self.port}")
68
+ if self.host != '' and self.port != 0:
69
+ print(f"\n Starting specmatic stub server on {self.host}:{self.port}")
63
70
  self.__process = subprocess.Popen(stub_command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
64
71
 
65
72
  def __start_reading_stub_output(self):
@@ -75,6 +82,8 @@ class SpecmaticStub:
75
82
  def __read_process_output(self):
76
83
  def signal_event_if_stub_has_started(line):
77
84
  if self.__stub_running_success_message in line:
85
+ if self.port == 0:
86
+ self.port = line.split(self.host + ':')[1].split('.')[0]
78
87
  self.__stub_started_event.set()
79
88
 
80
89
  def read_and_print_output_line_by_line():
@@ -108,6 +117,9 @@ class SpecmaticStub:
108
117
  cmd.append("--config=" + self.project_root + "/specmatic.json")
109
118
  cmd += [
110
119
  '--host=' + self.host,
111
- "--port=" + str(self.port)
112
120
  ]
121
+ if self.port != 0:
122
+ cmd += [
123
+ "--port=" + str(self.port)
124
+ ]
113
125
  return cmd
@@ -1,3 +1,4 @@
1
+ import socket
1
2
  import threading
2
3
 
3
4
  from specmatic.server.wsgi_server_thread import WSGIServerThread
@@ -6,10 +7,17 @@ from specmatic.server.wsgi_server_thread import WSGIServerThread
6
7
  class WSGIServer:
7
8
  server: WSGIServerThread = None
8
9
 
9
- def __init__(self, app, host: str, port: int):
10
+ def __init__(self, app, host: str = '127.0.0.1', port: int = 0):
10
11
  self.app = app
11
12
  self.host = host
12
- self.port = port
13
+ self.port = self.__find_available_port() if port == 0 else port
14
+
15
+ def __find_available_port(self):
16
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
17
+ sock.bind(('localhost', 0))
18
+ port = sock.getsockname()[1]
19
+ sock.close()
20
+ return port
13
21
 
14
22
  def start(self):
15
23
  self.server = WSGIServerThread(self.app, self.host, self.port)
specmatic/version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = '0.4.1'
1
+ __version__ = '0.5.0'
2
2
  __specmatic_version__ = '0.67.0'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: specmatic
3
- Version: 0.4.1
3
+ Version: 0.5.0
4
4
  Summary: A Python module for using the Specmatic Library.
5
5
  Home-page: https://github.com/znsio/specmatic-python-extensions
6
6
  Author: Specmatic Builders
@@ -74,15 +74,15 @@ Specmatic is a contract driven development tool that allows us to turn OpenAPI c
74
74
  pass
75
75
 
76
76
 
77
- stub = Specmatic.create_stub(PROJECT_ROOT, stub_host, stub_port)
78
- stub.start()
77
+ stub = Specmatic.start_stub(PROJECT_ROOT, stub_host, stub_port)
79
78
  stub.set_expectations([expectation_json_file])
80
79
 
81
- app = WSGIServer(app, app_host, app_port)
82
- app.start()
83
- Specmatic.run_tests(PROJECT_ROOT, TestContract, app_host, app_port)
80
+ app_server = WSGIServer(app, app_host, app_port)
81
+ app_server.start()
84
82
 
85
- app.stop()
83
+ Specmatic.test(PROJECT_ROOT, TestContract, app_host, app_port)
84
+
85
+ app_server.stop()
86
86
  stub.stop()
87
87
 
88
88
  if __name__ == '__main__':
@@ -91,14 +91,23 @@ Specmatic is a contract driven development tool that allows us to turn OpenAPI c
91
91
 
92
92
  The above code can be broken down into three parts
93
93
  - **Stub setup:**
94
- Use the ``````Specmatic.create_stub()`````` method to create a stub instance.
95
- You can set expectations on the stub by calling the ``````set_expectations()`````` method and passing a list of expectation json files.
94
+ Use the ``````Specmatic.start_stub()`````` method to create an start a stub instance.
95
+ You can explicitly supply the host/port to run.
96
+ If no host/port is supplied, the stub will be started on http://127.0.0.1:9000
97
+ You can set expectations on the stub by calling the ``````set_expectations()`````` method and passing a list of expectation json files.
98
+ The stub has attributes: ``````stub.host`````` and ``````stub.port`````` to tell us where it's running on.
99
+
96
100
  - **App Setup:**
97
- Create an instance of the ``````WSGIServer`````` class by passing it an instance of a WSGIApplication like a flask app and the host and port to run on.
98
- - **Run Tests**
99
- Call ``````Specmatic.run_tests()`````` by passing it your root directory, an empty Test class and the host/port on which your app is running.
100
-
101
-
101
+ Create an instance of the ``````WSGIServer`````` class by passing it an instance of a WSGIApplication like a flask app and the host and port to run on.
102
+ If no host/port is supplied, then the app server will be started on a random freely available port.
103
+ The app_server also has attributes: ``````app_server.host`````` and ``````app_server.port`````` to tell us where it's running on.
104
+
105
+ - **Run Tests:**
106
+ Call ``````Specmatic.test()`````` by passing it your root directory, an empty Test class and the host/port on which your app server is running.
107
+ - Here are some examples to demonstrate how the library can be used in different ways for WSGI apps:
108
+ ``````test\test_contract.py`````` : Running specmatic test, stub and a flask app using decorators.
109
+ ``````test\test_contract_using_api.py`````` : Running specmatic test, stub and a flask app using the api by explicitly defining all the hosts and ports.
110
+ ``````test\test_contract_using_api_implicit.py`````` : Running specmatic test, stub and a flask app using the api without setting explicit ports anywhere .
102
111
 
103
112
 
104
113
 
@@ -1,30 +1,31 @@
1
1
  specmatic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  specmatic/utils.py,sha256=Ns3Ai5jEoP6IaZ4pWRVQUvMtbkyN7p4jj65-qptcNEQ,318
3
- specmatic/version.py,sha256=b9uSV_2NNzvvC5hUWYFGr363AsKnHmS7uKjdPXPjanI,55
3
+ specmatic/version.py,sha256=UVZ_s20NIPLFY4r3RADtcCjzw968IBNTlp_h4sH68UE,55
4
4
  specmatic/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- specmatic/core/decorators.py,sha256=nwXviT7dgd4VIfD_Np1eBK4LsNwgDg-LFuaYOfx0qRA,1908
5
+ specmatic/core/decorators.py,sha256=G4uD-7T171KNlV9vy1Hc29YSPlJpxat2BD4INF_WfgY,2201
6
6
  specmatic/core/specmatic.jar,sha256=S0FO6mbbx-7LNGI2dUiJQMEvU5YpxQptMCExW5DtiBo,87153377
7
- specmatic/core/specmatic.py,sha256=Q-K1DpFyGnHDBEk3yVl3ebLzRzPd4bkJ2eeimYQWyJY,1318
8
- specmatic/core/specmatic_stub.py,sha256=40sLpdOkVs1HrHj0rGBJZXdUD-lr4gw1_J531kof_W8,4184
7
+ specmatic/core/specmatic.py,sha256=gN2FAWugNTJVG8sNd7YLbKWqfDVaOazuInw1dsLFX5k,1126
8
+ specmatic/core/specmatic_stub.py,sha256=tYRTsbj8q8qWDDMml0KrluUTcMDIDNgHbg5Qsryaee0,4533
9
9
  specmatic/core/specmatic_test.py,sha256=v_8MHdL1UZ6OgfosrK-BRtTXOp470GkVyS8A6rAju4g,1634
10
10
  specmatic/generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  specmatic/generators/pytest_generator.py,sha256=x5p_k9wD0dNhV5kvIAM3_rvrQIE2ypCuJFTkyP3Q78Q,735
12
12
  specmatic/generators/test_generator_base.py,sha256=2YiiKpsiYIeAO2l9mpivmUXf68IKveqK53BJrLk_6Wk,662
13
13
  specmatic/generators/unittest_generator.py,sha256=Vkm8BP-_rZ-tFbLwUTNc90OgQCMJ4OHd8w5mShhcFyU,713
14
14
  specmatic/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- specmatic/server/wsgi_server.py,sha256=5Tursi4Qd_PjfGZqcKRSJpCc1hG0Qav76fPbpCrIXYQ,474
15
+ specmatic/server/wsgi_server.py,sha256=hebxDxEip9YSdLLwIdBnW6WZiW1vIndis5_csvfIcRI,770
16
16
  specmatic/server/wsgi_server_thread.py,sha256=DcuhN8VoGpG6Xxvie2vLYQlUXfL1nCh43q4pGg-zKpw,799
17
17
  test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- test/test_contract.py,sha256=7AIk7tzuttjcBDTeqokjCvtxJ68M_HmdP4t2pGrccbo,608
19
- test/test_contract_using_api.py,sha256=pxNMKIfFi7BGM0mM3giCNPOfrcShaz5o0G3fYrrtd9w,718
18
+ test/test_contract.py,sha256=Eog9EAeER4deCHvlekip21sKDUzAcZ3qLdU_vpPFj2w,602
19
+ test/test_contract_using_api.py,sha256=hsSJIL6g7uXszTsvoRXFDjwo6LrGHcxstzXdQ_Q5mdw,721
20
+ test/test_contract_using_api_implicit.py,sha256=t6p4FbpVDQj6tC_CAeDcyK4UQD0pD14PFJrG-ssFnoI,737
20
21
  test/test_contract_with_local_specs.py,sha256=5P0rhFCs0A9dRNT_jfDSC4ecsL7OakKZI44epZCKCxw,812
21
- test/api/__init__.py,sha256=MDUtXg97ptIlrwePzHkdU-tfeWwmHjLtJeOfQlTyCTA,252
22
+ test/api/__init__.py,sha256=SL3gC_LcG1G1u9gAZXDxYqAz50gd7QJs59hKFE_k-7k,252
22
23
  test/api/models.py,sha256=LZhpMsdLzeBWZ2FvlbzdT3KM64PfhcWN5FeH8cYfNzA,1118
23
24
  test/api/routes.py,sha256=XXuCaH1yhNfwNFXP4_jP71EV2klj7vywaE6tl8_2SIY,202
24
- test/db/Orders.py,sha256=71T7Mzl8j7gLxd30aJCfFRcLtE6GQQ6sy11q860XZkc,414
25
- test/db/Products.py,sha256=3zBrK5Ih8pBKx5QyvNBcjOOL7z04vdtSRBc8MdZj44E,536
25
+ test/db/Orders.py,sha256=SBhYJhcajg1OOxwaX_ZJS72H0BJhSvO4YfVMxbTQimQ,435
26
+ test/db/Products.py,sha256=_isXcKg47DsYc8-byYU71zu9VUyGevM7vJMeDzMbqUA,569
26
27
  test/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- specmatic-0.4.1.dist-info/METADATA,sha256=34rFKKe1J-SgxVGfpZ-0YZTDwEw0eEJpTVd6fZvZsXo,4879
28
- specmatic-0.4.1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
29
- specmatic-0.4.1.dist-info/top_level.txt,sha256=caucaopz5jgkj72Pbo7QEzUR3jXNPuLv-m-4JHTG878,15
30
- specmatic-0.4.1.dist-info/RECORD,,
28
+ specmatic-0.5.0.dist-info/METADATA,sha256=zHi2U4oaJgcri6kzsdy6i8WF9trpGOr23nyIGB50RIc,5917
29
+ specmatic-0.5.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
30
+ specmatic-0.5.0.dist-info/top_level.txt,sha256=caucaopz5jgkj72Pbo7QEzUR3jXNPuLv-m-4JHTG878,15
31
+ specmatic-0.5.0.dist-info/RECORD,,
test/api/__init__.py CHANGED
@@ -5,7 +5,7 @@ from dotenv import load_dotenv
5
5
 
6
6
  load_dotenv()
7
7
  app = Flask(__name__)
8
- app.config["ORDER_API_HOST"] = os.getenv("ORDER_API_PORT")
9
- app.config["ORDER_API_PORT"] = os.getenv("ORDER_API_HOST")
8
+ app.config["ORDER_API_HOST"] = os.getenv("ORDER_API_HOST")
9
+ app.config["ORDER_API_PORT"] = os.getenv("ORDER_API_PORT")
10
10
 
11
11
  from test.api.routes import *
test/db/Orders.py CHANGED
@@ -1,10 +1,10 @@
1
- import json
2
-
3
1
  import requests
4
2
  from test.api import app
5
3
 
4
+
6
5
  class Orders:
7
- orders_api = f'http://{app.config["ORDER_API_HOST"]}:{app.config["ORDER_API_PORT"]}/orders'
6
+ def __init__(self):
7
+ self.orders_api = f'http://{app.config["ORDER_API_HOST"]}:{app.config["ORDER_API_PORT"]}/orders'
8
8
 
9
9
  def create(self, order):
10
10
  # Set the headers to specify that we're sending JSON data
test/db/Products.py CHANGED
@@ -1,9 +1,10 @@
1
- import requests
2
1
  from test.api import app
2
+ import requests
3
3
 
4
4
 
5
5
  class Products:
6
- products_api = f'http://{app.config["ORDER_API_HOST"]}:{app.config["ORDER_API_PORT"]}/products'
6
+ def __init__(self):
7
+ self.products_api = f'http://{app.config["ORDER_API_HOST"]}:{app.config["ORDER_API_PORT"]}/products'
7
8
 
8
9
  def search(self, product_type: str):
9
10
  try:
test/test_contract.py CHANGED
@@ -9,11 +9,12 @@ app_port = 5000
9
9
  stub_host = "127.0.0.1"
10
10
  stub_port = 8080
11
11
  PROJECT_ROOT = get_project_root()
12
+ APP_ROOT = PROJECT_ROOT + '/test'
12
13
  expectation_json_file = PROJECT_ROOT + '/test/data/expectation.json'
13
14
 
14
15
 
15
- @specmatic_contract_test(PROJECT_ROOT, app_host, app_port)
16
- @start_app(app, app_host, app_port)
16
+ @specmatic_contract_test(PROJECT_ROOT)
17
+ @start_app(app)
17
18
  @specmatic_stub(PROJECT_ROOT, stub_host, stub_port, [expectation_json_file])
18
19
  class TestContract:
19
20
  pass
@@ -16,15 +16,15 @@ class TestContract:
16
16
  pass
17
17
 
18
18
 
19
- stub = Specmatic.create_stub(PROJECT_ROOT, stub_host, stub_port)
20
- stub.start()
19
+ stub = Specmatic.start_stub(PROJECT_ROOT, stub_host, stub_port)
21
20
  stub.set_expectations([expectation_json_file])
22
21
 
23
- app = WSGIServer(app, app_host, app_port)
24
- app.start()
25
- Specmatic.run_tests(PROJECT_ROOT, TestContract, app_host, app_port)
22
+ app_server = WSGIServer(app, app_host, app_port)
23
+ app_server.start()
26
24
 
27
- app.stop()
25
+ Specmatic.test(PROJECT_ROOT, TestContract, app_host, app_port)
26
+
27
+ app_server.stop()
28
28
  stub.stop()
29
29
 
30
30
  if __name__ == '__main__':
@@ -0,0 +1,32 @@
1
+ import pytest
2
+ from specmatic.core.specmatic import Specmatic
3
+ from specmatic.server.wsgi_server import WSGIServer
4
+ from specmatic.utils import get_project_root
5
+ from test.api import app
6
+
7
+
8
+ stub_host = "127.0.0.1"
9
+ stub_port = 8080
10
+ PROJECT_ROOT = get_project_root()
11
+ expectation_json_file = PROJECT_ROOT + '/test/data/expectation.json'
12
+
13
+
14
+ class TestContract:
15
+ pass
16
+
17
+
18
+ stub = Specmatic.start_stub(PROJECT_ROOT)
19
+ stub.set_expectations([expectation_json_file])
20
+
21
+ app.config['ORDER_API_HOST'] = stub.host
22
+ app.config['ORDER_API_PORT'] = stub.port
23
+ app_server = WSGIServer(app)
24
+ app_server.start()
25
+
26
+ Specmatic.test(PROJECT_ROOT, TestContract, app_server.host, app_server.port)
27
+
28
+ app_server.stop()
29
+ stub.stop()
30
+
31
+ if __name__ == '__main__':
32
+ pytest.main()