genlayer-test 0.10.1__tar.gz → 0.12.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 (50) hide show
  1. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/LICENSE +2 -2
  2. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/PKG-INFO +271 -6
  3. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/README.md +270 -5
  4. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/genlayer_test.egg-info/PKG-INFO +271 -6
  5. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/genlayer_test.egg-info/SOURCES.txt +7 -0
  6. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/genlayer_test.egg-info/entry_points.txt +1 -0
  7. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/genlayer_test.egg-info/top_level.txt +1 -0
  8. genlayer_test-0.12.0/gltest/direct/__init__.py +31 -0
  9. genlayer_test-0.12.0/gltest/direct/loader.py +288 -0
  10. genlayer_test-0.12.0/gltest/direct/pytest_plugin.py +117 -0
  11. genlayer_test-0.12.0/gltest/direct/sdk_loader.py +260 -0
  12. genlayer_test-0.12.0/gltest/direct/types.py +18 -0
  13. genlayer_test-0.12.0/gltest/direct/vm.py +432 -0
  14. genlayer_test-0.12.0/gltest/direct/wasi_mock.py +219 -0
  15. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/types.py +14 -0
  16. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/validators/validator_factory.py +37 -11
  17. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest_cli/config/plugin.py +8 -6
  18. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/pyproject.toml +2 -1
  19. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/genlayer_test.egg-info/dependency_links.txt +0 -0
  20. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/genlayer_test.egg-info/requires.txt +0 -0
  21. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/__init__.py +0 -0
  22. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/accounts.py +0 -0
  23. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/artifacts/__init__.py +0 -0
  24. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/artifacts/contract.py +0 -0
  25. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/assertions.py +0 -0
  26. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/clients.py +0 -0
  27. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/contracts/__init__.py +0 -0
  28. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/contracts/contract.py +0 -0
  29. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/contracts/contract_factory.py +0 -0
  30. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/contracts/contract_functions.py +0 -0
  31. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/contracts/method_stats.py +0 -0
  32. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/contracts/stats_collector.py +0 -0
  33. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/contracts/utils.py +0 -0
  34. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/exceptions.py +0 -0
  35. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/fixtures.py +0 -0
  36. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/helpers/__init__.py +0 -0
  37. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/helpers/fixture_snapshot.py +0 -0
  38. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/helpers/take_snapshot.py +0 -0
  39. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/logging.py +0 -0
  40. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/utils.py +0 -0
  41. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest/validators/__init__.py +0 -0
  42. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest_cli/config/__init__.py +0 -0
  43. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest_cli/config/constants.py +0 -0
  44. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest_cli/config/general.py +0 -0
  45. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest_cli/config/pytest_context.py +0 -0
  46. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest_cli/config/types.py +0 -0
  47. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest_cli/config/user.py +0 -0
  48. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest_cli/logging.py +0 -0
  49. {genlayer_test-0.10.1 → genlayer_test-0.12.0}/gltest_cli/main.py +0 -0
  50. {genlayer_test-0.10.1 → genlayer_test-0.12.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.12.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
  ```
@@ -312,6 +312,72 @@ The chain type determines various behaviors including RPC endpoints, consensus m
312
312
  - **Prompt Testing & Statistical Analysis** – Evaluate and statistically test prompts for AI-driven contract execution.
313
313
  - **Scalability to Security & Audit Tools** – Designed to extend into security testing and smart contract auditing.
314
314
  - **Custom Transaction Context** – Set custom validators with specific LLM providers and models, and configure GenVM datetime for deterministic testing scenarios.
315
+ - **Direct Execution Mode** – Run contracts directly in Python for ultra-fast unit testing (~ms vs minutes).
316
+
317
+ ## ⚡ Direct vs Simulator Mode
318
+
319
+ The testing suite provides two execution modes:
320
+
321
+ | Mode | How it works | Speed | Use case |
322
+ |------|--------------|-------|----------|
323
+ | **Simulator** | Deploy to GenLayer simulator, interact via RPC | ~minutes | Integration tests, consensus validation |
324
+ | **Direct** | Run Python code directly in-memory | ~milliseconds | Unit tests, rapid development |
325
+
326
+ ### Quick Start with Direct Mode
327
+
328
+ ```python
329
+ def test_token_transfer(direct_vm, direct_deploy):
330
+ # Deploy contract directly in Python (no simulator)
331
+ token = direct_deploy("contracts/Token.py", initial_supply=1000)
332
+
333
+ # Create test addresses
334
+ from gltest.direct import create_address
335
+ alice = create_address("alice")
336
+ bob = create_address("bob")
337
+
338
+ # Set sender and interact
339
+ direct_vm.sender = alice
340
+ token.mint(alice, 500)
341
+ token.transfer(bob, 100)
342
+
343
+ assert token.balances[bob] == 100
344
+ ```
345
+
346
+ ### Available Fixtures
347
+
348
+ | Fixture | Description |
349
+ |---------|-------------|
350
+ | `direct_vm` | VM context with cheatcodes |
351
+ | `direct_deploy` | Deploy contracts directly |
352
+ | `direct_alice`, `direct_bob`, `direct_charlie` | Test addresses |
353
+ | `direct_owner` | Default sender address |
354
+ | `direct_accounts` | List of 10 test addresses |
355
+
356
+ ### Cheatcodes
357
+
358
+ ```python
359
+ # Change sender
360
+ direct_vm.sender = alice
361
+
362
+ # Prank (temporary sender change)
363
+ with direct_vm.prank(bob):
364
+ contract.method() # Called as bob
365
+
366
+ # Snapshots
367
+ snap_id = direct_vm.snapshot()
368
+ contract.modify_state()
369
+ direct_vm.revert(snap_id) # State restored
370
+
371
+ # Expect revert
372
+ with direct_vm.expect_revert("Insufficient balance"):
373
+ contract.transfer(bob, 1000000)
374
+
375
+ # Mock web/LLM (for nondet operations)
376
+ direct_vm.mock_web(r"api\.example\.com", {"status": 200, "body": "{}"})
377
+ direct_vm.mock_llm(r"analyze.*", "positive sentiment")
378
+ ```
379
+
380
+ 📖 **[Full Direct Mode Documentation](docs/direct-runner.md)**
315
381
 
316
382
  ## 📚 Examples
317
383
 
@@ -622,6 +688,205 @@ The `.analyze()` method helps you:
622
688
  - Benchmark performance across multiple runs
623
689
 
624
690
 
691
+ ### Mock Web Responses
692
+
693
+ 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.
694
+
695
+ #### Basic Example
696
+
697
+ Here's a simple example of mocking a web API response:
698
+
699
+ ```python
700
+ from gltest import get_contract_factory, get_validator_factory
701
+ from gltest.types import MockedWebResponse
702
+ import json
703
+
704
+ def test_simple_web_mock():
705
+ # Define mock web responses
706
+ mock_web_response: MockedWebResponse = {
707
+ "nondet_web_request": {
708
+ "https://api.example.com/price": {
709
+ "method": "GET",
710
+ "status": 200,
711
+ "body": json.dumps({"price": 100.50})
712
+ }
713
+ }
714
+ }
715
+
716
+ # Create validators with mock web responses
717
+ validator_factory = get_validator_factory()
718
+ validators = validator_factory.batch_create_mock_validators(
719
+ count=5,
720
+ mock_web_response=mock_web_response
721
+ )
722
+
723
+ # Use validators in transaction context
724
+ transaction_context = {"validators": [v.to_dict() for v in validators]}
725
+
726
+ # Deploy and test contract
727
+ factory = get_contract_factory("PriceOracle")
728
+ contract = factory.deploy(transaction_context=transaction_context)
729
+
730
+ # Contract's web requests will receive the mocked response
731
+ result = contract.update_price().transact(transaction_context=transaction_context)
732
+ ```
733
+
734
+ #### Supported HTTP Methods
735
+
736
+ Mock web responses support all HTTP methods including GET, POST, PUT, DELETE, PATCH, etc.:
737
+
738
+ ```python
739
+ mock_web_response: MockedWebResponse = {
740
+ "nondet_web_request": {
741
+ # GET request
742
+ "https://api.example.com/users/123": {
743
+ "method": "GET",
744
+ "status": 200,
745
+ "body": '{"id": 123, "name": "Alice"}'
746
+ },
747
+ # POST request
748
+ "https://api.example.com/users": {
749
+ "method": "POST",
750
+ "status": 201,
751
+ "body": '{"id": 124, "name": "Bob", "created": true}'
752
+ },
753
+ # DELETE request
754
+ "https://api.example.com/users/123": {
755
+ "method": "DELETE",
756
+ "status": 204,
757
+ "body": ""
758
+ },
759
+ # PUT request
760
+ "https://api.example.com/users/123": {
761
+ "method": "PUT",
762
+ "status": 200,
763
+ "body": '{"id": 123, "name": "Alice Updated"}'
764
+ },
765
+ # Error response
766
+ "https://api.example.com/error": {
767
+ "method": "GET",
768
+ "status": 500,
769
+ "body": "Internal Server Error"
770
+ }
771
+ }
772
+ }
773
+ ```
774
+
775
+ #### How It Works
776
+
777
+ When a contract calls any web method (`gl.nondet.web.get()`, `gl.nondet.web.post()`, etc.):
778
+ 1. The mock system checks if the URL exists in the mock configuration
779
+ 2. If found, it returns the mocked response with the specified status and body
780
+ 3. If not found, the actual web request would be made (or fail if network access is disabled)
781
+
782
+ #### Complete Example: Twitter/X Username Storage
783
+
784
+ Here's a real-world example showing how to mock Twitter/X API responses:
785
+
786
+ ```python
787
+ # test_x_username_storage.py
788
+ from gltest import get_contract_factory, get_validator_factory
789
+ from gltest.assertions import tx_execution_succeeded
790
+ from gltest.types import MockedWebResponse
791
+ import json
792
+ import urllib.parse
793
+
794
+ def test_x_username_storage():
795
+ # Helper to build URL with query parameters
796
+ def get_username_url(username: str) -> str:
797
+ params = {"user.fields": "public_metrics,verified"}
798
+ return f"https://domain.com/api/twitter/users/by/username/{username}?{urllib.parse.urlencode(params)}"
799
+
800
+ # Define mock responses for different usernames
801
+ mock_web_response: MockedWebResponse = {
802
+ "nondet_web_request": {
803
+ get_username_url("user_a"): {
804
+ "method": "GET",
805
+ "status": 200,
806
+ "body": json.dumps({"username": "user_a", "verified": True})
807
+ },
808
+ get_username_url("user_b"): {
809
+ "method": "GET",
810
+ "status": 200,
811
+ "body": json.dumps({"username": "user_b", "verified": False})
812
+ }
813
+ }
814
+ }
815
+
816
+ # Create validators with mock web responses
817
+ validator_factory = get_validator_factory()
818
+ validators = validator_factory.batch_create_mock_validators(
819
+ count=5,
820
+ mock_web_response=mock_web_response
821
+ )
822
+ transaction_context = {"validators": [v.to_dict() for v in validators]}
823
+
824
+ # Deploy and test contract
825
+ factory = get_contract_factory("XUsernameStorage")
826
+ contract = factory.deploy(transaction_context=transaction_context)
827
+
828
+ # Test updating username - will use mocked response
829
+ tx_receipt = contract.update_username(args=["user_a"]).transact(
830
+ transaction_context=transaction_context
831
+ )
832
+ assert tx_execution_succeeded(tx_receipt)
833
+
834
+ # Verify the username was stored
835
+ username = contract.get_username().call(transaction_context=transaction_context)
836
+ assert username == "user_a"
837
+ ```
838
+
839
+ #### Combining Mock LLM and Web Responses
840
+
841
+ You can combine both mock LLM responses and mock web responses in the same test:
842
+
843
+ ```python
844
+ def test_combined_mocks():
845
+ # Define both mock types
846
+ mock_llm_response = {
847
+ "eq_principle_prompt_comparative": {
848
+ "values match": True
849
+ }
850
+ }
851
+
852
+ mock_web_response: MockedWebResponse = {
853
+ "nondet_web_request": {
854
+ "https://api.example.com/data": {
855
+ "method": "GET",
856
+ "status": 200,
857
+ "body": '{"value": 42}'
858
+ }
859
+ }
860
+ }
861
+
862
+ # Create validators with both mock types
863
+ validator_factory = get_validator_factory()
864
+ validators = validator_factory.batch_create_mock_validators(
865
+ count=5,
866
+ mock_llm_response=mock_llm_response,
867
+ mock_web_response=mock_web_response
868
+ )
869
+
870
+ # Use in your tests...
871
+ ```
872
+
873
+ #### Best Practices
874
+
875
+ 1. **URL Matching**: URLs must match exactly, including query parameters
876
+ 2. **Response Body**: Always provide the body as a string (use `json.dumps()` for JSON data)
877
+ 3. **Status Codes**: Use realistic HTTP status codes (200, 404, 500, etc.)
878
+ 4. **Method Matching**: Specify the correct HTTP method that your contract uses
879
+ 5. **Error Testing**: Mock error responses to test error handling paths
880
+ 6. **Deterministic Tests**: Mock web responses ensure tests don't depend on external services
881
+
882
+ #### Notes
883
+
884
+ - Mock web responses are only available when using mock validators
885
+ - URL matching is exact - the full URL including query parameters must match
886
+ - The method field should match the HTTP method used by the contract
887
+ - Useful for testing contracts that interact with external APIs without network dependencies
888
+ - All standard HTTP methods are supported (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
889
+
625
890
  ### Custom Transaction Context
626
891
 
627
892
  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 +1266,8 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
1001
1266
  ## 💬 Support
1002
1267
 
1003
1268
  - [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)
1269
+ - [Discord Community](https://discord.gg/qjCU4AWnKE)
1270
+ - [GitHub Issues](https://github.com/genlayerlabs/genlayer-testing-suite/issues)
1006
1271
  - [Twitter](https://x.com/GenLayer)
1007
1272
 
1008
1273
 
@@ -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
  ```
@@ -289,6 +289,72 @@ The chain type determines various behaviors including RPC endpoints, consensus m
289
289
  - **Prompt Testing & Statistical Analysis** – Evaluate and statistically test prompts for AI-driven contract execution.
290
290
  - **Scalability to Security & Audit Tools** – Designed to extend into security testing and smart contract auditing.
291
291
  - **Custom Transaction Context** – Set custom validators with specific LLM providers and models, and configure GenVM datetime for deterministic testing scenarios.
292
+ - **Direct Execution Mode** – Run contracts directly in Python for ultra-fast unit testing (~ms vs minutes).
293
+
294
+ ## ⚡ Direct vs Simulator Mode
295
+
296
+ The testing suite provides two execution modes:
297
+
298
+ | Mode | How it works | Speed | Use case |
299
+ |------|--------------|-------|----------|
300
+ | **Simulator** | Deploy to GenLayer simulator, interact via RPC | ~minutes | Integration tests, consensus validation |
301
+ | **Direct** | Run Python code directly in-memory | ~milliseconds | Unit tests, rapid development |
302
+
303
+ ### Quick Start with Direct Mode
304
+
305
+ ```python
306
+ def test_token_transfer(direct_vm, direct_deploy):
307
+ # Deploy contract directly in Python (no simulator)
308
+ token = direct_deploy("contracts/Token.py", initial_supply=1000)
309
+
310
+ # Create test addresses
311
+ from gltest.direct import create_address
312
+ alice = create_address("alice")
313
+ bob = create_address("bob")
314
+
315
+ # Set sender and interact
316
+ direct_vm.sender = alice
317
+ token.mint(alice, 500)
318
+ token.transfer(bob, 100)
319
+
320
+ assert token.balances[bob] == 100
321
+ ```
322
+
323
+ ### Available Fixtures
324
+
325
+ | Fixture | Description |
326
+ |---------|-------------|
327
+ | `direct_vm` | VM context with cheatcodes |
328
+ | `direct_deploy` | Deploy contracts directly |
329
+ | `direct_alice`, `direct_bob`, `direct_charlie` | Test addresses |
330
+ | `direct_owner` | Default sender address |
331
+ | `direct_accounts` | List of 10 test addresses |
332
+
333
+ ### Cheatcodes
334
+
335
+ ```python
336
+ # Change sender
337
+ direct_vm.sender = alice
338
+
339
+ # Prank (temporary sender change)
340
+ with direct_vm.prank(bob):
341
+ contract.method() # Called as bob
342
+
343
+ # Snapshots
344
+ snap_id = direct_vm.snapshot()
345
+ contract.modify_state()
346
+ direct_vm.revert(snap_id) # State restored
347
+
348
+ # Expect revert
349
+ with direct_vm.expect_revert("Insufficient balance"):
350
+ contract.transfer(bob, 1000000)
351
+
352
+ # Mock web/LLM (for nondet operations)
353
+ direct_vm.mock_web(r"api\.example\.com", {"status": 200, "body": "{}"})
354
+ direct_vm.mock_llm(r"analyze.*", "positive sentiment")
355
+ ```
356
+
357
+ 📖 **[Full Direct Mode Documentation](docs/direct-runner.md)**
292
358
 
293
359
  ## 📚 Examples
294
360
 
@@ -599,6 +665,205 @@ The `.analyze()` method helps you:
599
665
  - Benchmark performance across multiple runs
600
666
 
601
667
 
668
+ ### Mock Web Responses
669
+
670
+ 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.
671
+
672
+ #### Basic Example
673
+
674
+ Here's a simple example of mocking a web API response:
675
+
676
+ ```python
677
+ from gltest import get_contract_factory, get_validator_factory
678
+ from gltest.types import MockedWebResponse
679
+ import json
680
+
681
+ def test_simple_web_mock():
682
+ # Define mock web responses
683
+ mock_web_response: MockedWebResponse = {
684
+ "nondet_web_request": {
685
+ "https://api.example.com/price": {
686
+ "method": "GET",
687
+ "status": 200,
688
+ "body": json.dumps({"price": 100.50})
689
+ }
690
+ }
691
+ }
692
+
693
+ # Create validators with mock web responses
694
+ validator_factory = get_validator_factory()
695
+ validators = validator_factory.batch_create_mock_validators(
696
+ count=5,
697
+ mock_web_response=mock_web_response
698
+ )
699
+
700
+ # Use validators in transaction context
701
+ transaction_context = {"validators": [v.to_dict() for v in validators]}
702
+
703
+ # Deploy and test contract
704
+ factory = get_contract_factory("PriceOracle")
705
+ contract = factory.deploy(transaction_context=transaction_context)
706
+
707
+ # Contract's web requests will receive the mocked response
708
+ result = contract.update_price().transact(transaction_context=transaction_context)
709
+ ```
710
+
711
+ #### Supported HTTP Methods
712
+
713
+ Mock web responses support all HTTP methods including GET, POST, PUT, DELETE, PATCH, etc.:
714
+
715
+ ```python
716
+ mock_web_response: MockedWebResponse = {
717
+ "nondet_web_request": {
718
+ # GET request
719
+ "https://api.example.com/users/123": {
720
+ "method": "GET",
721
+ "status": 200,
722
+ "body": '{"id": 123, "name": "Alice"}'
723
+ },
724
+ # POST request
725
+ "https://api.example.com/users": {
726
+ "method": "POST",
727
+ "status": 201,
728
+ "body": '{"id": 124, "name": "Bob", "created": true}'
729
+ },
730
+ # DELETE request
731
+ "https://api.example.com/users/123": {
732
+ "method": "DELETE",
733
+ "status": 204,
734
+ "body": ""
735
+ },
736
+ # PUT request
737
+ "https://api.example.com/users/123": {
738
+ "method": "PUT",
739
+ "status": 200,
740
+ "body": '{"id": 123, "name": "Alice Updated"}'
741
+ },
742
+ # Error response
743
+ "https://api.example.com/error": {
744
+ "method": "GET",
745
+ "status": 500,
746
+ "body": "Internal Server Error"
747
+ }
748
+ }
749
+ }
750
+ ```
751
+
752
+ #### How It Works
753
+
754
+ When a contract calls any web method (`gl.nondet.web.get()`, `gl.nondet.web.post()`, etc.):
755
+ 1. The mock system checks if the URL exists in the mock configuration
756
+ 2. If found, it returns the mocked response with the specified status and body
757
+ 3. If not found, the actual web request would be made (or fail if network access is disabled)
758
+
759
+ #### Complete Example: Twitter/X Username Storage
760
+
761
+ Here's a real-world example showing how to mock Twitter/X API responses:
762
+
763
+ ```python
764
+ # test_x_username_storage.py
765
+ from gltest import get_contract_factory, get_validator_factory
766
+ from gltest.assertions import tx_execution_succeeded
767
+ from gltest.types import MockedWebResponse
768
+ import json
769
+ import urllib.parse
770
+
771
+ def test_x_username_storage():
772
+ # Helper to build URL with query parameters
773
+ def get_username_url(username: str) -> str:
774
+ params = {"user.fields": "public_metrics,verified"}
775
+ return f"https://domain.com/api/twitter/users/by/username/{username}?{urllib.parse.urlencode(params)}"
776
+
777
+ # Define mock responses for different usernames
778
+ mock_web_response: MockedWebResponse = {
779
+ "nondet_web_request": {
780
+ get_username_url("user_a"): {
781
+ "method": "GET",
782
+ "status": 200,
783
+ "body": json.dumps({"username": "user_a", "verified": True})
784
+ },
785
+ get_username_url("user_b"): {
786
+ "method": "GET",
787
+ "status": 200,
788
+ "body": json.dumps({"username": "user_b", "verified": False})
789
+ }
790
+ }
791
+ }
792
+
793
+ # Create validators with mock web responses
794
+ validator_factory = get_validator_factory()
795
+ validators = validator_factory.batch_create_mock_validators(
796
+ count=5,
797
+ mock_web_response=mock_web_response
798
+ )
799
+ transaction_context = {"validators": [v.to_dict() for v in validators]}
800
+
801
+ # Deploy and test contract
802
+ factory = get_contract_factory("XUsernameStorage")
803
+ contract = factory.deploy(transaction_context=transaction_context)
804
+
805
+ # Test updating username - will use mocked response
806
+ tx_receipt = contract.update_username(args=["user_a"]).transact(
807
+ transaction_context=transaction_context
808
+ )
809
+ assert tx_execution_succeeded(tx_receipt)
810
+
811
+ # Verify the username was stored
812
+ username = contract.get_username().call(transaction_context=transaction_context)
813
+ assert username == "user_a"
814
+ ```
815
+
816
+ #### Combining Mock LLM and Web Responses
817
+
818
+ You can combine both mock LLM responses and mock web responses in the same test:
819
+
820
+ ```python
821
+ def test_combined_mocks():
822
+ # Define both mock types
823
+ mock_llm_response = {
824
+ "eq_principle_prompt_comparative": {
825
+ "values match": True
826
+ }
827
+ }
828
+
829
+ mock_web_response: MockedWebResponse = {
830
+ "nondet_web_request": {
831
+ "https://api.example.com/data": {
832
+ "method": "GET",
833
+ "status": 200,
834
+ "body": '{"value": 42}'
835
+ }
836
+ }
837
+ }
838
+
839
+ # Create validators with both mock types
840
+ validator_factory = get_validator_factory()
841
+ validators = validator_factory.batch_create_mock_validators(
842
+ count=5,
843
+ mock_llm_response=mock_llm_response,
844
+ mock_web_response=mock_web_response
845
+ )
846
+
847
+ # Use in your tests...
848
+ ```
849
+
850
+ #### Best Practices
851
+
852
+ 1. **URL Matching**: URLs must match exactly, including query parameters
853
+ 2. **Response Body**: Always provide the body as a string (use `json.dumps()` for JSON data)
854
+ 3. **Status Codes**: Use realistic HTTP status codes (200, 404, 500, etc.)
855
+ 4. **Method Matching**: Specify the correct HTTP method that your contract uses
856
+ 5. **Error Testing**: Mock error responses to test error handling paths
857
+ 6. **Deterministic Tests**: Mock web responses ensure tests don't depend on external services
858
+
859
+ #### Notes
860
+
861
+ - Mock web responses are only available when using mock validators
862
+ - URL matching is exact - the full URL including query parameters must match
863
+ - The method field should match the HTTP method used by the contract
864
+ - Useful for testing contracts that interact with external APIs without network dependencies
865
+ - All standard HTTP methods are supported (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
866
+
602
867
  ### Custom Transaction Context
603
868
 
604
869
  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 +1243,8 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
978
1243
  ## 💬 Support
979
1244
 
980
1245
  - [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)
1246
+ - [Discord Community](https://discord.gg/qjCU4AWnKE)
1247
+ - [GitHub Issues](https://github.com/genlayerlabs/genlayer-testing-suite/issues)
983
1248
  - [Twitter](https://x.com/GenLayer)
984
1249
 
985
1250