generated from amazon-archives/__template_Apache-2.0
-
Notifications
You must be signed in to change notification settings - Fork 36
Add integration tests for cache behavior, version management, and cross-account access #145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
175969e
Add integration tests for cache behavior, version management, and cro…
reyhankoyun 1fa5180
Remove Clone traits
reyhankoyun c0eb46e
Fix format
reyhankoyun 500158b
Update tests
reyhankoyun d6f0612
Format tests
reyhankoyun 6077ad8
Improve integration tests: remove arbitrary sleeps and fix type consi…
reyhankoyun bf45881
replace arbitrary delay with validation + targeted sync
reyhankoyun e597756
Update cache_behavior.rs
simonmarty e1873b1
Address PR feedback
reyhankoyun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| mod common; | ||
|
|
||
| use common::*; | ||
| use std::time::Duration; | ||
| use tokio::time::sleep; | ||
|
|
||
| #[tokio::test] | ||
| async fn test_refresh_now_on_updated_secret_succeeds() { | ||
| let secrets = TestSecrets::setup().await; | ||
| let secret_name = secrets.secret_name(SecretType::Basic); | ||
reyhankoyun marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| let agent = AgentProcess::start().await; | ||
|
|
||
| // First request - populate cache with original value | ||
| let query = AgentQueryBuilder::default() | ||
| .secret_id(&secret_name) | ||
| .build() | ||
| .unwrap(); | ||
| let response1 = agent.make_request(&query).await; | ||
| let json1: serde_json::Value = serde_json::from_str(&response1).unwrap(); | ||
| let original_secret = json1["SecretString"].as_str().unwrap(); | ||
| assert!(original_secret.contains("testuser")); | ||
|
|
||
| // Update the secret in AWS (simulating manual update, not automatic rotation) | ||
| let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; | ||
| let client = aws_sdk_secretsmanager::Client::new(&config); | ||
|
|
||
| let updated_secret_value = r#"{"username":"rotateduser","password":"rotatedpass123"}"#; | ||
| let update_response = client | ||
| .update_secret() | ||
| .secret_id(&secret_name) | ||
| .secret_string(updated_secret_value) | ||
| .send() | ||
| .await | ||
| .expect("Failed to update secret"); | ||
|
|
||
| let new_version_id = update_response | ||
| .version_id() | ||
| .expect("No version ID returned"); | ||
|
|
||
| // Second request without refreshNow - should return stale cached value | ||
| let response2 = agent.make_request(&query).await; | ||
| let json2: serde_json::Value = serde_json::from_str(&response2).unwrap(); | ||
| let cached_secret = json2["SecretString"].as_str().unwrap(); | ||
|
|
||
| // Should still have the old value from cache | ||
| assert!(cached_secret.contains("testuser")); | ||
| assert!(!cached_secret.contains("rotateduser")); | ||
reyhankoyun marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // Third request with refreshNow=true - should get fresh value | ||
| let refresh_query = AgentQueryBuilder::default() | ||
| .secret_id(&secret_name) | ||
| .refresh_now(true) | ||
| .build() | ||
| .unwrap(); | ||
| let response3 = agent.make_request(&refresh_query).await; | ||
| let json3: serde_json::Value = serde_json::from_str(&response3).unwrap(); | ||
| let fresh_secret = json3["SecretString"].as_str().unwrap(); | ||
|
|
||
| // Should now have the updated value with new version ID and AWSCURRENT label | ||
| assert_eq!(json3["VersionId"].as_str().unwrap(), new_version_id); | ||
| assert!(json3["VersionStages"] | ||
| .as_array() | ||
| .unwrap() | ||
| .contains(&serde_json::Value::String("AWSCURRENT".to_string()))); | ||
| assert!(fresh_secret.contains("rotateduser")); | ||
| assert!(!fresh_secret.contains("testuser")); | ||
reyhankoyun marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn test_cache_expiration_and_refresh() { | ||
| let secrets = TestSecrets::setup().await; | ||
| let secret_name = secrets.secret_name(SecretType::Basic); | ||
|
|
||
| // Start agent with short TTL for faster testing | ||
| const TTL_SECONDS: u16 = 5; | ||
| let agent = AgentProcess::start_with_config(2777, TTL_SECONDS).await; | ||
reyhankoyun marked this conversation as resolved.
Show resolved
Hide resolved
reyhankoyun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| let query = AgentQueryBuilder::default() | ||
| .secret_id(&secret_name) | ||
| .build() | ||
| .unwrap(); | ||
|
|
||
| // First request - populate cache | ||
| let response1 = agent.make_request(&query).await; | ||
| let json1: serde_json::Value = serde_json::from_str(&response1).unwrap(); | ||
| let version1 = json1["VersionId"].as_str().unwrap(); | ||
reyhankoyun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| assert!(json1["SecretString"].as_str().unwrap().contains("testuser")); | ||
|
|
||
| // Second request immediately - should hit cache (same version) | ||
reyhankoyun marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| let response2 = agent.make_request(&query).await; | ||
| let json2: serde_json::Value = serde_json::from_str(&response2).unwrap(); | ||
| assert_eq!(json1["VersionId"], json2["VersionId"]); | ||
|
|
||
| // Update secret while cache is still valid | ||
| let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; | ||
| let client = aws_sdk_secretsmanager::Client::new(&config); | ||
|
|
||
| let update_response = client | ||
| .update_secret() | ||
| .secret_id(&secret_name) | ||
| .secret_string(r#"{"username":"expireduser","password":"expiredpass789"}"#) | ||
| .send() | ||
| .await | ||
| .expect("Failed to update secret"); | ||
|
|
||
| let new_version_id = update_response | ||
| .version_id() | ||
| .expect("No version ID returned"); | ||
|
|
||
reyhankoyun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // Third request before TTL expires - should still return cached value | ||
| let response3 = agent.make_request(&query).await; | ||
| let json3: serde_json::Value = serde_json::from_str(&response3).unwrap(); | ||
| assert_eq!(json3["VersionId"], version1); // Same version as cached | ||
| assert!(json3["SecretString"].as_str().unwrap().contains("testuser")); | ||
|
|
||
| // Wait for TTL to expire (TTL + buffer to ensure expiry) | ||
| sleep(Duration::from_secs(TTL_SECONDS as u64 + 1)).await; | ||
reyhankoyun marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // Fourth request after TTL expiry - should fetch fresh value from AWS | ||
| let response4 = agent.make_request(&query).await; | ||
| let json4: serde_json::Value = serde_json::from_str(&response4).unwrap(); | ||
|
|
||
| // Should now have the updated value with new version ID and AWSCURRENT label | ||
| assert_eq!(json4["VersionId"].as_str().unwrap(), new_version_id); | ||
| assert!(json4["VersionStages"] | ||
| .as_array() | ||
| .unwrap() | ||
| .contains(&serde_json::Value::String("AWSCURRENT".to_string()))); | ||
| assert!(json4["SecretString"] | ||
| .as_str() | ||
| .unwrap() | ||
| .contains("expireduser")); | ||
| assert!(!json4["SecretString"].as_str().unwrap().contains("testuser")); | ||
reyhankoyun marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // Fifth request immediately after - should use newly cached value | ||
| let response5 = agent.make_request(&query).await; | ||
reyhankoyun marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| let json5: serde_json::Value = serde_json::from_str(&response5).unwrap(); | ||
| assert_eq!(json4["VersionId"], json5["VersionId"]); // Same as previous | ||
| assert!(json5["SecretString"] | ||
| .as_str() | ||
| .unwrap() | ||
| .contains("expireduser")); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| mod common; | ||
|
|
||
| use common::*; | ||
| use std::time::Duration; | ||
| use tokio::time::sleep; | ||
|
|
||
| #[tokio::test] | ||
| async fn test_version_stage_transitions() { | ||
reyhankoyun marked this conversation as resolved.
Show resolved
Hide resolved
reyhankoyun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| let secrets = TestSecrets::setup().await; | ||
| let secret_name = secrets.secret_name(SecretType::Versioned); | ||
|
|
||
| let agent = AgentProcess::start().await; | ||
|
|
||
| // Wait for AWSPENDING version to be available | ||
| let _ = secrets | ||
| .wait_for_pending_version(SecretType::Versioned) | ||
| .await; | ||
|
|
||
| // Get the version IDs for both stages | ||
| let (current_version_id, pending_version_id) = | ||
| secrets.get_version_ids(SecretType::Versioned).await; | ||
|
|
||
| // Test AWSPENDING stage before promotion | ||
| let pending_query = AgentQueryBuilder::default() | ||
| .secret_id(&secret_name) | ||
| .version_stage("AWSPENDING") | ||
| .build() | ||
| .unwrap(); | ||
| let pending_response = agent.make_request(&pending_query).await; | ||
| let pending_json: serde_json::Value = serde_json::from_str(&pending_response).unwrap(); | ||
|
|
||
| assert_eq!(pending_json["VersionId"], pending_version_id); | ||
| assert!(pending_json["SecretString"] | ||
| .as_str() | ||
| .unwrap() | ||
| .contains("pendinguser")); | ||
|
|
||
| // Test AWSCURRENT stage before promotion | ||
| let current_query = AgentQueryBuilder::default() | ||
| .secret_id(&secret_name) | ||
| .version_stage("AWSCURRENT") | ||
| .build() | ||
| .unwrap(); | ||
| let current_response = agent.make_request(¤t_query).await; | ||
| let current_json: serde_json::Value = serde_json::from_str(¤t_response).unwrap(); | ||
|
|
||
| assert_eq!(current_json["VersionId"], current_version_id); | ||
| assert!(current_json["SecretString"] | ||
| .as_str() | ||
| .unwrap() | ||
| .contains("currentuser")); | ||
|
|
||
| // Promote AWSPENDING to AWSCURRENT using update_secret_version_stage | ||
| let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; | ||
| let client = aws_sdk_secretsmanager::Client::new(&config); | ||
|
|
||
| client | ||
| .update_secret_version_stage() | ||
| .secret_id(&secret_name) | ||
| .version_stage("AWSCURRENT") | ||
| .move_to_version_id(&pending_version_id) | ||
| .remove_from_version_id(¤t_version_id) | ||
| .send() | ||
| .await | ||
| .expect("Failed to promote version stage"); | ||
|
|
||
| // Wait for the promotion to propagate | ||
| sleep(Duration::from_secs(3)).await; | ||
|
|
||
| // Test that AWSCURRENT now points to the previously pending version (with refreshNow) | ||
| let promoted_query = AgentQueryBuilder::default() | ||
| .secret_id(&secret_name) | ||
| .version_stage("AWSCURRENT") | ||
| .refresh_now(true) | ||
| .build() | ||
| .unwrap(); | ||
| let promoted_response = agent.make_request(&promoted_query).await; | ||
| let promoted_json: serde_json::Value = serde_json::from_str(&promoted_response).unwrap(); | ||
|
|
||
| // After promotion, AWSCURRENT should now have the pending version ID and content | ||
| assert_eq!(promoted_json["VersionId"], pending_version_id); | ||
| assert!(promoted_json["SecretString"] | ||
| .as_str() | ||
| .unwrap() | ||
| .contains("pendinguser")); | ||
| assert!(promoted_json["VersionStages"] | ||
| .as_array() | ||
| .unwrap() | ||
| .contains(&serde_json::Value::String("AWSCURRENT".to_string()))); | ||
|
|
||
| // Verify the old current version is no longer AWSCURRENT | ||
| let old_current_query = AgentQueryBuilder::default() | ||
| .secret_id(&secret_name) | ||
| .version_id(¤t_version_id) | ||
| .refresh_now(true) | ||
| .build() | ||
| .unwrap(); | ||
| let old_current_response = agent.make_request(&old_current_query).await; | ||
| let old_current_json: serde_json::Value = serde_json::from_str(&old_current_response).unwrap(); | ||
|
|
||
| // The old version should still exist but not have AWSCURRENT stage | ||
| assert_eq!(old_current_json["VersionId"], current_version_id); | ||
| assert!(old_current_json["SecretString"] | ||
| .as_str() | ||
| .unwrap() | ||
| .contains("currentuser")); | ||
| assert!(!old_current_json["VersionStages"] | ||
| .as_array() | ||
| .unwrap() | ||
| .contains(&serde_json::Value::String("AWSCURRENT".to_string()))); | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.