diff --git a/src/hooks/useSpells.test.ts b/src/hooks/useSpells.test.ts index 9658772..638f2d8 100644 --- a/src/hooks/useSpells.test.ts +++ b/src/hooks/useSpells.test.ts @@ -27,13 +27,22 @@ function parsedSpell(overrides: Partial): ParsedSpell { describe('resolveTimestamp', () => { it('resolves a relative duration against now', () => { - const now = Math.floor(Date.now() / 1000); - expect(resolveTimestamp('7d')).toBeCloseTo(now - 7 * 86400, -1); + const before = Math.floor(Date.now() / 1000) - 7 * 86400; + const result = resolveTimestamp('7d'); + const after = Math.floor(Date.now() / 1000) - 7 * 86400; + // A range, not toBeCloseTo against a single captured `now` — a slow test + // runner or wall-clock skew between the two Date.now() calls could + // otherwise make this flaky. + expect(result).toBeGreaterThanOrEqual(before); + expect(result).toBeLessThanOrEqual(after); }); it('resolves "now"', () => { - const now = Math.floor(Date.now() / 1000); - expect(resolveTimestamp('now')).toBeCloseTo(now, -1); + const before = Math.floor(Date.now() / 1000); + const result = resolveTimestamp('now'); + const after = Math.floor(Date.now() / 1000); + expect(result).toBeGreaterThanOrEqual(before); + expect(result).toBeLessThanOrEqual(after); }); it('resolves an absolute unix timestamp', () => { @@ -88,6 +97,19 @@ describe('encodeSpellTags / parseSpell round-trip', () => { }); }); +describe('parseSpell', () => { + it('drops non-integer and negative k tags — kinds are non-negative integers', () => { + const event = spellEvent([ + ['cmd', 'REQ'], + ['k', '1'], + ['k', '1.5'], + ['k', '-1'], + ['k', 'not-a-number'], + ]); + expect(parseSpell(event).kinds).toEqual([1]); + }); +}); + describe('isValidTagLetter', () => { it('accepts a single letter', () => { expect(isValidTagLetter('t')).toBe(true); diff --git a/src/hooks/useSpells.ts b/src/hooks/useSpells.ts index e09a458..90d54e8 100644 --- a/src/hooks/useSpells.ts +++ b/src/hooks/useSpells.ts @@ -107,7 +107,11 @@ export function encodeSpellTags(input: SpellInput): string[][] { } export function parseSpell(event: NostrEvent): ParsedSpell { - const kinds = tagValues(event, 'k').map(Number).filter((n) => Number.isFinite(n)); + // Nostr kinds are non-negative integers — a relay-sourced spell claiming + // e.g. "1.5" or "-1" would otherwise pass through into a malformed filter. + const kinds = tagValues(event, 'k') + .map(Number) + .filter((n) => Number.isInteger(n) && n >= 0); const authorsTag = event.tags.find(([name]) => name === 'authors'); const tagFilterTag = event.tags.find(([name]) => name === 'tag'); const limitValue = tagValue(event, 'limit');