[{"data":1,"prerenderedAt":1609},["ShallowReactive",2],{"page-\u002Fproperty-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002F":3},{"id":4,"title":5,"body":6,"description":1602,"extension":1603,"meta":1604,"navigation":191,"path":1605,"seo":1606,"stem":1607,"__hash__":1608},"content\u002Fproperty-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002Findex.md","Advanced Property-Based Testing",{"type":7,"value":8,"toc":1592},"minimark",[9,13,23,26,31,59,73,97,101,108,128,139,457,468,472,499,502,683,708,729,733,755,762,1112,1132,1136,1150,1159,1343,1360,1364,1367,1373,1377,1476,1480,1509,1534,1546,1560,1588],[10,11,5],"h1",{"id":12},"advanced-property-based-testing",[14,15,16,17,22],"p",{},"As test suites mature, traditional example-based assertions inevitably struggle to cover edge cases in complex data transformations, distributed state machines, and boundary-heavy algorithms. Advanced property-based testing shifts the paradigm from verifying specific inputs to validating system invariants across mathematically generated input spaces. Building on foundational concepts outlined in ",[18,19,21],"a",{"href":20},"\u002Fproperty-based-fuzz-testing-strategies\u002F","Property-Based & Fuzz Testing Strategies",", this guide transitions from basic randomization to deterministic, production-grade test generation. Engineers will learn how to architect resilient test matrices, optimize execution pipelines, and integrate generative testing into continuous delivery workflows without compromising feedback loops.",[14,24,25],{},"The core value proposition of advanced property-based testing lies in its ability to expose latent defects that manual test case authoring consistently misses: off-by-one errors in temporal calculations, race conditions in state transitions, and silent data corruption in serialization layers. By treating invariants as executable contracts, teams can achieve exponential coverage growth while maintaining linear maintenance overhead. This article details the architectural patterns, execution optimizations, and debugging workflows required to scale property-based testing across enterprise-grade Python codebases.",[27,28,30],"h2",{"id":29},"prerequisites-and-architectural-foundations","Prerequisites and Architectural Foundations",[14,32,33,34,38,39,42,43,42,46,49,50,53,54,58],{},"Before implementing complex generative workflows, engineering teams must establish baseline competency in the underlying primitives. Readers should already be comfortable with basic ",[35,36,37],"code",{},"@given"," decorators, built-in strategy composition (",[35,40,41],{},"st.integers()",", ",[35,44,45],{},"st.text()",[35,47,48],{},"st.lists()","), and seamless ",[35,51,52],{},"pytest"," fixture integration. For those needing a refresher on core mechanics, the ",[18,55,57],{"href":56},"\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-framework-fundamentals\u002F","Hypothesis Framework Fundamentals"," guide covers essential setup, database configuration, and basic invariant definition.",[14,60,61,62,65,66,69,70,72],{},"Advanced workflows require deeper familiarity with Python's ",[35,63,64],{},"typing"," module, ",[35,67,68],{},"dataclasses",", and ",[35,71,52],{}," parametrization. Modern property-based testing heavily leverages type hint introspection to auto-generate strategies, reducing boilerplate while maintaining strict contract enforcement. Engineers must also understand the architectural boundaries between unit-level property tests and integration-level generative suites. Unit-level PBT focuses on pure functions, deterministic transformations, and isolated data structures. Integration-level PBT extends these concepts to stateful systems, external service contracts, and cross-module data pipelines.",[14,74,75,76,42,79,42,82,85,86,89,90,42,93,96],{},"The recommended toolchain for production deployments includes ",[35,77,78],{},"hypothesis>=6.80.0",[35,80,81],{},"pytest>=7.4.0",[35,83,84],{},"pytest-xdist"," for parallel execution, and ",[35,87,88],{},"pytest-cov"," for coverage tracking. Python 3.9+ is mandatory to leverage modern type hinting features (",[35,91,92],{},"typing.Annotated",[35,94,95],{},"typing.Protocol",") that streamline strategy inference. Additionally, teams should implement AST manipulation or runtime type introspection when building domain-specific generators that must respect complex business constraints. Establishing these foundations ensures that advanced property-based testing scales predictably across monorepo architectures and microservice boundaries.",[27,98,100],{"id":99},"composing-custom-strategies-for-domain-specific-data","Composing Custom Strategies for Domain-Specific Data",[14,102,103,104,107],{},"Domain-driven testing requires generators that respect business constraints natively. Naive filtering with ",[35,105,106],{},"st.integers().filter(lambda x: x > 0)"," introduces severe shrinking bottlenecks and unpredictable test timeouts. The generator produces arbitrary integers, discards invalid ones, and retries until a valid example emerges. As rejection rates climb above 20%, the framework's shrinking algorithm degrades exponentially, often stalling on complex composite types.",[14,109,110,111,114,115,118,119,122,123,127],{},"Instead, engineers should leverage ",[35,112,113],{},"@composite"," decorators and ",[35,116,117],{},"st.builds"," to construct valid-by-construction strategies. Advanced patterns include recursive data generation for nested JSON payloads, temporal constraint modeling (e.g., ensuring ",[35,120,121],{},"end_date >= start_date","), and cross-field dependency validation. For implementation details on building efficient, reusable generators, refer to ",[18,124,126],{"href":125},"\u002Fproperty-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002Fgenerating-custom-strategies-with-hypothesisstrategies\u002F","Generating custom strategies with hypothesis.strategies",". Proper composition reduces example rejection rates from 80% to under 5%, dramatically improving CI throughput and shrinking predictability.",[14,129,130,131,134,135,138],{},"Below is a production-ready implementation demonstrating a valid-by-construction strategy for a financial transaction domain object. It avoids ",[35,132,133],{},".filter()"," entirely by using conditional branching and ",[35,136,137],{},"assume()"," for early rejection of mathematically impossible states.",[140,141,146],"pre",{"className":142,"code":143,"language":144,"meta":145,"style":145},"language-python shiki shiki-themes github-light github-dark","import datetime\nfrom dataclasses import dataclass\nfrom typing import Literal\nfrom hypothesis import given, settings, assume\nfrom hypothesis import strategies as st\nimport pytest\n\n@dataclass\nclass Transaction:\n transaction_id: str\n amount: float\n currency: Literal[\"USD\", \"EUR\", \"GBP\"]\n timestamp: datetime.datetime\n status: Literal[\"pending\", \"completed\", \"failed\"]\n\n@st.composite\ndef valid_transactions(draw: st.DrawFn) -> Transaction:\n # Pre-generate base components\n currency = draw(st.sampled_from([\"USD\", \"EUR\", \"GBP\"]))\n status = draw(st.sampled_from([\"pending\", \"completed\", \"failed\"]))\n \n # Valid-by-construction amount generation\n # Avoids negative amounts for completed transactions\n if status == \"completed\":\n amount = draw(st.floats(min_value=0.01, max_value=1_000_000.0, allow_nan=False))\n else:\n amount = draw(st.floats(min_value=0.0, max_value=1_000_000.0, allow_nan=False))\n \n # Temporal constraint: timestamp must be within a realistic business window\n base_date = draw(st.datetimes(min_value=datetime.datetime(2020, 1, 1), max_value=datetime.datetime.now()))\n \n # Early rejection for impossible states (e.g., zero amount on completed)\n assume(amount > 0.0)\n \n return Transaction(\n transaction_id=f\"TXN-{draw(st.text(min_size=8, max_size=12, alphabet='0123456789ABCDEF'))}\",\n amount=amount,\n currency=currency,\n timestamp=base_date,\n status=status\n )\n\n@given(valid_transactions())\n@settings(max_examples=200, database=None)\ndef test_transaction_invariants(txn: Transaction) -> None:\n assert txn.amount >= 0.0\n assert txn.currency in {\"USD\", \"EUR\", \"GBP\"}\n assert txn.status in {\"pending\", \"completed\", \"failed\"}\n \n # Business invariant: completed transactions cannot have zero balance\n if txn.status == \"completed\":\n assert txn.amount > 0.0\n","python","",[35,147,148,156,162,168,174,180,186,193,199,205,211,217,223,229,235,240,246,252,258,264,270,276,282,288,294,300,306,312,317,323,329,334,340,346,351,357,363,369,375,381,387,393,398,404,410,416,422,428,434,439,445,451],{"__ignoreMap":145},[149,150,153],"span",{"class":151,"line":152},"line",1,[149,154,155],{},"import datetime\n",[149,157,159],{"class":151,"line":158},2,[149,160,161],{},"from dataclasses import dataclass\n",[149,163,165],{"class":151,"line":164},3,[149,166,167],{},"from typing import Literal\n",[149,169,171],{"class":151,"line":170},4,[149,172,173],{},"from hypothesis import given, settings, assume\n",[149,175,177],{"class":151,"line":176},5,[149,178,179],{},"from hypothesis import strategies as st\n",[149,181,183],{"class":151,"line":182},6,[149,184,185],{},"import pytest\n",[149,187,189],{"class":151,"line":188},7,[149,190,192],{"emptyLinePlaceholder":191},true,"\n",[149,194,196],{"class":151,"line":195},8,[149,197,198],{},"@dataclass\n",[149,200,202],{"class":151,"line":201},9,[149,203,204],{},"class Transaction:\n",[149,206,208],{"class":151,"line":207},10,[149,209,210],{}," transaction_id: str\n",[149,212,214],{"class":151,"line":213},11,[149,215,216],{}," amount: float\n",[149,218,220],{"class":151,"line":219},12,[149,221,222],{}," currency: Literal[\"USD\", \"EUR\", \"GBP\"]\n",[149,224,226],{"class":151,"line":225},13,[149,227,228],{}," timestamp: datetime.datetime\n",[149,230,232],{"class":151,"line":231},14,[149,233,234],{}," status: Literal[\"pending\", \"completed\", \"failed\"]\n",[149,236,238],{"class":151,"line":237},15,[149,239,192],{"emptyLinePlaceholder":191},[149,241,243],{"class":151,"line":242},16,[149,244,245],{},"@st.composite\n",[149,247,249],{"class":151,"line":248},17,[149,250,251],{},"def valid_transactions(draw: st.DrawFn) -> Transaction:\n",[149,253,255],{"class":151,"line":254},18,[149,256,257],{}," # Pre-generate base components\n",[149,259,261],{"class":151,"line":260},19,[149,262,263],{}," currency = draw(st.sampled_from([\"USD\", \"EUR\", \"GBP\"]))\n",[149,265,267],{"class":151,"line":266},20,[149,268,269],{}," status = draw(st.sampled_from([\"pending\", \"completed\", \"failed\"]))\n",[149,271,273],{"class":151,"line":272},21,[149,274,275],{}," \n",[149,277,279],{"class":151,"line":278},22,[149,280,281],{}," # Valid-by-construction amount generation\n",[149,283,285],{"class":151,"line":284},23,[149,286,287],{}," # Avoids negative amounts for completed transactions\n",[149,289,291],{"class":151,"line":290},24,[149,292,293],{}," if status == \"completed\":\n",[149,295,297],{"class":151,"line":296},25,[149,298,299],{}," amount = draw(st.floats(min_value=0.01, max_value=1_000_000.0, allow_nan=False))\n",[149,301,303],{"class":151,"line":302},26,[149,304,305],{}," else:\n",[149,307,309],{"class":151,"line":308},27,[149,310,311],{}," amount = draw(st.floats(min_value=0.0, max_value=1_000_000.0, allow_nan=False))\n",[149,313,315],{"class":151,"line":314},28,[149,316,275],{},[149,318,320],{"class":151,"line":319},29,[149,321,322],{}," # Temporal constraint: timestamp must be within a realistic business window\n",[149,324,326],{"class":151,"line":325},30,[149,327,328],{}," base_date = draw(st.datetimes(min_value=datetime.datetime(2020, 1, 1), max_value=datetime.datetime.now()))\n",[149,330,332],{"class":151,"line":331},31,[149,333,275],{},[149,335,337],{"class":151,"line":336},32,[149,338,339],{}," # Early rejection for impossible states (e.g., zero amount on completed)\n",[149,341,343],{"class":151,"line":342},33,[149,344,345],{}," assume(amount > 0.0)\n",[149,347,349],{"class":151,"line":348},34,[149,350,275],{},[149,352,354],{"class":151,"line":353},35,[149,355,356],{}," return Transaction(\n",[149,358,360],{"class":151,"line":359},36,[149,361,362],{}," transaction_id=f\"TXN-{draw(st.text(min_size=8, max_size=12, alphabet='0123456789ABCDEF'))}\",\n",[149,364,366],{"class":151,"line":365},37,[149,367,368],{}," amount=amount,\n",[149,370,372],{"class":151,"line":371},38,[149,373,374],{}," currency=currency,\n",[149,376,378],{"class":151,"line":377},39,[149,379,380],{}," timestamp=base_date,\n",[149,382,384],{"class":151,"line":383},40,[149,385,386],{}," status=status\n",[149,388,390],{"class":151,"line":389},41,[149,391,392],{}," )\n",[149,394,396],{"class":151,"line":395},42,[149,397,192],{"emptyLinePlaceholder":191},[149,399,401],{"class":151,"line":400},43,[149,402,403],{},"@given(valid_transactions())\n",[149,405,407],{"class":151,"line":406},44,[149,408,409],{},"@settings(max_examples=200, database=None)\n",[149,411,413],{"class":151,"line":412},45,[149,414,415],{},"def test_transaction_invariants(txn: Transaction) -> None:\n",[149,417,419],{"class":151,"line":418},46,[149,420,421],{}," assert txn.amount >= 0.0\n",[149,423,425],{"class":151,"line":424},47,[149,426,427],{}," assert txn.currency in {\"USD\", \"EUR\", \"GBP\"}\n",[149,429,431],{"class":151,"line":430},48,[149,432,433],{}," assert txn.status in {\"pending\", \"completed\", \"failed\"}\n",[149,435,437],{"class":151,"line":436},49,[149,438,275],{},[149,440,442],{"class":151,"line":441},50,[149,443,444],{}," # Business invariant: completed transactions cannot have zero balance\n",[149,446,448],{"class":151,"line":447},51,[149,449,450],{}," if txn.status == \"completed\":\n",[149,452,454],{"class":151,"line":453},52,[149,455,456],{}," assert txn.amount > 0.0\n",[14,458,459,460,463,464,467],{},"This pattern eliminates the combinatorial explosion associated with ",[35,461,462],{},"st.one_of()"," and ",[35,465,466],{},"st.just()"," in complex strategies. By constructing objects directly from constrained primitives, the shrinking algorithm receives semantically valid inputs, enabling it to reduce failing examples to minimal, human-readable counterexamples in milliseconds rather than minutes.",[27,469,471],{"id":470},"execution-optimization-and-ci-pipeline-integration","Execution Optimization and CI Pipeline Integration",[14,473,474,475,478,479,482,483,486,487,490,491,494,495,498],{},"Property-based tests introduce non-deterministic execution times that can destabilize CI feedback loops. Optimization requires strategic ",[35,476,477],{},"@settings"," configuration, including ",[35,480,481],{},"deadline"," adjustments, ",[35,484,485],{},"phases"," control, and ",[35,488,489],{},"verbosity"," tuning for headless environments. Teams should implement environment-aware profiles using ",[35,492,493],{},"HYPOTHESIS_PROFILE"," to scale ",[35,496,497],{},"max_examples"," based on pipeline stage. For comprehensive pipeline configuration patterns, see Integrating Fuzz Tests into CI.",[14,500,501],{},"The following configuration demonstrates a production-grade, environment-aware settings profile that scales execution parameters dynamically based on the CI context:",[140,503,505],{"className":142,"code":504,"language":144,"meta":145,"style":145},"import os\nfrom hypothesis import settings, Phase, Verbosity\n\n# Define environment-specific profiles\nif os.getenv(\"CI\") == \"true\":\n # Fast feedback for PR checks\n settings.register_profile(\n \"ci_pr\",\n max_examples=50,\n deadline=500,\n phases=[Phase.generate, Phase.shrink, Phase.explain],\n verbosity=Verbosity.quiet,\n database=None # Disable DB for ephemeral runners\n )\n settings.load_profile(\"ci_pr\")\nelif os.getenv(\"CI_NIGHTLY\") == \"true\":\n # Deep exploration for nightly builds\n settings.register_profile(\n \"ci_nightly\",\n max_examples=1000,\n deadline=2000,\n phases=[Phase.generate, Phase.shrink, Phase.explain],\n verbosity=Verbosity.normal,\n database=\".hypothesis\u002Fexamples\"\n )\n settings.load_profile(\"ci_nightly\")\nelse:\n # Developer local environment\n settings.register_profile(\n \"dev\",\n max_examples=100,\n deadline=1000,\n phases=[Phase.generate, Phase.shrink, Phase.explain],\n verbosity=Verbosity.verbose,\n database=\".hypothesis\u002Fexamples\"\n )\n settings.load_profile(\"dev\")\n",[35,506,507,512,517,521,526,531,536,541,546,551,556,561,566,571,575,580,585,590,594,599,604,609,613,618,623,627,632,637,642,646,651,656,661,665,670,674,678],{"__ignoreMap":145},[149,508,509],{"class":151,"line":152},[149,510,511],{},"import os\n",[149,513,514],{"class":151,"line":158},[149,515,516],{},"from hypothesis import settings, Phase, Verbosity\n",[149,518,519],{"class":151,"line":164},[149,520,192],{"emptyLinePlaceholder":191},[149,522,523],{"class":151,"line":170},[149,524,525],{},"# Define environment-specific profiles\n",[149,527,528],{"class":151,"line":176},[149,529,530],{},"if os.getenv(\"CI\") == \"true\":\n",[149,532,533],{"class":151,"line":182},[149,534,535],{}," # Fast feedback for PR checks\n",[149,537,538],{"class":151,"line":188},[149,539,540],{}," settings.register_profile(\n",[149,542,543],{"class":151,"line":195},[149,544,545],{}," \"ci_pr\",\n",[149,547,548],{"class":151,"line":201},[149,549,550],{}," max_examples=50,\n",[149,552,553],{"class":151,"line":207},[149,554,555],{}," deadline=500,\n",[149,557,558],{"class":151,"line":213},[149,559,560],{}," phases=[Phase.generate, Phase.shrink, Phase.explain],\n",[149,562,563],{"class":151,"line":219},[149,564,565],{}," verbosity=Verbosity.quiet,\n",[149,567,568],{"class":151,"line":225},[149,569,570],{}," database=None # Disable DB for ephemeral runners\n",[149,572,573],{"class":151,"line":231},[149,574,392],{},[149,576,577],{"class":151,"line":237},[149,578,579],{}," settings.load_profile(\"ci_pr\")\n",[149,581,582],{"class":151,"line":242},[149,583,584],{},"elif os.getenv(\"CI_NIGHTLY\") == \"true\":\n",[149,586,587],{"class":151,"line":248},[149,588,589],{}," # Deep exploration for nightly builds\n",[149,591,592],{"class":151,"line":254},[149,593,540],{},[149,595,596],{"class":151,"line":260},[149,597,598],{}," \"ci_nightly\",\n",[149,600,601],{"class":151,"line":266},[149,602,603],{}," max_examples=1000,\n",[149,605,606],{"class":151,"line":272},[149,607,608],{}," deadline=2000,\n",[149,610,611],{"class":151,"line":278},[149,612,560],{},[149,614,615],{"class":151,"line":284},[149,616,617],{}," verbosity=Verbosity.normal,\n",[149,619,620],{"class":151,"line":290},[149,621,622],{}," database=\".hypothesis\u002Fexamples\"\n",[149,624,625],{"class":151,"line":296},[149,626,392],{},[149,628,629],{"class":151,"line":302},[149,630,631],{}," settings.load_profile(\"ci_nightly\")\n",[149,633,634],{"class":151,"line":308},[149,635,636],{},"else:\n",[149,638,639],{"class":151,"line":314},[149,640,641],{}," # Developer local environment\n",[149,643,644],{"class":151,"line":319},[149,645,540],{},[149,647,648],{"class":151,"line":325},[149,649,650],{}," \"dev\",\n",[149,652,653],{"class":151,"line":331},[149,654,655],{}," max_examples=100,\n",[149,657,658],{"class":151,"line":336},[149,659,660],{}," deadline=1000,\n",[149,662,663],{"class":151,"line":342},[149,664,560],{},[149,666,667],{"class":151,"line":348},[149,668,669],{}," verbosity=Verbosity.verbose,\n",[149,671,672],{"class":151,"line":353},[149,673,622],{},[149,675,676],{"class":151,"line":359},[149,677,392],{},[149,679,680],{"class":151,"line":365},[149,681,682],{}," settings.load_profile(\"dev\")\n",[14,684,685,686,688,689,692,693,696,697,699,700,703,704,707],{},"Parallel execution via ",[35,687,84],{}," requires careful database isolation to prevent seed collision and race conditions during example caching. When running ",[35,690,691],{},"pytest -n auto",", each worker process must maintain an isolated ",[35,694,695],{},".hypothesis"," directory or use a shared network-backed database with atomic writes. Teams should cache the ",[35,698,695],{}," directory in CI pipelines using standard caching mechanisms (",[35,701,702],{},"actions\u002Fcache"," for GitHub Actions, ",[35,705,706],{},"cache"," directives for GitLab CI) to preserve historical failing examples across runs.",[14,709,710,711,714,715,718,719,722,723,725,726,728],{},"Memory profiling and garbage collection tuning become critical during high-volume test generation. Python's generational GC can trigger frequent minor collections when generating millions of short-lived strategy objects. To mitigate this, disable GC temporarily during heavy generation phases using ",[35,712,713],{},"gc.disable()",", or tune ",[35,716,717],{},"gc.set_threshold()"," based on observed allocation patterns. Use ",[35,720,721],{},"pytest --hypothesis-show-statistics"," to monitor strategy generation times, rejection rates, and shrinking durations. This telemetry enables data-driven adjustments to ",[35,724,481],{}," thresholds and ",[35,727,485],{}," configuration, ensuring deterministic CI performance.",[27,730,732],{"id":731},"debugging-shrinking-failures-and-analyzing-counterexamples","Debugging Shrinking Failures and Analyzing Counterexamples",[14,734,735,736,739,740,743,744,747,748,750,751,754],{},"When a property test fails, the framework's shrinking algorithm attempts to minimize the failing input to its simplest reproducible form. However, complex stateful systems or custom strategies can produce opaque counterexamples that obscure the root cause. Engineers must leverage ",[35,737,738],{},"@reproduce_failure"," decorators, ",[35,741,742],{},"hypothesis.extra"," debugging utilities, and custom ",[35,745,746],{},"report_multiple_bugs"," configurations. Understanding the difference between ",[35,749,137],{}," (input rejection) and ",[35,752,753],{},"filter()"," (post-generation validation) is critical for diagnosing shrinking stalls. For step-by-step failure triage workflows, consult Debugging failed hypothesis examples.",[14,756,757,758,761],{},"Stateful protocol validation introduces additional complexity. The ",[35,759,760],{},"RuleBasedStateMachine"," class models complex state transitions, API contracts, and resource lifecycle management. When a state machine fails, the framework must shrink not just input values, but entire execution traces. The following example demonstrates a stateful test for a simplified key-value store with transactional guarantees:",[140,763,765],{"className":142,"code":764,"language":144,"meta":145,"style":145},"from hypothesis.stateful import RuleBasedStateMachine, rule, invariant, precondition, Bundle\nimport hypothesis.strategies as st\nfrom typing import Dict, Any\n\nclass TransactionalKVStore:\n def __init__(self) -> None:\n self._data: Dict[str, Any] = {}\n self._in_transaction: bool = False\n self._buffer: Dict[str, Any] = {}\n\n def begin_transaction(self) -> None:\n self._in_transaction = True\n self._buffer = {}\n\n def put(self, key: str, value: Any) -> None:\n if self._in_transaction:\n self._buffer[key] = value\n else:\n self._data[key] = value\n\n def commit(self) -> None:\n if self._in_transaction:\n self._data.update(self._buffer)\n self._buffer.clear()\n self._in_transaction = False\n\n def rollback(self) -> None:\n if self._in_transaction:\n self._buffer.clear()\n self._in_transaction = False\n\n def get(self, key: str) -> Any:\n return self._data.get(key)\n\nclass KVStateMachine(RuleBasedStateMachine):\n keys = Bundle(\"keys\")\n store: TransactionalKVStore\n\n def __init__(self) -> None:\n super().__init__()\n self.store = TransactionalKVStore()\n\n @rule(target=keys, key=st.text(min_size=1, max_size=20))\n def generate_key(self, key: str) -> str:\n return key\n\n @rule(key=keys)\n def begin_tx(self, key: str) -> None:\n self.store.begin_transaction()\n\n @rule(key=keys, value=st.integers())\n def put_value(self, key: str, value: int) -> None:\n self.store.put(key, value)\n\n @rule()\n def commit_tx(self) -> None:\n self.store.commit()\n\n @rule()\n def rollback_tx(self) -> None:\n self.store.rollback()\n\n @invariant()\n def no_data_loss_on_rollback(self) -> None:\n # Invariant: rollback never mutates committed state\n initial_state = dict(self.store._data)\n self.store.rollback()\n assert self.store._data == initial_state\n\nTestKVStore = KVStateMachine.TestCase\n",[35,766,767,772,777,782,786,791,796,801,806,811,815,820,825,830,834,839,844,849,853,858,862,867,871,876,881,886,890,895,899,903,907,911,916,921,925,930,935,940,944,948,953,958,962,967,972,977,981,986,991,996,1000,1005,1010,1016,1021,1027,1033,1039,1044,1049,1055,1061,1066,1072,1078,1084,1090,1095,1101,1106],{"__ignoreMap":145},[149,768,769],{"class":151,"line":152},[149,770,771],{},"from hypothesis.stateful import RuleBasedStateMachine, rule, invariant, precondition, Bundle\n",[149,773,774],{"class":151,"line":158},[149,775,776],{},"import hypothesis.strategies as st\n",[149,778,779],{"class":151,"line":164},[149,780,781],{},"from typing import Dict, Any\n",[149,783,784],{"class":151,"line":170},[149,785,192],{"emptyLinePlaceholder":191},[149,787,788],{"class":151,"line":176},[149,789,790],{},"class TransactionalKVStore:\n",[149,792,793],{"class":151,"line":182},[149,794,795],{}," def __init__(self) -> None:\n",[149,797,798],{"class":151,"line":188},[149,799,800],{}," self._data: Dict[str, Any] = {}\n",[149,802,803],{"class":151,"line":195},[149,804,805],{}," self._in_transaction: bool = False\n",[149,807,808],{"class":151,"line":201},[149,809,810],{}," self._buffer: Dict[str, Any] = {}\n",[149,812,813],{"class":151,"line":207},[149,814,192],{"emptyLinePlaceholder":191},[149,816,817],{"class":151,"line":213},[149,818,819],{}," def begin_transaction(self) -> None:\n",[149,821,822],{"class":151,"line":219},[149,823,824],{}," self._in_transaction = True\n",[149,826,827],{"class":151,"line":225},[149,828,829],{}," self._buffer = {}\n",[149,831,832],{"class":151,"line":231},[149,833,192],{"emptyLinePlaceholder":191},[149,835,836],{"class":151,"line":237},[149,837,838],{}," def put(self, key: str, value: Any) -> None:\n",[149,840,841],{"class":151,"line":242},[149,842,843],{}," if self._in_transaction:\n",[149,845,846],{"class":151,"line":248},[149,847,848],{}," self._buffer[key] = value\n",[149,850,851],{"class":151,"line":254},[149,852,305],{},[149,854,855],{"class":151,"line":260},[149,856,857],{}," self._data[key] = value\n",[149,859,860],{"class":151,"line":266},[149,861,192],{"emptyLinePlaceholder":191},[149,863,864],{"class":151,"line":272},[149,865,866],{}," def commit(self) -> None:\n",[149,868,869],{"class":151,"line":278},[149,870,843],{},[149,872,873],{"class":151,"line":284},[149,874,875],{}," self._data.update(self._buffer)\n",[149,877,878],{"class":151,"line":290},[149,879,880],{}," self._buffer.clear()\n",[149,882,883],{"class":151,"line":296},[149,884,885],{}," self._in_transaction = False\n",[149,887,888],{"class":151,"line":302},[149,889,192],{"emptyLinePlaceholder":191},[149,891,892],{"class":151,"line":308},[149,893,894],{}," def rollback(self) -> None:\n",[149,896,897],{"class":151,"line":314},[149,898,843],{},[149,900,901],{"class":151,"line":319},[149,902,880],{},[149,904,905],{"class":151,"line":325},[149,906,885],{},[149,908,909],{"class":151,"line":331},[149,910,192],{"emptyLinePlaceholder":191},[149,912,913],{"class":151,"line":336},[149,914,915],{}," def get(self, key: str) -> Any:\n",[149,917,918],{"class":151,"line":342},[149,919,920],{}," return self._data.get(key)\n",[149,922,923],{"class":151,"line":348},[149,924,192],{"emptyLinePlaceholder":191},[149,926,927],{"class":151,"line":353},[149,928,929],{},"class KVStateMachine(RuleBasedStateMachine):\n",[149,931,932],{"class":151,"line":359},[149,933,934],{}," keys = Bundle(\"keys\")\n",[149,936,937],{"class":151,"line":365},[149,938,939],{}," store: TransactionalKVStore\n",[149,941,942],{"class":151,"line":371},[149,943,192],{"emptyLinePlaceholder":191},[149,945,946],{"class":151,"line":377},[149,947,795],{},[149,949,950],{"class":151,"line":383},[149,951,952],{}," super().__init__()\n",[149,954,955],{"class":151,"line":389},[149,956,957],{}," self.store = TransactionalKVStore()\n",[149,959,960],{"class":151,"line":395},[149,961,192],{"emptyLinePlaceholder":191},[149,963,964],{"class":151,"line":400},[149,965,966],{}," @rule(target=keys, key=st.text(min_size=1, max_size=20))\n",[149,968,969],{"class":151,"line":406},[149,970,971],{}," def generate_key(self, key: str) -> str:\n",[149,973,974],{"class":151,"line":412},[149,975,976],{}," return key\n",[149,978,979],{"class":151,"line":418},[149,980,192],{"emptyLinePlaceholder":191},[149,982,983],{"class":151,"line":424},[149,984,985],{}," @rule(key=keys)\n",[149,987,988],{"class":151,"line":430},[149,989,990],{}," def begin_tx(self, key: str) -> None:\n",[149,992,993],{"class":151,"line":436},[149,994,995],{}," self.store.begin_transaction()\n",[149,997,998],{"class":151,"line":441},[149,999,192],{"emptyLinePlaceholder":191},[149,1001,1002],{"class":151,"line":447},[149,1003,1004],{}," @rule(key=keys, value=st.integers())\n",[149,1006,1007],{"class":151,"line":453},[149,1008,1009],{}," def put_value(self, key: str, value: int) -> None:\n",[149,1011,1013],{"class":151,"line":1012},53,[149,1014,1015],{}," self.store.put(key, value)\n",[149,1017,1019],{"class":151,"line":1018},54,[149,1020,192],{"emptyLinePlaceholder":191},[149,1022,1024],{"class":151,"line":1023},55,[149,1025,1026],{}," @rule()\n",[149,1028,1030],{"class":151,"line":1029},56,[149,1031,1032],{}," def commit_tx(self) -> None:\n",[149,1034,1036],{"class":151,"line":1035},57,[149,1037,1038],{}," self.store.commit()\n",[149,1040,1042],{"class":151,"line":1041},58,[149,1043,192],{"emptyLinePlaceholder":191},[149,1045,1047],{"class":151,"line":1046},59,[149,1048,1026],{},[149,1050,1052],{"class":151,"line":1051},60,[149,1053,1054],{}," def rollback_tx(self) -> None:\n",[149,1056,1058],{"class":151,"line":1057},61,[149,1059,1060],{}," self.store.rollback()\n",[149,1062,1064],{"class":151,"line":1063},62,[149,1065,192],{"emptyLinePlaceholder":191},[149,1067,1069],{"class":151,"line":1068},63,[149,1070,1071],{}," @invariant()\n",[149,1073,1075],{"class":151,"line":1074},64,[149,1076,1077],{}," def no_data_loss_on_rollback(self) -> None:\n",[149,1079,1081],{"class":151,"line":1080},65,[149,1082,1083],{}," # Invariant: rollback never mutates committed state\n",[149,1085,1087],{"class":151,"line":1086},66,[149,1088,1089],{}," initial_state = dict(self.store._data)\n",[149,1091,1093],{"class":151,"line":1092},67,[149,1094,1060],{},[149,1096,1098],{"class":151,"line":1097},68,[149,1099,1100],{}," assert self.store._data == initial_state\n",[149,1102,1104],{"class":151,"line":1103},69,[149,1105,192],{"emptyLinePlaceholder":191},[149,1107,1109],{"class":151,"line":1108},70,[149,1110,1111],{},"TestKVStore = KVStateMachine.TestCase\n",[14,1113,1114,1115,1118,1119,1121,1122,1125,1126,1128,1129,1131],{},"When debugging stateful failures, enable ",[35,1116,1117],{},"report_multiple_bugs=True"," in ",[35,1120,477],{}," to capture all invariant violations in a single run, rather than stopping at the first failure. Use ",[35,1123,1124],{},"hypothesis.extra.pytest"," to integrate with ",[35,1127,52],{},"'s native debugging tools. Parse CI logs for ",[35,1130,738],{}," decorators, which encode the exact failing seed and execution trace. Extract these traces into isolated unit tests to verify fixes without re-running the full generation cycle.",[27,1133,1135],{"id":1134},"cross-ecosystem-fuzzing-and-c-extension-boundaries","Cross-Ecosystem Fuzzing and C-Extension Boundaries",[14,1137,1138,1139,1142,1143,1146,1147,1149],{},"Pure Python property tests cannot safely exercise C-extensions, memory-managed libraries, or network-bound services. Advanced architectures combine ",[35,1140,1141],{},"hypothesis"," for input generation with specialized fuzzers like ",[35,1144,1145],{},"atheris"," for low-level boundary testing. By exporting ",[35,1148,1141],{}," examples to byte buffers and feeding them into coverage-guided fuzzers, teams achieve hybrid testing that catches segmentation faults, memory leaks, and undefined behavior. For practical implementation of API and native boundary testing, review Fuzzing REST APIs with atheris.",[14,1151,1152,1153,1155,1156,1158],{},"The following implementation demonstrates a hybrid fuzzing bridge that serializes ",[35,1154,1141],{},"-generated inputs into structured byte buffers for consumption by ",[35,1157,1145],{},":",[140,1160,1162],{"className":142,"code":1161,"language":144,"meta":145,"style":145},"import struct\nimport atheris\nimport hypothesis.strategies as st\nfrom hypothesis import given, settings, Phase\n\n# Target C-extension or native function (simulated)\ndef native_parse_payload(data: bytes) -> None:\n # Simulates a native parser that expects a 4-byte header + variable payload\n if len(data) \u003C 4:\n raise ValueError(\"Header too short\")\n length = struct.unpack_from(\">I\", data, 0)[0]\n if length > len(data) - 4:\n raise BufferError(\"Payload length mismatch\")\n # Native parsing logic would execute here\n pass\n\ndef test_hypothesis_to_atheris_bridge() -> None:\n # Define a strategy that matches the native protocol\n payload_strategy = st.binary(min_size=4, max_size=256)\n \n # Generate examples and feed to atheris\n @given(payload_strategy)\n @settings(max_examples=500, phases=[Phase.generate])\n def run_fuzz(data: bytes) -> None:\n try:\n native_parse_payload(data)\n except (ValueError, BufferError):\n pass # Expected validation errors\n except Exception as e:\n # Unexpected crashes indicate memory safety violations\n raise AssertionError(f\"Native boundary violation: {e}\") from e\n\n # In a real atheris setup, this would be registered as a fuzz target\n # atheris.Setup(sys.argv, lambda data: native_parse_payload(data))\n # atheris.Fuzz()\n \n run_fuzz()\n",[35,1163,1164,1169,1174,1178,1183,1187,1192,1197,1202,1207,1212,1217,1222,1227,1232,1237,1241,1246,1251,1256,1260,1265,1270,1275,1280,1285,1290,1295,1300,1305,1310,1315,1319,1324,1329,1334,1338],{"__ignoreMap":145},[149,1165,1166],{"class":151,"line":152},[149,1167,1168],{},"import struct\n",[149,1170,1171],{"class":151,"line":158},[149,1172,1173],{},"import atheris\n",[149,1175,1176],{"class":151,"line":164},[149,1177,776],{},[149,1179,1180],{"class":151,"line":170},[149,1181,1182],{},"from hypothesis import given, settings, Phase\n",[149,1184,1185],{"class":151,"line":176},[149,1186,192],{"emptyLinePlaceholder":191},[149,1188,1189],{"class":151,"line":182},[149,1190,1191],{},"# Target C-extension or native function (simulated)\n",[149,1193,1194],{"class":151,"line":188},[149,1195,1196],{},"def native_parse_payload(data: bytes) -> None:\n",[149,1198,1199],{"class":151,"line":195},[149,1200,1201],{}," # Simulates a native parser that expects a 4-byte header + variable payload\n",[149,1203,1204],{"class":151,"line":201},[149,1205,1206],{}," if len(data) \u003C 4:\n",[149,1208,1209],{"class":151,"line":207},[149,1210,1211],{}," raise ValueError(\"Header too short\")\n",[149,1213,1214],{"class":151,"line":213},[149,1215,1216],{}," length = struct.unpack_from(\">I\", data, 0)[0]\n",[149,1218,1219],{"class":151,"line":219},[149,1220,1221],{}," if length > len(data) - 4:\n",[149,1223,1224],{"class":151,"line":225},[149,1225,1226],{}," raise BufferError(\"Payload length mismatch\")\n",[149,1228,1229],{"class":151,"line":231},[149,1230,1231],{}," # Native parsing logic would execute here\n",[149,1233,1234],{"class":151,"line":237},[149,1235,1236],{}," pass\n",[149,1238,1239],{"class":151,"line":242},[149,1240,192],{"emptyLinePlaceholder":191},[149,1242,1243],{"class":151,"line":248},[149,1244,1245],{},"def test_hypothesis_to_atheris_bridge() -> None:\n",[149,1247,1248],{"class":151,"line":254},[149,1249,1250],{}," # Define a strategy that matches the native protocol\n",[149,1252,1253],{"class":151,"line":260},[149,1254,1255],{}," payload_strategy = st.binary(min_size=4, max_size=256)\n",[149,1257,1258],{"class":151,"line":266},[149,1259,275],{},[149,1261,1262],{"class":151,"line":272},[149,1263,1264],{}," # Generate examples and feed to atheris\n",[149,1266,1267],{"class":151,"line":278},[149,1268,1269],{}," @given(payload_strategy)\n",[149,1271,1272],{"class":151,"line":284},[149,1273,1274],{}," @settings(max_examples=500, phases=[Phase.generate])\n",[149,1276,1277],{"class":151,"line":290},[149,1278,1279],{}," def run_fuzz(data: bytes) -> None:\n",[149,1281,1282],{"class":151,"line":296},[149,1283,1284],{}," try:\n",[149,1286,1287],{"class":151,"line":302},[149,1288,1289],{}," native_parse_payload(data)\n",[149,1291,1292],{"class":151,"line":308},[149,1293,1294],{}," except (ValueError, BufferError):\n",[149,1296,1297],{"class":151,"line":314},[149,1298,1299],{}," pass # Expected validation errors\n",[149,1301,1302],{"class":151,"line":319},[149,1303,1304],{}," except Exception as e:\n",[149,1306,1307],{"class":151,"line":325},[149,1308,1309],{}," # Unexpected crashes indicate memory safety violations\n",[149,1311,1312],{"class":151,"line":331},[149,1313,1314],{}," raise AssertionError(f\"Native boundary violation: {e}\") from e\n",[149,1316,1317],{"class":151,"line":336},[149,1318,192],{"emptyLinePlaceholder":191},[149,1320,1321],{"class":151,"line":342},[149,1322,1323],{}," # In a real atheris setup, this would be registered as a fuzz target\n",[149,1325,1326],{"class":151,"line":348},[149,1327,1328],{}," # atheris.Setup(sys.argv, lambda data: native_parse_payload(data))\n",[149,1330,1331],{"class":151,"line":353},[149,1332,1333],{}," # atheris.Fuzz()\n",[149,1335,1336],{"class":151,"line":359},[149,1337,275],{},[149,1339,1340],{"class":151,"line":365},[149,1341,1342],{}," run_fuzz()\n",[14,1344,1345,1346,1348,1349,1351,1352,1355,1356,1359],{},"This hybrid approach leverages ",[35,1347,1141],{}," for semantic correctness and ",[35,1350,1145],{}," for low-level memory and boundary testing. Timeout management is critical when bridging ecosystems; use ",[35,1353,1354],{},"signal.alarm()"," or ",[35,1357,1358],{},"threading.Timer"," to enforce execution limits on native calls. Crash triage across language boundaries requires symbol resolution, core dump analysis, and address sanitizer (ASan) integration. By serializing structured inputs into raw byte streams, teams can systematically probe memory boundaries while maintaining the reproducibility guarantees of property-based testing.",[27,1361,1363],{"id":1362},"conclusion-and-maintenance-strategies","Conclusion and Maintenance Strategies",[14,1365,1366],{},"Advanced property-based testing requires ongoing curation. Teams should implement strategy versioning, monitor shrinking performance metrics, and establish review gates for new invariant definitions. Regular pruning of redundant examples and database compaction prevents test suite bloat. By treating generative tests as living documentation of system invariants, engineering teams achieve sustainable coverage growth without linear test maintenance costs.",[14,1368,1369,1370,1372],{},"Maintenance workflows should include quarterly reviews of ",[35,1371,695],{}," database size, shrinking duration trends, and rejection rate metrics. Archive historical failing examples in version control to prevent regression. As business logic evolves, refactor composite strategies to reflect updated domain constraints rather than appending ad-hoc filters. This disciplined approach ensures that property-based testing remains a scalable, deterministic asset throughout the software lifecycle.",[27,1374,1376],{"id":1375},"common-pitfalls-and-antipatterns","Common Pitfalls and Antipatterns",[1378,1379,1380,1396],"table",{},[1381,1382,1383],"thead",{},[1384,1385,1386,1390,1393],"tr",{},[1387,1388,1389],"th",{},"Pitfall",[1387,1391,1392],{},"Consequence",[1387,1394,1395],{},"Mitigation",[1397,1398,1399,1424,1441,1459],"tbody",{},[1384,1400,1401,1410,1413],{},[1402,1403,1404,1405,463,1407,1409],"td",{},"Overusing ",[35,1406,462],{},[35,1408,466],{}," in complex strategies",[1402,1411,1412],{},"Exponential strategy space explosion and severe shrinking degradation",[1402,1414,1415,1416,1419,1420,1423],{},"Use ",[35,1417,1418],{},"st.sampled_from()"," for finite sets and ",[35,1421,1422],{},"st.builds()"," for structured composition",[1384,1425,1426,1432,1435],{},[1402,1427,1428,1429,1431],{},"Relying on ",[35,1430,133],{}," for business rule enforcement",[1402,1433,1434],{},"High rejection rates, timeout failures, and non-deterministic CI runs",[1402,1436,1437,1438,1440],{},"Implement valid-by-construction generators using ",[35,1439,113],{}," and conditional branching",[1384,1442,1443,1450,1453],{},[1402,1444,1445,1446,1449],{},"Ignoring ",[35,1447,1448],{},"database=DirectoryExampleDatabase"," in CI",[1402,1451,1452],{},"Loss of historical failing examples, forcing re-discovery of edge cases",[1402,1454,1455,1456,1458],{},"Persist ",[35,1457,695],{}," directories in CI cache and commit critical examples to version control",[1384,1460,1461,1468,1471],{},[1402,1462,1463,1464,1467],{},"Setting ",[35,1465,1466],{},"deadline=None"," globally",[1402,1469,1470],{},"Masking performance regressions and allowing infinite loops in shrinking",[1402,1472,1473,1474],{},"Use environment-specific deadlines and profile slow strategies with ",[35,1475,721],{},[27,1477,1479],{"id":1478},"frequently-asked-questions","Frequently Asked Questions",[14,1481,1482,1486,1487,1489,1490,1492,1493,1495,1496,1498,1499,1501,1502,1505,1506,1508],{},[1483,1484,1485],"strong",{},"How do I prevent property-based tests from slowing down my CI pipeline?","\nImplement environment-aware ",[35,1488,477],{}," profiles that scale ",[35,1491,497],{}," based on pipeline stage (e.g., 50 for PRs, 500 for nightly builds). Use ",[35,1494,84],{}," for parallel execution, cache the ",[35,1497,695],{}," database, and avoid ",[35,1500,133],{},"-heavy strategies. Monitor execution times with ",[35,1503,1504],{},"--hypothesis-show-statistics"," and adjust ",[35,1507,481],{}," thresholds accordingly.",[14,1510,1511,1521,1522,1524,1525,1527,1528,1530,1531,1533],{},[1483,1512,1513,1514,1517,1518,1520],{},"When should I use ",[35,1515,1516],{},"hypothesis.strategies.filter()"," vs ",[35,1519,137],{},"?","\nUse ",[35,1523,137],{}," inside ",[35,1526,37],{}," functions to reject invalid inputs early, preserving shrinking efficiency. Avoid ",[35,1529,133],{}," on strategies when possible, as it forces the generator to retry until a valid example is found, causing exponential slowdowns. Prefer valid-by-construction strategies using ",[35,1532,113],{}," for complex constraints.",[14,1535,1536,1539,1540,1542,1543,1545],{},[1483,1537,1538],{},"How do I reproduce a failing property test in production?","\nHypothesis automatically logs a ",[35,1541,738],{}," decorator in the test output. Copy this decorator into your test function to force the exact failing input. For CI environments, ensure the ",[35,1544,695],{}," database is cached and committed to version control to maintain deterministic replay across machines.",[14,1547,1548,1551,1552,1554,1555,1348,1557,1559],{},[1483,1549,1550],{},"Can I combine hypothesis with traditional fuzzers like AFL or atheris?","\nYes. Use ",[35,1553,1141],{}," to generate structured, type-safe inputs, then serialize them to byte buffers or JSON payloads for consumption by coverage-guided fuzzers. This hybrid approach leverages ",[35,1556,1141],{},[35,1558,1145],{}," for low-level memory and boundary testing.",[14,1561,1562,1565,1566,1355,1569,1571,1572,1574,1575,1355,1578,1581,1582,1584,1585,1587],{},[1483,1563,1564],{},"How do I test async functions or external API calls with property-based testing?","\nWrap async functions using ",[35,1567,1568],{},"pytest-asyncio",[35,1570,1141],{},"'s ",[35,1573,37],{}," with async test functions. For external APIs, use ",[35,1576,1577],{},"responses",[35,1579,1580],{},"vcrpy"," to mock network layers, and apply ",[35,1583,117],{}," to generate valid request payloads. Always isolate side effects and use ",[35,1586,137],{}," to skip invalid state combinations.",[1589,1590,1591],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":145,"searchDepth":158,"depth":158,"links":1593},[1594,1595,1596,1597,1598,1599,1600,1601],{"id":29,"depth":158,"text":30},{"id":99,"depth":158,"text":100},{"id":470,"depth":158,"text":471},{"id":731,"depth":158,"text":732},{"id":1134,"depth":158,"text":1135},{"id":1362,"depth":158,"text":1363},{"id":1375,"depth":158,"text":1376},{"id":1478,"depth":158,"text":1479},"As test suites mature, traditional example-based assertions inevitably struggle to cover edge cases in complex data transformations, distributed state machines, and boundary-heavy algorithms. Advanced property-based testing shifts the paradigm from verifying specific inputs to validating system invariants across mathematically generated input spaces. Building on foundational concepts outlined in Property-Based & Fuzz Testing Strategies, this guide transitions from basic randomization to deterministic, production-grade test generation. Engineers will learn how to architect resilient test matrices, optimize execution pipelines, and integrate generative testing into continuous delivery workflows without compromising feedback loops.","md",{},"\u002Fproperty-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing",{"title":5,"description":1602},"property-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002Findex","K_VZbpuxRU3dL4l3fg1elfiNLf9JbAPQKFFLO_HWGF0",1778004577658]