Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,15 @@ impl GetEntryDeliveryViewUseCase {
.map_err(|e| GetEntryDeliveryViewError::Storage(e.to_string()))?
.ok_or_else(|| GetEntryDeliveryViewError::EntryNotFound(entry_id.to_string()))?;

let current_peers: HashSet<DeviceId> = self
.peer_scope
.snapshot()
.await
.map_err(|e| GetEntryDeliveryViewError::Storage(format!("current peer scope: {e:?}")))?
.peer_device_ids
.into_iter()
.collect();

// 2. 历史 entry 直接降级:不合成 Pending,deliveries 留空,
// 视图层据此渲染"无投递记录"。
if !entry.delivery_tracked {
Expand Down Expand Up @@ -210,15 +219,6 @@ impl GetEntryDeliveryViewUseCase {

// 5. 本机 entry:当前成员范围与 trusted_peer 取交集后 LEFT JOIN
// delivery 表。可信关系和投递事实为历史验证保留,不能单独恢复当前资格。
let current_peers: HashSet<DeviceId> = self
.peer_scope
.snapshot()
.await
.map_err(|e| GetEntryDeliveryViewError::Storage(format!("current peer scope: {e:?}")))?
.peer_device_ids
.into_iter()
.collect();

let trusted = self
.trusted_peer_repo
.list()
Expand Down Expand Up @@ -732,6 +732,25 @@ mod tests {
assert!(view.deliveries.is_empty());
}

#[tokio::test]
async fn historical_entry_fails_when_current_peer_scope_is_unavailable() {
let entries = Arc::new(FakeEntryRepo::new());
entries.insert(make_entry("e1", "ev1", false));
let uc = build_uc_full_with_scope(
entries,
Arc::new(FakeEventRepo::new()),
Arc::new(FakeTrustedPeerRepo::new(vec![])),
Arc::new(FakeDeliveryRepo::new(vec![])),
Arc::new(FakeMemberRepo::new(vec![])),
Arc::new(make_empty_mobile_device_repo()),
Arc::new(FailingPeerScope),
);

let err = uc.execute(&entry_id("e1")).await.unwrap_err();

assert!(matches!(err, GetEntryDeliveryViewError::Storage(_)));
}

// ── 分支 3: source_device = None → Historical fallback ─────────────

#[tokio::test]
Expand All @@ -754,6 +773,25 @@ mod tests {
);
}

#[tokio::test]
async fn missing_source_device_fails_when_current_peer_scope_is_unavailable() {
let entries = Arc::new(FakeEntryRepo::new());
entries.insert(make_entry("e1", "ev1", true));
let uc = build_uc_full_with_scope(
entries,
Arc::new(FakeEventRepo::new()),
Arc::new(FakeTrustedPeerRepo::new(vec![])),
Arc::new(FakeDeliveryRepo::new(vec![])),
Arc::new(FakeMemberRepo::new(vec![])),
Arc::new(make_empty_mobile_device_repo()),
Arc::new(FailingPeerScope),
);

let err = uc.execute(&entry_id("e1")).await.unwrap_err();

assert!(matches!(err, GetEntryDeliveryViewError::Storage(_)));
}

// ── 分支 4: 远端 entry → Remote, deliveries 空 ─────────────────────

#[tokio::test]
Expand Down Expand Up @@ -782,6 +820,27 @@ mod tests {
);
}

#[tokio::test]
async fn remote_entry_fails_when_current_peer_scope_is_unavailable() {
let entries = Arc::new(FakeEntryRepo::new());
entries.insert(make_entry("e1", "ev1", true));
let events = Arc::new(FakeEventRepo::new());
events.set_source(&event_id("ev1"), Some(peer("origin-peer")));
let uc = build_uc_full_with_scope(
entries,
events,
Arc::new(FakeTrustedPeerRepo::new(vec![])),
Arc::new(FakeDeliveryRepo::new(vec![])),
Arc::new(FakeMemberRepo::new(vec![])),
Arc::new(make_empty_mobile_device_repo()),
Arc::new(FailingPeerScope),
);

let err = uc.execute(&entry_id("e1")).await.unwrap_err();

assert!(matches!(err, GetEntryDeliveryViewError::Storage(_)));
}

// ── 分支 5: 本机 entry · 无 peer → Local, deliveries 空 ─────────────

#[tokio::test]
Expand Down
1 change: 1 addition & 0 deletions docs/architecture/architecture-bible.md
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,7 @@ node scripts/release/verify-release-bundle.mjs <产物目录>

## 文档维护记录

- 2026-08-20:单条历史记录的发送视图在返回任何结果前确认当前成员范围,并用该范围与历史可信关系的交集组装本机发送情况;历史关系和旧发送事实继续保留,当前范围不可用时查询失败。该修改补齐既有当前成员范围规则,不改变分层或所有权。
- 2026-08-21:补充当前成员范围不可用时的脱敏原因分类,区分旧成员记录读取、空间保护关系读取以及缺少当前成员历史;只记录固定类别、保护模式、布尔值和数量,不记录设备名称、文件名、路径或用户内容。不改变成员判断、持久化或对外接口。

- 2026-08-21:修复已初始化旧资料完全缺少空间收敛记录时无法恢复的问题。除确认的首次安装外,缺少记录的既有安装会建立并加密保存旧资料迁移来源;已有记录始终优先,全新安装保持原行为,恢复过程不补造签名成员历史。不改变负责人、对外接口或持久化格式。
Expand Down
Loading