Skip to content

Commit 67f7b2c

Browse files
committed
fix(tests): adjust tests to use Retrack mock
1 parent 77c7c87 commit 67f7b2c

3 files changed

Lines changed: 67 additions & 8 deletions

File tree

src/security/api_ext.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,17 @@ mod tests {
10001000
// Required so `secrets(user)` can encrypt/decrypt server-side.
10011001
config.security.secrets_encryption_key =
10021002
Some("a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2".to_string());
1003+
// `clone_data` calls `generate_export` which unconditionally queries Retrack for the
1004+
// user's page/api trackers - point it at a local mock that returns an empty list so
1005+
// the test doesn't depend on a live Retrack (the source has no trackers anyway).
1006+
let retrack_server = MockServer::start();
1007+
retrack_server.mock(|when, then| {
1008+
when.method(httpmock::Method::GET).path("/api/trackers");
1009+
then.status(200)
1010+
.header("Content-Type", "application/json")
1011+
.json_body(json!([]));
1012+
});
1013+
config.retrack.host = Url::parse(&retrack_server.base_url())?;
10031014
let api = mock_api_with_config(pool, config).await?;
10041015

10051016
let source = mock_user_with_id(uuid::uuid!("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"))?;

src/server/handlers/security_users_clone.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,13 @@ mod tests {
416416
> {
417417
let mut config = mock_config()?;
418418
config.components.kratos_admin_url = Url::parse(kratos_admin_url)?;
419+
// The mock server doubles as Retrack: `clone_data` calls `generate_export`, which
420+
// always issues `GET /api/trackers` against `config.retrack.host` regardless of
421+
// whether the source has any trackers. Tests that reach the clone-data stage rely
422+
// on this catch-all 200/[] response (registered alongside the Kratos mocks at the
423+
// call site - see `rolls_back_when_recovery_link_minting_fails` and
424+
// `happy_path_returns_id_and_recovery_link`).
425+
config.retrack.host = Url::parse(kratos_admin_url)?;
419426
// Required so cloned secrets can be re-encrypted in clone_data() runs.
420427
config.security.secrets_encryption_key =
421428
Some("a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2".to_string());
@@ -690,6 +697,15 @@ mod tests {
690697
.header("Content-Type", "application/json")
691698
.json_body(identity_json(destination_id, destination_email));
692699
});
700+
// Retrack tracker-list call made by `clone_data` -> `generate_export`. Returning an
701+
// empty array lets clone_data succeed, so the failure mode under test (recovery-link
702+
// minting) is the one that actually trips the rollback.
703+
server.mock(|when, then| {
704+
when.method(httpmock::Method::GET).path("/api/trackers");
705+
then.status(200)
706+
.header("Content-Type", "application/json")
707+
.json_body(json!([]));
708+
});
693709
let recovery_mock = server.mock(|when, then| {
694710
when.method(httpmock::Method::POST)
695711
.path("/admin/recovery/code");
@@ -750,6 +766,14 @@ mod tests {
750766
.header("Content-Type", "application/json")
751767
.json_body(identity_json(destination_id, destination_email));
752768
});
769+
// Retrack tracker-list call made by `clone_data` -> `generate_export`. Source user
770+
// has no trackers so an empty array is the correct response.
771+
server.mock(|when, then| {
772+
when.method(httpmock::Method::GET).path("/api/trackers");
773+
then.status(200)
774+
.header("Content-Type", "application/json")
775+
.json_body(json!([]));
776+
});
753777
let recovery_mock = server.mock(|when, then| {
754778
when.method(httpmock::Method::POST)
755779
.path("/admin/recovery/code")

src/users/user_data/clone.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ mod tests {
141141
ContentSecurityPolicyDirective,
142142
},
143143
};
144+
use httpmock::MockServer;
145+
use serde_json::json;
144146
use sqlx::PgPool;
145147
use uuid::uuid;
146148

@@ -163,12 +165,29 @@ mod tests {
163165
}
164166

165167
/// Returns a `Config` configured for clone tests: a secrets encryption key is required
166-
/// so the ephemeral-passphrase round-trip in `clone_user_data` can encrypt/decrypt.
167-
fn clone_test_config() -> anyhow::Result<crate::config::Config> {
168+
/// so the ephemeral-passphrase round-trip in `clone_user_data` can encrypt/decrypt, and
169+
/// `retrack.host` is pointed at the supplied mock server so the export side's
170+
/// unconditional `GET /api/trackers` doesn't try to reach a real Retrack.
171+
///
172+
/// The returned `MockServer` must be kept alive for the duration of the test; dropping
173+
/// it tears the listener down. We also pre-register a catch-all `GET /api/trackers`
174+
/// handler that returns an empty array, which is what every clone test in this file
175+
/// expects (none of them seed page/api trackers).
176+
fn clone_test_config() -> anyhow::Result<(crate::config::Config, MockServer)> {
168177
let mut config = mock_config()?;
169178
config.security.secrets_encryption_key =
170179
Some("a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2".to_string());
171-
Ok(config)
180+
181+
let retrack_server = MockServer::start();
182+
retrack_server.mock(|when, then| {
183+
when.method(httpmock::Method::GET).path("/api/trackers");
184+
then.status(200)
185+
.header("Content-Type", "application/json")
186+
.json_body(json!([]));
187+
});
188+
config.retrack.host = url::Url::parse(&retrack_server.base_url())?;
189+
190+
Ok((config, retrack_server))
172191
}
173192

174193
fn source_id() -> uuid::Uuid {
@@ -183,7 +202,8 @@ mod tests {
183202
/// touches no destination tables (the destination user is brand-new).
184203
#[sqlx::test]
185204
async fn clones_empty_user_with_zero_counts(pool: PgPool) -> anyhow::Result<()> {
186-
let api = mock_api_with_config(pool, clone_test_config()?).await?;
205+
let (config, _retrack) = clone_test_config()?;
206+
let api = mock_api_with_config(pool, config).await?;
187207
let source = mock_user_with_id(source_id())?;
188208
let destination = mock_user_with_id(destination_id())?;
189209
api.db.insert_user(&source).await?;
@@ -207,7 +227,8 @@ mod tests {
207227
/// content of each entity is preserved verbatim on the destination side.
208228
#[sqlx::test]
209229
async fn clones_scripts_and_csps_under_new_ids(pool: PgPool) -> anyhow::Result<()> {
210-
let api = mock_api_with_config(pool, clone_test_config()?).await?;
230+
let (config, _retrack) = clone_test_config()?;
231+
let api = mock_api_with_config(pool, config).await?;
211232
let source = mock_user_with_id(source_id())?;
212233
let destination = mock_user_with_id(destination_id())?;
213234
api.db.insert_user(&source).await?;
@@ -274,7 +295,8 @@ mod tests {
274295
async fn clones_secrets_via_ephemeral_passphrase_round_trip(
275296
pool: PgPool,
276297
) -> anyhow::Result<()> {
277-
let api = mock_api_with_config(pool, clone_test_config()?).await?;
298+
let (config, _retrack) = clone_test_config()?;
299+
let api = mock_api_with_config(pool, config).await?;
278300
let source = mock_user_with_id(source_id())?;
279301
let destination = mock_user_with_id(destination_id())?;
280302
api.db.insert_user(&source).await?;
@@ -320,7 +342,8 @@ mod tests {
320342
async fn include_history_toggle_compiles_into_export_selection(
321343
pool: PgPool,
322344
) -> anyhow::Result<()> {
323-
let api = mock_api_with_config(pool, clone_test_config()?).await?;
345+
let (config, _retrack) = clone_test_config()?;
346+
let api = mock_api_with_config(pool, config).await?;
324347
let source = mock_user_with_id(source_id())?;
325348
let destination = mock_user_with_id(destination_id())?;
326349
api.db.insert_user(&source).await?;
@@ -356,7 +379,8 @@ mod tests {
356379
},
357380
};
358381

359-
let api = mock_api_with_config(pool, clone_test_config()?).await?;
382+
let (config, _retrack) = clone_test_config()?;
383+
let api = mock_api_with_config(pool, config).await?;
360384
let source = mock_user_with_id(source_id())?;
361385
api.db.insert_user(&source).await?;
362386

0 commit comments

Comments
 (0)