Summary
Add focused unit and hook tests for the video publish pipeline: construction of kind 34236 (NIP-71) events, kind 16 repost tags, and the shared useNostrPublish mutation (sign + relay publish). This complements #295 / upload hook coverage — upload prepares blobs; these paths decide what gets signed and sent.
Problem
src/hooks/usePublishVideo.ts contains non-exported helpers (generateVineId, buildImetaTag) and tag assembly that encodes NIP-71 rules (required tags, imeta, hashtags, alt, MIME guessing). Regressions only surface after publish or when relays/indexers reject events.
useRepostVideo (same file) builds kind 16 tags (a, p, k, client) with no direct tests.
useNostrPublish (src/hooks/useNostrPublish.ts) is the single mutation used across publish/like/delete/report flows: behaviour when logged out, signEvent, nostr.event timeout, and optional client tag injection must stay stable.
Today these hooks are only mocked from page/component tests (VideoPage, etc.), so hook behaviour is not regression-protected.
Expected outcome
- Pure logic is unit-tested — preferably by extracting
buildImetaTag, vine-id generation, and “build tags for publish/repost” into a small src/lib/ module if extraction keeps the PR reviewable; acceptable alternative: test via mutationFn invocation with heavily mocked dependencies if extraction is deferred.
useNostrPublish covered with renderHook + mocks: logged-in success path, missing user/signer throws "User is not logged in", nostr.event failure surfaces correctly.
What needs to be tested
A. Pure helpers (preferred extracted module)
buildImetaTag: correct ordering/subset of url, m, dim, blurhash, image, duration, size, x; empty/minimal metadata still yields valid ['imeta', ...] shape per implementation.
generateVineId (if kept): deterministic enough for tests (prefix / uniqueness pattern — assert shape, not wall-clock randomness).
B. usePublishVideo mutation (mock useNostrPublish)
- Default
duration (6), dimensions (480x480), title fallback 'Untitled'.
published_at tag present (numeric string).
- Hashtags:
#Foo stripped and lowercased for t tags.
- GIF vs MP4 MIME inference from
videoUrl suffix.
publishEvent called with kind: SHORT_VIDEO_KIND, expected tags including d, title, imeta, optional alt, client.
C. useRepostVideo
publishEvent called with kind: 16, empty content, tags a, p, k, client matching ${SHORT_VIDEO_KIND}:${pubkey}:${vineId} pattern.
D. useNostrPublish
- When no user/signer: mutation rejects with the existing error message.
- When signed in:
signer.signEvent receives merged tags; on HTTPS, client tag may be added with hostname (match current location.protocol behaviour — mock location in vi.stubGlobal if needed).
nostr.event called with timeout signal behaviour as implemented (AbortSignal.timeout(5000)).
Mocking strategy
vi.mock('@/hooks/useCurrentUser'), vi.mock('@nostrify/react') returning { nostr: { event: vi.fn() } }.
- Mock
signer.signEvent to return a deterministic signed event shape (id/pubkey/sig can be fixed hex-like strings).
- Follow patterns from
src/hooks/useProfileStats.test.ts / useVideoUpload.test.ts.
Acceptance criteria
Related files
| File |
Role |
src/hooks/usePublishVideo.ts |
Publish + repost mutations, buildImetaTag |
src/hooks/useNostrPublish.ts |
Shared publish mutation |
src/types/video.ts |
SHORT_VIDEO_KIND, VideoMetadata |
Notes
- Scope is publish/repost + nostr publish only — not
useVideoUpload (covered by #295).
- Avoid tightening NIP-71 semantics beyond what tests document today; refactors should preserve observable tag output.
Summary
Add focused unit and hook tests for the video publish pipeline: construction of kind 34236 (NIP-71) events, kind 16 repost tags, and the shared
useNostrPublishmutation (sign + relay publish). This complements #295 / upload hook coverage — upload prepares blobs; these paths decide what gets signed and sent.Problem
src/hooks/usePublishVideo.tscontains non-exported helpers (generateVineId,buildImetaTag) and tag assembly that encodes NIP-71 rules (required tags,imeta, hashtags,alt, MIME guessing). Regressions only surface after publish or when relays/indexers reject events.useRepostVideo(same file) builds kind 16 tags (a,p,k,client) with no direct tests.useNostrPublish(src/hooks/useNostrPublish.ts) is the single mutation used across publish/like/delete/report flows: behaviour when logged out,signEvent,nostr.eventtimeout, and optionalclienttag injection must stay stable.Today these hooks are only mocked from page/component tests (
VideoPage, etc.), so hook behaviour is not regression-protected.Expected outcome
buildImetaTag, vine-id generation, and “build tags for publish/repost” into a smallsrc/lib/module if extraction keeps the PR reviewable; acceptable alternative: test viamutationFninvocation with heavily mocked dependencies if extraction is deferred.useNostrPublishcovered withrenderHook+ mocks: logged-in success path, missing user/signer throws"User is not logged in",nostr.eventfailure surfaces correctly.What needs to be tested
A. Pure helpers (preferred extracted module)
buildImetaTag: correct ordering/subset ofurl,m,dim,blurhash,image,duration,size,x; empty/minimal metadata still yields valid['imeta', ...]shape per implementation.generateVineId(if kept): deterministic enough for tests (prefix / uniqueness pattern — assert shape, not wall-clock randomness).B.
usePublishVideomutation (mockuseNostrPublish)duration(6),dimensions(480x480),titlefallback'Untitled'.published_attag present (numeric string).#Foostripped and lowercased forttags.videoUrlsuffix.publishEventcalled withkind: SHORT_VIDEO_KIND, expectedtagsincludingd,title,imeta, optionalalt,client.C.
useRepostVideopublishEventcalled withkind: 16, emptycontent, tagsa,p,k,clientmatching${SHORT_VIDEO_KIND}:${pubkey}:${vineId}pattern.D.
useNostrPublishsigner.signEventreceives merged tags; on HTTPS,clienttag may be added with hostname (match currentlocation.protocolbehaviour — mocklocationinvi.stubGlobalif needed).nostr.eventcalled with timeout signal behaviour as implemented (AbortSignal.timeout(5000)).Mocking strategy
vi.mock('@/hooks/useCurrentUser'),vi.mock('@nostrify/react')returning{ nostr: { event: vi.fn() } }.signer.signEventto return a deterministic signed event shape (id/pubkey/sig can be fixed hex-like strings).src/hooks/useProfileStats.test.ts/useVideoUpload.test.ts.Acceptance criteria
src/lib/…PublishTags.test.ts(if extracted) and/orsrc/hooks/usePublishVideo.test.ts,src/hooks/useNostrPublish.test.ts.npm run testandnpx tsc --noEmitpass locally.console.logon success inuseNostrPublishif tests make noise — preferably align withdebugLogin a minimal follow-up if out of scope.Related files
src/hooks/usePublishVideo.tsbuildImetaTagsrc/hooks/useNostrPublish.tssrc/types/video.tsSHORT_VIDEO_KIND,VideoMetadataNotes
useVideoUpload(covered by #295).