Repository navigation
fix(data-rights): keep creation replay idempotent after lifecycle advance #79
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
seonghobae
merged 13 commits into
main
from
fix/data-rights-create-replay-after-transition-20260815
Aug 16, 2026
Merged
Changes from 4 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
595f05b
test(data-rights): preserve creation replay after lifecycle advance
seonghobae 4e88de1
fix(data-rights): decouple creation replay from mutable lifecycle
seonghobae fd24b79
test(data-rights): preserve verification replay after later state
seonghobae 81d7fd9
fix(data-rights): preserve verification replay after lifecycle advance
seonghobae 5f615c9
test(data-rights): isolate replay schemas across parallel tests
seonghobae 053adc3
fix(data-rights): validate lifecycle coherence on creation replay
seonghobae 1db3dbe
test(data-rights): cover corrupted verification replay branch
seonghobae af1734b
test(data-rights): isolate replay PostgreSQL schemas
seonghobae a6961fd
test(data-rights): isolate corruption PostgreSQL schema
seonghobae 6ce316d
test(data-rights): cover creation replay after processing starts
seonghobae 6d15ee3
test(data-rights): make processing replay fixture panic-safe
seonghobae 3cd8c80
docs(data-rights): explain replay behavior for beginners
seonghobae fee4fda
test(data-rights): reject incoherent advanced replay state
seonghobae 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
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,134 @@ | ||
| //! Creation and verification replay stay idempotent after the durable request lifecycle advances. | ||
|
|
||
| use postgres::{Client, NoTls}; | ||
| use psychometrics_commons_runtime::data_rights::{DataRightsRequest, DataRightsRequestKind}; | ||
| use psychometrics_commons_runtime::integration::IntegrationEvent; | ||
| use psychometrics_commons_runtime::postgres_data_rights::{ | ||
| apply_data_rights_migration, persist_data_rights_identity_verification, | ||
| persist_requested_data_rights_with_propagation, DataRightsPersistenceDisposition, | ||
| DataRightsPropagationTarget, DataRightsVerificationDisposition, | ||
| }; | ||
| use psychometrics_commons_runtime::postgres_integration::apply_integration_migration; | ||
|
|
||
| const DIGEST: &str = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; | ||
|
|
||
| fn ready_client() -> Client { | ||
| let url = std::env::var("TEST_DATABASE_URL").expect("TEST_DATABASE_URL is required"); | ||
| let mut client = Client::connect(&url, NoTls).expect("CI PostgreSQL must be reachable"); | ||
| let schema = format!("data_rights_creation_replay_{}", std::process::id()); | ||
| client | ||
| .batch_execute(&format!( | ||
| "DROP SCHEMA IF EXISTS {schema} CASCADE; CREATE SCHEMA {schema}; SET search_path TO {schema};" | ||
| )) | ||
| .unwrap(); | ||
| apply_integration_migration(&mut client).unwrap(); | ||
| apply_data_rights_migration(&mut client).unwrap(); | ||
| client | ||
| } | ||
|
|
||
| fn request() -> DataRightsRequest { | ||
| DataRightsRequest::new( | ||
| "data_rights_request_creation_replay", | ||
| "tenant_alpha", | ||
| "participant_alpha", | ||
| DataRightsRequestKind::Deletion, | ||
| "scope_alpha", | ||
| 10_000, | ||
| ) | ||
| .unwrap() | ||
| } | ||
|
|
||
| fn event() -> IntegrationEvent { | ||
| IntegrationEvent::new( | ||
| "data_rights_event_creation_replay", | ||
| "data_rights.deletion.requested", | ||
| "v1", | ||
| "psychometrics_commons", | ||
| "tenant_alpha", | ||
| "data_rights_request_creation_replay", | ||
| 10_000, | ||
| "data_rights_request_creation_replay", | ||
| None, | ||
| DIGEST, | ||
| ) | ||
| .unwrap() | ||
| } | ||
|
|
||
| fn persist_requested(client: &mut Client) { | ||
| let event = event(); | ||
| let targets = [DataRightsPropagationTarget::new( | ||
| "dependent_system_alpha", | ||
| &event, | ||
| )]; | ||
| assert_eq!( | ||
| persist_requested_data_rights_with_propagation(client, &request(), &targets, 3).unwrap(), | ||
| DataRightsPersistenceDisposition::Inserted | ||
| ); | ||
| } | ||
|
|
||
| fn verified_request() -> DataRightsRequest { | ||
| let mut request = request(); | ||
| request | ||
| .verify_identity("verification_evidence_creation_replay", 10_100) | ||
| .unwrap(); | ||
| request | ||
| } | ||
|
|
||
| #[test] | ||
| fn exact_creation_replay_remains_duplicate_after_identity_verification() { | ||
| let mut client = ready_client(); | ||
| let event = event(); | ||
| let targets = [DataRightsPropagationTarget::new( | ||
| "dependent_system_alpha", | ||
| &event, | ||
| )]; | ||
|
|
||
| persist_requested(&mut client); | ||
|
|
||
| let advanced = verified_request(); | ||
| { | ||
| let mut transaction = client.transaction().unwrap(); | ||
| persist_data_rights_identity_verification(&mut transaction, &advanced).unwrap(); | ||
| transaction.commit().unwrap(); | ||
| } | ||
|
|
||
| assert_eq!( | ||
| persist_requested_data_rights_with_propagation(&mut client, &request(), &targets, 3) | ||
| .unwrap(), | ||
| DataRightsPersistenceDisposition::Duplicate, | ||
| "retrying immutable creation evidence must not conflict merely because the stored lifecycle advanced" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn exact_identity_verification_replay_remains_duplicate_after_processing_state() { | ||
| let mut client = ready_client(); | ||
| persist_requested(&mut client); | ||
|
|
||
| let verified = verified_request(); | ||
| { | ||
| let mut transaction = client.transaction().unwrap(); | ||
| assert_eq!( | ||
| persist_data_rights_identity_verification(&mut transaction, &verified).unwrap(), | ||
| DataRightsVerificationDisposition::Verified | ||
| ); | ||
| transaction.commit().unwrap(); | ||
| } | ||
|
|
||
| client | ||
| .execute( | ||
| "UPDATE data_rights_request_state | ||
| SET current_state = 'processing', latest_event_at_unix_ms = $2 | ||
| WHERE request_ref = $1", | ||
| &[&verified.request_ref(), &10_200_i64], | ||
| ) | ||
| .unwrap(); | ||
|
|
||
| let mut transaction = client.transaction().unwrap(); | ||
| assert_eq!( | ||
| persist_data_rights_identity_verification(&mut transaction, &verified).unwrap(), | ||
| DataRightsVerificationDisposition::Duplicate, | ||
| "retrying immutable verification evidence must remain idempotent after a later lifecycle transition" | ||
| ); | ||
| transaction.commit().unwrap(); | ||
| } | ||
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.