fix: observe original presence through saved legacy Journal links
This commit is contained in:
@@ -404,6 +404,64 @@ fn original_source_metadata_failure_keeps_saved_journal_readable_and_does_not_cl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn legacy_segment_entry_links_reuse_saved_session_for_presence_without_mutating_provenance() {
|
||||||
|
let mut f = Fixture::new();
|
||||||
|
let mut candidate = f.candidate("legacy-presence", &[0, 1]);
|
||||||
|
for source in &mut candidate.source_refs {
|
||||||
|
source.session_id = None;
|
||||||
|
}
|
||||||
|
let candidate = f.store().stage_candidate(candidate).unwrap();
|
||||||
|
let selector = f.apply(&candidate);
|
||||||
|
let entry = f.fixed("first", 1, vec![]);
|
||||||
|
f.links(&selector, true);
|
||||||
|
let before = f.snapshot();
|
||||||
|
f.reopen();
|
||||||
|
for request in [
|
||||||
|
MemoryJournalRequest::Links {
|
||||||
|
selector: selector.clone(),
|
||||||
|
},
|
||||||
|
MemoryJournalRequest::Read {
|
||||||
|
selector: selector.clone(),
|
||||||
|
source_index: 1,
|
||||||
|
journal_id: f.journal_id.clone(),
|
||||||
|
entry_id: entry.clone(),
|
||||||
|
expected_token: None,
|
||||||
|
byte_offset: 0,
|
||||||
|
max_bytes: 4096,
|
||||||
|
},
|
||||||
|
] {
|
||||||
|
let mut calls = Vec::new();
|
||||||
|
let response = f
|
||||||
|
.store()
|
||||||
|
.execute_memory_journal_with_source_presence(&f.host, request, |session| {
|
||||||
|
calls.push(session.to_owned());
|
||||||
|
Some(false)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(calls, ["session"]);
|
||||||
|
match response {
|
||||||
|
MemoryJournalResponse::Links(page) => {
|
||||||
|
assert_eq!(page.sources.len(), 2);
|
||||||
|
assert!(
|
||||||
|
page.sources
|
||||||
|
.iter()
|
||||||
|
.all(|s| s.original_source == OriginalSourceAvailability::Missing)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
MemoryJournalResponse::Read(read) => {
|
||||||
|
assert_eq!(
|
||||||
|
read.source.original_source,
|
||||||
|
OriginalSourceAvailability::Missing
|
||||||
|
);
|
||||||
|
assert_eq!(read.page.unwrap().text, "Journal context for first");
|
||||||
|
}
|
||||||
|
_ => panic!("saved provenance result required"),
|
||||||
|
}
|
||||||
|
assert_eq!(f.snapshot(), before);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multiple_segment_anchors_map_to_fixed_intervals_and_subject_crossing_is_denied() {
|
fn multiple_segment_anchors_map_to_fixed_intervals_and_subject_crossing_is_denied() {
|
||||||
let f = Fixture::new();
|
let f = Fixture::new();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use super::memory_links::{
|
|||||||
MemoryJournalRequest, MemoryJournalResponse, OriginalSourceAvailability,
|
MemoryJournalRequest, MemoryJournalResponse, OriginalSourceAvailability,
|
||||||
};
|
};
|
||||||
use super::{JournalHostContext, JournalResult, SubjektivStore};
|
use super::{JournalHostContext, JournalResult, SubjektivStore};
|
||||||
|
use rusqlite::{OptionalExtension, params};
|
||||||
|
|
||||||
impl SubjektivStore {
|
impl SubjektivStore {
|
||||||
/// Executes the saved-reference operation first, then annotates original-source absence using
|
/// Executes the saved-reference operation first, then annotates original-source absence using
|
||||||
@@ -39,7 +40,20 @@ impl SubjektivStore {
|
|||||||
let source = candidate
|
let source = candidate
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|c| c.source_refs.get(link.source_index));
|
.and_then(|c| c.source_refs.get(link.source_index));
|
||||||
let session = source.and_then(|s| s.session_id.as_deref());
|
// Legacy anchors may lack a Session ID. A connected edge already resolved it
|
||||||
|
// from immutable attribution and stable Segment/entry identity; reuse that saved
|
||||||
|
// identity rather than fetching history or modifying the original candidate.
|
||||||
|
let saved_session: Option<String> = self.database.try_with_connection(|tx| {
|
||||||
|
Ok::<_, super::JournalError>(tx.query_row(
|
||||||
|
"SELECT source_session_id FROM memory_journal_links
|
||||||
|
WHERE subject_id=?1 AND candidate_id=?2 AND source_index=?3 AND reason='linked'",
|
||||||
|
params![host.subject_id, selector.candidate_id, link.source_index],
|
||||||
|
|row| row.get(0),
|
||||||
|
).optional()?)
|
||||||
|
})?;
|
||||||
|
let session = saved_session
|
||||||
|
.as_deref()
|
||||||
|
.or_else(|| source.and_then(|s| s.session_id.as_deref()));
|
||||||
let observed = if let Some(session) = session {
|
let observed = if let Some(session) = session {
|
||||||
if let Some(value) = observations.get(session) {
|
if let Some(value) = observations.get(session) {
|
||||||
*value
|
*value
|
||||||
|
|||||||
Reference in New Issue
Block a user