Skip to content

Commit fdf1041

Browse files
Merge pull request #6736 from jacinta-stacks/chore/aac-early-return-error-consensus-tests
AAC: consensus tests for `EarlyReturnError` variants
2 parents cf8d685 + 147105c commit fdf1041

14 files changed

+9294
-0
lines changed

stackslib/src/chainstate/tests/consensus.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1893,3 +1893,19 @@ fn problematic_supertype_list() {
18931893
deploy_epochs: &StacksEpochId::since(StacksEpochId::Epoch20),
18941894
);
18951895
}
1896+
1897+
#[test]
1898+
/// Test that a read-only function call can be included in a block without issue.
1899+
/// The fn also shows that a non-response is handled without issue with the testing framework.
1900+
fn read_only_transaction_block() {
1901+
contract_call_consensus_test!(
1902+
contract_name: "read-only-call",
1903+
contract_code: "
1904+
(define-read-only (trigger)
1905+
(some u1)
1906+
)
1907+
",
1908+
function_name: "trigger",
1909+
function_args: &[],
1910+
);
1911+
}
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
// Copyright (C) 2025 Stacks Open Internet Foundation
2+
//
3+
// This program is free software: you can redistribute it and/or modify
4+
// it under the terms of the GNU General Public License as published by
5+
// the Free Software Foundation, either version 3 of the License, or
6+
// (at your option) any later version.
7+
//
8+
// This program is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU General Public License
14+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
16+
//! This module contains consensus tests related to EarlyReturn errors.
17+
18+
use clarity::vm::errors::EarlyReturnError;
19+
use clarity::vm::types::ResponseData;
20+
use clarity::vm::Value as ClarityValue;
21+
22+
use crate::chainstate::tests::consensus::{
23+
contract_call_consensus_test, contract_deploy_consensus_test,
24+
};
25+
26+
/// Generates a coverage classification report for a specific [`EarlyReturnError`] variant.
27+
///
28+
/// This method exists purely for **documentation and tracking purposes**.
29+
/// It helps maintainers understand which error variants have been:
30+
///
31+
/// - ✅ **Tested** — verified through consensus tests.
32+
/// - ⚙️ **Ignored** — not tested on purpose.
33+
/// - 🚫 **Unreachable** — not testable from consensus test side for reasons.
34+
#[allow(dead_code)]
35+
fn variant_coverage_report(variant: EarlyReturnError) {
36+
enum VariantCoverage {
37+
// Cannot occur through valid execution. The string is to explain the reason.
38+
Unreachable_Functionally(&'static str),
39+
// Unexpected error, that should never happen
40+
Unreachable_ExpectLike,
41+
// Defined but never used
42+
Unreachable_NotUsed,
43+
// Not tested on purpose. The string is to explain the reason.
44+
Ignored(&'static str),
45+
// Covered by consensus tests. The func lists is for to link the variant with the related tests
46+
Tested(Vec<fn()>),
47+
}
48+
49+
use EarlyReturnError::*;
50+
use VariantCoverage::*;
51+
52+
_ = match variant {
53+
UnwrapFailed(_) => Tested(vec![
54+
native_try_ret_err_cdeploy,
55+
native_try_ret_err_ccall,
56+
native_try_ret_none_cdeploy,
57+
native_try_ret_none_ccall,
58+
native_unwrap_err_or_ret_cdeploy,
59+
native_unwrap_err_or_ret_ccall,
60+
native_unwrap_or_ret_none_cdeploy,
61+
native_unwrap_or_ret_none_ccall,
62+
]),
63+
AssertionFailed(_) => Tested(vec![
64+
native_special_asserts_cdeploy,
65+
native_special_asserts_ccall,
66+
]),
67+
};
68+
}
69+
70+
/// Error: [`EarlyReturnError::UnwrapFailed`]
71+
/// Caused by: attempting to `try!` unwrap an `err` response at deploy time.
72+
/// Outcome: block accepted
73+
#[test]
74+
fn native_try_ret_err_cdeploy() {
75+
contract_deploy_consensus_test!(
76+
contract_name: "unwrap-try-resp",
77+
contract_code: "(begin (try! (if true (err u200) (ok u1))))",
78+
);
79+
}
80+
81+
/// Error: [`EarlyReturnError::UnwrapFailed`]
82+
/// Caused by: attempting to `try!` unwrap an `err` response at call time.
83+
/// Outcome: block accepted
84+
/// Note: [`clarity::vm::callables::DefinedFunction::execute_apply`] converts [`EarlyReturnError::UnwrapFailed`]
85+
/// into a successful return wrapping the internal thrown value.
86+
#[test]
87+
fn native_try_ret_err_ccall() {
88+
contract_call_consensus_test!(
89+
contract_name: "unwrap-err",
90+
contract_code: "
91+
(define-public (trigger (resp (response uint uint)))
92+
(begin
93+
(try! resp)
94+
(ok u1)
95+
)
96+
)
97+
",
98+
function_name: "trigger",
99+
function_args: &[ClarityValue::Response(ResponseData {
100+
committed: false,
101+
data: Box::new(ClarityValue::UInt(42))
102+
})],
103+
);
104+
}
105+
106+
/// Error: [`EarlyReturnError::UnwrapFailed`]
107+
/// Caused by: attempting to `try!` unwrap a `None` optional at deploy time.
108+
/// Outcome: block accepted
109+
#[test]
110+
fn native_try_ret_none_cdeploy() {
111+
contract_deploy_consensus_test!(
112+
contract_name: "unwrap-try-opt",
113+
contract_code: "(begin (try! (if true none (some true))))",
114+
);
115+
}
116+
117+
/// Error: [`EarlyReturnError::UnwrapFailed`]
118+
/// Caused by: attempting to `try!` unwrap an `None` optional at call time.
119+
/// Outcome: block accepted
120+
/// Note: [`clarity::vm::callables::DefinedFunction::execute_apply`] converts [`EarlyReturnError::UnwrapFailed`]
121+
/// into a successful return wrapping the internal thrown value.
122+
#[test]
123+
fn native_try_ret_none_ccall() {
124+
contract_call_consensus_test!(
125+
contract_name: "unwrap-try-opt",
126+
contract_code: "
127+
(define-read-only (trigger-ro (opt (optional bool)))
128+
(begin
129+
(try! opt)
130+
(some true)
131+
)
132+
)
133+
(define-public (trigger (opt (optional bool)))
134+
(match (trigger-ro opt)
135+
value
136+
(ok u1)
137+
(err u404)
138+
)
139+
)
140+
",
141+
function_name: "trigger",
142+
function_args: &[ClarityValue::none()],
143+
);
144+
}
145+
146+
/// Error: [`EarlyReturnError::UnwrapFailed`]
147+
/// Caused by: calling `unwrap-err!` on an `(ok ...)` value at deploy time.
148+
/// Outcome: block accepted
149+
#[test]
150+
fn native_unwrap_err_or_ret_cdeploy() {
151+
contract_deploy_consensus_test!(
152+
contract_name: "unwrap-err",
153+
contract_code: "(begin (unwrap-err! (if true (ok u3) (err u1)) (err u9)))",
154+
);
155+
}
156+
157+
/// Error: [`EarlyReturnError::UnwrapFailed`]
158+
/// Caused by: calling `unwrap-err!` on an `(ok ...)` value at call time.
159+
/// Outcome: block accepted
160+
/// Note: [`clarity::vm::callables::DefinedFunction::execute_apply`] converts [`EarlyReturnError::UnwrapFailed`]
161+
/// into a successful return wrapping the internal thrown value.
162+
#[test]
163+
fn native_unwrap_err_or_ret_ccall() {
164+
contract_call_consensus_test!(
165+
contract_name: "unwrap-err",
166+
contract_code: "
167+
(define-public (trigger)
168+
(begin
169+
(unwrap-err! (if true (ok u3) (err u1)) (err u9))
170+
(ok u1)
171+
)
172+
)
173+
",
174+
function_name: "trigger",
175+
function_args: &[],
176+
);
177+
}
178+
179+
/// Error: [`EarlyReturnError::UnwrapFailed`]
180+
/// Caused by: calling `unwrap!` on a `None` optional at deploy time.
181+
/// Outcome: block accepted
182+
#[test]
183+
fn native_unwrap_or_ret_none_cdeploy() {
184+
contract_deploy_consensus_test!(
185+
contract_name: "unwrap-opt",
186+
contract_code: "(begin (unwrap! (if true none (some true)) (err u9)))",
187+
);
188+
}
189+
190+
/// Error: [`EarlyReturnError::UnwrapFailed`]
191+
/// Caused by: calling `unwrap!` on a `None` optional at call time.
192+
/// Outcome: block accepted
193+
/// Note: [`clarity::vm::callables::DefinedFunction::execute_apply`] converts [`EarlyReturnError::UnwrapFailed`]
194+
/// into a successful return wrapping the internal thrown value.
195+
#[test]
196+
fn native_unwrap_or_ret_none_ccall() {
197+
contract_call_consensus_test!(
198+
contract_name: "unwrap-opt",
199+
contract_code: "
200+
(define-public (trigger (opt (optional bool)))
201+
(begin
202+
(unwrap! opt (err false))
203+
(ok true)
204+
)
205+
)
206+
",
207+
function_name: "trigger",
208+
function_args: &[ClarityValue::none()],
209+
);
210+
}
211+
212+
/// Error: [`EarlyReturnError::AssertionFailed`]
213+
/// Caused by: failing `asserts!` condition at deploy time.
214+
/// Outcome: block accepted
215+
#[test]
216+
fn native_special_asserts_cdeploy() {
217+
contract_deploy_consensus_test!(
218+
contract_name: "asserts-fail",
219+
contract_code: "(begin (asserts! (is-eq 1 0) (err u0)) (ok u1))",
220+
);
221+
}
222+
223+
/// Error: [`EarlyReturnError::AssertionFailed`]
224+
/// Caused by: failing `asserts!` condition at call time.
225+
/// Outcome: block accepted
226+
/// Note: [`clarity::vm::callables::DefinedFunction::execute_apply`] converts [`EarlyReturnError::AssertionFailed`]
227+
/// into a successful return wrapping the internal thrown value.
228+
#[test]
229+
fn native_special_asserts_ccall() {
230+
contract_call_consensus_test!(
231+
contract_name: "asserts-fail",
232+
contract_code: "(define-public (trigger) (begin (asserts! false (err u0)) (ok u1)))",
233+
function_name: "trigger",
234+
function_args: &[],
235+
);
236+
}

stackslib/src/chainstate/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// You should have received a copy of the GNU General Public License
1414
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1515
pub mod consensus;
16+
mod early_return_tests;
1617
mod parse_tests;
1718
mod runtime_analysis_tests;
1819
mod runtime_tests;

0 commit comments

Comments
 (0)