mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 12:52:26 +00:00
Add search filter unit tests
This commit is contained in:
1 parent
08e5620824
commit
efcaf02e8c
3 files changed
+199
-72
No files matched your search
@@ -0,0 +1,80 @@
|
|||||||
|
import { ALLOW_NSFW_CONTENT } from "../config.js";
|
||||||
|
|
||||||
|
export function buildVideoSearchFilterMatcher(filters = {}, options = {}) {
|
||||||
|
const allowNsfw = options.allowNsfw ?? ALLOW_NSFW_CONTENT;
|
||||||
|
const authorSet = new Set(
|
||||||
|
Array.isArray(filters?.authorPubkeys)
|
||||||
|
? filters.authorPubkeys.map((author) => author.toLowerCase())
|
||||||
|
: [],
|
||||||
|
);
|
||||||
|
const tagSet = new Set(
|
||||||
|
Array.isArray(filters?.tags) ? filters.tags.map((tag) => tag.toLowerCase()) : [],
|
||||||
|
);
|
||||||
|
const afterDate = Number.isFinite(filters?.dateRange?.after)
|
||||||
|
? filters.dateRange.after
|
||||||
|
: null;
|
||||||
|
const beforeDate = Number.isFinite(filters?.dateRange?.before)
|
||||||
|
? filters.dateRange.before
|
||||||
|
: null;
|
||||||
|
const minDuration = Number.isFinite(filters?.duration?.minSeconds)
|
||||||
|
? filters.duration.minSeconds
|
||||||
|
: null;
|
||||||
|
const maxDuration = Number.isFinite(filters?.duration?.maxSeconds)
|
||||||
|
? filters.duration.maxSeconds
|
||||||
|
: null;
|
||||||
|
const hasMagnet = filters?.hasMagnet === true;
|
||||||
|
const hasUrl = filters?.hasUrl === true;
|
||||||
|
const nsfwFilter = typeof filters?.nsfw === "string" ? filters.nsfw : "any";
|
||||||
|
|
||||||
|
return (video) => {
|
||||||
|
if (!video) return false;
|
||||||
|
|
||||||
|
if (authorSet.size > 0) {
|
||||||
|
const pubkey = typeof video.pubkey === "string" ? video.pubkey.toLowerCase() : "";
|
||||||
|
if (!pubkey || !authorSet.has(pubkey)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagSet.size > 0) {
|
||||||
|
const tags = Array.isArray(video.tags) ? video.tags : [];
|
||||||
|
const hasTag = tags.some((tag) => {
|
||||||
|
if (Array.isArray(tag)) {
|
||||||
|
return tag[0] === "t" && tagSet.has(String(tag[1] || "").toLowerCase());
|
||||||
|
}
|
||||||
|
return tagSet.has(String(tag || "").toLowerCase());
|
||||||
|
});
|
||||||
|
if (!hasTag) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Number.isFinite(afterDate) && (!Number.isFinite(video.created_at) || video.created_at < afterDate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Number.isFinite(beforeDate) && (!Number.isFinite(video.created_at) || video.created_at > beforeDate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasMagnet && !video.magnet) return false;
|
||||||
|
if (hasUrl && !video.url) return false;
|
||||||
|
|
||||||
|
if (Number.isFinite(minDuration) || Number.isFinite(maxDuration)) {
|
||||||
|
const duration = Number(video.nip71?.duration);
|
||||||
|
if (!Number.isFinite(duration)) return false;
|
||||||
|
if (Number.isFinite(minDuration) && duration < minDuration) return false;
|
||||||
|
if (Number.isFinite(maxDuration) && duration > maxDuration) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowNsfw !== true && video.isNsfw === true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowNsfw === true) {
|
||||||
|
if ((nsfwFilter === "true" || nsfwFilter === "only") && video.isNsfw !== true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (nsfwFilter === "false" && video.isNsfw === true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
+2
-72
@@ -7,7 +7,6 @@ import { escapeHTML } from "./utils/domUtils.js";
|
|||||||
import { formatShortNpub, truncateMiddle } from "./utils/formatters.js";
|
import { formatShortNpub, truncateMiddle } from "./utils/formatters.js";
|
||||||
import { sanitizeProfileMediaUrl } from "./utils/profileMedia.js";
|
import { sanitizeProfileMediaUrl } from "./utils/profileMedia.js";
|
||||||
import { devLogger } from "./utils/logger.js";
|
import { devLogger } from "./utils/logger.js";
|
||||||
import { ALLOW_NSFW_CONTENT } from "./config.js";
|
|
||||||
import { attachFeedInfoPopover } from "./ui/components/FeedInfoPopover.js";
|
import { attachFeedInfoPopover } from "./ui/components/FeedInfoPopover.js";
|
||||||
import { setHashView } from "./hashView.js";
|
import { setHashView } from "./hashView.js";
|
||||||
import {
|
import {
|
||||||
@@ -17,6 +16,7 @@ import {
|
|||||||
setSearchFilterState,
|
setSearchFilterState,
|
||||||
syncSearchFilterStateFromHash,
|
syncSearchFilterStateFromHash,
|
||||||
} from "./search/searchFilterState.js";
|
} from "./search/searchFilterState.js";
|
||||||
|
import { buildVideoSearchFilterMatcher } from "./search/searchFilterMatchers.js";
|
||||||
|
|
||||||
function getApp() {
|
function getApp() {
|
||||||
return getApplication();
|
return getApplication();
|
||||||
@@ -771,77 +771,7 @@ async function performVideoSearch(query, token, filters = {}) {
|
|||||||
const applyAccessFilters = (videos) =>
|
const applyAccessFilters = (videos) =>
|
||||||
nostrService?.filterVideos ? nostrService.filterVideos(videos, filterOptions) : videos;
|
nostrService?.filterVideos ? nostrService.filterVideos(videos, filterOptions) : videos;
|
||||||
|
|
||||||
const authorSet = new Set(
|
const matchesCustomFilters = buildVideoSearchFilterMatcher(filters);
|
||||||
Array.isArray(filters?.authorPubkeys)
|
|
||||||
? filters.authorPubkeys.map((author) => author.toLowerCase())
|
|
||||||
: []
|
|
||||||
);
|
|
||||||
const tagSet = new Set(
|
|
||||||
Array.isArray(filters?.tags) ? filters.tags.map((tag) => tag.toLowerCase()) : []
|
|
||||||
);
|
|
||||||
const afterDate = Number.isFinite(filters?.dateRange?.after) ? filters.dateRange.after : null;
|
|
||||||
const beforeDate = Number.isFinite(filters?.dateRange?.before) ? filters.dateRange.before : null;
|
|
||||||
const minDuration = Number.isFinite(filters?.duration?.minSeconds)
|
|
||||||
? filters.duration.minSeconds
|
|
||||||
: null;
|
|
||||||
const maxDuration = Number.isFinite(filters?.duration?.maxSeconds)
|
|
||||||
? filters.duration.maxSeconds
|
|
||||||
: null;
|
|
||||||
const hasMagnet = filters?.hasMagnet === true;
|
|
||||||
const hasUrl = filters?.hasUrl === true;
|
|
||||||
const nsfwFilter = typeof filters?.nsfw === "string" ? filters.nsfw : "any";
|
|
||||||
|
|
||||||
const matchesCustomFilters = (video) => {
|
|
||||||
if (!video) return false;
|
|
||||||
|
|
||||||
if (authorSet.size > 0) {
|
|
||||||
const pubkey = typeof video.pubkey === "string" ? video.pubkey.toLowerCase() : "";
|
|
||||||
if (!pubkey || !authorSet.has(pubkey)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tagSet.size > 0) {
|
|
||||||
const tags = Array.isArray(video.tags) ? video.tags : [];
|
|
||||||
const hasTag = tags.some((tag) => {
|
|
||||||
if (Array.isArray(tag)) {
|
|
||||||
return tag[0] === "t" && tagSet.has(String(tag[1] || "").toLowerCase());
|
|
||||||
}
|
|
||||||
return tagSet.has(String(tag || "").toLowerCase());
|
|
||||||
});
|
|
||||||
if (!hasTag) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Number.isFinite(afterDate) && (!Number.isFinite(video.created_at) || video.created_at < afterDate)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (Number.isFinite(beforeDate) && (!Number.isFinite(video.created_at) || video.created_at > beforeDate)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasMagnet && !video.magnet) return false;
|
|
||||||
if (hasUrl && !video.url) return false;
|
|
||||||
|
|
||||||
if (Number.isFinite(minDuration) || Number.isFinite(maxDuration)) {
|
|
||||||
const duration = Number(video.nip71?.duration);
|
|
||||||
if (!Number.isFinite(duration)) return false;
|
|
||||||
if (Number.isFinite(minDuration) && duration < minDuration) return false;
|
|
||||||
if (Number.isFinite(maxDuration) && duration > maxDuration) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ALLOW_NSFW_CONTENT !== true && video.isNsfw === true) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ALLOW_NSFW_CONTENT === true) {
|
|
||||||
if ((nsfwFilter === "true" || nsfwFilter === "only") && video.isNsfw !== true) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (nsfwFilter === "false" && video.isNsfw === true) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 1. Local Search (Cache)
|
// 1. Local Search (Cache)
|
||||||
const localMatches = [];
|
const localMatches = [];
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { test } from "node:test";
|
||||||
|
|
||||||
|
import {
|
||||||
|
parseFilterQuery,
|
||||||
|
serializeFiltersToQuery,
|
||||||
|
} from "../../js/search/searchFilters.js";
|
||||||
|
import { buildVideoSearchFilterMatcher } from "../../js/search/searchFilterMatchers.js";
|
||||||
|
|
||||||
|
test("parseFilterQuery collects filters and text tokens", () => {
|
||||||
|
const parsed = parseFilterQuery(
|
||||||
|
"author:abc123 tag:#news duration:<=5m has:magnet nsfw:false hello world",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(parsed.filters.authorPubkeys, ["abc123"]);
|
||||||
|
assert.deepEqual(parsed.filters.tags, ["news"]);
|
||||||
|
assert.equal(parsed.filters.duration.maxSeconds, 300);
|
||||||
|
assert.equal(parsed.filters.hasMagnet, true);
|
||||||
|
assert.equal(parsed.filters.nsfw, "false");
|
||||||
|
assert.equal(parsed.text, "hello world");
|
||||||
|
assert.equal(parsed.errors.length, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("serializeFiltersToQuery emits filter tokens in order", () => {
|
||||||
|
const serialized = serializeFiltersToQuery({
|
||||||
|
authorPubkeys: ["npub123"],
|
||||||
|
tags: ["clips"],
|
||||||
|
kind: 30078,
|
||||||
|
relay: "wss://relay.example",
|
||||||
|
dateRange: { after: null, before: null },
|
||||||
|
duration: { minSeconds: 60, maxSeconds: 120 },
|
||||||
|
hasMagnet: true,
|
||||||
|
hasUrl: true,
|
||||||
|
nsfw: "false",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
serialized,
|
||||||
|
"author:npub123 tag:clips kind:30078 relay:wss://relay.example duration:>=1m duration:<=2m has:magnet has:url nsfw:false",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("video search filter matcher enforces tags, duration, and nsfw when allowed", () => {
|
||||||
|
const matcher = buildVideoSearchFilterMatcher(
|
||||||
|
{
|
||||||
|
tags: ["cats"],
|
||||||
|
duration: { minSeconds: 60, maxSeconds: 180 },
|
||||||
|
hasMagnet: true,
|
||||||
|
nsfw: "only",
|
||||||
|
},
|
||||||
|
{ allowNsfw: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
const videos = [
|
||||||
|
{
|
||||||
|
id: "a",
|
||||||
|
tags: [["t", "cats"]],
|
||||||
|
nip71: { duration: 120 },
|
||||||
|
magnet: "magnet:?xt=urn:btih:abc",
|
||||||
|
isNsfw: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "b",
|
||||||
|
tags: [["t", "cats"]],
|
||||||
|
nip71: { duration: 240 },
|
||||||
|
magnet: "magnet:?xt=urn:btih:def",
|
||||||
|
isNsfw: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "c",
|
||||||
|
tags: [["t", "dogs"]],
|
||||||
|
nip71: { duration: 120 },
|
||||||
|
magnet: "magnet:?xt=urn:btih:ghi",
|
||||||
|
isNsfw: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "d",
|
||||||
|
tags: [["t", "cats"]],
|
||||||
|
nip71: { duration: 120 },
|
||||||
|
magnet: "magnet:?xt=urn:btih:jkl",
|
||||||
|
isNsfw: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "e",
|
||||||
|
tags: [["t", "cats"]],
|
||||||
|
nip71: { duration: 120 },
|
||||||
|
url: "https://cdn.example/video.mp4",
|
||||||
|
isNsfw: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const matches = videos.filter(matcher);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
matches.map((video) => video.id),
|
||||||
|
["a"],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("video search filter matcher blocks nsfw when disallowed and requires url", () => {
|
||||||
|
const matcher = buildVideoSearchFilterMatcher(
|
||||||
|
{ hasUrl: true, nsfw: "true" },
|
||||||
|
{ allowNsfw: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
const videos = [
|
||||||
|
{ id: "a", url: "https://cdn.example/a.mp4", isNsfw: true },
|
||||||
|
{ id: "b", url: "https://cdn.example/b.mp4", isNsfw: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
const matches = videos.filter(matcher);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
matches.map((video) => video.id),
|
||||||
|
["b"],
|
||||||
|
);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user