Skip to content

Commit 113964f

Browse files
committed
Add integration tests for reset-replication + integrity-check
Three turmoil-based integration tests: - integrity_check_on_healthy_namespace: asserts POST /v1/namespaces/ :ns/integrity-check returns {ok:true, message:'ok', check:'quick'} for a healthy DB, and works with {full:true} as well. - reset_replication_preserves_data_on_healthy_namespace: creates a namespace, seeds 100 rows, calls reset-replication, confirms data is still there + writes still work + integrity-check reports ok. This is the idempotency guarantee for the endpoint. - reset_replication_on_nonexistent_namespace_returns_404: verifies the endpoint returns 404 (not 500) when the namespace doesn't exist. All 3 pass. Complements the ad-hoc /tmp/test_* bash scripts used during development.
1 parent 1de5a78 commit 113964f

1 file changed

Lines changed: 165 additions & 0 deletions

File tree

  • libsql-server/tests/namespaces

libsql-server/tests/namespaces/mod.rs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)