json_test/
lib.rs

1//! A testing library for JSON Path assertions in Rust.
2//!
3//! `json-test` provides a fluent API for testing JSON structures using JSONPath expressions.
4//! It's designed to make writing tests for JSON data structures clear, concise, and maintainable.
5//!
6//! # Core Concepts
7//!
8//! - **JsonTest**: The main entry point, providing methods to start assertions
9//! - **JsonPathAssertion**: Chainable assertions on JSON values
10//! - **PropertyAssertions**: Object property validation
11//! - **Matchers**: Flexible value matching and validation
12//!
13//! # Features
14//!
15//! - JSONPath-based value extraction and validation
16//! - Chainable, fluent assertion API
17//! - Type-safe operations
18//! - Property existence and value validation
19//! - String pattern matching with regex support
20//! - Numeric comparisons
21//! - Array and object validation
22//! - Custom matcher support
23//!
24//! # Examples
25//!
26//! ## Value Assertions
27//!
28//! ```rust
29//! use json_test::JsonTest;
30//! use serde_json::json;
31//!
32//! let data = json!({
33//!     "user": {
34//!         "name": "John Doe",
35//!         "age": 30
36//!     }
37//! });
38//!
39//! let mut test = JsonTest::new(&data);
40//!
41//! // Chain multiple assertions on a single value
42//! test.assert_path("$.user.name")
43//!     .exists()
44//!     .is_string()
45//!     .equals(json!("John Doe"));
46//! ```
47//!
48//! ## Numeric Validation
49//!
50//! ```rust
51//! # use json_test::JsonTest;
52//! # use serde_json::json;
53//! # let data = json!({"score": 85});
54//! # let mut test = JsonTest::new(&data);
55//! test.assert_path("$.score")
56//!     .is_number()
57//!     .is_greater_than(80)
58//!     .is_less_than(90)
59//!     .is_between(0, 100);
60//! ```
61//!
62//! ## Array Testing
63//!
64//! ```rust
65//! # use json_test::JsonTest;
66//! # use serde_json::json;
67//! # let data = json!({"roles": ["user", "admin"]});
68//! # let mut test = JsonTest::new(&data);
69//! test.assert_path("$.roles")
70//!     .is_array()
71//!     .has_length(2)
72//!     .contains(&json!("admin"));
73//! ```
74//!
75//! ## Property Chaining
76//!
77//! ```rust
78//! # use json_test::{JsonTest, PropertyAssertions};
79//! # use serde_json::json;
80//! let data = json!({
81//!     "user": {
82//!         "name": "John",
83//!         "settings": {
84//!             "theme": "dark",
85//!             "notifications": true
86//!         }
87//!     }
88//! });
89//!
90//! let mut test = JsonTest::new(&data);
91//!
92//! // Chain property assertions
93//! test.assert_path("$.user")
94//!     .has_property("name")
95//!     .has_property("settings")
96//!     .properties_matching(|key| !key.starts_with("_"))
97//!         .count(2)
98//!         .and()
99//!     .has_property_value("name", json!("John"));
100//! ```
101//!
102//! ## Advanced Matching
103//!
104//! ```rust
105//! # use json_test::JsonTest;
106//! # use serde_json::json;
107//! # let data = json!({"user": {"email": "test@example.com"}});
108//! # let mut test = JsonTest::new(&data);
109//! test.assert_path("$.user.email")
110//!     .is_string()
111//!     .contains_string("@")
112//!     .matches_pattern(r"^[^@]+@[^@]+\.[^@]+$")
113//!     .matches(|value| {
114//!         value.as_str()
115//!             .map(|s| !s.starts_with("admin@"))
116//!             .unwrap_or(false)
117//!     });
118//! ```
119//!
120//! # Error Messages
121//!
122//! The library provides clear, test-friendly error messages:
123//!
124//! ```text
125//! Property 'email' not found at $.user
126//! Available properties: name, age, roles
127//! ```
128//!
129//! ```text
130//! Value mismatch at $.user.age
131//! Expected: 25
132//! Actual: 30
133//! ```
134//!
135//! # Current Status
136//!
137//! This library is in active development (0.1.x). While the core API is stabilizing,
138//! minor breaking changes might occur before 1.0.
139
140mod assertions;
141mod error;
142mod matchers;
143
144pub use assertions::base::JsonPathAssertion;
145pub use assertions::property_assertions::PropertyAssertions;
146pub use error::{ErrorContext, JsonPathError};
147pub use matchers::{JsonMatcher, RegexMatcher, TypeMatcher, ValueMatcher};
148use serde_json::Value;
149
150/// Main entry point for JSON testing.
151///
152/// `JsonTest` provides methods to create assertions on JSON values using JSONPath expressions.
153/// It maintains a reference to the JSON being tested and enables creation of chainable assertions.
154///
155/// # Examples
156///
157/// ```rust
158/// use json_test::{JsonTest, PropertyAssertions};
159/// use serde_json::json;
160///
161/// let data = json!({
162///     "user": {
163///         "name": "John",
164///         "settings": {
165///             "theme": "dark"
166///         }
167///     }
168/// });
169///
170/// let mut test = JsonTest::new(&data);
171///
172/// // Test a single path with chained assertions
173/// test.assert_path("$.user")
174///     .has_property("name")
175///     .has_property("settings")
176///     .has_property_value("name", json!("John"));
177/// ```
178#[derive(Debug)]
179pub struct JsonTest<'a> {
180    json: &'a Value,
181}
182
183impl<'a> JsonTest<'a> {
184    /// Creates a new JSON test instance.
185    ///
186    /// Takes a reference to a JSON value that will be tested. The JSON value
187    /// must live at least as long as the test instance.
188    ///
189    /// # Examples
190    ///
191    /// ```rust
192    /// # use json_test::JsonTest;
193    /// # use serde_json::json;
194    /// let data = json!({"key": "value"});
195    /// let test = JsonTest::new(&data);
196    /// ```
197    pub fn new(json: &'a Value) -> Self {
198        Self { json }
199    }
200
201    /// Creates a new assertion for the given JSONPath expression.
202    ///
203    /// The path must be a valid JSONPath expression. Invalid paths will cause
204    /// a panic with a descriptive error message.
205    ///
206    /// # Examples
207    ///
208    /// ```rust
209    /// # use json_test::{JsonTest, PropertyAssertions};
210    /// # use serde_json::json;
211    /// let data = json!({
212    ///     "users": [
213    ///         {"name": "John", "role": "admin"},
214    ///         {"name": "Jane", "role": "user"}
215    ///     ]
216    /// });
217    ///
218    /// let mut test = JsonTest::new(&data);
219    ///
220    /// // Test array element with chained assertions
221    /// test.assert_path("$.users[0]")
222    ///     .has_property("name")
223    ///     .has_property_value("role", json!("admin"));
224    /// ```
225    ///
226    /// # Panics
227    ///
228    /// Panics if the JSONPath expression is invalid. This is appropriate for
229    /// testing scenarios where invalid paths indicate test specification errors.
230    pub fn assert_path(&'a mut self, path: &str) -> JsonPathAssertion<'a> {
231        JsonPathAssertion::new_with_test(self, self.json, path)
232    }
233}