Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/improsocial/impro
An extensible Bluesky client for web impro.social
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428import { describe, it } from "node:test";import assert from "node:assert/strict";import { PatchStore } from "/js/dataLayer/patchStore.js";
// The appliers require the patches array explicitly. These helpers fetch the// current patches for a key and apply them.function applyPostPatches(patchStore, post) { const patches = patchStore.$postPatches.get(post.uri) || []; return patchStore.applyPostPatches(post, patches);}
function applyProfilePatchesForDid(patchStore, profile) { const patches = patchStore.$profilePatches.get(profile.did) || []; return patchStore.applyProfilePatches(profile, patches);}
function applyMessagePatchesForId(patchStore, message) { const patches = patchStore.$messagePatches.get(message.id) || []; return patchStore.applyMessagePatches(message, patches);}
function applyConvoPatchesForId(patchStore, convo) { const patches = patchStore.$convoPatches.get(convo.id) || []; return patchStore.applyConvoPatches(convo, patches);}
function applyPreferencePatchesAll(patchStore, preferences) { return patchStore.applyPreferencePatches( preferences, patchStore.$preferencePatches.get(), );}
function applyCurrentUserPatchesAll(patchStore, user) { return patchStore.applyCurrentUserPatches( user, patchStore.$currentUserPatches.get(), );}
describe("Post Patches - Patch Management", () => { const postURI = "at://did:test/app.bsky.feed.post/test"; const basePost = { uri: postURI, likeCount: 5, viewer: { like: null }, };
it("should add a post patch and return a patch ID", () => { const patchStore = new PatchStore(); const patchId = patchStore.addPostPatch(postURI, { type: "addLike" }); assert.deepEqual(typeof patchId, "number"); assert(patchId >= 0); });
it("should generate unique patch IDs", () => { const patchStore = new PatchStore(); const id1 = patchStore.addPostPatch(postURI, { type: "addLike" }); const id2 = patchStore.addPostPatch(postURI, { type: "removeLike" }); assert(id1 !== id2); });
it("should remove a post patch by ID", () => { const patchStore = new PatchStore(); const patchId = patchStore.addPostPatch(postURI, { type: "addLike" });
// Verify patch exists const patchedPost = applyPostPatches(patchStore, basePost); assert.deepEqual(patchedPost.viewer.like, "fake like");
// Remove patch patchStore.removePostPatch(postURI, patchId);
// Verify patch is removed const unpatchedPost = applyPostPatches(patchStore, basePost); assert.deepEqual(unpatchedPost.viewer.like, null); });
it("should handle removing non-existent patch ID gracefully", () => { const patchStore = new PatchStore(); patchStore.addPostPatch(postURI, { type: "addLike" }); let errorThrown = false; try { patchStore.removePostPatch(postURI, 999); } catch (e) { errorThrown = true; } assert.deepEqual(errorThrown, false); });});
describe("Post Patches - Like Patches", () => { const postURI = "at://did:test/app.bsky.feed.post/test"; const basePost = { uri: postURI, likeCount: 5, viewer: { like: null }, };
it("should apply addLike patch correctly", () => { const patchStore = new PatchStore(); patchStore.addPostPatch(postURI, { type: "addLike" }); const result = applyPostPatches(patchStore, basePost);
assert.deepEqual(result.viewer.like, "fake like"); assert.deepEqual(result.likeCount, 6); assert.deepEqual(result.uri, postURI); });
it("should apply removeLike patch correctly", () => { const patchStore = new PatchStore(); const likedPost = { ...basePost, likeCount: 6, viewer: { like: "some-like-uri" }, };
patchStore.addPostPatch(postURI, { type: "removeLike" }); const result = applyPostPatches(patchStore, likedPost);
assert.deepEqual(result.viewer.like, null); assert.deepEqual(result.likeCount, 5); });
it("should apply multiple patches in order", () => { const patchStore = new PatchStore(); // Add like, then remove like patchStore.addPostPatch(postURI, { type: "addLike" }); patchStore.addPostPatch(postURI, { type: "removeLike" });
const result = applyPostPatches(patchStore, basePost);
assert.deepEqual(result.viewer.like, null); assert.deepEqual(result.likeCount, 5); // +1 -1 = 0, so 5 + 0 = 5 });
it("should preserve original post when no patches exist", () => { const patchStore = new PatchStore(); const result = applyPostPatches(patchStore, basePost); assert.deepEqual(result, basePost); assert(result !== basePost); // Should be a copy });});
describe("Post Patches - Error Handling", () => { const postURI = "at://did:test/app.bsky.feed.post/test"; const basePost = { uri: postURI, likeCount: 5, viewer: { like: null }, };
it("should throw error for unknown patch type", () => { const patchStore = new PatchStore(); patchStore.addPostPatch(postURI, { type: "unknownPatch" });
let errorThrown = false; let errorMessage = ""; try { applyPostPatches(patchStore, basePost); } catch (e) { errorThrown = true; errorMessage = e.message; } assert.deepEqual(errorThrown, true); assert(errorMessage.includes("Unknown patch type")); });});
describe("Profile Patches - Patch Management", () => { const profileDID = "did:test:profile"; const baseProfile = { did: profileDID, viewer: { following: null }, };
it("should add a profile patch and return a patch ID", () => { const patchStore = new PatchStore(); const patchId = patchStore.addProfilePatch(profileDID, { type: "followProfile", }); assert.deepEqual(typeof patchId, "number"); assert(patchId >= 0); });
it("should remove a profile patch by ID", () => { const patchStore = new PatchStore(); const patchId = patchStore.addProfilePatch(profileDID, { type: "followProfile", });
// Verify patch exists const patchedProfile = applyProfilePatchesForDid(patchStore, baseProfile); assert.deepEqual(patchedProfile.viewer.following, "fake following");
// Remove patch patchStore.removeProfilePatch(profileDID, patchId);
// Verify patch is removed const unpatchedProfile = applyProfilePatchesForDid(patchStore, baseProfile); assert.deepEqual(unpatchedProfile.viewer.following, null); });});
describe("Profile Patches - Follow Patches", () => { const profileDID = "did:test:profile"; const baseProfile = { did: profileDID, viewer: { following: null }, };
it("should apply followProfile patch correctly", () => { const patchStore = new PatchStore(); patchStore.addProfilePatch(profileDID, { type: "followProfile" }); const result = applyProfilePatchesForDid(patchStore, baseProfile);
assert.deepEqual(result.viewer.following, "fake following"); assert.deepEqual(result.did, profileDID); });
it("should apply unfollowProfile patch correctly", () => { const patchStore = new PatchStore(); const followedProfile = { ...baseProfile, viewer: { following: "some-follow-uri" }, };
patchStore.addProfilePatch(profileDID, { type: "unfollowProfile" }); const result = applyProfilePatchesForDid(patchStore, followedProfile);
assert.deepEqual(result.viewer.following, null); });
it("should apply multiple profile patches in order", () => { const patchStore = new PatchStore(); // Follow, then unfollow patchStore.addProfilePatch(profileDID, { type: "followProfile" }); patchStore.addProfilePatch(profileDID, { type: "unfollowProfile" });
const result = applyProfilePatchesForDid(patchStore, baseProfile); assert.deepEqual(result.viewer.following, null); });});
describe("Profile Patches - hasPendingProfilePatch", () => { const profileDID = "did:test:profile";
it("returns false when no patches exist for the profile", () => { const patchStore = new PatchStore(); assert.deepEqual( patchStore.hasPendingProfilePatch(profileDID, "followProfile"), false, ); });
it("returns true when a matching patch type exists", () => { const patchStore = new PatchStore(); patchStore.addProfilePatch(profileDID, { type: "followProfile" }); assert.deepEqual( patchStore.hasPendingProfilePatch(profileDID, "followProfile"), true, ); });
it("returns false when only non-matching patch types exist", () => { const patchStore = new PatchStore(); patchStore.addProfilePatch(profileDID, { type: "muteProfile" }); assert.deepEqual( patchStore.hasPendingProfilePatch(profileDID, "followProfile"), false, ); });
it("accepts an array of types and matches any of them", () => { const patchStore = new PatchStore(); patchStore.addProfilePatch(profileDID, { type: "unfollowProfile" }); assert.deepEqual( patchStore.hasPendingProfilePatch(profileDID, [ "followProfile", "unfollowProfile", ]), true, ); });
it("isolates pending state between profiles", () => { const patchStore = new PatchStore(); patchStore.addProfilePatch(profileDID, { type: "followProfile" }); assert.deepEqual( patchStore.hasPendingProfilePatch("did:test:other", "followProfile"), false, ); });
it("returns false after the matching patch is removed", () => { const patchStore = new PatchStore(); const patchId = patchStore.addProfilePatch(profileDID, { type: "followProfile", }); patchStore.removeProfilePatch(profileDID, patchId); assert.deepEqual( patchStore.hasPendingProfilePatch(profileDID, "followProfile"), false, ); });});
describe("Profile Patches - Error Handling", () => { const profileDID = "did:test:profile"; const baseProfile = { did: profileDID, viewer: { following: null }, };
it("should throw error for unknown profile patch type", () => { const patchStore = new PatchStore(); patchStore.addProfilePatch(profileDID, { type: "unknownPatch" });
let errorThrown = false; let errorMessage = ""; try { applyProfilePatchesForDid(patchStore, baseProfile); } catch (e) { errorThrown = true; errorMessage = e.message; } assert.deepEqual(errorThrown, true); assert(errorMessage.includes("Unknown patch type")); });});
describe("UUID Generation", () => { it("should generate sequential IDs", () => { const patchStore = new PatchStore(); const id1 = patchStore.addPostPatch("post1", { type: "addLike" }); const id2 = patchStore.addPostPatch("post2", { type: "addLike" }); const id3 = patchStore.addProfilePatch("profile1", { type: "followProfile", });
assert.deepEqual(id2, id1 + 1); assert.deepEqual(id3, id2 + 1); });});
describe("Patch Isolation", () => { it("should isolate patches between different posts", () => { const patchStore = new PatchStore(); const post1URI = "post1"; const post2URI = "post2"; const basePost1 = { uri: post1URI, likeCount: 5, viewer: { like: null } }; const basePost2 = { uri: post2URI, likeCount: 10, viewer: { like: null } };
patchStore.addPostPatch(post1URI, { type: "addLike" });
const result1 = applyPostPatches(patchStore, basePost1); const result2 = applyPostPatches(patchStore, basePost2);
assert.deepEqual(result1.likeCount, 6); assert.deepEqual(result2.likeCount, 10); // Unchanged });
it("should isolate patches between different profiles", () => { const patchStore = new PatchStore(); const profile1URI = "profile1"; const profile2URI = "profile2"; const baseProfile1 = { did: profile1URI, viewer: { following: null } }; const baseProfile2 = { did: profile2URI, viewer: { following: null } };
patchStore.addProfilePatch(profile1URI, { type: "followProfile" });
const result1 = applyProfilePatchesForDid(patchStore, baseProfile1); const result2 = applyProfilePatchesForDid(patchStore, baseProfile2);
assert.deepEqual(result1.viewer.following, "fake following"); assert.deepEqual(result2.viewer.following, null); // Unchanged });});
describe("Preference Patches - Labeler Patches", () => { it("should apply subscribeLabeler patch correctly", () => { const patchStore = new PatchStore(); const labelerDid = "did:plc:testlabeler"; const labelerInfo = { creator: { did: labelerDid }, policies: { labelValueDefinitions: [] }, };
// Create a mock preferences object with subscribeLabeler method const mockPreferences = { clone: () => mockPreferences, subscribeLabeler: (did, info) => ({ ...mockPreferences, _subscribedLabeler: did, _labelerInfo: info, }), };
patchStore.addPreferencePatch({ type: "subscribeLabeler", did: labelerDid, labelerInfo, }); const result = applyPreferencePatchesAll(patchStore, mockPreferences);
assert.deepEqual(result._subscribedLabeler, labelerDid); assert.deepEqual(result._labelerInfo, labelerInfo); });
it("should apply unsubscribeLabeler patch correctly", () => { const patchStore = new PatchStore(); const labelerDid = "did:plc:testlabeler";
// Create a mock preferences object with unsubscribeLabeler method const mockPreferences = { clone: () => mockPreferences, unsubscribeLabeler: (did) => ({ ...mockPreferences, _unsubscribedLabeler: did, }), };
patchStore.addPreferencePatch({ type: "unsubscribeLabeler", did: labelerDid, }); const result = applyPreferencePatchesAll(patchStore, mockPreferences);
assert.deepEqual(result._unsubscribedLabeler, labelerDid); });
it("should apply multiple labeler patches in order", () => { const patchStore = new PatchStore(); const labelerDid1 = "did:plc:labeler1"; const labelerDid2 = "did:plc:labeler2"; const labelerInfo1 = { creator: { did: labelerDid1 }, policies: { labelValueDefinitions: [] }, }; const labelerInfo2 = { creator: { did: labelerDid2 }, policies: { labelValueDefinitions: [] }, };
// Track calls in order const calls = []; const mockPreferences = { clone: () => mockPreferences, subscribeLabeler: (did, info) => { calls.push({ type: "subscribe", did, info }); return mockPreferences; }, unsubscribeLabeler: (did) => { calls.push({ type: "unsubscribe", did }); return mockPreferences; }, };
patchStore.addPreferencePatch({ type: "subscribeLabeler", did: labelerDid1, labelerInfo: labelerInfo1, }); patchStore.addPreferencePatch({ type: "subscribeLabeler", did: labelerDid2, labelerInfo: labelerInfo2, }); patchStore.addPreferencePatch({ type: "unsubscribeLabeler", did: labelerDid1, });
applyPreferencePatchesAll(patchStore, mockPreferences);
assert.deepEqual(calls.length, 3); assert.deepEqual(calls[0].type, "subscribe"); assert.deepEqual(calls[0].did, labelerDid1); assert.deepEqual(calls[1].type, "subscribe"); assert.deepEqual(calls[1].did, labelerDid2); assert.deepEqual(calls[2], { type: "unsubscribe", did: labelerDid1 }); });});
describe("Preference Patches - Pin Feed Patches", () => { it("should forward entryType to preferences.pinFeed", () => { const patchStore = new PatchStore(); const calls = []; const mockPreferences = { clone: () => mockPreferences, pinFeed: (feedUri, type) => { calls.push({ feedUri, type }); return mockPreferences; }, };
patchStore.addPreferencePatch({ type: "pinFeed", feedUri: "at://did:test/app.bsky.graph.list/abc", entryType: "list", }); applyPreferencePatchesAll(patchStore, mockPreferences);
assert.deepEqual(calls.length, 1); assert.deepEqual(calls[0].feedUri, "at://did:test/app.bsky.graph.list/abc"); assert.deepEqual(calls[0].type, "list"); });
it("should pass entryType undefined when patch omits it (default 'feed' applies)", () => { const patchStore = new PatchStore(); const calls = []; const mockPreferences = { clone: () => mockPreferences, pinFeed: (feedUri, type) => { calls.push({ feedUri, type }); return mockPreferences; }, };
patchStore.addPreferencePatch({ type: "pinFeed", feedUri: "at://did:test/app.bsky.feed.generator/xyz", }); applyPreferencePatchesAll(patchStore, mockPreferences);
assert.deepEqual(calls.length, 1); assert.deepEqual(calls[0].type, undefined); });});
describe("Preference Patches - Patch Management", () => { it("should add and remove preference patches", () => { const patchStore = new PatchStore();
const patchId1 = patchStore.addPreferencePatch({ type: "subscribeLabeler", did: "did:test1", }); const patchId2 = patchStore.addPreferencePatch({ type: "unsubscribeLabeler", did: "did:test2", });
assert.deepEqual(patchStore.$preferencePatches.get().length, 2);
patchStore.removePreferencePatch(patchId1); assert.deepEqual(patchStore.$preferencePatches.get().length, 1); assert.deepEqual( patchStore.$preferencePatches.get()[0].body.type, "unsubscribeLabeler", );
patchStore.removePreferencePatch(patchId2); assert.deepEqual(patchStore.$preferencePatches.get().length, 0); });
it("should generate unique IDs for preference patches", () => { const patchStore = new PatchStore();
const id1 = patchStore.addPreferencePatch({ type: "subscribeLabeler", did: "did:test1", }); const id2 = patchStore.addPreferencePatch({ type: "subscribeLabeler", did: "did:test2", });
assert(id1 !== id2); });});
describe("Preference Patches - Content Label Patches", () => { it("should apply setContentLabelPref patch correctly", () => { const patchStore = new PatchStore(); const labelerDid = "did:plc:testlabeler"; const label = "nsfw"; const visibility = "warn";
// Create a mock preferences object with setContentLabelPref method const mockPreferences = { clone: () => mockPreferences, setContentLabelPref: (params) => ({ ...mockPreferences, _contentLabelPref: params, }), };
patchStore.addPreferencePatch({ type: "setContentLabelPref", label, visibility, labelerDid, }); const result = applyPreferencePatchesAll(patchStore, mockPreferences);
assert.deepEqual(result._contentLabelPref.label, label); assert.deepEqual(result._contentLabelPref.visibility, visibility); assert.deepEqual(result._contentLabelPref.labelerDid, labelerDid); });
it("should apply multiple content label patches in order", () => { const patchStore = new PatchStore(); const labelerDid = "did:plc:testlabeler";
// Track calls in order const calls = []; const mockPreferences = { clone: () => mockPreferences, setContentLabelPref: (params) => { calls.push(params); return mockPreferences; }, };
patchStore.addPreferencePatch({ type: "setContentLabelPref", label: "nsfw", visibility: "warn", labelerDid, }); patchStore.addPreferencePatch({ type: "setContentLabelPref", label: "gore", visibility: "hide", labelerDid, });
applyPreferencePatchesAll(patchStore, mockPreferences);
assert.deepEqual(calls.length, 2); assert.deepEqual(calls[0].label, "nsfw"); assert.deepEqual(calls[0].visibility, "warn"); assert.deepEqual(calls[1].label, "gore"); assert.deepEqual(calls[1].visibility, "hide"); });
it("should mix content label patches with labeler patches", () => { const patchStore = new PatchStore(); const labelerDid = "did:plc:testlabeler"; const labelerInfo = { creator: { did: labelerDid }, policies: { labelValueDefinitions: [] }, };
const calls = []; const mockPreferences = { clone: () => mockPreferences, subscribeLabeler: (did, info) => { calls.push({ type: "subscribe", did, info }); return mockPreferences; }, setContentLabelPref: (params) => { calls.push({ type: "setContentLabelPref", ...params }); return mockPreferences; }, };
patchStore.addPreferencePatch({ type: "subscribeLabeler", did: labelerDid, labelerInfo, }); patchStore.addPreferencePatch({ type: "setContentLabelPref", label: "nsfw", visibility: "warn", labelerDid, });
applyPreferencePatchesAll(patchStore, mockPreferences);
assert.deepEqual(calls.length, 2); assert.deepEqual(calls[0].type, "subscribe"); assert.deepEqual(calls[0].did, labelerDid); assert.deepEqual(calls[1].type, "setContentLabelPref"); assert.deepEqual(calls[1].label, "nsfw"); });});
describe("Current User Patches", () => { const baseUser = { did: "did:plc:me", handle: "me.test" };
it("should overlay pinnedPost via setPinnedPost", () => { const patchStore = new PatchStore(); patchStore.addCurrentUserPatch({ type: "setPinnedPost", pinnedPost: { uri: "at://x/y/1", cid: "c1" }, }); const patched = applyCurrentUserPatchesAll(patchStore, baseUser); assert.deepEqual(patched.pinnedPost.uri, "at://x/y/1"); });
it("should remove pinnedPost via clearPinnedPost", () => { const patchStore = new PatchStore(); patchStore.addCurrentUserPatch({ type: "clearPinnedPost" }); const user = { ...baseUser, pinnedPost: { uri: "at://x/y/1", cid: "c1" } }; const patched = applyCurrentUserPatchesAll(patchStore, user); assert.deepEqual(patched.pinnedPost, undefined); });
it("should return null user unchanged", () => { const patchStore = new PatchStore(); patchStore.addCurrentUserPatch({ type: "clearPinnedPost" }); assert.deepEqual(applyCurrentUserPatchesAll(patchStore, null), null); });
it("should drop the patch after remove", () => { const patchStore = new PatchStore(); const id = patchStore.addCurrentUserPatch({ type: "setPinnedPost", pinnedPost: { uri: "at://x/y/1", cid: "c1" }, }); patchStore.removeCurrentUserPatch(id); const patched = applyCurrentUserPatchesAll(patchStore, baseUser); assert.deepEqual(patched.pinnedPost, undefined); });});
describe("Author Feed Patches", () => { const feedURI = "did:plc:me-posts"; const targetPost = { uri: "at://did:plc:me/app.bsky.feed.post/p1", cid: "c1", };
it("should pin a post in the feed via pinPost", () => { const patchStore = new PatchStore(); const feed = { feed: [{ post: { uri: "at://other" } }, { post: targetPost }], cursor: "x", }; patchStore.addAuthorFeedPatch(feedURI, { type: "pinPost", post: targetPost, }); const patched = patchStore.applyAuthorFeedPatches(feedURI, feed); assert.deepEqual(patched.feed[0].post.uri, targetPost.uri); assert.deepEqual( patched.feed[0].reason.$type, "app.bsky.feed.defs#reasonPin", ); assert.deepEqual(patched.feed.length, 2); assert.deepEqual(patched.cursor, "x"); });
it("should clear the pin reason on the item via unpinPost", () => { const patchStore = new PatchStore(); const feed = { feed: [ { post: targetPost, reason: { $type: "app.bsky.feed.defs#reasonPin" }, }, ], cursor: "x", }; patchStore.addAuthorFeedPatch(feedURI, { type: "unpinPost", post: targetPost, }); const patched = patchStore.applyAuthorFeedPatches(feedURI, feed); assert.deepEqual(patched.feed.length, 1); assert.deepEqual(patched.feed[0].post.uri, targetPost.uri); assert.deepEqual(patched.feed[0].reason, undefined); });
it("should return null feed unchanged", () => { const patchStore = new PatchStore(); patchStore.addAuthorFeedPatch(feedURI, { type: "pinPost", post: targetPost, }); assert.deepEqual(patchStore.applyAuthorFeedPatches(feedURI, null), null); });
it("should drop the patch after remove", () => { const patchStore = new PatchStore(); const id = patchStore.addAuthorFeedPatch(feedURI, { type: "pinPost", post: targetPost, }); patchStore.removeAuthorFeedPatch(feedURI, id); const feed = { feed: [{ post: targetPost }], cursor: "" }; const patched = patchStore.applyAuthorFeedPatches(feedURI, feed); assert.deepEqual(patched.feed[0].reason, undefined); });});
describe("Convo Patches - Patch Management", () => { const convoId = "convo-1"; const baseConvo = { id: convoId, muted: false };
it("should add a convo patch and return a patch ID", () => { const patchStore = new PatchStore(); const patchId = patchStore.addConvoPatch(convoId, { type: "setConvoMuted", muted: true, }); assert.deepEqual(typeof patchId, "number"); assert(patchId >= 0); });
it("should generate unique patch IDs across convos", () => { const patchStore = new PatchStore(); const id1 = patchStore.addConvoPatch(convoId, { type: "setConvoMuted", muted: true, }); const id2 = patchStore.addConvoPatch("convo-2", { type: "setConvoMuted", muted: true, }); assert(id1 !== id2); });
it("should remove a convo patch by ID", () => { const patchStore = new PatchStore(); const patchId = patchStore.addConvoPatch(convoId, { type: "setConvoMuted", muted: true, });
const patched = applyConvoPatchesForId(patchStore, baseConvo); assert.deepEqual(patched.muted, true);
patchStore.removeConvoPatch(convoId, patchId);
const unpatched = applyConvoPatchesForId(patchStore, baseConvo); assert.deepEqual(unpatched.muted, false); });
it("should handle removing a non-existent patch ID gracefully", () => { const patchStore = new PatchStore(); patchStore.addConvoPatch(convoId, { type: "setConvoMuted", muted: true }); let errorThrown = false; try { patchStore.removeConvoPatch(convoId, 999); } catch (e) { errorThrown = true; } assert.deepEqual(errorThrown, false); });});
describe("Convo Patches - setConvoMuted", () => { const convoId = "convo-1";
it("should apply setConvoMuted(true) to a previously unmuted convo", () => { const patchStore = new PatchStore(); patchStore.addConvoPatch(convoId, { type: "setConvoMuted", muted: true }); const patched = applyConvoPatchesForId(patchStore, { id: convoId, muted: false, }); assert.deepEqual(patched.muted, true); assert.deepEqual(patched.id, convoId); });
it("should apply setConvoMuted(false) to a previously muted convo", () => { const patchStore = new PatchStore(); patchStore.addConvoPatch(convoId, { type: "setConvoMuted", muted: false }); const patched = applyConvoPatchesForId(patchStore, { id: convoId, muted: true, }); assert.deepEqual(patched.muted, false); });
it("should apply patches in order (last one wins)", () => { const patchStore = new PatchStore(); patchStore.addConvoPatch(convoId, { type: "setConvoMuted", muted: true }); patchStore.addConvoPatch(convoId, { type: "setConvoMuted", muted: false }); const patched = applyConvoPatchesForId(patchStore, { id: convoId, muted: false, }); assert.deepEqual(patched.muted, false); });
it("should preserve unrelated convo fields", () => { const patchStore = new PatchStore(); patchStore.addConvoPatch(convoId, { type: "setConvoMuted", muted: true }); const patched = applyConvoPatchesForId(patchStore, { id: convoId, muted: false, rev: "rev-1", unreadCount: 3, members: [{ did: "did:plc:a" }], }); assert.deepEqual(patched.rev, "rev-1"); assert.deepEqual(patched.unreadCount, 3); assert.deepEqual(patched.members, [{ did: "did:plc:a" }]); });
it("should return the convo unchanged when no patches exist", () => { const patchStore = new PatchStore(); const convo = { id: convoId, muted: false }; const patched = applyConvoPatchesForId(patchStore, convo); assert.deepEqual(patched, convo); });});
describe("Convo Patches - Error Handling", () => { it("should throw for an unknown convo patch type", () => { const patchStore = new PatchStore(); patchStore.addConvoPatch("convo-1", { type: "unknownConvoPatch" }); assert.throws(() => applyConvoPatchesForId(patchStore, { id: "convo-1", muted: false }), ); });});
describe("Post Patches - Reposts, Bookmarks, HidePost", () => { const postURI = "at://did:test/app.bsky.feed.post/test"; const basePost = { uri: postURI, likeCount: 5, repostCount: 2, bookmarkCount: 1, viewer: { like: null, repost: null, bookmarked: false, isHidden: false }, };
it("should apply createRepost / deleteRepost patches", () => { const patchStore = new PatchStore(); patchStore.addPostPatch(postURI, { type: "createRepost" }); let result = applyPostPatches(patchStore, basePost); assert.deepEqual(result.viewer.repost, "fake repost"); assert.deepEqual(result.repostCount, 3);
patchStore.addPostPatch(postURI, { type: "deleteRepost" }); result = applyPostPatches(patchStore, basePost); assert.deepEqual(result.viewer.repost, null); assert.deepEqual(result.repostCount, 2); });
it("should apply addBookmark / removeBookmark patches", () => { const patchStore = new PatchStore(); patchStore.addPostPatch(postURI, { type: "addBookmark" }); let result = applyPostPatches(patchStore, basePost); assert.deepEqual(result.viewer.bookmarked, true); assert.deepEqual(result.bookmarkCount, 2);
const bookmarkedPost = { ...basePost, viewer: { ...basePost.viewer, bookmarked: true }, bookmarkCount: 2, }; const patchStore2 = new PatchStore(); patchStore2.addPostPatch(postURI, { type: "removeBookmark" }); result = applyPostPatches(patchStore2, bookmarkedPost); assert.deepEqual(result.viewer.bookmarked, false); assert.deepEqual(result.bookmarkCount, 1); });
it("should apply hidePost patch", () => { const patchStore = new PatchStore(); patchStore.addPostPatch(postURI, { type: "hidePost" }); const result = applyPostPatches(patchStore, basePost); assert.deepEqual(result.viewer.isHidden, true); });});
describe("Post Patches - setEmbeddingDisabled", () => { const postURI = "at://did:test/app.bsky.feed.post/test";
it("sets embeddingDisabled on the viewer", () => { const patchStore = new PatchStore(); patchStore.addPostPatch(postURI, { type: "setEmbeddingDisabled", embeddingDisabled: true, }); const result = applyPostPatches(patchStore, { uri: postURI, viewer: { like: null }, }); assert.deepEqual(result.viewer.embeddingDisabled, true); assert.deepEqual(result.viewer.like, null); });
it("clears embeddingDisabled", () => { const patchStore = new PatchStore(); patchStore.addPostPatch(postURI, { type: "setEmbeddingDisabled", embeddingDisabled: false, }); const result = applyPostPatches(patchStore, { uri: postURI, viewer: { embeddingDisabled: true }, }); assert.deepEqual(result.viewer.embeddingDisabled, false); });});
describe("Post Patches - setThreadgateAllow", () => { const postURI = "at://did:test/app.bsky.feed.post/test";
it("creates a threadgate view when the post has none", () => { const patchStore = new PatchStore(); const allow = [{ $type: "app.bsky.feed.threadgate#mentionRule" }]; patchStore.addPostPatch(postURI, { type: "setThreadgateAllow", allow }); const result = applyPostPatches(patchStore, { uri: postURI }); assert.deepEqual(result.threadgate.record, { $type: "app.bsky.feed.threadgate", post: postURI, allow, }); assert.deepEqual(result.threadgate.lists, []); });
it("preserves the existing record fields and lists", () => { const patchStore = new PatchStore(); const post = { uri: postURI, threadgate: { uri: "at://did:test/app.bsky.feed.threadgate/test", lists: [{ uri: "at://list", name: "A list" }], record: { $type: "app.bsky.feed.threadgate", post: postURI, allow: [], hiddenReplies: ["at://did:test/app.bsky.feed.post/reply"], }, }, }; const allow = [{ $type: "app.bsky.feed.threadgate#followingRule" }]; patchStore.addPostPatch(postURI, { type: "setThreadgateAllow", allow }); const result = applyPostPatches(patchStore, post); assert.deepEqual(result.threadgate.record.allow, allow); assert.deepEqual(result.threadgate.record.hiddenReplies, [ "at://did:test/app.bsky.feed.post/reply", ]); assert.deepEqual(result.threadgate.lists, post.threadgate.lists); assert.deepEqual(result.threadgate.uri, post.threadgate.uri); });
it("deletes the allow key for null (everybody)", () => { const patchStore = new PatchStore(); const post = { uri: postURI, threadgate: { lists: [], record: { $type: "app.bsky.feed.threadgate", post: postURI, allow: [], }, }, }; patchStore.addPostPatch(postURI, { type: "setThreadgateAllow", allow: null, }); const result = applyPostPatches(patchStore, post); assert(!("allow" in result.threadgate.record)); });
it("sets an empty allow array for nobody", () => { const patchStore = new PatchStore(); patchStore.addPostPatch(postURI, { type: "setThreadgateAllow", allow: [], }); const result = applyPostPatches(patchStore, { uri: postURI }); assert.deepEqual(result.threadgate.record.allow, []); });});
describe("Profile Patches - Mute/Block/NotificationSubscription", () => { const did = "did:plc:test"; const baseProfile = { did, followersCount: 5, viewer: { muted: false, blocking: null, following: null, activitySubscription: null, }, };
it("should apply muteProfile / unmuteProfile patches", () => { const patchStore = new PatchStore(); patchStore.addProfilePatch(did, { type: "muteProfile" }); let result = applyProfilePatchesForDid(patchStore, baseProfile); assert.deepEqual(result.viewer.muted, true);
const patchStore2 = new PatchStore(); patchStore2.addProfilePatch(did, { type: "unmuteProfile" }); result = applyProfilePatchesForDid(patchStore2, { ...baseProfile, viewer: { ...baseProfile.viewer, muted: true }, }); assert.deepEqual(result.viewer.muted, false); });
it("should apply blockProfile / unblockProfile patches", () => { const patchStore = new PatchStore(); patchStore.addProfilePatch(did, { type: "blockProfile" }); let result = applyProfilePatchesForDid(patchStore, baseProfile); assert.deepEqual(result.viewer.blocking, "fake blocking");
const patchStore2 = new PatchStore(); patchStore2.addProfilePatch(did, { type: "unblockProfile" }); result = applyProfilePatchesForDid(patchStore2, { ...baseProfile, viewer: { ...baseProfile.viewer, blocking: "some-block-uri" }, }); assert.deepEqual(result.viewer.blocking, null); });
it("should apply updatePostNotificationSubscription patch", () => { const patchStore = new PatchStore(); patchStore.addProfilePatch(did, { type: "updatePostNotificationSubscription", activitySubscription: { post: true, reply: false }, }); const result = applyProfilePatchesForDid(patchStore, baseProfile); assert.deepEqual(result.viewer.activitySubscription, { post: true, reply: false, }); });});
describe("Message Patches", () => { const messageId = "msg-1"; const currentUserDid = "did:plc:me"; const otherDid = "did:plc:other"; const baseMessage = { id: messageId, reactions: [], };
it("should add and remove a message patch by ID", () => { const patchStore = new PatchStore(); const patchId = patchStore.addMessagePatch(messageId, { type: "addReaction", reaction: { sender: { did: currentUserDid }, value: "👍" }, }); assert.deepEqual(typeof patchId, "number");
let patched = applyMessagePatchesForId(patchStore, baseMessage); assert.deepEqual(patched.reactions.length, 1); assert.deepEqual(patched.reactions[0].value, "👍");
patchStore.removeMessagePatch(messageId, patchId); patched = applyMessagePatchesForId(patchStore, baseMessage); assert.deepEqual(patched.reactions.length, 0); });
it("should apply removeReaction patch for the current user's reaction", () => { const patchStore = new PatchStore(); const messageWithReaction = { id: messageId, reactions: [{ sender: { did: currentUserDid }, value: "👍" }], }; patchStore.addMessagePatch(messageId, { type: "removeReaction", currentUserDid, value: "👍", }); const patched = applyMessagePatchesForId(patchStore, messageWithReaction); assert.deepEqual(patched.reactions.length, 0); });
it("should keep other users' matching reactions and the user's other-emoji reactions when removing one reaction", () => { const patchStore = new PatchStore(); const messageWithReactions = { id: messageId, reactions: [ { sender: { did: currentUserDid }, value: "👍" }, { sender: { did: otherDid }, value: "👍" }, { sender: { did: currentUserDid }, value: "❤️" }, ], }; patchStore.addMessagePatch(messageId, { type: "removeReaction", currentUserDid, value: "👍", }); const patched = applyMessagePatchesForId(patchStore, messageWithReactions); const surviving = patched.reactions.map( (reaction) => `${reaction.sender.did}:${reaction.value}`, ); assert.deepEqual(surviving, [`${otherDid}:👍`, `${currentUserDid}:❤️`]); });
it("should throw for an unknown message patch type", () => { const patchStore = new PatchStore(); patchStore.addMessagePatch(messageId, { type: "nope" }); assert.throws(() => applyMessagePatchesForId(patchStore, baseMessage)); });
it("should not mutate the input message", () => { const patchStore = new PatchStore(); patchStore.addMessagePatch(messageId, { type: "addReaction", reaction: { sender: { did: currentUserDid }, value: "🎉" }, }); const patched = applyMessagePatchesForId(patchStore, baseMessage); assert.deepEqual(patched.reactions.length, 1); assert.deepEqual(baseMessage.reactions, []); });});
describe("Preference Patches - unpinFeed", () => { it("should apply unpinFeed by delegating to preferences.unpinFeed", () => { const patchStore = new PatchStore(); const seen = []; const mockPreferences = { clone: () => mockPreferences, unpinFeed: (feedUri) => { seen.push(feedUri); return { after: feedUri }; }, }; patchStore.addPreferencePatch({ type: "unpinFeed", feedUri: "at://feed/1", }); const result = applyPreferencePatchesAll(patchStore, mockPreferences); assert.deepEqual(seen, ["at://feed/1"]); assert.deepEqual(result, { after: "at://feed/1" }); });});
describe("Author Feed Patches - pinPost apply", () => { const feedURI = "did:plc:author-posts"; const targetPost = { uri: "at://did:plc:author/app.bsky.feed.post/x" }; const otherPost = { uri: "at://did:plc:author/app.bsky.feed.post/y" };
it("should prepend the pinned post with a reasonPin marker", () => { const patchStore = new PatchStore(); patchStore.addAuthorFeedPatch(feedURI, { type: "pinPost", post: targetPost, }); const feed = { feed: [{ post: otherPost }, { post: targetPost }], cursor: "c", }; const patched = patchStore.applyAuthorFeedPatches(feedURI, feed); assert.deepEqual(patched.feed[0].post.uri, targetPost.uri); assert.deepEqual( patched.feed[0].reason?.$type, "app.bsky.feed.defs#reasonPin", ); // Second entry is the other post; the original targetPost occurrence is dropped. assert.deepEqual(patched.feed.length, 2); assert.deepEqual(patched.feed[1].post.uri, otherPost.uri); assert.deepEqual(patched.cursor, "c"); });});
describe("Post Patches - convergence", () => { const postURI = "at://did:test/app.bsky.feed.post/test";
it("should no-op addLike on a post that already reflects the like", () => { const patchStore = new PatchStore(); const likedPost = { uri: postURI, likeCount: 6, viewer: { like: "server-like-uri" }, }; patchStore.addPostPatch(postURI, { type: "addLike" }); const result = applyPostPatches(patchStore, likedPost); assert.deepEqual(result.likeCount, 6); assert.deepEqual(result.viewer.like, "server-like-uri"); });
it("should no-op removeLike on a post without a like", () => { const patchStore = new PatchStore(); const post = { uri: postURI, likeCount: 5, viewer: { like: null } }; patchStore.addPostPatch(postURI, { type: "removeLike" }); const result = applyPostPatches(patchStore, post); assert.deepEqual(result.likeCount, 5); assert.deepEqual(result.viewer.like, null); });
it("should no-op createRepost on a post that already reflects the repost", () => { const patchStore = new PatchStore(); const repostedPost = { uri: postURI, repostCount: 3, viewer: { repost: "server-repost-uri" }, }; patchStore.addPostPatch(postURI, { type: "createRepost" }); const result = applyPostPatches(patchStore, repostedPost); assert.deepEqual(result.repostCount, 3); assert.deepEqual(result.viewer.repost, "server-repost-uri"); });
it("should no-op deleteRepost on a post without a repost", () => { const patchStore = new PatchStore(); const post = { uri: postURI, repostCount: 2, viewer: { repost: null } }; patchStore.addPostPatch(postURI, { type: "deleteRepost" }); const result = applyPostPatches(patchStore, post); assert.deepEqual(result.repostCount, 2); });
it("should no-op addBookmark / removeBookmark when already converged", () => { const patchStore = new PatchStore(); const bookmarkedPost = { uri: postURI, bookmarkCount: 2, viewer: { bookmarked: true }, }; patchStore.addPostPatch(postURI, { type: "addBookmark" }); let result = applyPostPatches(patchStore, bookmarkedPost); assert.deepEqual(result.bookmarkCount, 2);
const plainPost = { uri: postURI, bookmarkCount: 1, viewer: { bookmarked: false }, }; const patchStore2 = new PatchStore(); patchStore2.addPostPatch(postURI, { type: "removeBookmark" }); result = applyPostPatches(patchStore2, plainPost); assert.deepEqual(result.bookmarkCount, 1); });
it("should not double-apply when a canonical refresh lands while the patch is installed", () => { const patchStore = new PatchStore(); patchStore.addPostPatch(postURI, { type: "addLike" }); const stalePost = { uri: postURI, likeCount: 5, viewer: { like: null } }; assert.deepEqual(applyPostPatches(patchStore, stalePost).likeCount, 6);
// Refresh delivers the server state with the like already applied. const refreshedPost = { uri: postURI, likeCount: 6, viewer: { like: "server-like-uri" }, }; const patched = applyPostPatches(patchStore, refreshedPost); assert.deepEqual(patched.likeCount, 6); assert.deepEqual(patched.viewer.like, "server-like-uri"); });});
describe("Profile Patches - convergence", () => { const did = "did:plc:test";
it("should no-op followProfile on an already-followed profile", () => { const patchStore = new PatchStore(); const followedProfile = { did, followersCount: 11, viewer: { following: "server-follow-uri" }, }; patchStore.addProfilePatch(did, { type: "followProfile" }); const result = applyProfilePatchesForDid(patchStore, followedProfile); assert.deepEqual(result.followersCount, 11); assert.deepEqual(result.viewer.following, "server-follow-uri"); });
it("should no-op unfollowProfile on a profile without a follow", () => { const patchStore = new PatchStore(); const profile = { did, followersCount: 10, viewer: { following: null } }; patchStore.addProfilePatch(did, { type: "unfollowProfile" }); const result = applyProfilePatchesForDid(patchStore, profile); assert.deepEqual(result.followersCount, 10); });
it("should no-op blockProfile / unblockProfile when already converged", () => { const patchStore = new PatchStore(); const blockedProfile = { did, viewer: { blocking: "server-block-uri" }, }; patchStore.addProfilePatch(did, { type: "blockProfile" }); let result = applyProfilePatchesForDid(patchStore, blockedProfile); assert.deepEqual(result.viewer.blocking, "server-block-uri");
const patchStore2 = new PatchStore(); const plainProfile = { did, viewer: { blocking: null } }; patchStore2.addProfilePatch(did, { type: "unblockProfile" }); result = applyProfilePatchesForDid(patchStore2, plainProfile); assert.deepEqual(result.viewer.blocking, null); });});
describe("Message Patches - convergence", () => { const messageId = "msg-1"; const currentUserDid = "did:plc:me";
it("should not duplicate a reaction the canonical message already carries", () => { const patchStore = new PatchStore(); // The canonical reaction carries a full sender profile; the optimistic // one only has the did. const messageWithReaction = { id: messageId, reactions: [ { sender: { did: currentUserDid, handle: "me.test", displayName: "Me" }, value: "👍", }, ], }; patchStore.addMessagePatch(messageId, { type: "addReaction", reaction: { sender: { did: currentUserDid }, value: "👍" }, }); const patched = applyMessagePatchesForId(patchStore, messageWithReaction); assert.deepEqual(patched.reactions.length, 1); assert.deepEqual(patched.reactions[0].sender.handle, "me.test"); });
it("should still add a reaction when only the emoji or sender differs", () => { const patchStore = new PatchStore(); const messageWithReaction = { id: messageId, reactions: [{ sender: { did: "did:plc:other" }, value: "👍" }], }; patchStore.addMessagePatch(messageId, { type: "addReaction", reaction: { sender: { did: currentUserDid }, value: "👍" }, }); patchStore.addMessagePatch(messageId, { type: "addReaction", reaction: { sender: { did: currentUserDid }, value: "❤️" }, }); const patched = applyMessagePatchesForId(patchStore, messageWithReaction); assert.deepEqual(patched.reactions.length, 3); });});