nostr-tools/filter.test.ts

231 lines
6.9 KiB
TypeScript

import { describe, test, expect } from 'bun:test'
import { getFilterLimit, matchFilter, matchFilters, mergeFilters } from './filter.ts'
import { buildEvent } from './test-helpers.ts'
describe('Filter', () => {
describe('matchFilter', () => {
test('should return true when all filter conditions are met', () => {
const filter = {
ids: ['123', '456'],
kinds: [1, 2, 3],
authors: ['abc'],
since: 100,
until: 200,
'#tag': ['value'],
}
const event = buildEvent({
id: '123',
kind: 1,
pubkey: 'abc',
created_at: 150,
tags: [['tag', 'value']],
})
const result = matchFilter(filter, event)
expect(result).toEqual(true)
})
test('should return false when the event id is not in the filter', () => {
const filter = { ids: ['123', '456'] }
const event = buildEvent({ id: '789' })
const result = matchFilter(filter, event)
expect(result).toEqual(false)
})
test('should return false when the event kind is not in the filter', () => {
const filter = { kinds: [1, 2, 3] }
const event = buildEvent({ kind: 4 })
const result = matchFilter(filter, event)
expect(result).toEqual(false)
})
test('should return false when the event author is not in the filter', () => {
const filter = { authors: ['abc', 'def'] }
const event = buildEvent({ pubkey: 'ghi' })
const result = matchFilter(filter, event)
expect(result).toEqual(false)
})
test('should return false when a tag is not present in the event', () => {
const filter = { '#tag': ['value1', 'value2'] }
const event = buildEvent({ tags: [['not_tag', 'value1']] })
const result = matchFilter(filter, event)
expect(result).toEqual(false)
})
test('should return false when a tag value is not present in the event', () => {
const filter = { '#tag': ['value1', 'value2'] }
const event = buildEvent({ tags: [['tag', 'value3']] })
const result = matchFilter(filter, event)
expect(result).toEqual(false)
})
test('should return true when filter has tags that is present in the event', () => {
const filter = { '#tag1': ['foo'] }
const event = buildEvent({
id: '123',
kind: 1,
pubkey: 'abc',
created_at: 150,
tags: [
['tag1', 'foo'],
['tag2', 'bar'],
],
})
const result = matchFilter(filter, event)
expect(result).toEqual(true)
})
test('should return false when the event is before the filter since value', () => {
const filter = { since: 100 }
const event = buildEvent({ created_at: 50 })
const result = matchFilter(filter, event)
expect(result).toEqual(false)
})
test('should return true when the timestamp of event is equal to the filter since value', () => {
const filter = { since: 100 }
const event = buildEvent({ created_at: 100 })
const result = matchFilter(filter, event)
expect(result).toEqual(true)
})
test('should return false when the event is after the filter until value', () => {
const filter = { until: 100 }
const event = buildEvent({ created_at: 150 })
const result = matchFilter(filter, event)
expect(result).toEqual(false)
})
test('should return true when the timestamp of event is equal to the filter until value', () => {
const filter = { until: 100 }
const event = buildEvent({ created_at: 100 })
const result = matchFilter(filter, event)
expect(result).toEqual(true)
})
})
describe('matchFilters', () => {
test('should return true when at least one filter matches the event', () => {
const filters = [
{ ids: ['123'], kinds: [1], authors: ['abc'] },
{ ids: ['456'], kinds: [2], authors: ['def'] },
{ ids: ['789'], kinds: [3], authors: ['ghi'] },
]
const event = buildEvent({ id: '789', kind: 3, pubkey: 'ghi' })
const result = matchFilters(filters, event)
expect(result).toEqual(true)
})
test('should return true when event matches one or more filters and some have limit set', () => {
const filters = [
{ ids: ['123'], limit: 1 },
{ kinds: [1], limit: 2 },
{ authors: ['abc'], limit: 3 },
]
const event = buildEvent({
id: '123',
kind: 1,
pubkey: 'abc',
created_at: 150,
})
const result = matchFilters(filters, event)
expect(result).toEqual(true)
})
test('should return false when no filters match the event', () => {
const filters = [
{ ids: ['123'], kinds: [1], authors: ['abc'] },
{ ids: ['456'], kinds: [2], authors: ['def'] },
{ ids: ['789'], kinds: [3], authors: ['ghi'] },
]
const event = buildEvent({ id: '100', kind: 4, pubkey: 'jkl' })
const result = matchFilters(filters, event)
expect(result).toEqual(false)
})
test('should return false when event matches none of the filters and some have limit set', () => {
const filters = [
{ ids: ['123'], limit: 1 },
{ kinds: [1], limit: 2 },
{ authors: ['abc'], limit: 3 },
]
const event = buildEvent({
id: '456',
kind: 2,
pubkey: 'def',
created_at: 200,
})
const result = matchFilters(filters, event)
expect(result).toEqual(false)
})
})
describe('mergeFilters', () => {
test('should merge filters', () => {
expect(mergeFilters({ ids: ['a', 'b'], limit: 3 }, { authors: ['x'], ids: ['b', 'c'] })).toEqual({
ids: ['a', 'b', 'c'],
limit: 3,
authors: ['x'],
})
expect(
mergeFilters({ kinds: [1], since: 15, until: 30 }, { since: 10, kinds: [7], until: 15 }, { kinds: [9, 10] }),
).toEqual({ kinds: [1, 7, 9, 10], since: 10, until: 30 })
})
})
describe('getFilterLimit', () => {
test('should handle ids', () => {
expect(getFilterLimit({ ids: ['123'] })).toEqual(1)
expect(getFilterLimit({ ids: ['123'], limit: 2 })).toEqual(1)
expect(getFilterLimit({ ids: ['123'], limit: 0 })).toEqual(0)
expect(getFilterLimit({ ids: ['123'], limit: -1 })).toEqual(0)
})
test('should count the authors times replaceable kinds', () => {
expect(getFilterLimit({ kinds: [0], authors: ['alex'] })).toEqual(1)
expect(getFilterLimit({ kinds: [0, 3], authors: ['alex'] })).toEqual(2)
expect(getFilterLimit({ kinds: [0, 3], authors: ['alex', 'fiatjaf'] })).toEqual(4)
})
test('should return Infinity for authors with regular kinds', () => {
expect(getFilterLimit({ kinds: [1], authors: ['alex'] })).toEqual(Infinity)
})
test('should return Infinity for empty filters', () => {
expect(getFilterLimit({})).toEqual(Infinity)
})
test('empty tags return 0', () => {
expect(getFilterLimit({ '#p': [] })).toEqual(0)
})
})
})