genlayer-test 0.10.1__tar.gz → 0.11.0__tar.gz

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.
Files changed (43) hide show
  1. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/LICENSE +2 -2
  2. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/PKG-INFO +205 -6
  3. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/README.md +204 -5
  4. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/genlayer_test.egg-info/PKG-INFO +205 -6
  5. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/types.py +14 -0
  6. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/validators/validator_factory.py +37 -11
  7. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest_cli/config/plugin.py +8 -6
  8. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/pyproject.toml +1 -1
  9. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/genlayer_test.egg-info/SOURCES.txt +0 -0
  10. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/genlayer_test.egg-info/dependency_links.txt +0 -0
  11. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/genlayer_test.egg-info/entry_points.txt +0 -0
  12. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/genlayer_test.egg-info/requires.txt +0 -0
  13. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/genlayer_test.egg-info/top_level.txt +0 -0
  14. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/__init__.py +0 -0
  15. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/accounts.py +0 -0
  16. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/artifacts/__init__.py +0 -0
  17. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/artifacts/contract.py +0 -0
  18. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/assertions.py +0 -0
  19. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/clients.py +0 -0
  20. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/contracts/__init__.py +0 -0
  21. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/contracts/contract.py +0 -0
  22. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/contracts/contract_factory.py +0 -0
  23. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/contracts/contract_functions.py +0 -0
  24. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/contracts/method_stats.py +0 -0
  25. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/contracts/stats_collector.py +0 -0
  26. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/contracts/utils.py +0 -0
  27. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/exceptions.py +0 -0
  28. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/fixtures.py +0 -0
  29. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/helpers/__init__.py +0 -0
  30. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/helpers/fixture_snapshot.py +0 -0
  31. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/helpers/take_snapshot.py +0 -0
  32. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/logging.py +0 -0
  33. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/utils.py +0 -0
  34. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest/validators/__init__.py +0 -0
  35. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest_cli/config/__init__.py +0 -0
  36. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest_cli/config/constants.py +0 -0
  37. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest_cli/config/general.py +0 -0
  38. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest_cli/config/pytest_context.py +0 -0
  39. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest_cli/config/types.py +0 -0
  40. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest_cli/config/user.py +0 -0
  41. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest_cli/logging.py +0 -0
  42. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/gltest_cli/main.py +0 -0
  43. {genlayer_test-0.10.1 → genlayer_test-0.11.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 YeagerAI
3
+ Copyright (c) 2024 GenLayer Labs
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: genlayer-test
3
- Version: 0.10.1
3
+ Version: 0.11.0
4
4
  Summary: GenLayer Testing Suite
5
5
  Author: GenLayer
6
6
  License-Expression: MIT
@@ -24,8 +24,8 @@ Dynamic: license-file
24
24
  # GenLayer Testing Suite
25
25
 
26
26
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/license/mit/)
27
- [![Discord](https://dcbadge.vercel.app/api/server/8Jm4v89VAu?compact=true&style=flat)](https://discord.gg/VpfmXEMN66)
28
- [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/yeagerai.svg?style=social&label=Follow%20%40GenLayer)](https://x.com/GenLayer)
27
+ [![Discord](https://dcbadge.vercel.app/api/server/8Jm4v89VAu?compact=true&style=flat)](https://discord.gg/qjCU4AWnKE)
28
+ [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/genlayerlabs.svg?style=social&label=Follow%20%40GenLayer)](https://x.com/GenLayer)
29
29
  [![PyPI version](https://badge.fury.io/py/genlayer-test.svg)](https://badge.fury.io/py/genlayer-test)
30
30
  [![Documentation](https://img.shields.io/badge/docs-genlayer-blue)](https://docs.genlayer.com/api-references/genlayer-test)
31
31
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
@@ -107,7 +107,7 @@ $ pip install genlayer-test
107
107
 
108
108
  2. Install from source:
109
109
  ```bash
110
- $ git clone https://github.com/yeagerai/genlayer-testing-suite
110
+ $ git clone https://github.com/genlayerlabs/genlayer-testing-suite
111
111
  $ cd genlayer-testing-suite
112
112
  $ pip install -e .
113
113
  ```
@@ -622,6 +622,205 @@ The `.analyze()` method helps you:
622
622
  - Benchmark performance across multiple runs
623
623
 
624
624
 
625
+ ### Mock Web Responses
626
+
627
+ The Mock Web Response system allows you to simulate HTTP responses for web requests made by intelligent contracts using GenLayer's web methods (`gl.nondet.web.get()`, `gl.nondet.web.post()`, etc.). This feature enables deterministic testing of contracts that interact with external web services without making actual HTTP calls.
628
+
629
+ #### Basic Example
630
+
631
+ Here's a simple example of mocking a web API response:
632
+
633
+ ```python
634
+ from gltest import get_contract_factory, get_validator_factory
635
+ from gltest.types import MockedWebResponse
636
+ import json
637
+
638
+ def test_simple_web_mock():
639
+ # Define mock web responses
640
+ mock_web_response: MockedWebResponse = {
641
+ "nondet_web_request": {
642
+ "https://api.example.com/price": {
643
+ "method": "GET",
644
+ "status": 200,
645
+ "body": json.dumps({"price": 100.50})
646
+ }
647
+ }
648
+ }
649
+
650
+ # Create validators with mock web responses
651
+ validator_factory = get_validator_factory()
652
+ validators = validator_factory.batch_create_mock_validators(
653
+ count=5,
654
+ mock_web_response=mock_web_response
655
+ )
656
+
657
+ # Use validators in transaction context
658
+ transaction_context = {"validators": [v.to_dict() for v in validators]}
659
+
660
+ # Deploy and test contract
661
+ factory = get_contract_factory("PriceOracle")
662
+ contract = factory.deploy(transaction_context=transaction_context)
663
+
664
+ # Contract's web requests will receive the mocked response
665
+ result = contract.update_price().transact(transaction_context=transaction_context)
666
+ ```
667
+
668
+ #### Supported HTTP Methods
669
+
670
+ Mock web responses support all HTTP methods including GET, POST, PUT, DELETE, PATCH, etc.:
671
+
672
+ ```python
673
+ mock_web_response: MockedWebResponse = {
674
+ "nondet_web_request": {
675
+ # GET request
676
+ "https://api.example.com/users/123": {
677
+ "method": "GET",
678
+ "status": 200,
679
+ "body": '{"id": 123, "name": "Alice"}'
680
+ },
681
+ # POST request
682
+ "https://api.example.com/users": {
683
+ "method": "POST",
684
+ "status": 201,
685
+ "body": '{"id": 124, "name": "Bob", "created": true}'
686
+ },
687
+ # DELETE request
688
+ "https://api.example.com/users/123": {
689
+ "method": "DELETE",
690
+ "status": 204,
691
+ "body": ""
692
+ },
693
+ # PUT request
694
+ "https://api.example.com/users/123": {
695
+ "method": "PUT",
696
+ "status": 200,
697
+ "body": '{"id": 123, "name": "Alice Updated"}'
698
+ },
699
+ # Error response
700
+ "https://api.example.com/error": {
701
+ "method": "GET",
702
+ "status": 500,
703
+ "body": "Internal Server Error"
704
+ }
705
+ }
706
+ }
707
+ ```
708
+
709
+ #### How It Works
710
+
711
+ When a contract calls any web method (`gl.nondet.web.get()`, `gl.nondet.web.post()`, etc.):
712
+ 1. The mock system checks if the URL exists in the mock configuration
713
+ 2. If found, it returns the mocked response with the specified status and body
714
+ 3. If not found, the actual web request would be made (or fail if network access is disabled)
715
+
716
+ #### Complete Example: Twitter/X Username Storage
717
+
718
+ Here's a real-world example showing how to mock Twitter/X API responses:
719
+
720
+ ```python
721
+ # test_x_username_storage.py
722
+ from gltest import get_contract_factory, get_validator_factory
723
+ from gltest.assertions import tx_execution_succeeded
724
+ from gltest.types import MockedWebResponse
725
+ import json
726
+ import urllib.parse
727
+
728
+ def test_x_username_storage():
729
+ # Helper to build URL with query parameters
730
+ def get_username_url(username: str) -> str:
731
+ params = {"user.fields": "public_metrics,verified"}
732
+ return f"https://domain.com/api/twitter/users/by/username/{username}?{urllib.parse.urlencode(params)}"
733
+
734
+ # Define mock responses for different usernames
735
+ mock_web_response: MockedWebResponse = {
736
+ "nondet_web_request": {
737
+ get_username_url("user_a"): {
738
+ "method": "GET",
739
+ "status": 200,
740
+ "body": json.dumps({"username": "user_a", "verified": True})
741
+ },
742
+ get_username_url("user_b"): {
743
+ "method": "GET",
744
+ "status": 200,
745
+ "body": json.dumps({"username": "user_b", "verified": False})
746
+ }
747
+ }
748
+ }
749
+
750
+ # Create validators with mock web responses
751
+ validator_factory = get_validator_factory()
752
+ validators = validator_factory.batch_create_mock_validators(
753
+ count=5,
754
+ mock_web_response=mock_web_response
755
+ )
756
+ transaction_context = {"validators": [v.to_dict() for v in validators]}
757
+
758
+ # Deploy and test contract
759
+ factory = get_contract_factory("XUsernameStorage")
760
+ contract = factory.deploy(transaction_context=transaction_context)
761
+
762
+ # Test updating username - will use mocked response
763
+ tx_receipt = contract.update_username(args=["user_a"]).transact(
764
+ transaction_context=transaction_context
765
+ )
766
+ assert tx_execution_succeeded(tx_receipt)
767
+
768
+ # Verify the username was stored
769
+ username = contract.get_username().call(transaction_context=transaction_context)
770
+ assert username == "user_a"
771
+ ```
772
+
773
+ #### Combining Mock LLM and Web Responses
774
+
775
+ You can combine both mock LLM responses and mock web responses in the same test:
776
+
777
+ ```python
778
+ def test_combined_mocks():
779
+ # Define both mock types
780
+ mock_llm_response = {
781
+ "eq_principle_prompt_comparative": {
782
+ "values match": True
783
+ }
784
+ }
785
+
786
+ mock_web_response: MockedWebResponse = {
787
+ "nondet_web_request": {
788
+ "https://api.example.com/data": {
789
+ "method": "GET",
790
+ "status": 200,
791
+ "body": '{"value": 42}'
792
+ }
793
+ }
794
+ }
795
+
796
+ # Create validators with both mock types
797
+ validator_factory = get_validator_factory()
798
+ validators = validator_factory.batch_create_mock_validators(
799
+ count=5,
800
+ mock_llm_response=mock_llm_response,
801
+ mock_web_response=mock_web_response
802
+ )
803
+
804
+ # Use in your tests...
805
+ ```
806
+
807
+ #### Best Practices
808
+
809
+ 1. **URL Matching**: URLs must match exactly, including query parameters
810
+ 2. **Response Body**: Always provide the body as a string (use `json.dumps()` for JSON data)
811
+ 3. **Status Codes**: Use realistic HTTP status codes (200, 404, 500, etc.)
812
+ 4. **Method Matching**: Specify the correct HTTP method that your contract uses
813
+ 5. **Error Testing**: Mock error responses to test error handling paths
814
+ 6. **Deterministic Tests**: Mock web responses ensure tests don't depend on external services
815
+
816
+ #### Notes
817
+
818
+ - Mock web responses are only available when using mock validators
819
+ - URL matching is exact - the full URL including query parameters must match
820
+ - The method field should match the HTTP method used by the contract
821
+ - Useful for testing contracts that interact with external APIs without network dependencies
822
+ - All standard HTTP methods are supported (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
823
+
625
824
  ### Custom Transaction Context
626
825
 
627
826
  The GenLayer Testing Suite allows you to customize the transaction execution environment by providing a `transaction_context` parameter with custom validators and GenVM datetime settings.
@@ -1001,8 +1200,8 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
1001
1200
  ## 💬 Support
1002
1201
 
1003
1202
  - [Documentation](https://docs.genlayer.com/api-references/genlayer-test)
1004
- - [Discord Community](https://discord.gg/VpfmXEMN66)
1005
- - [GitHub Issues](https://github.com/yeagerai/genlayer-testing-suite/issues)
1203
+ - [Discord Community](https://discord.gg/qjCU4AWnKE)
1204
+ - [GitHub Issues](https://github.com/genlayerlabs/genlayer-testing-suite/issues)
1006
1205
  - [Twitter](https://x.com/GenLayer)
1007
1206
 
1008
1207
 
@@ -1,8 +1,8 @@
1
1
  # GenLayer Testing Suite
2
2
 
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/license/mit/)
4
- [![Discord](https://dcbadge.vercel.app/api/server/8Jm4v89VAu?compact=true&style=flat)](https://discord.gg/VpfmXEMN66)
5
- [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/yeagerai.svg?style=social&label=Follow%20%40GenLayer)](https://x.com/GenLayer)
4
+ [![Discord](https://dcbadge.vercel.app/api/server/8Jm4v89VAu?compact=true&style=flat)](https://discord.gg/qjCU4AWnKE)
5
+ [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/genlayerlabs.svg?style=social&label=Follow%20%40GenLayer)](https://x.com/GenLayer)
6
6
  [![PyPI version](https://badge.fury.io/py/genlayer-test.svg)](https://badge.fury.io/py/genlayer-test)
7
7
  [![Documentation](https://img.shields.io/badge/docs-genlayer-blue)](https://docs.genlayer.com/api-references/genlayer-test)
8
8
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
@@ -84,7 +84,7 @@ $ pip install genlayer-test
84
84
 
85
85
  2. Install from source:
86
86
  ```bash
87
- $ git clone https://github.com/yeagerai/genlayer-testing-suite
87
+ $ git clone https://github.com/genlayerlabs/genlayer-testing-suite
88
88
  $ cd genlayer-testing-suite
89
89
  $ pip install -e .
90
90
  ```
@@ -599,6 +599,205 @@ The `.analyze()` method helps you:
599
599
  - Benchmark performance across multiple runs
600
600
 
601
601
 
602
+ ### Mock Web Responses
603
+
604
+ The Mock Web Response system allows you to simulate HTTP responses for web requests made by intelligent contracts using GenLayer's web methods (`gl.nondet.web.get()`, `gl.nondet.web.post()`, etc.). This feature enables deterministic testing of contracts that interact with external web services without making actual HTTP calls.
605
+
606
+ #### Basic Example
607
+
608
+ Here's a simple example of mocking a web API response:
609
+
610
+ ```python
611
+ from gltest import get_contract_factory, get_validator_factory
612
+ from gltest.types import MockedWebResponse
613
+ import json
614
+
615
+ def test_simple_web_mock():
616
+ # Define mock web responses
617
+ mock_web_response: MockedWebResponse = {
618
+ "nondet_web_request": {
619
+ "https://api.example.com/price": {
620
+ "method": "GET",
621
+ "status": 200,
622
+ "body": json.dumps({"price": 100.50})
623
+ }
624
+ }
625
+ }
626
+
627
+ # Create validators with mock web responses
628
+ validator_factory = get_validator_factory()
629
+ validators = validator_factory.batch_create_mock_validators(
630
+ count=5,
631
+ mock_web_response=mock_web_response
632
+ )
633
+
634
+ # Use validators in transaction context
635
+ transaction_context = {"validators": [v.to_dict() for v in validators]}
636
+
637
+ # Deploy and test contract
638
+ factory = get_contract_factory("PriceOracle")
639
+ contract = factory.deploy(transaction_context=transaction_context)
640
+
641
+ # Contract's web requests will receive the mocked response
642
+ result = contract.update_price().transact(transaction_context=transaction_context)
643
+ ```
644
+
645
+ #### Supported HTTP Methods
646
+
647
+ Mock web responses support all HTTP methods including GET, POST, PUT, DELETE, PATCH, etc.:
648
+
649
+ ```python
650
+ mock_web_response: MockedWebResponse = {
651
+ "nondet_web_request": {
652
+ # GET request
653
+ "https://api.example.com/users/123": {
654
+ "method": "GET",
655
+ "status": 200,
656
+ "body": '{"id": 123, "name": "Alice"}'
657
+ },
658
+ # POST request
659
+ "https://api.example.com/users": {
660
+ "method": "POST",
661
+ "status": 201,
662
+ "body": '{"id": 124, "name": "Bob", "created": true}'
663
+ },
664
+ # DELETE request
665
+ "https://api.example.com/users/123": {
666
+ "method": "DELETE",
667
+ "status": 204,
668
+ "body": ""
669
+ },
670
+ # PUT request
671
+ "https://api.example.com/users/123": {
672
+ "method": "PUT",
673
+ "status": 200,
674
+ "body": '{"id": 123, "name": "Alice Updated"}'
675
+ },
676
+ # Error response
677
+ "https://api.example.com/error": {
678
+ "method": "GET",
679
+ "status": 500,
680
+ "body": "Internal Server Error"
681
+ }
682
+ }
683
+ }
684
+ ```
685
+
686
+ #### How It Works
687
+
688
+ When a contract calls any web method (`gl.nondet.web.get()`, `gl.nondet.web.post()`, etc.):
689
+ 1. The mock system checks if the URL exists in the mock configuration
690
+ 2. If found, it returns the mocked response with the specified status and body
691
+ 3. If not found, the actual web request would be made (or fail if network access is disabled)
692
+
693
+ #### Complete Example: Twitter/X Username Storage
694
+
695
+ Here's a real-world example showing how to mock Twitter/X API responses:
696
+
697
+ ```python
698
+ # test_x_username_storage.py
699
+ from gltest import get_contract_factory, get_validator_factory
700
+ from gltest.assertions import tx_execution_succeeded
701
+ from gltest.types import MockedWebResponse
702
+ import json
703
+ import urllib.parse
704
+
705
+ def test_x_username_storage():
706
+ # Helper to build URL with query parameters
707
+ def get_username_url(username: str) -> str:
708
+ params = {"user.fields": "public_metrics,verified"}
709
+ return f"https://domain.com/api/twitter/users/by/username/{username}?{urllib.parse.urlencode(params)}"
710
+
711
+ # Define mock responses for different usernames
712
+ mock_web_response: MockedWebResponse = {
713
+ "nondet_web_request": {
714
+ get_username_url("user_a"): {
715
+ "method": "GET",
716
+ "status": 200,
717
+ "body": json.dumps({"username": "user_a", "verified": True})
718
+ },
719
+ get_username_url("user_b"): {
720
+ "method": "GET",
721
+ "status": 200,
722
+ "body": json.dumps({"username": "user_b", "verified": False})
723
+ }
724
+ }
725
+ }
726
+
727
+ # Create validators with mock web responses
728
+ validator_factory = get_validator_factory()
729
+ validators = validator_factory.batch_create_mock_validators(
730
+ count=5,
731
+ mock_web_response=mock_web_response
732
+ )
733
+ transaction_context = {"validators": [v.to_dict() for v in validators]}
734
+
735
+ # Deploy and test contract
736
+ factory = get_contract_factory("XUsernameStorage")
737
+ contract = factory.deploy(transaction_context=transaction_context)
738
+
739
+ # Test updating username - will use mocked response
740
+ tx_receipt = contract.update_username(args=["user_a"]).transact(
741
+ transaction_context=transaction_context
742
+ )
743
+ assert tx_execution_succeeded(tx_receipt)
744
+
745
+ # Verify the username was stored
746
+ username = contract.get_username().call(transaction_context=transaction_context)
747
+ assert username == "user_a"
748
+ ```
749
+
750
+ #### Combining Mock LLM and Web Responses
751
+
752
+ You can combine both mock LLM responses and mock web responses in the same test:
753
+
754
+ ```python
755
+ def test_combined_mocks():
756
+ # Define both mock types
757
+ mock_llm_response = {
758
+ "eq_principle_prompt_comparative": {
759
+ "values match": True
760
+ }
761
+ }
762
+
763
+ mock_web_response: MockedWebResponse = {
764
+ "nondet_web_request": {
765
+ "https://api.example.com/data": {
766
+ "method": "GET",
767
+ "status": 200,
768
+ "body": '{"value": 42}'
769
+ }
770
+ }
771
+ }
772
+
773
+ # Create validators with both mock types
774
+ validator_factory = get_validator_factory()
775
+ validators = validator_factory.batch_create_mock_validators(
776
+ count=5,
777
+ mock_llm_response=mock_llm_response,
778
+ mock_web_response=mock_web_response
779
+ )
780
+
781
+ # Use in your tests...
782
+ ```
783
+
784
+ #### Best Practices
785
+
786
+ 1. **URL Matching**: URLs must match exactly, including query parameters
787
+ 2. **Response Body**: Always provide the body as a string (use `json.dumps()` for JSON data)
788
+ 3. **Status Codes**: Use realistic HTTP status codes (200, 404, 500, etc.)
789
+ 4. **Method Matching**: Specify the correct HTTP method that your contract uses
790
+ 5. **Error Testing**: Mock error responses to test error handling paths
791
+ 6. **Deterministic Tests**: Mock web responses ensure tests don't depend on external services
792
+
793
+ #### Notes
794
+
795
+ - Mock web responses are only available when using mock validators
796
+ - URL matching is exact - the full URL including query parameters must match
797
+ - The method field should match the HTTP method used by the contract
798
+ - Useful for testing contracts that interact with external APIs without network dependencies
799
+ - All standard HTTP methods are supported (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
800
+
602
801
  ### Custom Transaction Context
603
802
 
604
803
  The GenLayer Testing Suite allows you to customize the transaction execution environment by providing a `transaction_context` parameter with custom validators and GenVM datetime settings.
@@ -978,8 +1177,8 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
978
1177
  ## 💬 Support
979
1178
 
980
1179
  - [Documentation](https://docs.genlayer.com/api-references/genlayer-test)
981
- - [Discord Community](https://discord.gg/VpfmXEMN66)
982
- - [GitHub Issues](https://github.com/yeagerai/genlayer-testing-suite/issues)
1180
+ - [Discord Community](https://discord.gg/qjCU4AWnKE)
1181
+ - [GitHub Issues](https://github.com/genlayerlabs/genlayer-testing-suite/issues)
983
1182
  - [Twitter](https://x.com/GenLayer)
984
1183
 
985
1184
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: genlayer-test
3
- Version: 0.10.1
3
+ Version: 0.11.0
4
4
  Summary: GenLayer Testing Suite
5
5
  Author: GenLayer
6
6
  License-Expression: MIT
@@ -24,8 +24,8 @@ Dynamic: license-file
24
24
  # GenLayer Testing Suite
25
25
 
26
26
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/license/mit/)
27
- [![Discord](https://dcbadge.vercel.app/api/server/8Jm4v89VAu?compact=true&style=flat)](https://discord.gg/VpfmXEMN66)
28
- [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/yeagerai.svg?style=social&label=Follow%20%40GenLayer)](https://x.com/GenLayer)
27
+ [![Discord](https://dcbadge.vercel.app/api/server/8Jm4v89VAu?compact=true&style=flat)](https://discord.gg/qjCU4AWnKE)
28
+ [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/genlayerlabs.svg?style=social&label=Follow%20%40GenLayer)](https://x.com/GenLayer)
29
29
  [![PyPI version](https://badge.fury.io/py/genlayer-test.svg)](https://badge.fury.io/py/genlayer-test)
30
30
  [![Documentation](https://img.shields.io/badge/docs-genlayer-blue)](https://docs.genlayer.com/api-references/genlayer-test)
31
31
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
@@ -107,7 +107,7 @@ $ pip install genlayer-test
107
107
 
108
108
  2. Install from source:
109
109
  ```bash
110
- $ git clone https://github.com/yeagerai/genlayer-testing-suite
110
+ $ git clone https://github.com/genlayerlabs/genlayer-testing-suite
111
111
  $ cd genlayer-testing-suite
112
112
  $ pip install -e .
113
113
  ```
@@ -622,6 +622,205 @@ The `.analyze()` method helps you:
622
622
  - Benchmark performance across multiple runs
623
623
 
624
624
 
625
+ ### Mock Web Responses
626
+
627
+ The Mock Web Response system allows you to simulate HTTP responses for web requests made by intelligent contracts using GenLayer's web methods (`gl.nondet.web.get()`, `gl.nondet.web.post()`, etc.). This feature enables deterministic testing of contracts that interact with external web services without making actual HTTP calls.
628
+
629
+ #### Basic Example
630
+
631
+ Here's a simple example of mocking a web API response:
632
+
633
+ ```python
634
+ from gltest import get_contract_factory, get_validator_factory
635
+ from gltest.types import MockedWebResponse
636
+ import json
637
+
638
+ def test_simple_web_mock():
639
+ # Define mock web responses
640
+ mock_web_response: MockedWebResponse = {
641
+ "nondet_web_request": {
642
+ "https://api.example.com/price": {
643
+ "method": "GET",
644
+ "status": 200,
645
+ "body": json.dumps({"price": 100.50})
646
+ }
647
+ }
648
+ }
649
+
650
+ # Create validators with mock web responses
651
+ validator_factory = get_validator_factory()
652
+ validators = validator_factory.batch_create_mock_validators(
653
+ count=5,
654
+ mock_web_response=mock_web_response
655
+ )
656
+
657
+ # Use validators in transaction context
658
+ transaction_context = {"validators": [v.to_dict() for v in validators]}
659
+
660
+ # Deploy and test contract
661
+ factory = get_contract_factory("PriceOracle")
662
+ contract = factory.deploy(transaction_context=transaction_context)
663
+
664
+ # Contract's web requests will receive the mocked response
665
+ result = contract.update_price().transact(transaction_context=transaction_context)
666
+ ```
667
+
668
+ #### Supported HTTP Methods
669
+
670
+ Mock web responses support all HTTP methods including GET, POST, PUT, DELETE, PATCH, etc.:
671
+
672
+ ```python
673
+ mock_web_response: MockedWebResponse = {
674
+ "nondet_web_request": {
675
+ # GET request
676
+ "https://api.example.com/users/123": {
677
+ "method": "GET",
678
+ "status": 200,
679
+ "body": '{"id": 123, "name": "Alice"}'
680
+ },
681
+ # POST request
682
+ "https://api.example.com/users": {
683
+ "method": "POST",
684
+ "status": 201,
685
+ "body": '{"id": 124, "name": "Bob", "created": true}'
686
+ },
687
+ # DELETE request
688
+ "https://api.example.com/users/123": {
689
+ "method": "DELETE",
690
+ "status": 204,
691
+ "body": ""
692
+ },
693
+ # PUT request
694
+ "https://api.example.com/users/123": {
695
+ "method": "PUT",
696
+ "status": 200,
697
+ "body": '{"id": 123, "name": "Alice Updated"}'
698
+ },
699
+ # Error response
700
+ "https://api.example.com/error": {
701
+ "method": "GET",
702
+ "status": 500,
703
+ "body": "Internal Server Error"
704
+ }
705
+ }
706
+ }
707
+ ```
708
+
709
+ #### How It Works
710
+
711
+ When a contract calls any web method (`gl.nondet.web.get()`, `gl.nondet.web.post()`, etc.):
712
+ 1. The mock system checks if the URL exists in the mock configuration
713
+ 2. If found, it returns the mocked response with the specified status and body
714
+ 3. If not found, the actual web request would be made (or fail if network access is disabled)
715
+
716
+ #### Complete Example: Twitter/X Username Storage
717
+
718
+ Here's a real-world example showing how to mock Twitter/X API responses:
719
+
720
+ ```python
721
+ # test_x_username_storage.py
722
+ from gltest import get_contract_factory, get_validator_factory
723
+ from gltest.assertions import tx_execution_succeeded
724
+ from gltest.types import MockedWebResponse
725
+ import json
726
+ import urllib.parse
727
+
728
+ def test_x_username_storage():
729
+ # Helper to build URL with query parameters
730
+ def get_username_url(username: str) -> str:
731
+ params = {"user.fields": "public_metrics,verified"}
732
+ return f"https://domain.com/api/twitter/users/by/username/{username}?{urllib.parse.urlencode(params)}"
733
+
734
+ # Define mock responses for different usernames
735
+ mock_web_response: MockedWebResponse = {
736
+ "nondet_web_request": {
737
+ get_username_url("user_a"): {
738
+ "method": "GET",
739
+ "status": 200,
740
+ "body": json.dumps({"username": "user_a", "verified": True})
741
+ },
742
+ get_username_url("user_b"): {
743
+ "method": "GET",
744
+ "status": 200,
745
+ "body": json.dumps({"username": "user_b", "verified": False})
746
+ }
747
+ }
748
+ }
749
+
750
+ # Create validators with mock web responses
751
+ validator_factory = get_validator_factory()
752
+ validators = validator_factory.batch_create_mock_validators(
753
+ count=5,
754
+ mock_web_response=mock_web_response
755
+ )
756
+ transaction_context = {"validators": [v.to_dict() for v in validators]}
757
+
758
+ # Deploy and test contract
759
+ factory = get_contract_factory("XUsernameStorage")
760
+ contract = factory.deploy(transaction_context=transaction_context)
761
+
762
+ # Test updating username - will use mocked response
763
+ tx_receipt = contract.update_username(args=["user_a"]).transact(
764
+ transaction_context=transaction_context
765
+ )
766
+ assert tx_execution_succeeded(tx_receipt)
767
+
768
+ # Verify the username was stored
769
+ username = contract.get_username().call(transaction_context=transaction_context)
770
+ assert username == "user_a"
771
+ ```
772
+
773
+ #### Combining Mock LLM and Web Responses
774
+
775
+ You can combine both mock LLM responses and mock web responses in the same test:
776
+
777
+ ```python
778
+ def test_combined_mocks():
779
+ # Define both mock types
780
+ mock_llm_response = {
781
+ "eq_principle_prompt_comparative": {
782
+ "values match": True
783
+ }
784
+ }
785
+
786
+ mock_web_response: MockedWebResponse = {
787
+ "nondet_web_request": {
788
+ "https://api.example.com/data": {
789
+ "method": "GET",
790
+ "status": 200,
791
+ "body": '{"value": 42}'
792
+ }
793
+ }
794
+ }
795
+
796
+ # Create validators with both mock types
797
+ validator_factory = get_validator_factory()
798
+ validators = validator_factory.batch_create_mock_validators(
799
+ count=5,
800
+ mock_llm_response=mock_llm_response,
801
+ mock_web_response=mock_web_response
802
+ )
803
+
804
+ # Use in your tests...
805
+ ```
806
+
807
+ #### Best Practices
808
+
809
+ 1. **URL Matching**: URLs must match exactly, including query parameters
810
+ 2. **Response Body**: Always provide the body as a string (use `json.dumps()` for JSON data)
811
+ 3. **Status Codes**: Use realistic HTTP status codes (200, 404, 500, etc.)
812
+ 4. **Method Matching**: Specify the correct HTTP method that your contract uses
813
+ 5. **Error Testing**: Mock error responses to test error handling paths
814
+ 6. **Deterministic Tests**: Mock web responses ensure tests don't depend on external services
815
+
816
+ #### Notes
817
+
818
+ - Mock web responses are only available when using mock validators
819
+ - URL matching is exact - the full URL including query parameters must match
820
+ - The method field should match the HTTP method used by the contract
821
+ - Useful for testing contracts that interact with external APIs without network dependencies
822
+ - All standard HTTP methods are supported (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
823
+
625
824
  ### Custom Transaction Context
626
825
 
627
826
  The GenLayer Testing Suite allows you to customize the transaction execution environment by providing a `transaction_context` parameter with custom validators and GenVM datetime settings.
@@ -1001,8 +1200,8 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
1001
1200
  ## 💬 Support
1002
1201
 
1003
1202
  - [Documentation](https://docs.genlayer.com/api-references/genlayer-test)
1004
- - [Discord Community](https://discord.gg/VpfmXEMN66)
1005
- - [GitHub Issues](https://github.com/yeagerai/genlayer-testing-suite/issues)
1203
+ - [Discord Community](https://discord.gg/qjCU4AWnKE)
1204
+ - [GitHub Issues](https://github.com/genlayerlabs/genlayer-testing-suite/issues)
1006
1205
  - [Twitter](https://x.com/GenLayer)
1007
1206
 
1008
1207
 
@@ -20,6 +20,20 @@ class MockedLLMResponse(TypedDict):
20
20
  eq_principle_prompt_non_comparative: Dict[str, bool]
21
21
 
22
22
 
23
+ class MockedWebResponseData(TypedDict):
24
+ """Mocked web response data with method for matching"""
25
+
26
+ method: str # GET, POST, PUT, DELETE, etc.
27
+ status: int # status code of the response
28
+ body: str # body of the response
29
+
30
+
31
+ class MockedWebResponse(TypedDict):
32
+ """Maps urls to responses"""
33
+
34
+ nondet_web_request: Dict[str, MockedWebResponseData]
35
+
36
+
23
37
  class ValidatorConfig(TypedDict):
24
38
  """Validator information."""
25
39
 
@@ -1,4 +1,4 @@
1
- from gltest.types import MockedLLMResponse
1
+ from gltest.types import MockedLLMResponse, MockedWebResponse
2
2
  from dataclasses import dataclass
3
3
  from typing import Dict, Any, Optional, List
4
4
  from copy import deepcopy
@@ -16,6 +16,7 @@ class Validator:
16
16
  # Mock configuration
17
17
  mock_enabled: bool
18
18
  mock_llm_response: Optional[MockedLLMResponse]
19
+ mock_web_response: Optional[MockedWebResponse]
19
20
 
20
21
  def to_dict(self) -> Dict[str, Any]:
21
22
  normal_config = {
@@ -29,21 +30,27 @@ class Validator:
29
30
  if not self.mock_enabled:
30
31
  return normal_config
31
32
 
32
- mock = self.mock_llm_response or {}
33
- mock_config = {
34
- "response": mock.get("nondet_exec_prompt", {}),
35
- "eq_principle_prompt_comparative": mock.get(
33
+ # Mock llm response
34
+ mock_llm = self.mock_llm_response or {}
35
+ mock_llm_config = {
36
+ "response": mock_llm.get("nondet_exec_prompt", {}),
37
+ "eq_principle_prompt_comparative": mock_llm.get(
36
38
  "eq_principle_prompt_comparative", {}
37
39
  ),
38
- "eq_principle_prompt_non_comparative": mock.get(
40
+ "eq_principle_prompt_non_comparative": mock_llm.get(
39
41
  "eq_principle_prompt_non_comparative", {}
40
42
  ),
41
43
  }
44
+ mock_web = self.mock_web_response or {}
45
+ mock_web_config = {
46
+ "nondet_web_request": mock_web.get("nondet_web_request", {}),
47
+ }
42
48
  return {
43
49
  **normal_config,
44
50
  "plugin_config": {
45
51
  **self.plugin_config,
46
- "mock_response": mock_config,
52
+ "mock_response": mock_llm_config,
53
+ "mock_web_response": mock_web_config,
47
54
  },
48
55
  }
49
56
 
@@ -60,6 +67,7 @@ class Validator:
60
67
  plugin_config=deepcopy(self.plugin_config),
61
68
  mock_enabled=self.mock_enabled,
62
69
  mock_llm_response=deepcopy(self.mock_llm_response),
70
+ mock_web_response=deepcopy(self.mock_web_response),
63
71
  )
64
72
 
65
73
 
@@ -85,6 +93,7 @@ class ValidatorFactory:
85
93
  plugin_config=deepcopy(plugin_config),
86
94
  mock_enabled=False,
87
95
  mock_llm_response=None,
96
+ mock_web_response=None,
88
97
  )
89
98
 
90
99
  def batch_create_validators(
@@ -109,7 +118,15 @@ class ValidatorFactory:
109
118
  for _ in range(count)
110
119
  ]
111
120
 
112
- def create_mock_validator(self, mock_llm_response: MockedLLMResponse) -> Validator:
121
+ def create_mock_validator(
122
+ self,
123
+ mock_llm_response: Optional[MockedLLMResponse] = None,
124
+ mock_web_response: Optional[MockedWebResponse] = None,
125
+ ) -> Validator:
126
+ if mock_llm_response is None and mock_web_response is None:
127
+ raise ValueError(
128
+ "mock_llm_response and mock_web_response cannot both be None"
129
+ )
113
130
  return Validator(
114
131
  stake=8,
115
132
  provider="openai",
@@ -121,15 +138,24 @@ class ValidatorFactory:
121
138
  "api_url": "https://api.openai.com",
122
139
  },
123
140
  mock_enabled=True,
124
- mock_llm_response=deepcopy(mock_llm_response),
141
+ mock_llm_response=(
142
+ deepcopy(mock_llm_response) if mock_llm_response is not None else None
143
+ ),
144
+ mock_web_response=(
145
+ deepcopy(mock_web_response) if mock_web_response is not None else None
146
+ ),
125
147
  )
126
148
 
127
149
  def batch_create_mock_validators(
128
150
  self,
129
151
  count: int,
130
- mock_llm_response: MockedLLMResponse,
152
+ mock_llm_response: Optional[MockedLLMResponse] = None,
153
+ mock_web_response: Optional[MockedWebResponse] = None,
131
154
  ) -> List[Validator]:
132
- return [self.create_mock_validator(mock_llm_response) for _ in range(count)]
155
+ return [
156
+ self.create_mock_validator(mock_llm_response, mock_web_response)
157
+ for _ in range(count)
158
+ ]
133
159
 
134
160
 
135
161
  def get_validator_factory() -> ValidatorFactory:
@@ -13,8 +13,6 @@ from gltest_cli.config.general import (
13
13
  from gltest_cli.config.types import PluginConfig
14
14
  from gltest_cli.config.pytest_context import _pytest_context
15
15
  from gltest_cli.config.constants import (
16
- DEFAULT_WAIT_INTERVAL,
17
- DEFAULT_WAIT_RETRIES,
18
16
  DEFAULT_LEADER_ONLY,
19
17
  CHAINS,
20
18
  )
@@ -39,14 +37,14 @@ def pytest_addoption(parser):
39
37
  group.addoption(
40
38
  "--default-wait-interval",
41
39
  action="store",
42
- default=DEFAULT_WAIT_INTERVAL,
40
+ default=None,
43
41
  help="Default interval (ms) between transaction receipt checks",
44
42
  )
45
43
 
46
44
  group.addoption(
47
45
  "--default-wait-retries",
48
46
  action="store",
49
- default=DEFAULT_WAIT_RETRIES,
47
+ default=None,
50
48
  help="Default number of retries for transaction receipt checks",
51
49
  )
52
50
 
@@ -122,8 +120,12 @@ def pytest_configure(config):
122
120
  plugin_config.artifacts_dir = (
123
121
  Path(artifacts_dir) if artifacts_dir is not None else None
124
122
  )
125
- plugin_config.default_wait_interval = int(default_wait_interval)
126
- plugin_config.default_wait_retries = int(default_wait_retries)
123
+ plugin_config.default_wait_interval = (
124
+ int(default_wait_interval) if default_wait_interval is not None else None
125
+ )
126
+ plugin_config.default_wait_retries = (
127
+ int(default_wait_retries) if default_wait_retries is not None else None
128
+ )
127
129
  plugin_config.rpc_url = rpc_url
128
130
  plugin_config.network_name = network
129
131
  plugin_config.leader_only = leader_only
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "genlayer-test"
7
- version = "0.10.1"
7
+ version = "0.11.0"
8
8
  description = "GenLayer Testing Suite"
9
9
  authors = [
10
10
  { name = "GenLayer" }
File without changes