|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +# vim:fenc=utf-8 |
| 3 | +# |
| 4 | +# Copyright (c) 2025 Fayaz Yusuf Khan <[email protected]> |
| 5 | +# |
| 6 | +# Distributed under terms of the MIT license. |
1 | 7 | """Test cases written using Hypothesis stateful testing framework.""" |
2 | | -from hypothesis import assume, given, settings, strategies as st |
| 8 | +from hypothesis import HealthCheck, settings, strategies as st |
3 | 9 | from hypothesis.stateful import Bundle, RuleBasedStateMachine, consumes, invariant, rule |
4 | 10 | from sqlalchemy import Column, Integer, Boolean, create_engine |
5 | 11 | from sqlalchemy.ext.declarative import declarative_base |
6 | | -from sqlalchemy.orm import sessionmaker |
| 12 | +from sqlalchemy.orm import joinedload, sessionmaker |
7 | 13 |
|
8 | 14 | from sqlalchemy_mptt import BaseNestedSets, mptt_sessionmaker |
9 | 15 |
|
@@ -37,43 +43,52 @@ def __init__(self): |
37 | 43 | def add_root_node(self, visible): |
38 | 44 | node = Tree(visible=visible) |
39 | 45 | self.session.add(node) |
40 | | - self.session.flush() |
| 46 | + self.session.commit() |
41 | 47 | assert node.left < node.right |
42 | 48 | return node |
43 | 49 |
|
44 | 50 | @rule(node=consumes(node)) |
45 | 51 | def delete_node(self, node): |
46 | | - assume(node in self.session) |
| 52 | + # Consume all descendants of the node |
| 53 | + for name, value in list(self.names_to_values.items()): |
| 54 | + if value not in self.session or node.is_ancestor_of(value): |
| 55 | + for var_reference in self.bundles["node"][:]: |
| 56 | + if var_reference.name == name: |
| 57 | + self.bundles["node"].remove(var_reference) |
| 58 | + # Remove the object as well for garbage collection |
| 59 | + del self.names_to_values[name] |
47 | 60 | self.session.delete(node) |
48 | | - self.session.flush() |
| 61 | + self.session.commit() |
49 | 62 |
|
50 | 63 | @rule(target=node, node=node, visible=st.none() | st.booleans()) |
51 | 64 | def add_child(self, node, visible): |
52 | | - assume(node in self.session) |
53 | 65 | child = Tree(parent=node, visible=visible) |
54 | 66 | self.session.add(child) |
55 | | - self.session.flush() |
| 67 | + self.session.commit() |
56 | 68 | assert node.left < child.left < child.right < node.right |
57 | 69 | return child |
58 | 70 |
|
59 | 71 | @invariant() |
60 | 72 | def check_get_tree_integrity(self): |
61 | 73 | """Check that get_tree response is valid after each operation.""" |
62 | | - response = Tree.get_tree(self.session) |
| 74 | + response = Tree.get_tree( |
| 75 | + self.session, |
| 76 | + query=lambda x: x.execution_options(populate_existing=True).options(joinedload(Tree.children))) |
63 | 77 | assert isinstance(response, list) |
64 | 78 | for node in response: |
65 | | - self.session.refresh(node['node']) |
66 | 79 | validate_get_tree_node(node) |
67 | 80 |
|
68 | 81 | @invariant() |
69 | | - @given(st.none() | st.booleans()) |
70 | | - def check_get_tree_with_custom_query(self, visible): |
| 82 | + def check_get_tree_with_custom_query(self): |
71 | 83 | """Check that get_tree response is valid with custom queries.""" |
72 | | - response = Tree.get_tree(self.session, query=lambda x: x.filter_by(visible=visible)) |
73 | | - assert isinstance(response, list) |
74 | | - for node in response: |
75 | | - self.session.refresh(node['node']) |
76 | | - validate_get_tree_node_for_custom_query(node) |
| 84 | + for visible in [None, True, False]: |
| 85 | + response = Tree.get_tree( |
| 86 | + self.session, |
| 87 | + query=lambda x: x.filter_by(visible=visible) |
| 88 | + .execution_options(populate_existing=True).options(joinedload(Tree.children))) |
| 89 | + assert isinstance(response, list) |
| 90 | + for node in response: |
| 91 | + validate_get_tree_node_for_custom_query(node) |
77 | 92 |
|
78 | 93 |
|
79 | 94 | def validate_get_tree_node(node_response, level=1): |
@@ -101,5 +116,5 @@ def validate_get_tree_node_for_custom_query(node_response): |
101 | 116 | # Export the stateful test case |
102 | 117 | TestTreeStates = TreeStateMachine.TestCase |
103 | 118 | TestTreeStates.settings = settings( |
104 | | - max_examples=75, stateful_step_count=25 |
| 119 | + max_examples=75, stateful_step_count=25, suppress_health_check=[HealthCheck.too_slow] |
105 | 120 | ) |
0 commit comments