@@ -137,3 +137,168 @@ fn delete_namespace() {
137137
138138 sim. run ( ) . unwrap ( ) ;
139139}
140+
141+ #[ test]
142+ fn integrity_check_on_healthy_namespace ( ) {
143+ let mut sim = Builder :: new ( )
144+ . simulation_duration ( Duration :: from_secs ( 1000 ) )
145+ . build ( ) ;
146+ let tmp = tempdir ( ) . unwrap ( ) ;
147+ make_primary ( & mut sim, tmp. path ( ) . to_path_buf ( ) ) ;
148+
149+ sim. client ( "client" , async {
150+ let client = Client :: new ( ) ;
151+ client
152+ . post ( "http://primary:9090/v1/namespaces/chk/create" , json ! ( { } ) )
153+ . await ?;
154+
155+ let db = Database :: open_remote_with_connector (
156+ "http://chk.primary:8080" ,
157+ "" ,
158+ TurmoilConnector ,
159+ ) ?;
160+ let conn = db. connect ( ) ?;
161+ conn. execute ( "create table t(v text)" , ( ) ) . await ?;
162+ conn. execute ( "insert into t values ('alive')" , ( ) ) . await ?;
163+
164+ // quick_check should report ok on a healthy DB.
165+ let resp = client
166+ . post (
167+ "http://primary:9090/v1/namespaces/chk/integrity-check" ,
168+ json ! ( { "full" : false } ) ,
169+ )
170+ . await ?;
171+ assert_eq ! ( resp. status( ) , hyper:: http:: StatusCode :: OK ) ;
172+ let v = resp. json_value ( ) . await ?;
173+ assert_eq ! ( v[ "ok" ] , json!( true ) ) ;
174+ assert_eq ! ( v[ "message" ] , json!( "ok" ) ) ;
175+ assert_eq ! ( v[ "check" ] , json!( "quick" ) ) ;
176+
177+ // Full integrity_check should also succeed.
178+ let resp = client
179+ . post (
180+ "http://primary:9090/v1/namespaces/chk/integrity-check" ,
181+ json ! ( { "full" : true } ) ,
182+ )
183+ . await ?;
184+ let v = resp. json_value ( ) . await ?;
185+ assert_eq ! ( v[ "ok" ] , json!( true ) ) ;
186+ assert_eq ! ( v[ "check" ] , json!( "full" ) ) ;
187+
188+ Ok ( ( ) )
189+ } ) ;
190+
191+ sim. run ( ) . unwrap ( ) ;
192+ }
193+
194+ #[ test]
195+ fn reset_replication_preserves_data_on_healthy_namespace ( ) {
196+ let mut sim = Builder :: new ( )
197+ . simulation_duration ( Duration :: from_secs ( 1000 ) )
198+ . build ( ) ;
199+ let tmp = tempdir ( ) . unwrap ( ) ;
200+ make_primary ( & mut sim, tmp. path ( ) . to_path_buf ( ) ) ;
201+
202+ sim. client ( "client" , async {
203+ let client = Client :: new ( ) ;
204+ client
205+ . post ( "http://primary:9090/v1/namespaces/reset/create" , json ! ( { } ) )
206+ . await ?;
207+
208+ let db = Database :: open_remote_with_connector (
209+ "http://reset.primary:8080" ,
210+ "" ,
211+ TurmoilConnector ,
212+ ) ?;
213+ let conn = db. connect ( ) ?;
214+ conn. execute ( "create table t(v text)" , ( ) ) . await ?;
215+ for i in 0 ..100 {
216+ conn. execute (
217+ & format ! ( "insert into t values ('row-{i}')" ) ,
218+ ( ) ,
219+ )
220+ . await ?;
221+ }
222+
223+ // Before reset: 100 rows.
224+ let mut rows = conn. query ( "select count(*) from t" , ( ) ) . await ?;
225+ assert ! ( matches!(
226+ rows. next( ) . await . unwrap( ) . unwrap( ) . get_value( 0 ) ?,
227+ Value :: Integer ( 100 )
228+ ) ) ;
229+
230+ // Reset the replication log on a healthy namespace.
231+ let resp = client
232+ . post (
233+ "http://primary:9090/v1/namespaces/reset/reset-replication" ,
234+ json ! ( { } ) ,
235+ )
236+ . await ?;
237+ assert_eq ! ( resp. status( ) , hyper:: http:: StatusCode :: OK ) ;
238+
239+ // After reset: still 100 rows (data preserved).
240+ let db2 = Database :: open_remote_with_connector (
241+ "http://reset.primary:8080" ,
242+ "" ,
243+ TurmoilConnector ,
244+ ) ?;
245+ let conn2 = db2. connect ( ) ?;
246+ let mut rows = conn2. query ( "select count(*) from t" , ( ) ) . await ?;
247+ assert ! ( matches!(
248+ rows. next( ) . await . unwrap( ) . unwrap( ) . get_value( 0 ) ?,
249+ Value :: Integer ( 100 )
250+ ) ) ;
251+
252+ // And writes still work.
253+ conn2
254+ . execute ( "insert into t values ('post-reset')" , ( ) )
255+ . await ?;
256+ let mut rows = conn2. query ( "select count(*) from t" , ( ) ) . await ?;
257+ assert ! ( matches!(
258+ rows. next( ) . await . unwrap( ) . unwrap( ) . get_value( 0 ) ?,
259+ Value :: Integer ( 101 )
260+ ) ) ;
261+
262+ // Integrity check confirms the new DB is fine.
263+ let resp = client
264+ . post (
265+ "http://primary:9090/v1/namespaces/reset/integrity-check" ,
266+ json ! ( { } ) ,
267+ )
268+ . await ?;
269+ let v = resp. json_value ( ) . await ?;
270+ assert_eq ! ( v[ "ok" ] , json!( true ) ) ;
271+
272+ Ok ( ( ) )
273+ } ) ;
274+
275+ sim. run ( ) . unwrap ( ) ;
276+ }
277+
278+ #[ test]
279+ fn reset_replication_on_nonexistent_namespace_returns_404 ( ) {
280+ let mut sim = Builder :: new ( )
281+ . simulation_duration ( Duration :: from_secs ( 1000 ) )
282+ . build ( ) ;
283+ let tmp = tempdir ( ) . unwrap ( ) ;
284+ make_primary ( & mut sim, tmp. path ( ) . to_path_buf ( ) ) ;
285+
286+ sim. client ( "client" , async {
287+ let client = Client :: new ( ) ;
288+ let resp = client
289+ . post (
290+ "http://primary:9090/v1/namespaces/missing/reset-replication" ,
291+ json ! ( { } ) ,
292+ )
293+ . await ;
294+ // Server-error path: post_with_headers bails on 5xx, but 404
295+ // should come through cleanly as an error response.
296+ match resp {
297+ Ok ( r) => assert_eq ! ( r. status( ) , hyper:: http:: StatusCode :: NOT_FOUND ) ,
298+ Err ( e) => panic ! ( "expected 404 response, got error: {e}" ) ,
299+ }
300+ Ok ( ( ) )
301+ } ) ;
302+
303+ sim. run ( ) . unwrap ( ) ;
304+ }
0 commit comments