2e491e20c1
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.
158 lines
4.0 KiB
Go
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)
|
|
}
|
|
}
|