Files
kindexr/internal/nostr/parser_test.go
T
enki 2e491e20c1 feat: Phase 1 — Nostr reader + Torznab API
Reader (internal/nostr):
- Connects to all configured relays via WebSocket
- Subscribes to kind 2003 (NIP-35) events since (now - backfill_days)
- Parses x/title/file/tracker/i/t tags into DB rows
- Reconnects with exponential backoff (5s → 5min) on disconnect

Torznab API (internal/torznab):
- GET /api?t=caps  — XML capabilities doc (exact shape Sonarr expects)
- GET /api?t=search — FTS5 full-text search over title+description
- All other t= types (tvsearch, movie, etc.) route to the same search handler
- API key auth via ?apikey= query param; 401 XML error on missing/bad key
- Magnet links built from info_hash + event tracker tags

DB (internal/db/queries.go):
- InsertTorrent with upsert + related rows (trackers, tags, files)
- Search with FTS5 + optional category filter (parent cat expands to range)
- GetAPIKey, CreateAPIKey, UpsertRelay, UpdateRelaySync/LastEvent

CLI (cmd/kindexr-cli):
- apikey create --label <name> [--visibility all|wot|curated]

Health endpoint now reports live relays_connected count.
2026-05-16 19:19:49 -07:00

158 lines
4.0 KiB
Go

package nostr_test
import (
"testing"
nostrlib "github.com/nbd-wtf/go-nostr"
"git.utn.lol/enki/kindexr/internal/nostr"
)
func makeEvent(tags nostrlib.Tags, content string) *nostrlib.Event {
return &nostrlib.Event{
ID: "aaaa111100000000000000000000000000000000000000000000000000000000",
Kind: nostrlib.KindTorrent,
PubKey: "bbbb222200000000000000000000000000000000000000000000000000000000",
CreatedAt: 1715000000,
Content: content,
Tags: tags,
}
}
func TestParseBasic(t *testing.T) {
event := makeEvent(nostrlib.Tags{
{"x", "abcdef1234abcdef1234abcdef1234abcdef1234"},
{"title", "Some.Show.S01E02.1080p.WEB-DL.x265-GRP"},
{"tracker", "udp://tracker.opentrackr.org:1337"},
{"i", "newznab:5040"},
{"t", "tv"},
}, "a description")
rec, err := nostr.Parse(event)
if err != nil {
t.Fatalf("Parse error: %v", err)
}
if rec.InfoHash != "abcdef1234abcdef1234abcdef1234abcdef1234" {
t.Errorf("wrong info_hash: %q", rec.InfoHash)
}
if rec.Title != "Some.Show.S01E02.1080p.WEB-DL.x265-GRP" {
t.Errorf("wrong title: %q", rec.Title)
}
if len(rec.Trackers) != 1 || rec.Trackers[0] != "udp://tracker.opentrackr.org:1337" {
t.Errorf("wrong trackers: %v", rec.Trackers)
}
if rec.NewznabCat == nil || *rec.NewznabCat != 5040 {
t.Errorf("wrong newznab_cat: %v", rec.NewznabCat)
}
if len(rec.Tags) != 1 || rec.Tags[0] != "tv" {
t.Errorf("wrong tags: %v", rec.Tags)
}
}
func TestParseTitleFallback(t *testing.T) {
event := makeEvent(nostrlib.Tags{
{"x", "abcdef1234abcdef1234abcdef1234abcdef1234"},
}, "Title From Content\nmore description")
rec, err := nostr.Parse(event)
if err != nil {
t.Fatalf("Parse error: %v", err)
}
if rec.Title != "Title From Content" {
t.Errorf("expected title from content first line, got %q", rec.Title)
}
}
func TestParseMissingInfoHash(t *testing.T) {
event := makeEvent(nostrlib.Tags{
{"title", "Some Torrent"},
}, "")
_, err := nostr.Parse(event)
if err == nil {
t.Fatal("expected error for missing info_hash")
}
}
func TestParseInvalidInfoHash(t *testing.T) {
event := makeEvent(nostrlib.Tags{
{"x", "not-a-hash"},
{"title", "Some Torrent"},
}, "")
_, err := nostr.Parse(event)
if err == nil {
t.Fatal("expected error for invalid info_hash")
}
}
func TestParseFileSizes(t *testing.T) {
event := makeEvent(nostrlib.Tags{
{"x", "abcdef1234abcdef1234abcdef1234abcdef1234"},
{"title", "My Torrent"},
{"file", "file1.mkv", "1000000"},
{"file", "file2.srt", "5000"},
}, "")
rec, err := nostr.Parse(event)
if err != nil {
t.Fatalf("Parse error: %v", err)
}
if rec.SizeBytes == nil {
t.Fatal("expected SizeBytes to be set")
}
if *rec.SizeBytes != 1005000 {
t.Errorf("expected SizeBytes=1005000, got %d", *rec.SizeBytes)
}
if len(rec.Files) != 2 {
t.Errorf("expected 2 files, got %d", len(rec.Files))
}
}
func TestParseITagIDs(t *testing.T) {
event := makeEvent(nostrlib.Tags{
{"x", "abcdef1234abcdef1234abcdef1234abcdef1234"},
{"title", "Breaking Bad"},
{"i", "imdb:tt0903747"},
{"i", "tmdb:tv:1396"},
{"i", "tvdb:81189"},
{"i", "tcat:video,tv"},
}, "")
rec, err := nostr.Parse(event)
if err != nil {
t.Fatalf("Parse error: %v", err)
}
if rec.ImdbID != "tt0903747" {
t.Errorf("wrong ImdbID: %q", rec.ImdbID)
}
if rec.TmdbID != "tv:1396" {
t.Errorf("wrong TmdbID: %q", rec.TmdbID)
}
if rec.TvdbID != "81189" {
t.Errorf("wrong TvdbID: %q", rec.TvdbID)
}
// tcat should set NewznabCat to 5000 (video,tv)
if rec.NewznabCat == nil || *rec.NewznabCat != 5000 {
t.Errorf("expected NewznabCat=5000, got %v", rec.NewznabCat)
}
}
func TestParseNewznabOverridesTcat(t *testing.T) {
event := makeEvent(nostrlib.Tags{
{"x", "abcdef1234abcdef1234abcdef1234abcdef1234"},
{"title", "Some UHD Show"},
{"i", "tcat:video,tv"},
{"i", "newznab:5045"},
}, "")
rec, err := nostr.Parse(event)
if err != nil {
t.Fatalf("Parse error: %v", err)
}
// newznab: tag takes precedence over tcat:
if rec.NewznabCat == nil || *rec.NewznabCat != 5045 {
t.Errorf("expected NewznabCat=5045, got %v", rec.NewznabCat)
}
}