feat: add reproducer for similar content logic bug

Adds a minimal reproducer for the `similar-content` logic bug where candidates are not correctly ordered or filtered.
This confirms the failures seen in `tests/app/similar-content.test.mjs`.
The reproducer is located in `examples/reproducers/3-similar-content-logic-bug/`.
This commit is contained in:
google-labs-jules[bot] committed 2026-01-20 12:27:51 +00:00
1 parent 6c99146261
commit 9853128fbf
2 files changed
+273

No files matched your search

@@ -0,0 +1,32 @@
# Similar Content Logic Bug
This reproducer demonstrates bugs in the logic for calculating "Similar Content" candidates.
## The Bugs
The `computeSimilarContentCandidates` function in `js/app.js` (or related controller logic) has several issues identified by unit tests:
1. **Empty Results:** It seems to be returning empty results even when valid candidates exist.
2. **Tag Handling:** It may not be correctly identifying or counting shared tags.
3. **Filtering:** It fails to return public videos when mixed with NSFW/Private ones (possibly due to returning nothing at all).
## Running the Reproducer
```bash
node --import ./tests/test-helpers/setup-localstorage.mjs examples/reproducers/3-similar-content-logic-bug/reproduce_issue.js
```
## Expected Output (Failure)
```
Running Similar Content Logic Reproducer...
FAIL: orders similar videos by shared tags then timestamp
Expected 3 matches, got 0
FAIL: skips candidates without tag metadata
Expected 1 match, got 0
FAIL: filters NSFW and private videos when NSFW content is disabled
Expected ["video-public"], got []
Summary: FAIL
```
@@ -0,0 +1,241 @@
import Application from "../../../js/app.js";
import assert from "assert";
// Mock the method we want to test by grabbing it from the prototype
// Note: We use the prototype because the test file did so.
const computeSimilarContent = Application.prototype.computeSimilarContentCandidates;
function createAppStub({ videos = [] } = {}) {
const videosMap = new Map();
for (const video of videos) {
if (video && typeof video.id === "string") {
videosMap.set(video.id, video);
}
}
return {
videoListView: { currentVideos: videos },
videosMap,
getKnownVideoPostedAt(video) {
if (Number.isFinite(video?.rootCreatedAt)) {
return Math.floor(video.rootCreatedAt);
}
if (Number.isFinite(video?.created_at)) {
return Math.floor(video.created_at);
}
return null;
},
buildShareUrlFromEventId(id) {
return id ? `https://bitvid.invalid/${id}` : "";
},
formatTimeAgo(timestamp) {
return Number.isFinite(timestamp) ? `${timestamp}s` : "";
},
deriveVideoPointerInfo(video) {
if (video?.pointerInfo) {
return video.pointerInfo;
}
if (typeof video?.pointerKey === "string" && video.pointerKey.trim()) {
return { key: video.pointerKey.trim() };
}
return null;
},
isAuthorBlocked() {
return false;
},
};
}
function createVideo({
id,
pubkey = "author",
displayTags,
tags,
rootCreatedAt,
created_at,
pointerKey,
pointerInfo,
shareUrl,
isPrivate = false,
isNsfw = false,
} = {}) {
const video = {
id,
pubkey,
isPrivate,
isNsfw,
};
if (Array.isArray(displayTags)) {
video.displayTags = displayTags.slice();
}
if (Array.isArray(tags)) {
video.tags = tags.slice();
}
if (Number.isFinite(rootCreatedAt)) {
video.rootCreatedAt = Math.floor(rootCreatedAt);
}
if (Number.isFinite(created_at)) {
video.created_at = Math.floor(created_at);
}
if (typeof pointerKey === "string") {
video.pointerKey = pointerKey;
}
if (pointerInfo) {
video.pointerInfo = pointerInfo;
}
if (typeof shareUrl === "string") {
video.shareUrl = shareUrl;
}
return video;
}
function runTests() {
console.log("Running Similar Content Logic Reproducer...\n");
let failed = false;
// Test 1: Ordering by shared tags then timestamp
try {
const activeVideo = createVideo({
id: "root-video",
displayTags: ["Art", "Music", "News"],
});
const candidateA = createVideo({
id: "video-a",
displayTags: ["art", "music"],
rootCreatedAt: 100,
});
const candidateB = createVideo({
id: "video-b",
displayTags: ["art"],
rootCreatedAt: 200,
});
const candidateC = createVideo({
id: "video-c",
displayTags: ["art", "music"],
rootCreatedAt: 50,
});
const app = createAppStub({
videos: [candidateA, candidateB, candidateC],
});
const matches = computeSimilarContent.call(app, { activeVideo, maxItems: 5 });
if (matches.length !== 3) {
throw new Error(`Expected 3 matches, got ${matches.length}`);
}
const ids = matches.map((entry) => entry.video.id);
const counts = matches.map((entry) => entry.sharedTagCount);
const expectedIds = ["video-a", "video-c", "video-b"];
const expectedCounts = [2, 2, 1];
if (JSON.stringify(ids) !== JSON.stringify(expectedIds)) {
throw new Error(`Order mismatch. Expected ${JSON.stringify(expectedIds)}, got ${JSON.stringify(ids)}`);
}
if (JSON.stringify(counts) !== JSON.stringify(expectedCounts)) {
throw new Error(`Counts mismatch. Expected ${JSON.stringify(expectedCounts)}, got ${JSON.stringify(counts)}`);
}
console.log("PASS: orders similar videos by shared tags then timestamp");
} catch (error) {
console.error("FAIL: orders similar videos by shared tags then timestamp");
console.error(" ", error.message);
failed = true;
}
// Test 2: Skips candidates without tag metadata
try {
const activeVideo = createVideo({
id: "root-video",
displayTags: ["art"],
});
const candidateWithTags = createVideo({
id: "video-a",
displayTags: ["art"],
});
const candidateWithoutTags = createVideo({
id: "video-b",
});
const app = createAppStub({
videos: [candidateWithTags, candidateWithoutTags],
});
const matches = computeSimilarContent.call(app, { activeVideo, maxItems: 5 });
if (matches.length !== 1) {
throw new Error(`Expected 1 match, got ${matches.length}`);
}
if (matches[0].video.id !== "video-a") {
throw new Error(`Expected 'video-a', got '${matches[0]?.video?.id}'`);
}
console.log("PASS: skips candidates without tag metadata");
} catch (error) {
console.error("FAIL: skips candidates without tag metadata");
console.error(" ", error.message);
failed = true;
}
// Test 3: Filters NSFW and private videos
try {
const activeVideo = createVideo({
id: "root-video",
displayTags: ["art"],
});
const publicVideo = createVideo({
id: "video-public",
displayTags: ["art"],
});
const nsfwVideo = createVideo({
id: "video-nsfw",
displayTags: ["art"],
isNsfw: true,
});
const privateVideo = createVideo({
id: "video-private",
displayTags: ["art"],
isPrivate: true,
});
const app = createAppStub({
videos: [publicVideo, nsfwVideo, privateVideo],
});
const matches = computeSimilarContent.call(app, { activeVideo, maxItems: 5 });
const matchedIds = matches.map((entry) => entry.video.id);
const expectedIds = ["video-public"];
if (JSON.stringify(matchedIds) !== JSON.stringify(expectedIds)) {
throw new Error(`Expected ${JSON.stringify(expectedIds)}, got ${JSON.stringify(matchedIds)}`);
}
console.log("PASS: filters NSFW and private videos when NSFW content is disabled");
} catch (error) {
console.error("FAIL: filters NSFW and private videos when NSFW content is disabled");
console.error(" ", error.message);
failed = true;
}
if (failed) {
console.log("\nSummary: FAIL");
process.exit(1);
} else {
console.log("\nSummary: PASS");
}
}
runTests();