mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
refactor(profiles): batch profile fetch through SubscriptionManager (P4)
performProfileFetch now issues ONE deduped, health-gated list() across all relays via the L1 SubscriptionManager (the pool merges per-relay results), replacing the manual per-relay fan-out. Newest-per-author selection unchanged; legacy per-relay path retained as the manager-less fallback. Spec correction: tests/app-batch-fetch-profiles.test.mjs asserted one query PER relay (implementation detail); updated to assert one BATCHED query across all relays. All behavioral assertions (cached hydration, newest profile rendered, failing relay tolerated) unchanged; added a relays-targeting assertion. Documented in TEST_INTEGRITY.md (SCN-profiles-batched). Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
dd3fbfdae6
commit
d693bf55aa
3 files changed
+79
-24
No files matched your search
@@ -0,0 +1,39 @@
|
||||
# Test Integrity Notes
|
||||
|
||||
Machine-readable record of test-expectation changes made under the Spec
|
||||
Correction Protocol (see CLAUDE.md → Dark Factory rules). Each entry justifies
|
||||
why a previous expectation was wrong relative to intended behavior and confirms
|
||||
the replacement is equally strict (or stricter).
|
||||
|
||||
---
|
||||
|
||||
```yaml
|
||||
test_integrity_note:
|
||||
date: 2026-06-14
|
||||
file: tests/app-batch-fetch-profiles.test.mjs
|
||||
change_type: ["spec_correction"]
|
||||
scenarios:
|
||||
- id: SCN-profiles-batched
|
||||
given: "Multiple relays configured (one fast, one failing) and uncached, valid pubkeys to fetch"
|
||||
when: "batchFetchProfiles runs after profile fetches were migrated to the L1 SubscriptionManager"
|
||||
then: "Profiles are fetched in ONE batched query across all relays (not one query per relay); the fast relay's newest profile still renders, cached profiles hydrate immediately, and a failing relay does not break the result"
|
||||
observable_outcomes:
|
||||
- "exactly one pool.list call, targeting all configured relays"
|
||||
- "query authors = only uncached/valid pubkeys"
|
||||
- "DOM updates: cached profile first, then newest fast-relay profile"
|
||||
- "cache set with the newest (Fast) profile only"
|
||||
determinism_controls:
|
||||
- "mock pool.list with deterministic per-relay responses; fixed createdAt ordering"
|
||||
anti_cheat_rationale:
|
||||
prevents:
|
||||
- "over-mocking internal logic"
|
||||
- "hard-coded return value"
|
||||
note: "Only the per-relay CALL-COUNT assertion changed (2 -> 1), because the
|
||||
refactor intentionally batches the fetch. All behavioral assertions
|
||||
(correct/newest profile rendered, cached hydration, failing relay
|
||||
tolerated) are unchanged, and a new assertion verifies the single batched
|
||||
query targets all relays — so coverage is equal or stronger."
|
||||
relaxation:
|
||||
did_relax_any_assertion: false
|
||||
if_true_explain_spec_basis: ""
|
||||
```
|
||||
@@ -106,33 +106,43 @@ async function performProfileFetch({
|
||||
|
||||
const newestProfiles = new Map();
|
||||
|
||||
// Prefer the L1 SubscriptionManager: ONE deduped, health-gated list() across
|
||||
// all relays (the pool merges per-relay results) instead of a manual
|
||||
// per-relay fan-out. Falls back to the legacy per-relay path in manager-less
|
||||
// environments (tests / mock clients).
|
||||
const manager =
|
||||
typeof nostr.getSubscriptionManager === "function"
|
||||
? nostr.getSubscriptionManager()
|
||||
: null;
|
||||
|
||||
let events = [];
|
||||
if (manager) {
|
||||
try {
|
||||
const result = await manager.list({ filters: [filter], relays: relayUrls });
|
||||
events = Array.isArray(result) ? result : [];
|
||||
} catch (error) {
|
||||
logger.warn("[profileMetadata] Batched profile fetch failed:", error);
|
||||
}
|
||||
} else {
|
||||
const results = await Promise.allSettled(
|
||||
relayUrls.map((relayUrl) =>
|
||||
nostr.pool
|
||||
.list([relayUrl], [filter])
|
||||
.then((events) => ({ relayUrl, events }))
|
||||
.then((relayEvents) => ({ relayUrl, relayEvents }))
|
||||
)
|
||||
);
|
||||
|
||||
results.forEach((result, index) => {
|
||||
const relayUrl =
|
||||
result.status === "fulfilled"
|
||||
? result.value.relayUrl
|
||||
: relayUrls[index];
|
||||
|
||||
if (result.status !== "fulfilled") {
|
||||
logger.warn(
|
||||
`[profileMetadata] Failed to fetch profiles from relay ${relayUrl}:`,
|
||||
`[profileMetadata] Failed to fetch profiles from relay ${relayUrls[index]}:`,
|
||||
result.reason,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const events = Array.isArray(result.value.events)
|
||||
? result.value.events
|
||||
: [];
|
||||
if (!events.length) {
|
||||
return;
|
||||
if (Array.isArray(result.value.relayEvents)) {
|
||||
events.push(...result.value.relayEvents);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (const event of events) {
|
||||
@@ -141,9 +151,7 @@ async function performProfileFetch({
|
||||
continue;
|
||||
}
|
||||
|
||||
const createdAt = Number.isFinite(event?.created_at)
|
||||
? event.created_at
|
||||
: 0;
|
||||
const createdAt = Number.isFinite(event?.created_at) ? event.created_at : 0;
|
||||
const previous = newestProfiles.get(pubkey);
|
||||
if (previous && previous.createdAt >= createdAt) {
|
||||
continue;
|
||||
@@ -154,13 +162,8 @@ async function performProfileFetch({
|
||||
continue;
|
||||
}
|
||||
|
||||
newestProfiles.set(pubkey, {
|
||||
createdAt,
|
||||
profile,
|
||||
event,
|
||||
});
|
||||
newestProfiles.set(pubkey, { createdAt, profile, event });
|
||||
}
|
||||
});
|
||||
|
||||
return newestProfiles;
|
||||
}
|
||||
|
||||
@@ -107,9 +107,22 @@ test("batchFetchProfiles handles fast and failing relays", async () => {
|
||||
// console.log("poolListCalls.length:", poolListCalls.length);
|
||||
// console.log("nostrClient.relays:", nostrClient.relays);
|
||||
|
||||
assert.equal(poolListCalls.length, 2, "expected a query per relay");
|
||||
// Spec correction (SCN-profiles-batched): profile fetches are now BATCHED
|
||||
// through the L1 SubscriptionManager — ONE query across all relays instead of
|
||||
// one query per relay. The pool merges per-relay results and tolerates a
|
||||
// failing relay internally, so the fast relay's profile still renders.
|
||||
assert.equal(
|
||||
poolListCalls.length,
|
||||
1,
|
||||
"expected a single batched query across all relays",
|
||||
);
|
||||
|
||||
const [fastCall] = poolListCalls;
|
||||
assert.deepEqual(
|
||||
fastCall.relays,
|
||||
[FAST_RELAY, FAIL_RELAY],
|
||||
"batched query should target all configured relays",
|
||||
);
|
||||
assert.deepEqual(
|
||||
fastCall.filters[0].authors,
|
||||
[VALID_PUBKEY],
|
||||
|
||||
Reference in new issue
Block a user