test: cover zap helpers and newsletter data
- useZapTotals: zapTargetKey (regular id vs addressable coord) and zapReceiptSats (amount tag -> bolt11 -> description JSON -> 0, malformed safe) - newsletterIssues: schema, newest-first ordering, publish hashtags
This commit is contained in:
parent
81eeba7797
commit
bcd99994a9
2 changed files with 121 additions and 0 deletions
85
src/hooks/useZapTotals.test.ts
Normal file
85
src/hooks/useZapTotals.test.ts
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import type { NostrEvent } from '@nostrify/nostrify';
|
||||||
|
import { zapTargetKey, zapReceiptSats } from './useZapTotals';
|
||||||
|
|
||||||
|
/** Minimal event factory — only the fields these helpers read. */
|
||||||
|
function ev(partial: Partial<NostrEvent> & Pick<NostrEvent, 'kind' | 'pubkey' | 'id'>): NostrEvent {
|
||||||
|
return {
|
||||||
|
content: '',
|
||||||
|
created_at: 0,
|
||||||
|
sig: '',
|
||||||
|
tags: [],
|
||||||
|
...partial,
|
||||||
|
} as NostrEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('zapTargetKey', () => {
|
||||||
|
it('uses the event id for regular (non-addressable) events', () => {
|
||||||
|
const e = ev({ kind: 1, pubkey: 'aa', id: 'abc123' });
|
||||||
|
expect(zapTargetKey(e)).toBe('abc123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the addressable coordinate for kind 30000-39999', () => {
|
||||||
|
const e = ev({
|
||||||
|
kind: 30818,
|
||||||
|
pubkey: 'deadbeef',
|
||||||
|
id: 'ignored',
|
||||||
|
tags: [['d', 'tilcayo']],
|
||||||
|
});
|
||||||
|
expect(zapTargetKey(e)).toBe('30818:deadbeef:tilcayo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles an addressable event with a missing d tag', () => {
|
||||||
|
const e = ev({ kind: 30818, pubkey: 'deadbeef', id: 'ignored' });
|
||||||
|
expect(zapTargetKey(e)).toBe('30818:deadbeef:');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('zapReceiptSats', () => {
|
||||||
|
it('reads the amount tag (millisats -> sats)', () => {
|
||||||
|
const receipt = ev({
|
||||||
|
kind: 9735,
|
||||||
|
pubkey: 'lnurl-server',
|
||||||
|
id: 'r1',
|
||||||
|
tags: [['amount', '21000']],
|
||||||
|
});
|
||||||
|
expect(zapReceiptSats(receipt)).toBe(21);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to the bolt11 invoice amount', () => {
|
||||||
|
const receipt = ev({
|
||||||
|
kind: 9735,
|
||||||
|
pubkey: 'lnurl-server',
|
||||||
|
id: 'r2',
|
||||||
|
tags: [['bolt11', 'lnbc210n1pvjluezpp5abcdef']],
|
||||||
|
});
|
||||||
|
expect(zapReceiptSats(receipt)).toBe(21);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to the zap request in the description tag', () => {
|
||||||
|
const receipt = ev({
|
||||||
|
kind: 9735,
|
||||||
|
pubkey: 'lnurl-server',
|
||||||
|
id: 'r3',
|
||||||
|
tags: [
|
||||||
|
['description', JSON.stringify({ tags: [['amount', '1000000']] })],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(zapReceiptSats(receipt)).toBe(1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 0 when no amount can be determined', () => {
|
||||||
|
const receipt = ev({ kind: 9735, pubkey: 'lnurl-server', id: 'r4' });
|
||||||
|
expect(zapReceiptSats(receipt)).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 0 for a malformed description instead of throwing', () => {
|
||||||
|
const receipt = ev({
|
||||||
|
kind: 9735,
|
||||||
|
pubkey: 'lnurl-server',
|
||||||
|
id: 'r5',
|
||||||
|
tags: [['description', '{not json']],
|
||||||
|
});
|
||||||
|
expect(zapReceiptSats(receipt)).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
36
src/lib/newsletterIssues.test.ts
Normal file
36
src/lib/newsletterIssues.test.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { NEWSLETTER_ISSUES } from './newsletterIssues';
|
||||||
|
|
||||||
|
describe('NEWSLETTER_ISSUES data', () => {
|
||||||
|
it('has at least one issue', () => {
|
||||||
|
expect(NEWSLETTER_ISSUES.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('every issue satisfies the schema', () => {
|
||||||
|
for (const issue of NEWSLETTER_ISSUES) {
|
||||||
|
expect(typeof issue.number).toBe('number');
|
||||||
|
expect(issue.date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
||||||
|
expect(issue.vibe.trim().length).toBeGreaterThan(0);
|
||||||
|
expect(issue.communityNote.trim().length).toBeGreaterThan(0);
|
||||||
|
expect(issue.nostrText.trim().length).toBeGreaterThan(0);
|
||||||
|
expect(Array.isArray(issue.updates)).toBe(true);
|
||||||
|
expect(issue.updates.length).toBeGreaterThan(0);
|
||||||
|
for (const u of issue.updates) {
|
||||||
|
expect(u.title.trim().length).toBeGreaterThan(0);
|
||||||
|
expect(u.body.trim().length).toBeGreaterThan(0);
|
||||||
|
expect(u.tag.trim().length).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is ordered newest-first (descending issue numbers)', () => {
|
||||||
|
for (let i = 1; i < NEWSLETTER_ISSUES.length; i++) {
|
||||||
|
expect(NEWSLETTER_ISSUES[i - 1].number).toBeGreaterThan(NEWSLETTER_ISSUES[i].number);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('includes the d-tagged hashtags used to publish issues', () => {
|
||||||
|
const withNewsTag = NEWSLETTER_ISSUES.filter((i) => i.nostrText.includes('#skatespotsnews'));
|
||||||
|
expect(withNewsTag.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue